Sassyブログ

好きなことで暮らしを豊かにするブログ

TypeScript Compiler APIを使ってimportのモジュールパスを取得する

今回は前回の延長線上でASTからimport宣言のモジュールパスを取得してみようと思います。

前回の記事を見ていない方はこちらをどうぞ

www.sassy-blog.com

前回のソースコードから以下のように変更しました。

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.tstestTscApi/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面白いですね。

現在これを使ってやりたいことがあるので引き続き色々調べていきたいと思います!