背景
最近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;