Browserify, czyli moduł Node.js w przeglądarce

Pisałem ostatnio o TypeScripcie. Artykuł został napisany w oparciu o materiały dostępne online oraz o własne doświadczenia. W ramach zdobywania tychże napisałem dwa proste testowe projekty; jeden miał służyć jako zależność, a drugi jako konsument tejże zależności. Na koniec przepisałem jeden z moich starych projektów w JS całkowicie na TypeScript. Problemem jednak było zbudowanie wersji modułu, którą dałoby się uruchomić w przeglądarce.

Na początek trochę historii. W 2014 roku napisałem narzędzie o nazwie uAccess, umożliwiające zarządzanie uprawnieniami dostępu bazującymi na rolach lub indywidualnych uprawnienaich (RBAC oraz ACL). Narzędzie powstało w wersji PHP, a później zostało również przeportowane do JavaScriptu, na początku tylko w wersji dla przeglądarek, a później również dopisałem wersję Node.js. Ponieważ jednak wersja przeglądarkowa była pierwsza, nie miałem żadnego problemu z ładowaniem poszczególnych modułów. Całość kodu i tak byłą łączona w jeden plik i jedyne, co musiałem zrobić, by napisać wersję Node.js, to zastąpienie umieszczania narzędzia na obiekcie window eksportem typowym dla konwencji CommonJS. Czyli kaszka z mleczkiem.

Problem pojawił się po przepisaniu narzędzia w TypeScripcie. Ponieważ zacząłem od wersji Node.js, nie zastanawiałem się nad łączeniem poszczególnych plików w jeden. Dodatkowo, TypeScript co prawda przetwarza kod na JS, ale mimo wszystko używa konwencji ładowania modułów niekoniecznie typowych dla przeglądarek. Niby jest możliwe ich użycie poprzez RequireJS lub SystemJS, ale szczerze mówiąc, mało uśmiechało mi się zakładanie, że użytkownik będzie specjalnie dla uAccess zaczynał korzystać z któregoś z wymienionych narzędzi do ładowania modułów. I tu właśnie wszedł do akcji Browserify.

Browserify to ciekawe narzędzie, które, zgodnie z tym, co sugeruje jego nazwa, jest w stanie „uprzeglądarkowić” moduł napisany w Node.js. Problemem jest tutaj głównie to, że Node korzysta z wielu plików, stanowiących niezależne moduły, których ładowanie jest diametralnie inne od tego, z którym mamy do czynienia w przeglądarce. Przeglądarka nie jest w stanie dynamicznie doładowywać kodu w oddzielnych plikach bez wykonywania wielu dodatkowych żądań do serwera. Browserify łączy wszystkie przetwarzane pliki wraz z ich zależnościami w jeden plik, dodając jednocześnie własny mechanizm ładowania modułów, umożliwiający odpalenie wszystkiego razem.

A zatem do rzeczy. Pierwsze, co można zrobić, to przetestowanie Browserify z linii komend. Narzędzie instalujemy poleceniem:

npm install -g browserify

Następnie możemy przekazać do niego skrypt JS i poprosić o stworzenie pliku wynikowego, który łączył będzie w sobie wszystkie zależności tego pliku:

browserify src/uAccess.js -o dist/uAccess.js

Niestety, samo stworzenie pliku wynikowego jeszcze nie umieści nam w globalnej przestrzeni nazw żadnego nowego obiektu. Na potrzeby Browserify utworzyłem zatem plik index.ts:

import {uAccess} from "./uAccess";
export = uAccess;
if (typeof window !== "undefined" && window) {
    window["Mingos"] = window["Mingos"] || {};
    window["Mingos"].uAccess = uAccess;
}

Kod co prawda jest napisany w TypeScripcie, ale chyba nie trzeba tłumaczyć, co robi. Wynikowy kod JS jest niemal identyczny. Różni się tylko ładowaniem modułu przez funkcję require() oraz eksportem przez module.exports. Plik może zatem być użyty zarówno w module Node.js, jak i w przeglądarce. W tym ostatnim przypadku, gdzie dostępny jest obiekt window, w globalnej przestrzeni nazw zostanie umieszczony dodatkowo obiekt, który na potrzeby Node.js eksportujemy. Cały moduł przetwarzamy jeszcze raz za pomocą Browserify:

browserify src/index.js -o dist/uAccess.js

Zawartość pliku wynikowego można spokojnie wkleić w narzędziach deweloperskich przeglądarki i faktycznie będzie tam dostępny obiekt Mingos.uAccess. Ja jednak wolałem pójść o krok dalej. Nie będę przecież za każdym razem wołał Browserify z linii poleceń. Ponieważ jest to krok konieczny do zbudowania projektu, umieściłem go w zadaniu Gulpa. Zacząłem od zainstalowania odpowiedniego modułu:

npm install --save-dev gulp-browserify

Zadanie przygotowujące kod na potrzeby przeglądarki bazuje na plikach uprzednio skompilowanych z TypeScriptu oraz dodatkowo zapisuje wersję zminifikowaną. Nie będę na potrzeby wpisu okrajał tego i wkleję kod w takiej postaci, w jakiej na chwilę obecną występuje w repozytorium. Wygląda to następująco:

var gulp = require("gulp");
var browserify = require("gulp-browserify");
var rename = require("gulp-rename");
var uglify = require("gulp-uglify");

gulp.task("dist", ["ts:dist", "bower"], function() {
    gulp.src("lib/index.js")
        .pipe(browserify())
        .pipe(rename("uAccess.js"))
        .pipe(gulp.dest("dist"))
        .pipe(uglify())
        .pipe(rename("uAccess.min.js"))
        .pipe(gulp.dest("dist"));
});

Jak widać, użycie Browserify w zadaniu Gulpa jest dziecinnie proste i nie wymaga nawet podawania żadnych dodatkowych argumentów ani żadnej konfiguracji.


Komentarze

Agmar

2016-01-05, 12:13

Muszę przyznać, że pierwszy raz się z tym spotkałem. Muszę spróbować się z tym "pobawić". Myślisz, że nawet osoby które nie miały zbyt dużego doświadczenia z programowaniem sobie poradzą?

Dominik Marczuk

2016-01-05, 12:29

Ciężko mi się jednoznacznie ustosunkować do pytania. Na pewno pisząc tego posta czynię pewne założenia: że odbiorca ma zainstalowanego Node.js, że jest w miarę obeznany z użyciem npm. Jeśli takie rzeczy dla Ciebie nie są nowe, to na pewno sobie poradzisz bez problemu.


Napisz komentarz


Szukaj wpisów


Chmura tagów