Open10
ts-morphについてまとめる
Projectを作成する&SourceFileの作成
ts-morphの基本は、Projectを作って、そのProject内でSourceFileをファイルから読み込んだり、直接作成したりする。
import { Project } from "ts-morph";
const project = new Project();
// SourceFileを作成する
const sourceFile = project.createSourceFile("./hoge");
// ファイルからSourceFileを作成する
const sourceFile = project.getSourceFile("./hoge.ts")
// Project内の全てのファイルからSourceFileを作成する
const sourceFiles = project.getSourceFiles()
// 引数にglobパターン文字列を渡すと、それにマッチしたファイルのみロードする
const sourceFiles = project.getSourceFiles("*.test.ts")
Projectの設定
import { Project } from "ts-morph";
new Project({
// tsconfig.jsonの設定
compilerOptions: CompilerOptions,
// tsconfig.jsonへのパス
tsConfigFilePath: string,
// 指定したtsconfig.jsonからのファイルの追加をスキップするかどうか
skipAddingFilesFromTsConfig: boolean,
// ファイルが追加された時などに、依存関係の解決をスキップするかどうか
skipFileDependencyResolution: boolean,
// lib ファイルのロードをスキップします。
// コンパイラ API とは異なり、ts-morph はこれらを node_modules フォルダーからロードしませんが、
// 代わりに他の JS コードからロードし、存在するために偽のパスを使用します。
// カスタム lib フォルダーパスを使用する場合は、libFolderPath オプションを使用してパスを指定します。
skipLoadingLibFiles: boolean,
// libファイルのロードに使用するフォルダーへのパス
libFolderPath: string,
// ファイルを保存する時の設定?
manipulationSettings: {
indentationText: IndentationText, // インデントに使う文字列
newLineKind: NewLineKind, // 改行コード
quoteKind: QuoteKind, // クォーテーションの種類
useTrailingCommas: boolean, // 末尾のコンマを使用するかどうか。
// プロパティ省略代入、バインディング要素、importやexport名の名前変更を有効にするかどうか。
usePrefixAndSuffixTextForRename: boolean,
},
// インメモリのファイルシステムを使用するかどうか
useInMemoryFileSystem: true,
// オプションのファイル システム ホスト。
// ファイル システムへのアクセスをモックするのに役立ちます。
// @remarks: 代わりに「useInMemoryFileSystem」の使用を検討してください。
fileSystem: FileSystemHost,
// カスタム モジュールや型参照ディレクティブの解決する関数を指定。
// 見た感じ、denoとかに対応するためのやつッポイ。
resolutionHost: ResolutionHostFactory,
})
ソースコードの読み込み
index.ts
import * as path from 'path'
import { Project } from 'ts-morph'
// プロジェクトを作成、この時点でファイルのロードが発生する
const project = new Project({
tsConfigFilePath: path.join(process.cwd(), 'tsconfig.json'),
})
// index.tsのsourceオブジェクトを取得する
const source = project.getSourceFileOrThrow('index.ts')
// index.tsファイルの中身を出力
console.log(source.getText())
// 出力結果
/*
import * as path from 'path'
import { Project, Node } from 'ts-morph'
// プロジェクトを作成、この時点でファイルのロードが発生する
const project = new Project({
tsConfigFilePath: path.join(process.cwd(), 'tsconfig.json'),
})
// index.tsのsourceオブジェクトを取得する
const source = project.getSourceFileOrThrow('index.ts')
// index.tsファイルの中身を出力
console.log(source.getText())
*/
Type Guardを使ってNodeの判定に型を付けたい
Node
をインポートして、そこからType Guard関数を使うことができる。
import { Node } from "ts-morph"
// 変数のnodeを取得する
const varDec = source.getVariableDeclarationOrThrow("hoge")
// 右辺のnodeを取得する
const initDec = varDec.getInitializerOrThrow()
// アロー関数だった場合
if( Node.isArrowFunction(initDec) ) {
console.log( initDec.getBody() ) // 型エラーにならない
}
console.log( initDec.getBody() ) // 型エラーになる
参照
コメントの取得
コメントの取得方法をまとめます。
詳細は、以下のドキュメントを参照してください👇
SourceFileからコメントを取得する
import * as path from 'path'
import { Project, Node } from 'ts-morph'
// プロジェクトを作成、この時点でファイルのロードが発生する
const project = new Project({
tsConfigFilePath: path.join(process.cwd(), 'tsconfig.json'),
})
// index.tsのsourceオブジェクトを取得する
const source = project.getSourceFileOrThrow('test.ts')
// コメントを含めたStatement配列を返す
const statements = source.getStatementsWithComments()
statements.forEach((statement) => {
if (Node.isCommentNode(statement)) {
console.log(statement.getText()) // コメントの内容を出力する
}
})
/*
===== 出力結果 =====
// プロジェクトを作成、この時点でファイルのロードが発生する
// index.tsのsourceオブジェクトを取得する
// コメントを含めたStatement配列を返す
*/
Interfaceからコメントを取得する
普通のコメント
interface Hoge {
// このコメントは取得できます
hello: string // このコメントは取得できません
obj: {
// このコメントは取得できません
hoge: string // このコメントは取得できません
}
}
// interfaceのNodeを取得する
const interfaceDec = source.getInterfaceOrThrow('Hoge')
// interface内のStatementを配列で受け取る
const membersDec = interfaceDec.getMembersWithComments()
membersDec.forEach((statement) => {
if (Node.isCommentNode(statement)) {
console.log( statement.getText() ) // コメントの内容を出力する
}
})
/*
===== 出力結果 =====
// このコメントは取得できます
*/
JSDoc
/**
* @description インターフェースのJSDoc
*/
interface Hoge {
/**
* @description プロパティのJSDoc
*/
world: string
}
// interfaceのNodeを取得する
const interfaceDec = source.getInterfaceOrThrow("Hoge");
// interfaceについているJSDocを表示
interfaceDec.getJsDocs().forEach((doc) => console.log(doc.getText()));
// プロパティについているJSDocを表示
interfaceDec.getProperties().forEach((prop) => {
prop.getJsDocs().forEach((doc) => console.log(doc.getText()));
});
// ===== 出力結果 =====
//
// /**
// * @description インターフェースのJSDoc
// */
//
// /**
// * @description プロパティのJSDoc
// */
注意として、コメントの位置によって取得できないコメントがあります。
上記の実装では、interfaceのフィールド内に記述されているコメントは取得できますが、プロパティと同じ行に記述されていたり、別のフィールド(上記の場合だと、obj
のフィールド)に記述されているコメントは別の実装が必要になってきます。
特定のライブラリがインポートされているか判定する
test.ts
import * as path from 'path'
import { Project } from 'ts-morph'
// プロジェクトを作成、この時点でファイルのロードが発生する
const project = new Project({
tsConfigFilePath: path.join(process.cwd(), 'tsconfig.json'),
})
// test.tsのsourceオブジェクトを取得する
const source = project.getSourceFileOrThrow('test.ts')
const imports = source.getImportDeclarations()
// ts-morphがインポートしているか判定する
const isImportedTsMorph = imports.some((node) =>
node.getModuleSpecifier().getText().match('ts-morph')
)
if (isImportedTsMorph) {
console.log(`このファイルはts-morphをインポートしています`)
}
/*
===== 出力結果 =====
このファイルはts-morphをインポートしています
*/