LoginSignup
20
17

More than 5 years have passed since last update.

非CommonJSモジュールなJavaScriptコードを本体変更無しにCommonJS対応にする

Last updated at Posted at 2014-04-27

背景

最近TypeScriptコンパイラ触る時とかPEG.jsで生成したパーサを使う時とかに、トップレベルにTypeScriptとかPEGとかいう変数がポコンと作られるだけのコードがあります。

こう…俺はこういうコードを追加してほしいんだよ…!!みたいな気持ちになります。

(function (global) {
    var lib = ...; // なんか適当にライブラリの定義

    if (typeof define === 'function' && define.amd) {
        define(function() { return lib; });
    } else if (typeof exports === 'object') {
        module.exports = lib;
    } else {
        global.lib = lib;
    }
})(this);

ライブラリ本体にpull request送ったりして根本的に解決すれば幸せになれるんですが、とりあえず使えるようにしたい時に便利なテクを最近知ったので紹介します。

今まで、僕は上記のようなコードを末尾にconcatするgruntタスクとかを作っておいて、連結後のコードを参照するようにしていました。
ただ、ライブラリや自動生成されたコードを自分のプロジェクト内で弄くるのってなんか罪悪感ありますよね?僕はあります。

解決方法

Node.jsにはvmモジュールというものがあります。
ぶっちゃけた話、安全なevalみたいな感じです。

Node.jsに新しく実行コンテキストを作らせて、そのサンドボックスの中でコードを実行し、終了後に残った変数を地引網的に回収します。
これから出す例では、sandbox変数が実行コードのグローバルオブジェクト的に使われますので、テキトーに回収します。

実際に最近作ったTypeScriptコードはこんな感じです。

/// <reference path="../typings/node/node.d.ts" />
/// <reference path="./PEG-definition.d.ts" />

import vm = require("vm");
import fs = require("fs");

var fileName = __dirname + "/../resources/grammer.js";
var pegCode = fs.readFileSync(fileName, {encoding: "utf-8"});

var sandbox: { PEG: typeof PEG; } = <any>{};
vm.runInNewContext(pegCode, sandbox, "grammer.js");
var PEG = sandbox.PEG;

export = PEG;

未だにTypeScriptをやってない人向けにコンパイル後のJavaScriptコードも一応置いておきます。

var vm = require("vm");
var fs = require("fs");

var fileName = __dirname + "/../resources/grammer.js";
var pegCode = fs.readFileSync(fileName, { encoding: "utf-8" });

var sandbox = {};
vm.runInNewContext(pegCode, sandbox, "grammer.js");
var PEG = sandbox.PEG;

module.exports = PEG;
20
17
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
17