Grunt.js で TypeScript をコンパイルする grunt-typescript を作りました。と、公開しました。

昨日、TypeScript が公開されて、かなりこれは来た感が満載なわけです。お盆のころからちょこちょこ作って、もうそろそろ公開しようかなと思ってた 2000 行近い JavaScript のライブラリを全部書き直そうかって衝動に駆られるくらいな勢いです。まぁ、どこがイケてるのか、どういう言語なのか、使ってみました的なことはいろんな所で書かれているので、ここでは違った角度の話をかこうと思います。

で、TypeScript をいろいろ試したいと思っても環境が VS2012 のアドインで、提供されているプロジェクトテンプレートが C# のプロジェクトを潰した奴だとか、普通の Web プロジェクトにアイテムテンプレートで ts ファイルを追加してもコンパイルされないとか、 Web Essentials の更新当てても Windows Store アプリのプロジェクトだと ts ファイル編集しにかかった瞬間に VS ごと落ちるとか、結局変更するたびにコマンドライン叩かなダメとか書いた後の実行までがめんどくさい。

ってことで、コンパイルを何とか Grunt.js で実行できないかと頑張ってみました。これで grunt watch しておけば変更するたんびにコンパイルされて確認も実行もお手軽です。で、結論としてコンパイルできて npm にも登録したので、どうやって実行しているかのお話を。

まず前提として、 Grunt.js で動かすので node.js で動かす必要があります。かつ、Grunt.js のカスタムタスクで npm からとってきた状態で動かしたかったので、 npm で公開されている TypeScript のコンパイラーが必要です。ちなみに TypeScript を npm でとってくるには以下のコマンドです。

npm install -g typescript

で、こいつでとってこれたフォルダの下の bin フォルダの下に typescript.js ってので使ってます。ただしこいつは、 node のモジュールとしてエクスポートされてないので require しても何も返ってきません。なので、ソースを直接呼んで取り込む必要がありました。以下のような感じ。

var fs = require('fs'),
    vm = require('vm'),
    code = fs.readFileSync('typescript.js'),
    ts;

vm.runInThisContext(code, 'typescript.js');
ts = TypeScript;

これで TypeScript オブジェクトは取得できるんで、あとはソースをゴリゴリ読んでコンパイルの実行を行うだけです。コンパイルを実行しているオブジェクトはすぐに見つかりました。 TypeScriptCompiler ってのがそれです。こいつを new して、ゴネゴネってすればコンパイルされました。

var outfile = {
  source: '',
  // どうやらコンパイル後のコードはこの関数に渡されるらしいので、連結。
  Write: function(s) {this.source += s;},
  WriteLine: function(s) {this.source += s + "\r\n";},
  Close: function(){}
};
var outerr = {
  Write: function(s) {},
  WriteLine: function(s) {},
  Close: function() {},
};

// TypeScriptCompiler をインスタンス
var compiler = new ts.TypeScriptCompiler(outfile, outerr);

// 第1引数に TypeScript コードを渡す。 第2引数にはファイル名。なんでもイイっぽい。
compiler.addUnit('var example = (a) => a;', 'example.ts');

// コンパイルを実行
compiler.emit(false, function(){ return outfile;});

// コンパイル後のソース
// var example = function(a) {
//   return a;
// }
var js = outfile.source;

こんな感じです。詳細はまだ TypeScript のコードを読み込んでないので読み込んであらかた理解できて、勢いがあればまた書きます。

さてさて、こんな感じでコンパイルできることが分かったのであとは Grunt.js のカスタムタスク書いて、 npm で公開するだけです。コードは GitHub に公開してますのでどうぞ。

git@github.com:k-maru/grunt-typescript.git

npm で取得するには以下のようにします。

npm install grunt-typescript

Grunt.js は以下のような感じで記述。こいつと watch タスク組み合わせればめちゃ便利。

grunt.initConfig({
    ...
    typescript: {
      base: {
        src: ['path/to/typescript/files/*.ts'],
        dest: 'where/you/want/your/js/files'
      }
    },
    ...
});

まずは、最低限動きますよって状態なので、オプションとか設定できないので追々更新していきます。たぶん、まずはタスクのコードを TypeScript で書くところからですかね。

ちなみ、node.js はあまり詳しくなく Grunt.js のカスタムタスクと npm への登録については今回初めてなので、いろいろイケてないところとか不具合とか、もっとこうすれば簡単などなどフィードバック募集中です。