TypeScriptのASTを使ってコードを解析しよう

TypeScriptのASTを操作する方法が、[GitHubのWiki](https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API)に公開されていました。これを使えば,TypeScriptのコードを解析することができます。

## 準備
### TypeScriptのインストール
`npm install typescript`を実行しTypeScriptを取得します。
### tsconfig.jsonを設定する(Atomを利用している場合)
`./node_modules/typescript/bin/typescript.d.ts`をfilesGlobに追加することで、TypeScriptのASTのAPIのコード補完が可能となります(./node_modules/typescript/bin以下のd.tsファイルを追加すると、Atomが重くなりコード補完は使い物になりません)

```json:tsconfig.json
{
    "version": "1.4.1",
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "declaration": false,
        "noImplicitAny": false,
        "removeComments": true,
        "noLib": false
    },
    "filesGlob": [
        "./**/*.ts",
        "!./node_modules/**/*.ts",
        "./node_modules/typescript/bin/typescript.d.ts"
    ],
    "files": [
        "./index.ts",
        "./typings/node/node.d.ts",
        "./node_modules/typescript/bin/typescript.d.ts"
    ]
}
```

## AST解析の進め方
ASTの解析はVisitorパターンを使って、ASTのNodeの種類ごとに、解析を進めていきます。
サンプルプログラムでは、クラスの一覧を標準出力に出力します。

### 1. typescript を import する
`import * as ts from 'typescript'` でtypescritをインポートします

### 2. 解析対象のソースを準備する
解析対象のソースを準備します。本来はfileから読み込むべきですが、ファイル操作のコードを省略するために、変数として用意します。

```typescript
let source = `
 class Sample{

 }

 class Sample2{

 }
`;

```

### 3. SourceFile を生成する
TypeScriptのASTのエントリポイントは、ソースコードをParseして得られるSourceFileから開始します。

```typescript
let sourceFile = ts.createSourceFile(
          /*ファイル名*/       'sample.ts', 
          /*ソース*/           source, 
          /*ターゲット*/       ts.ScriptTarget.ES6, 
         /*setParentNodes */  true);

```

### 4. Visitor パターンを実装する
TypeScriptのAPIに`ts.forEachChild`用意されており、これを使うことでVisitorパターンを実装することができます.

下記のサンプルはClassの宣言を見つけるためのVisitorパターンの実装です。node.kindとts.SyntaxKindを比較することで、Nodeの種類を特定することができます。

```typescript
ts.forEachChild(sourceFile, each);

function each(node: ts.Node) {
    switch (node.kind) {
        case ts.SyntaxKind.ClassDeclaration:
            classDeclaration(<ts.ClassDeclaration>node);
            break;
        default:
            next();

    }

    function next()   {
            ts.forEachChild(node, each);
    }

}
```

### 5. クラス宣言の場合のコールバックメソッドを作成する
クラス宣言の場合のコールバックメソッドでコンソールにクラス名を出力します。

```typescript
function classDeclaration(node: ts.ClassDeclaration) {
  console.log(node.name.text);
}
```

## 動作確認
作成したコードを実行すると、解析対象のソースにある２つのクラス名がコンソールに出力されます

```
$ node index.js 
Sample
Sample2
```

## まとめ
TypeScriptにASTを操作するためAPIが用意されているので、Visitorパターンを利用すれば、ソースコードの解析は簡単に実現できます。しかし、ソースコードを書き換える処理を実装しようとすると、falafelに比べて、APIが足りないので面倒な実装をしなければなりません。ソースコードを書き換えは、次回の記事に書こうと思います。

## 開発したソース

```typescript:index.ts
import * as ts from 'typescript'

let source = `
 class Sample{

 }

 class Sample2{

 }
`;

let sourceFile = ts.createSourceFile('sample.ts', source, ts.ScriptTarget.ES6, /*setParentNodes */ true);

ts.forEachChild(sourceFile, each);

function each(node: ts.Node) {
    switch (node.kind) {
        case ts.SyntaxKind.ClassDeclaration:
            classDeclaration(<ts.ClassDeclaration>node);
            break;
        default:
            next();

    }

    function next()   {
            ts.forEachChild(node, each);
    }

}

function classDeclaration(node: ts.ClassDeclaration) {
  console.log(node.name.text);
}

```