音の鳴るブログ

鳴らないこともある

require されても exports しない

最近そういう書き方を思いついて実践している。

前提

  • 100-200行程度のファイルの集合
  • 10000行程度のスタンドアロンライブラリ
  • node.js でテストして、最終的には結合してブラウザで使う
  • 外部モジュールは使用しない

大雑把な説明

  • ブラウザで require するの難しい。。
  • require したくない。。
  • require 消したいけど、消しにくい。。
  • require で代入するから消しにくいのでは?
  • 大きい名前空間をつくって各モジュールが名前空間を拡張すれば良いのでは?
  • それで最後に依存関係に従って単純に貼り付けていけば良さそう

例えば color というモジュールはこういう感じで書く。 グローバル変数として名前空間があって、そこにモジュールを追加するだけで exports していない。

// color.js
(function(namespace) {

  namespace.color = {
    fromRGB: function(r, g, b) {
      return [ r, g, b ];
    }
  };

})(namespace); // namespace というのはグローバル変数

color モジュールの利用方法はこういう感じ。 require するけど変数には代入せずに、変わりに名前空間経由でモジュールを取得する。node.js では先にモジュールが読み込まれるので問題なく動くし、ブラウザ向けに結合するときは main.js より先に color.js を貼り付ければよい(その際 require は不要になるので行ごと消す)。要は require は依存関係の表明としてだけ使う。

// main.js
(function(namespace) {
  require("./color"); // color.js が事前に読み込まれればこの行は不要になる

  var color = namespace.color;

  var white = color.fromRGB(255, 255, 255);

  namespace.main = {
    run: function() {
      console.log(white);
    }
  };

})(namespace);

最終的にライブラリとして外部に提供する部分。名前空間のうち特定の部分だけを外に出す。

// exports.js
(function(namespace) {

  require("./main");

  if (typeof module !== "undefined") {
    global.module.exports = namespace.main;
  } else {
    global.myApp = namespace.main;
  }

})(namespace);

最終的にライブラリとしてパッケージするときに名前空間グローバル変数は消してしまう。

node.js の場合

global.namespace = {};

module.exports = require("./exports");

delete global.namespace;

ブラウザの場合

(function(global) {
  var namespace = {};

  // color.js を貼り付け
  // main.js を貼り付け
  // exports.js を貼り付け
  // (各スクリプトを貼り付けるときに require の行を削除する)

})(this);

注意点