p5js : Gulp + Browserify + Babel で p5を使えるテンプレート

p5.jsがWEBGL対応し、Javascriptでできる表現の幅が広がりそうです。

本家プロジェクトではEditorとかいろいろアプリのScaffolding的なものも用意されているのですが、いわゆるフロントエンド系の環境にならって個人でアプリプロジェクトのテンプレを作ってみました。

gulpによる自動化

下記のように、npm スクリプトによるJavascriptのビルドなども例としてはあるのですが、今回はgulpで自動化タスクを記述しました。

maxmechanic/simple-p5js-browserify · GitHub

タスクにしておいたほうが取り回しがいいのと、後々拡張しやすいと思ったので。 Gruntでもいいのですが、せっかくなのでgulpにしてみました。

あとp5.jsのbowerによる配信は非公式かつ、requireでの解決ができません。 npmならモジュールなので、requireで読み込むことができます。ES6のモジュールベースで作るプロジェクトと相性がよいのでnpmの配信を使います。

gulpfileは下記で公開されているものがよかったので、ベースにしました。

まずはuglifysource mapなどを入れてJavascriptビルドを盛りつつ、いらないタスクを削ります。

Babel

続いて本命のBabelです。

Processingは作りとしてはOpenGL的な命令セットで、明示的にオブジェクト指向のようなパラダイムをもっていませんが、言語としてはサポートしているのでコードが大きくなってくるとやはりオブジェクト設計をしたくなります。そしてファイルも分割したいと。

JavascriptではCommonJS、AMDなど歴史的にいくつかのモジュール構文が存在しますが、今後の筆頭はやはりES6だろうということでBabelを利用してこれを実現していきます。

最初は普通にglup-babelでES5に変換してみたのですが、nodeのつもりでCommonJSのrequire構文に変換されるためフロントエンド環境では動きません。

gulp.task('js', function() {
    return gulp.src(config.js.files)
        .pipe($.babel({
            presets: ['es2015']
        }))
        .pipe(gulp.dest(config.js.output.directory));
});

ブラウザでBabelによる変換後のコードを動かすためにBrowserifyと合わせてビルドする必要があるのですが、babelifyの方が一緒にタスク化しやすかったので、 今回はbabelifyを利用します。

Browserify + Babelify

必要なモジュールをインストール。

$ npm install --save-dev browserify babelify babel-preset-es2015

変換は下記のワンライナーで実行できます。

var bundler = browserify('./app/js/appljs').transform('babelify', { presets: ["es2015"]} );

この結果のままだとgulpでpipeできないので、gulpのstreamで扱えるようにしてpipeできるようにします。

いくつか方法があったので、メモしておきます。今回作成したテンプレートでは①を使っています。

① vinyl-source-stream, vinyl-bufferを利用する
gulp.task('js', function() {
    var bundler = browserify('./app/js/app.js').transform('babelify', {presets: ["es2015"]});

    return bundler.bundle()
        .on('error', function(error) {console.log(error);})
        .pipe(source('app.js'))
        .pipe(buffer())
        .pipe(gulp.dest('./dist/js2'));
});

(参考) gulp & browserify (+watchify +babelify) · GitHub

② vinyl-transformを利用する(動作しない)
gulp.task('js', function() {
    var browserified = transform(function(filename) {
        var b = browserify(filename);
        b.transform('babelify', { presets: ["es2015"]});
        return b.bundle();
    });

    return gulp.src('./app/js/app.js')
        .pipe(browserified)
        .pipe(gulp.dest('./dist'));
});

(参考) Gulp+Browserifyでsrc/**/*.jsをdist/**/*.jsにする | 高橋文樹.com

しかしvinyl-transformを利用したコードは、下記にあるように現在の最新(1.0.0)だと動きません。

③ through2を利用する

vinyl-transformが動作しないissueで言及されていますが、下記にあるようにthrough2を使って書き直す方法もあります。

gulp.task('js', function() {
    return gulp.src('./app/js/app.js')
        .pipe(through2.obj(function (file, enc, next){
            console.log('browserify: ' + file.path);
            browserify(file.path)
                .transform('babelify', {presets: ["es2015"]})
                .bundle(function(err, res){
                    // assumes file.contents is a Buffer
                    if (err) {console.log(err);}

                    file.contents = res;
                    next(null, file);
                });
        }))
        .pipe(gulp.dest('./dist'));
});

(参考) exception: browserify + vinyl-transform · Issue #1198 · substack/node-browserify · GitHub

まとめ

というわけで、JSのビルドができました。後はeslintがes2015に対応するようにしたり、p5.jsモジュールでサンプルを実装したりと細々作業を行って…完成!

できたテンプレートはこちらになります。