はじめに
こちらは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でもそのまま使えました