タグ: browserify

  • WordPress等のCMSがjQuery等を出力するとき、browserifyで上手いことやる。

    最近、jsでrequireが無いと生きれない身体になってしまいました。

    CoffeeScriptから卒業して、ES6でコードを書くようにもしています。ES6のClass構文とかアロー関数が無いと人生辛くなってきました。

    そんなわけで自分はbrowserifyを使ってコードを書いてます。requireを良い感じにコンパイルして1つのファイルにまとめてくれる奴です。

    そんなときに結構困るのがwp_head()や、wp_footer()からscriptタグでjQueryunderscore.jsBackbone.js等の有名処のライブラリが出力される場合があることです。

    functions.phpで無理矢理消しても良いのですがそれは筋が悪そうなので、browserify-shimを使って指定されたライブラリはグローバル変数のものを使うようにします。

    そのコードがこんな感じ。

    gist: https://gist.github.com/torounit/0da591b62d03187b4641

    package.json

    {
      "browserify-shim": {
        "jquery": "global:jQuery",
        "underscore": "global:_",
        "backbone": "global:Backbone"
      },
      "devDependencies": {
        "babelify": "^6.1.3",
        "browserify": "^11.0.0",
        "browserify-shim": "^3.8.10",
        "debowerify": "^1.3.1",
        "gulp": "^3.9.0",
        "gulp-notify": "^2.2.0",
        "vinyl-buffer": "^1.0.0",
        "vinyl-source-stream": "^1.1.0",
        "watchify": "^3.3.0"
      },
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "Toro_Unit",
      "license": "ISC"
    }
    

    gulpfile.js

    'use strict';
    
    // ==================================
    //
    // Config.
    //
    // ==================================
    
    var config = {
        browserify: {
            bundleOption: {
                cache: {}, packageCache: {}, fullPaths: false, //for watchfy.
                debug: true,
                entries: './src/scripts/app.js',
                extensions: ['js', 'jsx'],
            },
            dest: './dist/scripts/',
            filename: 'app.js'
    
        }
    }
    
    // ==================================
    //
    // Load modules.
    //
    // ==================================
    
    var source = require('vinyl-source-stream');
    var browserify = require('browserify');
    var watchify = require('watchify');
    var gulp = require('gulp');
    var notify = require("gulp-notify");
    
    var handleErrors = function() {
        var args = Array.prototype.slice.call(arguments);
        notify.onError({
            title: "Compile Error",
            message: "<%= error.message %>"
        }).apply(this, args);
        this.emit('end');
    };
    
    // ==================================
    //
    // Compile JavaScripts.
    //
    // ==================================
    
    gulp.task('setWatch', function () {
        global.isWatching = true;
    });
    
    gulp.task('browserify', function () {
    
        var b = browserify(config.browserify.bundleOption)
            .transform('babelify')
            .transform("browserify-shim")
            .transform("debowerify");
    
        var bundle = function () {
            b.bundle().on('error', handleErrors)
                .pipe(source(config.browserify.filename))
                .pipe(gulp.dest(config.browserify.dest));
        };
        if (global.isWatching) {
            var bundler = watchify(b);
            bundler.on('update', bundle);
        }
        bundle();
    });
    
    gulp.task('watchify', ['setWatch', 'browserify']);
    

    タスク

    $ gulp browserify //コンパイル
    $ gulp watchify //ファイルの変更を監視して自動コンパイル

    といった格好になります。

    browserify-shim

    browserifyには、transformというAPIがありまして、ここにプラグインを突っ込むことが出来ます。

    アプリケーションコード上のJSから呼び出せるのは通常、node_modulesにインストールされているものか、ファイル名で指定されたJSのみですが、require(‘jquery’) のようなコードを書いたときに、node_modulesではなく、グローバル変数のjQueryを呼んでくることが出来るようになります。

    package.jsonにbrowserify-shimという項目を作ってこんな感じで指定すれば動作します。

    {
      "browserify-shim": {
        "jquery": "global:jQuery",
        "underscore": "global:_",
        "backbone": "global:Backbone"
      }
    }

    debowerify

    debowerifyは、bowerでインストールしたライブラリをrequireやimportできるようにするプラグインです。

    最近オワコンと言われているbowerですが、jQueryプラグインなどをnpmでインストールして持ってくると、そこからnode_modules内のjqueryを取ってきてしまうと言う問題があります。shimの方の設定が悪いのかどうなのか解らないのですが、そんなわけでその手のものは仕方ないのでbowerで持ってきてrequire(import)します。

    npmだけで管理はしたいのでどうにかしたいところです。最悪、jQueryプラグインも普通にscriptタグで読み込ませるコトもたまにやります。

     

    まとめ

    とりあえずこれで、ライブラリの多重インストールを避けつつインストールが出来ます。後はテーマ側で

    $js_path = get_template_directory_uri() . 'dist/scripts/app.js';
    wp_enqueue_script( 'app', $js_path, array( 'underscore', 'jquery' ), '1.0.0', true );
    

    とかやればとりあえずは終了です。

    まぁ、WordPressのunderscore.jsとかBackbone.jsバージョンが古い問題とかもあるんですけどね。。。

     

  • Gruntでbrowserify使ってCoffeescriptをコンパイルする。

    なんかCoffeescriptをGruntでコンパイルして、concatしたりとかだるいなーってずーっと思ってました。
    Classとかを他のファイルから参照しようとするとwindow.classNameとか@classNameとかにしないといけないですし、ファイル名順に結合するので、アルファベット順で後ろにくるクラスを読もうとするとエラー吐いたり、いまいち依存関係が見えなかったり。

    小規模な開発であれば問題ないのですが、Coffeescriptで大量にClassを作ったりすると、結構大きな問題になってきます。かといってrequire.jsとか面倒くさい。

    というわけで、最近流行のbrowserifyを使ってみました。

    npmとbrowserifyを使ったクライアントサイドのウェブアプリ開発
    Browserify: それはrequire()を使うための魔法の杖

    ざっくり言うと、ブラウザで、node.jsのようなrequireが使えるようになるツールです。

    たとえば、以下のようなHoge.coffeeがあるとすると

    class Hoge
        piyo: ->
    
    module.exports = Hoge
    

    以下のコードで、読み込んでくることができるという代物です。

    Hoge = require("./Hoge")
    hoge = new Hoge()
    

    debowerifyとかをtransformに指定しておくと、bowerでインストールしたモジュールなんかも取ってこれるので、大変素敵です。

    $ = require("jquery")

    それをgruntでファイル監視して自動化するようにしたのがこちらです。

    module.exports = function(grunt) {
      grunt.initConfig({
        dir: {
          coffee: 'coffee',
          javascripts: 'javascripts'
        },
        esteWatch: {
          options: {
            dirs: [ 'coffee/**' ],
            extensions: ['coffee'],
          }
          coffee: function(filepath) {
            return ["browserify"];
          }
        },
        browserify: {
          dist: {
            files: {
              'javascripts/all.js': ['coffee/all.coffee']
            },
            options: {
              transform: ['coffeeify', "debowerify"],
              browserifyOptions: {
                extensions: ['.coffee']
              }
            }
          }
        }
      });
      grunt.loadNpmTasks('grunt-browserify');
      grunt.registerTask('default', ['esteWatch']);
    };

    transformにcoffeeifyを設定しておくと、CoffeeScriptでもコンパイルできるようになります。
    browserifyOptionsのextensionsに.coffeeを指定しておくと、CoffeeScriptのファイルでも拡張子を省略してrequireできました。

    名前空間も汚染しないし、依存するClass等は明示的になるし、読み込み順に左右されないJSを書くことができるようになります。その代わり、$ = require(“jquery”)等は各ファイルで行う必要があります。実行時には一度しか読み込まれないようなので、パフォーマンス的にもよろしいです。

    GruntでBowerしたり、Coffeeしたり、ConcatしてJSを書いている人にはホントに便利です。