はじめに
こちらはGotanda.js #6 in OisixのLT資料になります
自己紹介
- twitter: @_sisisin
- github: @sisisin
- 普段触ってる技術
- 仕事では主にAngular1系+TypeScriptでSPA開発
- 個人では主にReact,Angular2系,Node.js.最近はちょろっとRuby
ASTとは
- Abstract Syntax Tree(抽象構文木)のこと
- コードをパースして木構造の集合として扱えるように出来る
- 詳しくはazuさんの資料が参考になります(http://azu.github.io/slide/JSojisan/)
- 気軽にASTを試すならAST explorerおすすめ
TypeScriptとAST
- TypeScriptのコンパイラの内部ではTypeScriptのソースコードを変換するためにASTを利用している
- 今回はそのコンパイラAPIでASTを取り扱う方法を紹介
コンパイラAPIの概要
- コンパイラAPIではソースファイル = AST Nodeとして扱うことが出来、そのソースファイルノードの集合として1つのプロジェクトを構成している
- それぞれ
Node,Programという型がtypescript.d.tsに定義されている- なお、ソースファイルは
Node型を継承したSourceFileという型が定義されている - ソースファイルだけでなく、全てのAST Nodeは
Node型を継承したinterfaceが定義されている
- なお、ソースファイルは
- プロジェクトに対しての操作などを行うAPIは
CompilerHostに定義されている- この辺使わなかったのでよくわかってません><
実際にTypeScriptのプロジェクトを読み込んでみる
動作環境
TypeScript1.8
*2系で試すときはbreaking change入ってるかもなので注意
準備
npm init -y && npm i -S typescript- 下記のようなファイルを用意
index.ts
import * as ts from 'typescript';
簡単!
- 型定義ファイルも含まれているのでそのままTypeScriptで書けてGood
- (というか型定義ファイルなしではやってられない
- もちろんトランスパイルしないと動かないので、実行する際は
$(npm bin)/tscを実行してからnode index.jsと叩いてやる
使う
-
ts.createProgram()を利用してProgramを作成する- 第一引数に対象のファイル名配列、第二引数にコンパイラオプションを渡す
- コンパイラオプションは
tsconfig.jsonまるっと渡しても良い
- コンパイラオプションは
- 対象のTypeScriptプロジェクトを取り扱えるメソッド郡を持ったObjectが返ってくる
- 返り値はクラスのインスタンスではなく生のJSオブジェクトっぽいのでややこしい言い回しになった
- 第一引数に対象のファイル名配列、第二引数にコンパイラオプションを渡す
-
Program.getSourceFiles()を利用してSourceFileを取得する-
SourceFileのインスタンスの配列が返ってくる - こいつは
Programと違ってクラスのインスタンスっぽい(SourceFileObjectクラス) -
SourceFileはファイル名とかが含まれたNodeになっている
-
-
ts.forEachChild()を利用して、AST Nodeを走査する- 走査対象の
Nodeがもつ子Node全てに処理を行う関数- なお、孫以下は走査されない
- 第一引数に走査対象
Nodeを渡し、第二引数にコールバック関数を渡す - コールバック関数の引数は
Nodeのインスタンス- 更に子の
Nodeを探索する場合はこのコールバック内でさらにts.forEachChild()を呼ぶ形になる
- 更に子の
- 走査対象の
-
Node.kindを用いてAST Nodeの種類によって処理を分岐する-
Node.kindにはts.SyntaxKindのenum値が格納されている -
SyntaxKindの種類によって扱えるプロパティが違うので、その場合はキャストする- 例えば
ts.SyntaxKind.VariableStatementの場合はVariableStatement.declarationListというプロパティを持っていたりする
- 例えば
- TypeScriptのコードがどの
SyntaxKindにパースされるかはenumの定義見て察する必要がありそうだった- ドキュメント・・・
- AST explorerで試しながら探してみると非常に捗るのでおすすめ
-
サンプルコード
クラス定義をコンソールに表示するプログラムのサンプルコードはこんな感じ
index.ts
import * as ts from 'typescript';
const files = ['sample/s1.ts', 'sample/d/s2.ts'];
const tsconfig = require('../sample/tsconfig.json');
const program = ts.createProgram(files, tsconfig);
for (const sourceFile of program.getSourceFiles()) {
if (sourceFile.fileName.substr(-5) === '.d.ts') continue;
ts.forEachChild(sourceFile, visit);
}
function visit(node: ts.Node) {
if (node.kind === ts.SyntaxKind.ClassDeclaration) {
console.log((<ts.ClassDeclaration>node).name.text);
}
ts.forEachChild(node, visit);
}
終わりに
- 簡単にTypeScriptのコンパイラAPIを用いたASTの扱い方を紹介しました
- 以上の知識があれば最低限TypeScriptのAST使ってみることは出来ると思います
- 正規表現に限界を感じたりしたら是非
※後者2つの記事ではTypeScript1.4を使ってますが、1.8でもそのまま使えました