2021年には--explainFiles
コードベースをより注意深く調べたい場合(タイプのみのインポートとランタイムインポートを区別するなど)、TypescriptAPIを使用できます。可能性は無限ですが、おそらく以下のこれらの部分はあなたを良い方向に導くのに役立ちます(おそらくバグがあります):
import * as ts from "typescript";
interface FoundReference {
typeOnly: boolean;
relativePathReference: boolean;
referencingPath: string;
referencedSpecifier: string;
}
const specifierRelativeFile = /^\..*(?<!\.(less|svg|png|woff))$/;
const specifierNodeModule = /^[^\.]/;
const diveDeeper = (path: string, node: ts.Node, found: FoundReference[]) =>
Promise.all(node.getChildren().map(n => findAllReferencesNode(path, n, found)));
const findAllReferencesNode = async (path: string, node: ts.Node, found: FoundReference[]) => {
switch (node.kind) {
case ts.SyntaxKind.ExportDeclaration:
const exportDeclaration = node as ts.ExportDeclaration;
if (exportDeclaration.moduleSpecifier) {
const specifier = (exportDeclaration.moduleSpecifier as ts.StringLiteral).text;
if (specifier) {
if (specifierRelativeFile.test(specifier)) {
found.push({
typeOnly: exportDeclaration.isTypeOnly,
relativePathReference: true,
referencingPath: path,
referencedSpecifier: specifier
});
} else if (specifierNodeModule.test(specifier)) {
found.push({
typeOnly: exportDeclaration.isTypeOnly,
relativePathReference: false,
referencingPath: path,
referencedSpecifier: specifier
});
}
}
}
break;
case ts.SyntaxKind.ImportDeclaration:
const importDeclaration = node as ts.ImportDeclaration;
const importClause = importDeclaration.importClause;
const specifier = (importDeclaration.moduleSpecifier as ts.StringLiteral).text;
if (specifier) {
if (specifierRelativeFile.test(specifier)) {
found.push({
typeOnly: (!!importClause && !importClause.isTypeOnly),
relativePathReference: true,
referencingPath: path,
referencedSpecifier: specifier
});
} else if (specifierNodeModule.test(specifier)) {
found.push({
typeOnly: (!!importClause && !importClause.isTypeOnly),
relativePathReference: false,
referencingPath: path,
referencedSpecifier: specifier
});
}
}
break;
case ts.SyntaxKind.CallExpression:
const callExpression = node as ts.CallExpression;
if ((callExpression.expression.kind === ts.SyntaxKind.ImportKeyword ||
(callExpression.expression.kind === ts.SyntaxKind.Identifier &&
callExpression.expression.getText() === "require")) &&
callExpression.arguments[0]?.kind === ts.SyntaxKind.StringLiteral) {
const specifier = (callExpression.arguments[0] as ts.StringLiteral).text;
if (specifierRelativeFile.test(specifier)) {
found.push({
typeOnly: false,
relativePathReference: true,
referencingPath: path,
referencedSpecifier: specifier
});
} else if (specifierNodeModule.test(specifier)) {
found.push({
typeOnly: false,
relativePathReference: false,
referencingPath: path,
referencedSpecifier: specifier
});
} else {
await diveDeeper(path, node, found);
}
} else {
await diveDeeper(path, node, found);
}
break;
default:
await diveDeeper(path, node, found);
break;
}
}
const path = "example.ts";
const source = `
import foo from "./foo";
import * as bar from "./bar";
import { buzz } from "./fizz/buzz";
export foo from "./foo";
export * as bar from "./bar";
export { buzz } from "./fizz/buzz";
const whatever = require("whatever");
const stuff = async () => {
require("whatever");
const x = await import("xyz");
}
`
const rootNode = ts.createSourceFile(
path,
source,
ts.ScriptTarget.Latest,
/*setParentNodes */ true
);
const found: FoundReference[] = [];
findAllReferencesNode(path, rootNode, found)
.then(() => {
console.log(found);
});
[
{
"typeOnly": true,
"relativePathReference": true,
"referencingPath": "example.ts",
"referencedSpecifier": "./foo"
},
{
"typeOnly": true,
"relativePathReference": true,
"referencingPath": "example.ts",
"referencedSpecifier": "./bar"
},
{
"typeOnly": true,
"relativePathReference": true,
"referencingPath": "example.ts",
"referencedSpecifier": "./fizz/buzz"
},
{
"typeOnly": false,
"relativePathReference": true,
"referencingPath": "example.ts",
"referencedSpecifier": "./bar"
},
{
"typeOnly": false,
"relativePathReference": true,
"referencingPath": "example.ts",
"referencedSpecifier": "./fizz/buzz"
},
{
"typeOnly": false,
"relativePathReference": false,
"referencingPath": "example.ts",
"referencedSpecifier": "whatever"
},
{
"typeOnly": false,
"relativePathReference": false,
"referencingPath": "example.ts",
"referencedSpecifier": "whatever"
},
{
"typeOnly": false,
"relativePathReference": false,
"referencingPath": "example.ts",
"referencedSpecifier": "xyz"
}
]
referenceSpecifierを取得したら、それを次のパスに解決し、次に解決されたファイルを使用して探索を繰り返すための基本的なロジックが必要です。