今回は前回の延長線上でASTからimport宣言のモジュールパスを取得してみようと思います。
前回の記事を見ていない方はこちらをどうぞ
前回のソースコードから以下のように変更しました。
testTscApi/test.ts
ではimport文を適当に増やしています。
importの数だけファイルを増やしてリネームしました。
testTscApi/test.ts
import { testFunc2 } from "./test2"; import { testFunc3 } from "./test3"; import { testFunc4 } from "./test4”; import { testFunc5 } from "./test5"; import { testFunc6 } from "./test6”; export const testFunc1 = () => { console.log(‘test1’); testFunc2() }
そして、このtestTscApi/test.ts
をtestTscApi/main.ts
から読み込んでtest.ts
に宣言しているimport文のモジュールパスを取得してみるコードは以下のように書きました。
testTscApi/main.ts
import * as ts from "typescript"; const program = ts.createProgram(["test.ts"], {}); const source = program.getSourceFile("test.ts"); let importModuleNames: string[] = []; if (source) { ts.forEachChild(source, node => { if(ts.isImportDeclaration(node)){ const moduleNameText = node.moduleSpecifier.getText(source); const moduleName = moduleNameText.slice(1, moduleNameText.length - 1); importModuleNames.push(moduleName); } }) } console.log(importModuleNames)
これを実行してみます。
以下、出力結果です。
[ './test2', './test3', './test4', './test5', './test6' ]
ちゃんとモジュールのパス部分のみ取得できています。
ではサラッとどんな感じにやっているかを解説していきます。
以下の処理ではforEachChild
を使ってソースを第一引数に渡して、その中からNode
を取得しています。
ts.forEachChild(source, node => { ・・・省略 })
Nodeはすべてのnodeの共通インターフェースとなっていて、コールバック関数の引数にわたってくるnodeオブジェクトのkind
プロパティを見て、どの型のnodeであるかを判断します。
TypeScriptではis~
というタイプガードで実装された関数が用意されているのでこの関数の引数にnodeを渡すことによって、そのkind
と一致していれば一致した型であると推論されるのでキャストが不要になります。
以下のように書くことでimport宣言部分のみ処理を行うことができます。
if(ts.isImportDeclaration(node)){ ・・・省略 }
以下はif文の中の処理です。
・・・省略 const moduleNameText = node.moduleSpecifier.getText(source); const moduleName = moduleNameText.slice(1, moduleNameText.length - 1); importModuleNames.push(moduleName); ・・・省略
import宣言のモジュールパス部分の情報はmoduleSpecifier
プロパティに入っているので、その中のオブジェクトのプロパティであるtext
から文字列を取得します。
text
プロパティの値を取得するgetterが用意されているのでこちらを使用します。
引数にはsource
を渡していますが使い方あっているのかイマイチよくわかってませんが、想定の値は取得できているのでこのまま進めます。
const moduleNameText = node.moduleSpecifier.getText(source);
次は取得したモジュールパスは"./test2"
のようになっており、不要な文字が混入しているのでslice関数でダブルクォーテーションを省きます。
const moduleName = moduleNameText.slice(1, moduleNameText.length - 1);
最後は処理しやすいように配列に入れて完成です。
importModuleNames.push(moduleName);
こんな感じで簡単にimportのモジュールパスを取得することができました。
TypeScriptCompilerAPI面白いですね。
現在これを使ってやりたいことがあるので引き続き色々調べていきたいと思います!