20
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

browserify (browser-pack) はどんなコードを出力するのか?

Last updated at Posted at 2014-02-10

はじめに

browserifyは、CommonJSスタイルで書かれたJavaScript (Node.js)のコードをクライアントで実行可能な形式に変換するライブラリです。ここではbrowserifyがビルドの際に用いているbrowser-packが、与えられたコードをどんなコードを出力するのかを見ていきます。browser-packのバージョンは6.0.1。

入力

Readmeにある以下のjsonを試してみる。この形式はちょうどmodule-depsの出力結果と同じもので、browserifyも内部でmodule-depsを利用している。内容は

  • 各モジュールを一意に指すID
  • コードの本体
  • 各モジュールが依存する(内部でrequire()を使って呼び出している)モジュールのパスとID
  • そのモジュールがエントリポイントかどうか

の4種類。

[
  {
    "id": "a1b5af78",
    "source": "console.log(require('./foo')(5))",
    "deps": { "./foo": "b8f69fa5" },
    "entry": true
  },
  {
    "id": "b8f69fa5",
    "source": "module.exports = function(n) { return n * 111 }",
    "deps": {}
  }
]

出力

関数outer()を即時実行するコードがビルドされているのが見てとれる。index.jsを読むとわかるのだが、関数定義はすでにprelude.jsに書かれており、入力したjsonの情報は関数outer()に与える引数の形に整形されている(なお実際のビルドにはprelude.jsを圧縮・難読化した_prelude.jsnpm prepublish時に生成され、そちらが使用される)。

(function outer(modules, cache, entry) {

  var previousRequire = typeof require == "function" && require;

  function newRequire(name, jumped) {
    if (!cache[name]) {
      if (!modules[name]) {
        var currentRequire = typeof require == "function" && require;
        if (!jumped && currentRequire) return currentRequire(name, true);

        if (previousRequire) return previousRequire(name, true);
        var err = new Error('Cannot find module \'' + name + '\'');
        err.code = 'MODULE_NOT_FOUND';
        throw err;
      }
      var m = cache[name] = { exports: {} };
      modules[name][0].call(m.exports, function (x){
        var id = modules[name][1][x];
        return newRequire(id ? id : x);
      }, m, m.exports, outer, modules, cache, entry);
    }
    return cache[name].exports;
  }
  
  for (var i = 0; i < entry.length; i++) newRequire(entry[i]);

  return newRequire;

})({
  "a1b5af78": [function(require, module, exports) { console.log(require('./foo')(5)); }, { "./foo": "b8f69fa5" }],
  "b8f69fa5": [function(require, module, exports) { module.exports = function (n) { return n * 111; }; }, {}]
}, {}, ["a1b5af78"]);

理解のためのメモ

  • 関数outer()は関数newRequire()を返す。ちなみにbrowserifyの-rオプションでモジュールを外部から利用できるようにすると、この結果がグローバル変数requireに代入され(先頭にrequire = のついたコードがビルドされるだけ)、外からrequire()が呼び出せるようになる。
  • またouter()newRequire()を返す傍ら、エントリポイントのそれぞれに対してnewRequire()を実行する。
  • 同じモジュールを複数回require()する場合はキャッシュを利用して再度評価されるのを防ぐ。
  • 関数outer()の引数で各モジュールのコードがfunction(require, module, exports) { ... }にラップされることで、クライアントサイドでもrequire()moduleexportsが参照できるようになっている。
20
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?