TypeScript Compiler Service APIでシンプルなコンパイラを書いた
TypeScript 1.4が出てCompiler Service APIのドキュメントが出てきた。
よーし触ってみようと思ったのだけど、APIがファイルを前提にしていて、ちょっとしたTypeScriptコード片を文字列で渡して変換結果を取り出す、みたいなことをするだけなのに50行ぐらい必要。
まあTypeScriptの性質上、外部ファイルを参照していたら型解決のために読み取る必要があるわけなのでファイルを前提にするのはしょうがない。ただもう少し簡単なAPIが欲しいので、typescript-simpleというモジュールを作った。
使い方はこれだけ。シンプル。
var tss = require('typescript-simple'); var js = tss('var n: number = 1;'); console.log(js); // 'var n = 1;'
TypeScript Compiler Service APIの使い方
せっかくなのでTypeScript Compiler Service APIの使い方を簡単に。
まずはパッケージにtypescript
をインストール。
$ npm install typescript
JSの場合は普通にrequireすればOK。
var ts = require('typescript');
TypeScriptから使う場合は、typescript
モジュールに入ってるtypescript.d.ts
をreferenceタグで参照しつつimportする。
/// <reference path="node_modules/typescript/bin/typescript.d.ts" /> import ts = require('typescript');
コンパイルの手順は少しクセがあって、以下のようになる。
- CompilerHostの生成
- Programの生成
- Checkerの生成
- アウトプットをemit
- 各エラーの処理
実際のコードはオフィシャルページのminimal compilerが分かりやすい。
import ts = require("typescript"); export function compile(filenames: string[], options: ts.CompilerOptions): void { var host = ts.createCompilerHost(options); var program = ts.createProgram(filenames, options, host); var checker = ts.createTypeChecker(program, /*produceDiagnostics*/ true); var result = checker.emitFiles(); var allDiagnostics = program.getDiagnostics() .concat(checker.getDiagnostics()) .concat(result.diagnostics); allDiagnostics.forEach(diagnostic => { var lineChar = diagnostic.file.getLineAndCharacterFromPosition(diagnostic.start); console.log(`${diagnostic.file.filename} (${lineChar.line},${lineChar.character}): ${diagnostic.messageText}`); }); console.log(`Process exited with code '${result.emitResultStatus}'.`); } compile(process.argv.slice(2), { noImplicitAny: true, noEmitOnError: true, target: ts.ScriptTarget.ES5, module: ts.ModuleKind.CommonJS});
見ると分かるように、全てのAPIはファイルであることを前提にしてるので、typescript-simpleではsimple transformationを参考にダミーのファイルを内部的に生成することで文字列だけのコンパイルを実現している。
本家ドキュメントには、他にもASTトラバースやインクリメンタルビルドなど興味深いサンプルが載ってたのでぜひどうぞ。