traceur-compilerでスクリプトをあらかじめコンパイルしておく方法

traceur-compilerを使って書いたプログラムを公開しようというとき、ユーザー側でコンパイラのプログラムのロードとスクリプトコンパイルが走ってしまうのが速度の点で気になります。そこであらかじめコンパイルしておく方法を紹介します。

スクリプトコンパイルする方法

Windowsな人も別にビルドなどする必要なくいけます。

Node.jsのインストールとリポジトリのclone

まず、Node.jsをインストールします。
次にtraceur-compilerのリポジトリをcloneします。 https://github.com/google/traceur-compiler

cloneしたリポジトリディレクトリに入って

>node traceur

を実行してみる。すると「npm install commander」を実行しろといわれるので言われた通りにする。そして再びnode traceurを実行すると

  Error: At least one input file is needed

  Usage: traceur [options] [files]

  Options:

    -h, --help      output usage information
    --out <FILE>    Compile all input files into a single file
    --sourcemap     Generate source maps
    --longhelp      Show all known options
    --experimental  Turns on all experimental features

  Examples:

    $ traceur a.js
    $ traceur b.js c.js --out compiled.js

というように出力されます。

traceurという名前でコマンドが使えるようにする

"node /traceur"といちいち入力するのは長いので単にtraceurという名前でコマンドが使えるようにしておきます。このステップは省略してもいいです。

Windowsの場合はPATHの通った場所に以下の内容でtraceur.cmdというファイルを作ります。

@node C:\Users\User\repos\traceur-compiler\traceur %*

C:\Users\User\repos\traceur-compilerの部分はあなたがcloneした場所に変更します。

UNIXの人はPATHの通った場所にcloneしたワーキングディレクトリの中にあるtraceurというファイルへのシンボリックリンクを置くだけでいいです。

実際に簡単なスクリプトコンパイルしてみる

こんなファイルを用意します (t.js)

class Hello {
	hello(x) {
		console.log(`Hello, ${x}.`);
	}
}

new Hello().hello("world");

次のコマンドでコンパイルします。

traceur --out compiled.js t.js

するとcompiled.jsがこんな内容で生成されます。

var $__getDescriptors = function(object) {
  var descriptors = {}, name, names = Object.getOwnPropertyNames(object);
  for (var i = 0; i < names.length; i++) {
    var name = names[i];
    descriptors[name] = Object.getOwnPropertyDescriptor(object, name);
  }
  return descriptors;
}, $__createClassNoExtends = function(object, staticObject) {
  var ctor = object.constructor;
  Object.defineProperty(object, 'constructor', {enumerable: false});
  ctor.prototype = object;
  Object.defineProperties(ctor, $__getDescriptors(staticObject));
  return ctor;
};
var Hello = function() {
  'use strict';
  var $Hello = ($__createClassNoExtends)({
    constructor: function() {},
    hello: function(x) {
      console.log(("Hello, " + x + "."));
    }
  }, {});
  return $Hello;
}();
new Hello().hello("world");
コンパイル済みのファイルをブラウザで実行してみる

こんなファイルを用意して開きます。

<!DOCTYPE html>
<title>hello</title>
<script src="https://raw.github.com/google/traceur-compiler/master/bin/traceur.js"></script>
<script src="compiled.js"></script>
コンパイル済みのファイルをNode.jsで実行する

ダサい方法しかわからなかったのでひとまずそれを紹介しておきます。
traceur.jsを読み込まないといけないんですが、nodeは実行するファイルを複数指定することができないのであらかじめ連結しておきます。

>type C:\Users\User\repos\traceur-compiler\bin\traceur.js compiled.js > compiled2.js

(UNIXな人はtypeではなくcat)
そして実行。

>node compiled2.js
Hello, world.

bin/traceur.jsはデカすぎてロードに時間がかかるので小さくしたい

bin/traceur.jsにはコンパイラ部分も含まれているので巨大です。必要なのはランタイム部分だけなので、それだけをロードしたいものです。

ランタイム部分のソースはsrc/runtime/runtime.jsにまとめられています。これはJS.nextではなくJSで書かれているので、これだけロードすればいいんではないかと考えたくなります。
しかし、traceur.runtime.elementGetなどの実装がruntime.jsの外で定義されているtraceur.optionsというものに依存しております。

function elementGet(object, name) {
  if (traceur.options.trapMemberLookup &&
      hasPrivateNameProperty(object, elementGetName)) {
    return getProperty(object, elementGetName).call(object, name);
  }
  return getProperty(object, name);
}

traceur.options.trapMemberLookupの真偽を見ているだけなので"traceur.options={trapMemberLookup:false}"とかどこかに入れてしまうといい方法もありますがちょっと美しくないです。

そこでコンパイラ部分を除いたバイナリを作ってしまいましょう。

traceur-compilerのワーキングディレクトリ内の/srcにtraceur-mini.jsというファイルを以下の内容で作ります。

module traceur {
  import {options} from './options.js';
  export options;
  export var runtime;
  export function setRuntime(rt) {
    runtime = rt;
  }
}

そして/srcがカレントディレクトリである状態で以下を実行します。

>node ../traceur --out traceur-mini.compiled.js traceur-mini.js runtime/runtime.js

するとtraceur-mini.compiled.jsができます。今まで使っていたbin/traceur.jsの代わりにこれを使いましょう。

最後に、作ったtraceur-mini.compiled.jsとbin/traceur.jsのサイズを比べてみます。

C:\Users\User\repos\traceur-compiler>ls -lh bin\traceur.js src\traceur-mini.compiled.js
-rw-rw-rw-  1 User 0 808K 2013-04-13 12:02 bin\traceur.js
-rw-rw-rw-  1 User 0  19K 2013-04-13 12:44 src\traceur-mini.compiled.js

808キロバイトが19キロバイトに減りました。

Linuxだとカレントディレクトリがリポジトリのルートの状態で以下を実行してうまくいったのですが、Windowsだとパス名の扱いに問題があるようでうまくいきません。
上では本当は/bin/traceur-mini.jsに出力したかったのですが、出力先ディレクトリが異なるとこれまた失敗するので現在のディレクトリにtraceur-mini.compiled.jsという名前で出力するようにしておきました。

>node traceur --out bin/traceur-mini.js src/traceur-mini.js src/runtime/runtime.js