Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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が参照できるようになっている。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした