foresthouse 1.0.0-dev.1 → 1.0.0-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +32 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +35 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/tree-Cp8CNmeY.mjs +755 -0
- package/dist/tree-Cp8CNmeY.mjs.map +1 -0
- package/package.json +3 -2
- package/dist/tree-BiiNljTI.mjs +0 -276
- package/dist/tree-BiiNljTI.mjs.map +0 -1
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
import { builtinModules } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import ts from "typescript";
|
|
5
|
+
import { parseSync, visitorKeys } from "oxc-parser";
|
|
6
|
+
//#region src/config.ts
|
|
7
|
+
function loadCompilerOptions(searchFrom, explicitConfigPath) {
|
|
8
|
+
const configPath = explicitConfigPath === void 0 ? findNearestConfig(searchFrom) : path.resolve(searchFrom, explicitConfigPath);
|
|
9
|
+
if (configPath === void 0) return { compilerOptions: defaultCompilerOptions() };
|
|
10
|
+
const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
11
|
+
if (readResult.error !== void 0) throw new Error(`Failed to read TypeScript config at ${configPath}: ${formatDiagnostic(readResult.error)}`);
|
|
12
|
+
const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath), defaultCompilerOptions(), configPath);
|
|
13
|
+
if (parsed.errors.length > 0) {
|
|
14
|
+
const [firstError] = parsed.errors;
|
|
15
|
+
if (firstError === void 0) throw new Error(`Failed to parse TypeScript config at ${configPath}.`);
|
|
16
|
+
throw new Error(`Failed to parse TypeScript config at ${configPath}: ${formatDiagnostic(firstError)}`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
path: configPath,
|
|
20
|
+
compilerOptions: parsed.options
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function findNearestConfig(searchFrom) {
|
|
24
|
+
let currentDirectory = path.resolve(searchFrom);
|
|
25
|
+
while (true) {
|
|
26
|
+
const tsconfigPath = path.join(currentDirectory, "tsconfig.json");
|
|
27
|
+
if (ts.sys.fileExists(tsconfigPath)) return tsconfigPath;
|
|
28
|
+
const jsconfigPath = path.join(currentDirectory, "jsconfig.json");
|
|
29
|
+
if (ts.sys.fileExists(jsconfigPath)) return jsconfigPath;
|
|
30
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
31
|
+
if (parentDirectory === currentDirectory) return;
|
|
32
|
+
currentDirectory = parentDirectory;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function defaultCompilerOptions() {
|
|
36
|
+
return {
|
|
37
|
+
allowJs: true,
|
|
38
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
39
|
+
module: ts.ModuleKind.NodeNext,
|
|
40
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
41
|
+
target: ts.ScriptTarget.ESNext,
|
|
42
|
+
resolveJsonModule: true,
|
|
43
|
+
esModuleInterop: true
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function formatDiagnostic(diagnostic) {
|
|
47
|
+
return ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/path-utils.ts
|
|
51
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
52
|
+
".js",
|
|
53
|
+
".jsx",
|
|
54
|
+
".ts",
|
|
55
|
+
".tsx",
|
|
56
|
+
".mjs",
|
|
57
|
+
".cjs",
|
|
58
|
+
".mts",
|
|
59
|
+
".cts"
|
|
60
|
+
]);
|
|
61
|
+
function normalizeFilePath(filePath) {
|
|
62
|
+
return path.normalize(filePath);
|
|
63
|
+
}
|
|
64
|
+
function toDisplayPath(filePath, cwd) {
|
|
65
|
+
const relativePath = path.relative(cwd, filePath);
|
|
66
|
+
if (relativePath === "") return ".";
|
|
67
|
+
const normalizedPath = relativePath.split(path.sep).join("/");
|
|
68
|
+
return normalizedPath.startsWith("..") ? filePath : normalizedPath;
|
|
69
|
+
}
|
|
70
|
+
function isSourceCodeFile(filePath) {
|
|
71
|
+
return SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/analyzer.ts
|
|
75
|
+
const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
|
|
76
|
+
function analyzeDependencies(entryFile, options = {}) {
|
|
77
|
+
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
78
|
+
const resolvedEntryPath = resolveExistingPath(cwd, entryFile);
|
|
79
|
+
const { compilerOptions, path: configPath } = loadCompilerOptions(path.dirname(resolvedEntryPath), options.configPath);
|
|
80
|
+
const host = {
|
|
81
|
+
fileExists: ts.sys.fileExists,
|
|
82
|
+
readFile: ts.sys.readFile,
|
|
83
|
+
directoryExists: ts.sys.directoryExists,
|
|
84
|
+
getCurrentDirectory: () => cwd,
|
|
85
|
+
getDirectories: ts.sys.getDirectories,
|
|
86
|
+
...ts.sys.realpath === void 0 ? {} : { realpath: ts.sys.realpath }
|
|
87
|
+
};
|
|
88
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
89
|
+
visitFile(resolvedEntryPath, compilerOptions, host, nodes);
|
|
90
|
+
return {
|
|
91
|
+
cwd,
|
|
92
|
+
entryId: resolvedEntryPath,
|
|
93
|
+
nodes,
|
|
94
|
+
...configPath === void 0 ? {} : { configPath }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function graphToSerializableTree(graph) {
|
|
98
|
+
const visited = /* @__PURE__ */ new Set();
|
|
99
|
+
return serializeNode(graph.entryId, graph, visited);
|
|
100
|
+
}
|
|
101
|
+
function serializeNode(filePath, graph, visited) {
|
|
102
|
+
const node = graph.nodes.get(filePath);
|
|
103
|
+
const displayPath = toDisplayPath(filePath, graph.cwd);
|
|
104
|
+
if (node === void 0) return {
|
|
105
|
+
path: displayPath,
|
|
106
|
+
kind: "missing",
|
|
107
|
+
dependencies: []
|
|
108
|
+
};
|
|
109
|
+
if (visited.has(filePath)) return {
|
|
110
|
+
path: displayPath,
|
|
111
|
+
kind: "circular",
|
|
112
|
+
dependencies: []
|
|
113
|
+
};
|
|
114
|
+
visited.add(filePath);
|
|
115
|
+
const dependencies = node.dependencies.map((dependency) => {
|
|
116
|
+
if (dependency.kind !== "source") return {
|
|
117
|
+
specifier: dependency.specifier,
|
|
118
|
+
referenceKind: dependency.referenceKind,
|
|
119
|
+
isTypeOnly: dependency.isTypeOnly,
|
|
120
|
+
kind: dependency.kind,
|
|
121
|
+
target: dependency.kind === "missing" ? dependency.target : toDisplayPath(dependency.target, graph.cwd)
|
|
122
|
+
};
|
|
123
|
+
return {
|
|
124
|
+
specifier: dependency.specifier,
|
|
125
|
+
referenceKind: dependency.referenceKind,
|
|
126
|
+
isTypeOnly: dependency.isTypeOnly,
|
|
127
|
+
kind: dependency.kind,
|
|
128
|
+
target: toDisplayPath(dependency.target, graph.cwd),
|
|
129
|
+
node: serializeNode(dependency.target, graph, new Set(visited))
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
path: displayPath,
|
|
134
|
+
kind: filePath === graph.entryId ? "entry" : "source",
|
|
135
|
+
dependencies
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function visitFile(filePath, compilerOptions, host, nodes) {
|
|
139
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
140
|
+
if (nodes.has(normalizedPath)) return;
|
|
141
|
+
const sourceText = fs.readFileSync(normalizedPath, "utf8");
|
|
142
|
+
const dependencies = collectModuleReferences(ts.createSourceFile(normalizedPath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(normalizedPath))).map((reference) => resolveDependency(reference, normalizedPath, compilerOptions, host));
|
|
143
|
+
nodes.set(normalizedPath, {
|
|
144
|
+
id: normalizedPath,
|
|
145
|
+
dependencies
|
|
146
|
+
});
|
|
147
|
+
for (const dependency of dependencies) if (dependency.kind === "source") visitFile(dependency.target, compilerOptions, host, nodes);
|
|
148
|
+
}
|
|
149
|
+
function collectModuleReferences(sourceFile) {
|
|
150
|
+
const references = [];
|
|
151
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152
|
+
function addReference(specifier, referenceKind, isTypeOnly) {
|
|
153
|
+
const key = `${referenceKind}:${isTypeOnly ? "type" : "value"}:${specifier}`;
|
|
154
|
+
if (seen.has(key)) return;
|
|
155
|
+
seen.add(key);
|
|
156
|
+
references.push({
|
|
157
|
+
specifier,
|
|
158
|
+
referenceKind,
|
|
159
|
+
isTypeOnly
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function visit(node) {
|
|
163
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "import", node.importClause?.isTypeOnly ?? false);
|
|
164
|
+
else if (ts.isExportDeclaration(node) && node.moduleSpecifier !== void 0 && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "export", node.isTypeOnly ?? false);
|
|
165
|
+
else if (ts.isImportEqualsDeclaration(node)) {
|
|
166
|
+
const moduleReference = node.moduleReference;
|
|
167
|
+
if (ts.isExternalModuleReference(moduleReference) && moduleReference.expression !== void 0 && ts.isStringLiteralLike(moduleReference.expression)) addReference(moduleReference.expression.text, "import-equals", false);
|
|
168
|
+
} else if (ts.isCallExpression(node)) {
|
|
169
|
+
if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1) {
|
|
170
|
+
const [argument] = node.arguments;
|
|
171
|
+
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "dynamic-import", false);
|
|
172
|
+
}
|
|
173
|
+
if (ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1) {
|
|
174
|
+
const [argument] = node.arguments;
|
|
175
|
+
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "require", false);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
ts.forEachChild(node, visit);
|
|
179
|
+
}
|
|
180
|
+
visit(sourceFile);
|
|
181
|
+
return references;
|
|
182
|
+
}
|
|
183
|
+
function resolveDependency(reference, containingFile, compilerOptions, host) {
|
|
184
|
+
const specifier = reference.specifier;
|
|
185
|
+
if (BUILTIN_MODULES.has(specifier)) return createEdge(reference, "builtin", specifier);
|
|
186
|
+
const resolution = ts.resolveModuleName(specifier, containingFile, compilerOptions, host).resolvedModule;
|
|
187
|
+
if (resolution !== void 0) {
|
|
188
|
+
const resolvedPath = normalizeFilePath(resolution.resolvedFileName);
|
|
189
|
+
if (resolution.isExternalLibraryImport || resolvedPath.includes(`${path.sep}node_modules${path.sep}`)) return createEdge(reference, "external", specifier);
|
|
190
|
+
if (isSourceCodeFile(resolvedPath) && !resolvedPath.endsWith(".d.ts")) return createEdge(reference, "source", resolvedPath);
|
|
191
|
+
}
|
|
192
|
+
if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return createEdge(reference, "external", specifier);
|
|
193
|
+
return createEdge(reference, "missing", specifier);
|
|
194
|
+
}
|
|
195
|
+
function createEdge(reference, kind, target) {
|
|
196
|
+
return {
|
|
197
|
+
specifier: reference.specifier,
|
|
198
|
+
referenceKind: reference.referenceKind,
|
|
199
|
+
isTypeOnly: reference.isTypeOnly,
|
|
200
|
+
kind,
|
|
201
|
+
target
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function getScriptKind(filePath) {
|
|
205
|
+
switch (path.extname(filePath).toLowerCase()) {
|
|
206
|
+
case ".js":
|
|
207
|
+
case ".mjs":
|
|
208
|
+
case ".cjs": return ts.ScriptKind.JS;
|
|
209
|
+
case ".jsx": return ts.ScriptKind.JSX;
|
|
210
|
+
case ".tsx": return ts.ScriptKind.TSX;
|
|
211
|
+
case ".json": return ts.ScriptKind.JSON;
|
|
212
|
+
default: return ts.ScriptKind.TS;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function resolveExistingPath(cwd, entryFile) {
|
|
216
|
+
const normalizedPath = normalizeFilePath(path.resolve(cwd, entryFile));
|
|
217
|
+
if (!fs.existsSync(normalizedPath)) throw new Error(`Entry file not found: ${entryFile}`);
|
|
218
|
+
if (!isSourceCodeFile(normalizedPath)) throw new Error(`Entry file must be a JS/TS source file: ${entryFile}`);
|
|
219
|
+
return normalizedPath;
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/react-analyzer.ts
|
|
223
|
+
const FUNCTION_NODE_TYPES = new Set([
|
|
224
|
+
"FunctionDeclaration",
|
|
225
|
+
"FunctionExpression",
|
|
226
|
+
"ArrowFunctionExpression",
|
|
227
|
+
"TSDeclareFunction",
|
|
228
|
+
"TSEmptyBodyFunctionExpression"
|
|
229
|
+
]);
|
|
230
|
+
function analyzeReactUsage(entryFile, options = {}) {
|
|
231
|
+
const dependencyGraph = analyzeDependencies(entryFile, options);
|
|
232
|
+
const reachableFiles = new Set([dependencyGraph.entryId, ...dependencyGraph.nodes.keys()]);
|
|
233
|
+
const fileAnalyses = /* @__PURE__ */ new Map();
|
|
234
|
+
for (const filePath of [...reachableFiles].sort()) {
|
|
235
|
+
if (!isSourceCodeFile(filePath) || filePath.endsWith(".d.ts")) continue;
|
|
236
|
+
const parseResult = parseSync(filePath, fs.readFileSync(filePath, "utf8"), {
|
|
237
|
+
astType: "ts",
|
|
238
|
+
sourceType: "unambiguous"
|
|
239
|
+
});
|
|
240
|
+
const dependencyNode = dependencyGraph.nodes.get(filePath);
|
|
241
|
+
const sourceDependencies = /* @__PURE__ */ new Map();
|
|
242
|
+
dependencyNode?.dependencies.forEach((dependency) => {
|
|
243
|
+
if (dependency.kind === "source") sourceDependencies.set(dependency.specifier, dependency.target);
|
|
244
|
+
});
|
|
245
|
+
fileAnalyses.set(filePath, analyzeReactFile(parseResult.program, filePath, sourceDependencies));
|
|
246
|
+
}
|
|
247
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
248
|
+
for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.symbolsById.values()) nodes.set(symbol.id, {
|
|
249
|
+
id: symbol.id,
|
|
250
|
+
name: symbol.name,
|
|
251
|
+
kind: symbol.kind,
|
|
252
|
+
filePath: symbol.filePath,
|
|
253
|
+
exportNames: [...symbol.exportNames].sort(),
|
|
254
|
+
usages: []
|
|
255
|
+
});
|
|
256
|
+
for (const fileAnalysis of fileAnalyses.values()) fileAnalysis.importsByLocalName.forEach((binding, localName) => {
|
|
257
|
+
if (binding.sourcePath !== void 0) return;
|
|
258
|
+
if (!isHookName(localName) && !isHookName(binding.importedName)) return;
|
|
259
|
+
const externalNode = createExternalHookNode(binding, localName);
|
|
260
|
+
if (!nodes.has(externalNode.id)) nodes.set(externalNode.id, externalNode);
|
|
261
|
+
});
|
|
262
|
+
for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.symbolsById.values()) {
|
|
263
|
+
const usages = /* @__PURE__ */ new Map();
|
|
264
|
+
symbol.componentReferences.forEach((referenceName) => {
|
|
265
|
+
const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "component");
|
|
266
|
+
if (targetId !== void 0 && targetId !== symbol.id) usages.set(`render:${targetId}`, {
|
|
267
|
+
kind: "render",
|
|
268
|
+
target: targetId
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
symbol.hookReferences.forEach((referenceName) => {
|
|
272
|
+
const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "hook");
|
|
273
|
+
if (targetId !== void 0 && targetId !== symbol.id) usages.set(`hook:${targetId}`, {
|
|
274
|
+
kind: "hook-call",
|
|
275
|
+
target: targetId
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
const node = nodes.get(symbol.id);
|
|
279
|
+
if (node === void 0) continue;
|
|
280
|
+
const sortedUsages = [...usages.values()].sort((left, right) => compareReactNodeIds(left.target, right.target, nodes));
|
|
281
|
+
nodes.set(symbol.id, {
|
|
282
|
+
...node,
|
|
283
|
+
usages: sortedUsages
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
cwd: dependencyGraph.cwd,
|
|
288
|
+
entryId: dependencyGraph.entryId,
|
|
289
|
+
nodes
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function graphToSerializableReactTree(graph, options = {}) {
|
|
293
|
+
return {
|
|
294
|
+
kind: "react-usage",
|
|
295
|
+
roots: getReactUsageRoots(graph, options.filter).map((rootId) => serializeReactUsageNode(rootId, graph, options.filter ?? "all", /* @__PURE__ */ new Set()))
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function getReactUsageRoots(graph, filter = "all") {
|
|
299
|
+
const filteredNodes = getFilteredReactUsageNodes(graph, filter);
|
|
300
|
+
const inboundCounts = /* @__PURE__ */ new Map();
|
|
301
|
+
filteredNodes.forEach((node) => {
|
|
302
|
+
inboundCounts.set(node.id, 0);
|
|
303
|
+
});
|
|
304
|
+
filteredNodes.forEach((node) => {
|
|
305
|
+
getFilteredUsages(node, graph, filter).forEach((usage) => {
|
|
306
|
+
inboundCounts.set(usage.target, (inboundCounts.get(usage.target) ?? 0) + 1);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
const roots = filteredNodes.filter((node) => (inboundCounts.get(node.id) ?? 0) === 0).map((node) => node.id);
|
|
310
|
+
if (roots.length > 0) return roots.sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
|
|
311
|
+
return filteredNodes.map((node) => node.id).sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
|
|
312
|
+
}
|
|
313
|
+
function getFilteredUsages(node, graph, filter = "all") {
|
|
314
|
+
return node.usages.filter((usage) => {
|
|
315
|
+
const targetNode = graph.nodes.get(usage.target);
|
|
316
|
+
return targetNode !== void 0 && matchesReactFilter(targetNode, filter);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function analyzeReactFile(program, filePath, sourceDependencies) {
|
|
320
|
+
const symbolsByName = /* @__PURE__ */ new Map();
|
|
321
|
+
program.body.forEach((statement) => {
|
|
322
|
+
collectTopLevelReactSymbols(statement, filePath, symbolsByName);
|
|
323
|
+
});
|
|
324
|
+
const importsByLocalName = /* @__PURE__ */ new Map();
|
|
325
|
+
const exportsByName = /* @__PURE__ */ new Map();
|
|
326
|
+
program.body.forEach((statement) => {
|
|
327
|
+
collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName);
|
|
328
|
+
});
|
|
329
|
+
symbolsByName.forEach((symbol) => {
|
|
330
|
+
analyzeSymbolUsages(symbol);
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
filePath,
|
|
334
|
+
importsByLocalName,
|
|
335
|
+
exportsByName,
|
|
336
|
+
symbolsById: new Map([...symbolsByName.values()].map((symbol) => [symbol.id, symbol])),
|
|
337
|
+
symbolsByName
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function collectTopLevelReactSymbols(statement, filePath, symbolsByName) {
|
|
341
|
+
switch (statement.type) {
|
|
342
|
+
case "FunctionDeclaration":
|
|
343
|
+
addFunctionSymbol(statement, filePath, symbolsByName);
|
|
344
|
+
return;
|
|
345
|
+
case "VariableDeclaration":
|
|
346
|
+
statement.declarations.forEach((declarator) => {
|
|
347
|
+
addVariableSymbol(declarator, filePath, symbolsByName);
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
case "ExportNamedDeclaration":
|
|
351
|
+
if (statement.declaration !== null) collectTopLevelReactSymbols(statement.declaration, filePath, symbolsByName);
|
|
352
|
+
return;
|
|
353
|
+
case "ExportDefaultDeclaration":
|
|
354
|
+
addDefaultExportSymbol(statement, filePath, symbolsByName);
|
|
355
|
+
return;
|
|
356
|
+
default: return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function addFunctionSymbol(declaration, filePath, symbolsByName) {
|
|
360
|
+
const name = declaration.id?.name;
|
|
361
|
+
if (name === void 0) return;
|
|
362
|
+
const kind = classifyReactSymbol(name, declaration);
|
|
363
|
+
if (kind === void 0) return;
|
|
364
|
+
symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration));
|
|
365
|
+
}
|
|
366
|
+
function addVariableSymbol(declarator, filePath, symbolsByName) {
|
|
367
|
+
if (declarator.id.type !== "Identifier" || declarator.init === null) return;
|
|
368
|
+
if (declarator.init.type !== "ArrowFunctionExpression" && declarator.init.type !== "FunctionExpression") return;
|
|
369
|
+
const name = declarator.id.name;
|
|
370
|
+
const kind = classifyReactSymbol(name, declarator.init);
|
|
371
|
+
if (kind === void 0) return;
|
|
372
|
+
symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declarator.init));
|
|
373
|
+
}
|
|
374
|
+
function addDefaultExportSymbol(declaration, filePath, symbolsByName) {
|
|
375
|
+
if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") addFunctionSymbol(declaration.declaration, filePath, symbolsByName);
|
|
376
|
+
else if (declaration.declaration.type === "ArrowFunctionExpression") {
|
|
377
|
+
const name = "default";
|
|
378
|
+
const kind = declaration.declaration.body ? classifyReactSymbol(name, declaration.declaration) : void 0;
|
|
379
|
+
if (kind !== void 0) symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration.declaration));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function createPendingSymbol(filePath, name, kind, declaration) {
|
|
383
|
+
return {
|
|
384
|
+
id: `${filePath}#${kind}:${name}`,
|
|
385
|
+
name,
|
|
386
|
+
kind,
|
|
387
|
+
filePath,
|
|
388
|
+
declaration,
|
|
389
|
+
exportNames: /* @__PURE__ */ new Set(),
|
|
390
|
+
componentReferences: /* @__PURE__ */ new Set(),
|
|
391
|
+
hookReferences: /* @__PURE__ */ new Set()
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName) {
|
|
395
|
+
switch (statement.type) {
|
|
396
|
+
case "ImportDeclaration":
|
|
397
|
+
collectImportBindings(statement, sourceDependencies, importsByLocalName);
|
|
398
|
+
return;
|
|
399
|
+
case "ExportNamedDeclaration":
|
|
400
|
+
collectNamedExports(statement, symbolsByName, exportsByName);
|
|
401
|
+
return;
|
|
402
|
+
case "ExportDefaultDeclaration":
|
|
403
|
+
collectDefaultExport(statement, symbolsByName, exportsByName);
|
|
404
|
+
return;
|
|
405
|
+
default: return;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function collectImportBindings(declaration, sourceDependencies, importsByLocalName) {
|
|
409
|
+
if (declaration.importKind === "type") return;
|
|
410
|
+
const sourceSpecifier = declaration.source.value;
|
|
411
|
+
const sourcePath = sourceDependencies.get(declaration.source.value);
|
|
412
|
+
declaration.specifiers.forEach((specifier) => {
|
|
413
|
+
const binding = getImportBinding(specifier, sourceSpecifier, sourcePath);
|
|
414
|
+
if (binding === void 0) return;
|
|
415
|
+
importsByLocalName.set(binding.localName, {
|
|
416
|
+
importedName: binding.importedName,
|
|
417
|
+
sourceSpecifier: binding.sourceSpecifier,
|
|
418
|
+
...binding.sourcePath === void 0 ? {} : { sourcePath: binding.sourcePath }
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
function getImportBinding(specifier, sourceSpecifier, sourcePath) {
|
|
423
|
+
if (specifier.type === "ImportSpecifier") {
|
|
424
|
+
if (specifier.importKind === "type") return;
|
|
425
|
+
return {
|
|
426
|
+
localName: specifier.local.name,
|
|
427
|
+
importedName: toModuleExportName(specifier.imported),
|
|
428
|
+
sourceSpecifier,
|
|
429
|
+
...sourcePath === void 0 ? {} : { sourcePath }
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
if (specifier.type === "ImportDefaultSpecifier") return {
|
|
433
|
+
localName: specifier.local.name,
|
|
434
|
+
importedName: "default",
|
|
435
|
+
sourceSpecifier,
|
|
436
|
+
...sourcePath === void 0 ? {} : { sourcePath }
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function collectNamedExports(declaration, symbolsByName, exportsByName) {
|
|
440
|
+
if (declaration.exportKind === "type") return;
|
|
441
|
+
if (declaration.declaration !== null) {
|
|
442
|
+
if (declaration.declaration.type === "FunctionDeclaration") {
|
|
443
|
+
const name = declaration.declaration.id?.name;
|
|
444
|
+
if (name !== void 0) addExportBinding(name, name, symbolsByName, exportsByName);
|
|
445
|
+
} else if (declaration.declaration.type === "VariableDeclaration") declaration.declaration.declarations.forEach((declarator) => {
|
|
446
|
+
if (declarator.id.type === "Identifier") addExportBinding(declarator.id.name, declarator.id.name, symbolsByName, exportsByName);
|
|
447
|
+
});
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (declaration.source !== null) return;
|
|
451
|
+
declaration.specifiers.forEach((specifier) => {
|
|
452
|
+
if (specifier.exportKind === "type") return;
|
|
453
|
+
addExportBinding(toModuleExportName(specifier.local), toModuleExportName(specifier.exported), symbolsByName, exportsByName);
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function collectDefaultExport(declaration, symbolsByName, exportsByName) {
|
|
457
|
+
if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") {
|
|
458
|
+
const localName = declaration.declaration.id?.name;
|
|
459
|
+
if (localName !== void 0) addExportBinding(localName, "default", symbolsByName, exportsByName);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (declaration.declaration.type === "Identifier") {
|
|
463
|
+
addExportBinding(declaration.declaration.name, "default", symbolsByName, exportsByName);
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
if (declaration.declaration.type === "ArrowFunctionExpression") addExportBinding("default", "default", symbolsByName, exportsByName);
|
|
467
|
+
}
|
|
468
|
+
function addExportBinding(localName, exportedName, symbolsByName, exportsByName) {
|
|
469
|
+
const symbol = symbolsByName.get(localName);
|
|
470
|
+
if (symbol === void 0) return;
|
|
471
|
+
symbol.exportNames.add(exportedName);
|
|
472
|
+
exportsByName.set(exportedName, symbol.id);
|
|
473
|
+
}
|
|
474
|
+
function analyzeSymbolUsages(symbol) {
|
|
475
|
+
const root = symbol.declaration.type === "ArrowFunctionExpression" ? symbol.declaration.body : symbol.declaration.body;
|
|
476
|
+
if (root === null) return;
|
|
477
|
+
walkReactUsageTree(root, (node) => {
|
|
478
|
+
if (node.type === "JSXElement") {
|
|
479
|
+
const name = getComponentReferenceName(node);
|
|
480
|
+
if (name !== void 0) symbol.componentReferences.add(name);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (node.type === "CallExpression") {
|
|
484
|
+
const hookReference = getHookReferenceName(node);
|
|
485
|
+
if (hookReference !== void 0) symbol.hookReferences.add(hookReference);
|
|
486
|
+
const componentReference = getCreateElementComponentReferenceName(node);
|
|
487
|
+
if (componentReference !== void 0) symbol.componentReferences.add(componentReference);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
function classifyReactSymbol(name, declaration) {
|
|
492
|
+
if (isHookName(name)) return "hook";
|
|
493
|
+
if (isComponentName(name) && returnsReactElement(declaration)) return "component";
|
|
494
|
+
}
|
|
495
|
+
function returnsReactElement(declaration) {
|
|
496
|
+
if (declaration.type === "ArrowFunctionExpression" && declaration.expression) return containsReactElementLikeExpression(declaration.body);
|
|
497
|
+
const body = declaration.body;
|
|
498
|
+
if (body === null) return false;
|
|
499
|
+
let found = false;
|
|
500
|
+
walkReactUsageTree(body, (node) => {
|
|
501
|
+
if (node.type !== "ReturnStatement" || node.argument === null) return;
|
|
502
|
+
if (containsReactElementLikeExpression(node.argument)) found = true;
|
|
503
|
+
});
|
|
504
|
+
return found;
|
|
505
|
+
}
|
|
506
|
+
function containsReactElementLikeExpression(expression) {
|
|
507
|
+
let found = false;
|
|
508
|
+
walkNode(expression, (node) => {
|
|
509
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment" || node.type === "CallExpression" && isReactCreateElementCall(node)) found = true;
|
|
510
|
+
});
|
|
511
|
+
return found;
|
|
512
|
+
}
|
|
513
|
+
function getComponentReferenceName(node) {
|
|
514
|
+
const name = getJsxName(node.openingElement.name);
|
|
515
|
+
return name !== void 0 && isComponentName(name) ? name : void 0;
|
|
516
|
+
}
|
|
517
|
+
function getHookReferenceName(node) {
|
|
518
|
+
const calleeName = getIdentifierName(node.callee);
|
|
519
|
+
return calleeName !== void 0 && isHookName(calleeName) ? calleeName : void 0;
|
|
520
|
+
}
|
|
521
|
+
function getCreateElementComponentReferenceName(node) {
|
|
522
|
+
if (!isReactCreateElementCall(node)) return;
|
|
523
|
+
const [firstArgument] = node.arguments;
|
|
524
|
+
if (firstArgument === void 0 || firstArgument.type !== "Identifier") return;
|
|
525
|
+
return isComponentName(firstArgument.name) ? firstArgument.name : void 0;
|
|
526
|
+
}
|
|
527
|
+
function isReactCreateElementCall(node) {
|
|
528
|
+
const callee = unwrapExpression(node.callee);
|
|
529
|
+
if (callee.type !== "MemberExpression" || callee.computed) return false;
|
|
530
|
+
return callee.object.type === "Identifier" && callee.object.name === "React" && callee.property.name === "createElement";
|
|
531
|
+
}
|
|
532
|
+
function getJsxName(name) {
|
|
533
|
+
if (name.type === "JSXIdentifier") return name.name;
|
|
534
|
+
}
|
|
535
|
+
function getIdentifierName(expression) {
|
|
536
|
+
const unwrapped = unwrapExpression(expression);
|
|
537
|
+
return unwrapped.type === "Identifier" ? unwrapped.name : void 0;
|
|
538
|
+
}
|
|
539
|
+
function unwrapExpression(expression) {
|
|
540
|
+
let current = expression;
|
|
541
|
+
while (true) {
|
|
542
|
+
if (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
543
|
+
current = current.expression;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
return current;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function walkReactUsageTree(root, visit) {
|
|
550
|
+
walkNode(root, visit, true);
|
|
551
|
+
}
|
|
552
|
+
function walkNode(node, visit, allowNestedFunctions = false) {
|
|
553
|
+
visit(node);
|
|
554
|
+
const keys = visitorKeys[node.type];
|
|
555
|
+
if (keys === void 0) return;
|
|
556
|
+
keys.forEach((key) => {
|
|
557
|
+
const value = node[key];
|
|
558
|
+
walkChild(value, visit, allowNestedFunctions);
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function walkChild(value, visit, allowNestedFunctions) {
|
|
562
|
+
if (Array.isArray(value)) {
|
|
563
|
+
value.forEach((entry) => {
|
|
564
|
+
walkChild(entry, visit, allowNestedFunctions);
|
|
565
|
+
});
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (!isNode(value)) return;
|
|
569
|
+
if (!allowNestedFunctions && FUNCTION_NODE_TYPES.has(value.type)) return;
|
|
570
|
+
walkNode(value, visit, false);
|
|
571
|
+
}
|
|
572
|
+
function isNode(value) {
|
|
573
|
+
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
574
|
+
}
|
|
575
|
+
function resolveReactReference(fileAnalysis, fileAnalyses, name, kind) {
|
|
576
|
+
const localSymbol = fileAnalysis.symbolsByName.get(name);
|
|
577
|
+
if (localSymbol !== void 0 && localSymbol.kind === kind) return localSymbol.id;
|
|
578
|
+
const importBinding = fileAnalysis.importsByLocalName.get(name);
|
|
579
|
+
if (importBinding === void 0) return;
|
|
580
|
+
if (importBinding.sourcePath === void 0) return kind === "hook" ? getExternalHookNodeId(importBinding, name) : void 0;
|
|
581
|
+
const sourceFileAnalysis = fileAnalyses.get(importBinding.sourcePath);
|
|
582
|
+
if (sourceFileAnalysis === void 0) return;
|
|
583
|
+
const targetId = sourceFileAnalysis.exportsByName.get(importBinding.importedName);
|
|
584
|
+
if (targetId === void 0) return;
|
|
585
|
+
return sourceFileAnalysis.symbolsById.get(targetId)?.kind === kind ? targetId : void 0;
|
|
586
|
+
}
|
|
587
|
+
function createExternalHookNode(binding, localName) {
|
|
588
|
+
const name = getExternalHookName(binding, localName);
|
|
589
|
+
return {
|
|
590
|
+
id: getExternalHookNodeId(binding, localName),
|
|
591
|
+
name,
|
|
592
|
+
kind: "hook",
|
|
593
|
+
filePath: binding.sourceSpecifier,
|
|
594
|
+
exportNames: [binding.importedName],
|
|
595
|
+
usages: []
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function getExternalHookNodeId(binding, localName) {
|
|
599
|
+
return `external:${binding.sourceSpecifier}#hook:${getExternalHookName(binding, localName)}`;
|
|
600
|
+
}
|
|
601
|
+
function getExternalHookName(binding, localName) {
|
|
602
|
+
return binding.importedName === "default" ? localName : binding.importedName;
|
|
603
|
+
}
|
|
604
|
+
function getFilteredReactUsageNodes(graph, filter) {
|
|
605
|
+
return [...graph.nodes.values()].filter((node) => matchesReactFilter(node, filter)).sort((left, right) => compareReactNodes(left, right));
|
|
606
|
+
}
|
|
607
|
+
function matchesReactFilter(node, filter) {
|
|
608
|
+
return filter === "all" || node.kind === filter;
|
|
609
|
+
}
|
|
610
|
+
function serializeReactUsageNode(nodeId, graph, filter, visited) {
|
|
611
|
+
const node = graph.nodes.get(nodeId);
|
|
612
|
+
if (node === void 0) return {
|
|
613
|
+
id: nodeId,
|
|
614
|
+
name: nodeId,
|
|
615
|
+
symbolKind: "circular",
|
|
616
|
+
filePath: "",
|
|
617
|
+
exportNames: [],
|
|
618
|
+
usages: []
|
|
619
|
+
};
|
|
620
|
+
if (visited.has(nodeId)) return {
|
|
621
|
+
id: node.id,
|
|
622
|
+
name: node.name,
|
|
623
|
+
symbolKind: "circular",
|
|
624
|
+
filePath: toDisplayPath(node.filePath, graph.cwd),
|
|
625
|
+
exportNames: node.exportNames,
|
|
626
|
+
usages: []
|
|
627
|
+
};
|
|
628
|
+
const nextVisited = new Set(visited);
|
|
629
|
+
nextVisited.add(nodeId);
|
|
630
|
+
return {
|
|
631
|
+
id: node.id,
|
|
632
|
+
name: node.name,
|
|
633
|
+
symbolKind: node.kind,
|
|
634
|
+
filePath: toDisplayPath(node.filePath, graph.cwd),
|
|
635
|
+
exportNames: node.exportNames,
|
|
636
|
+
usages: getFilteredUsages(node, graph, filter).map((usage) => ({
|
|
637
|
+
kind: usage.kind,
|
|
638
|
+
targetId: usage.target,
|
|
639
|
+
node: serializeReactUsageNode(usage.target, graph, filter, nextVisited)
|
|
640
|
+
}))
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
function compareReactNodeIds(leftId, rightId, nodes) {
|
|
644
|
+
const left = nodes.get(leftId);
|
|
645
|
+
const right = nodes.get(rightId);
|
|
646
|
+
if (left === void 0 || right === void 0) return leftId.localeCompare(rightId);
|
|
647
|
+
return compareReactNodes(left, right);
|
|
648
|
+
}
|
|
649
|
+
function compareReactNodes(left, right) {
|
|
650
|
+
return left.filePath.localeCompare(right.filePath) || left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind);
|
|
651
|
+
}
|
|
652
|
+
function toModuleExportName(name) {
|
|
653
|
+
return name.type === "Literal" ? name.value : name.name;
|
|
654
|
+
}
|
|
655
|
+
function isHookName(name) {
|
|
656
|
+
return /^use[A-Z0-9]/.test(name);
|
|
657
|
+
}
|
|
658
|
+
function isComponentName(name) {
|
|
659
|
+
return /^[A-Z]/.test(name);
|
|
660
|
+
}
|
|
661
|
+
//#endregion
|
|
662
|
+
//#region src/react-tree.ts
|
|
663
|
+
function printReactUsageTree(graph, options = {}) {
|
|
664
|
+
const cwd = options.cwd ?? graph.cwd;
|
|
665
|
+
const filter = options.filter ?? "all";
|
|
666
|
+
const roots = getReactUsageRoots(graph, filter);
|
|
667
|
+
if (roots.length === 0) return "No React symbols found.";
|
|
668
|
+
const lines = [];
|
|
669
|
+
roots.forEach((rootId, index) => {
|
|
670
|
+
const root = graph.nodes.get(rootId);
|
|
671
|
+
if (root === void 0) return;
|
|
672
|
+
lines.push(formatReactNodeLabel(root, cwd));
|
|
673
|
+
const usages = getFilteredUsages(root, graph, filter);
|
|
674
|
+
usages.forEach((usage, usageIndex) => {
|
|
675
|
+
lines.push(...renderUsage(usage, graph, cwd, filter, new Set([root.id]), "", usageIndex === usages.length - 1));
|
|
676
|
+
});
|
|
677
|
+
if (index < roots.length - 1) lines.push("");
|
|
678
|
+
});
|
|
679
|
+
return lines.join("\n");
|
|
680
|
+
}
|
|
681
|
+
function renderUsage(usage, graph, cwd, filter, visited, prefix, isLast) {
|
|
682
|
+
const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
|
|
683
|
+
const target = graph.nodes.get(usage.target);
|
|
684
|
+
if (target === void 0) return [`${branch}${usage.target}`];
|
|
685
|
+
if (visited.has(target.id)) return [`${branch}${formatReactNodeLabel(target, cwd)} (circular)`];
|
|
686
|
+
const childLines = [`${branch}${formatReactNodeLabel(target, cwd)}`];
|
|
687
|
+
const nextVisited = new Set(visited);
|
|
688
|
+
nextVisited.add(target.id);
|
|
689
|
+
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
690
|
+
const childUsages = getFilteredUsages(target, graph, filter);
|
|
691
|
+
childUsages.forEach((childUsage, index) => {
|
|
692
|
+
childLines.push(...renderUsage(childUsage, graph, cwd, filter, nextVisited, nextPrefix, index === childUsages.length - 1));
|
|
693
|
+
});
|
|
694
|
+
return childLines;
|
|
695
|
+
}
|
|
696
|
+
function formatReactNodeLabel(node, cwd) {
|
|
697
|
+
return `${node.name} [${node.kind}] (${toDisplayPath(node.filePath, cwd)})`;
|
|
698
|
+
}
|
|
699
|
+
//#endregion
|
|
700
|
+
//#region src/tree.ts
|
|
701
|
+
function printDependencyTree(graph, options = {}) {
|
|
702
|
+
const cwd = options.cwd ?? graph.cwd;
|
|
703
|
+
const includeExternals = options.includeExternals ?? false;
|
|
704
|
+
const rootLines = [toDisplayPath(graph.entryId, cwd)];
|
|
705
|
+
const visited = new Set([graph.entryId]);
|
|
706
|
+
const entryNode = graph.nodes.get(graph.entryId);
|
|
707
|
+
if (entryNode === void 0) return rootLines.join("\n");
|
|
708
|
+
const rootDependencies = filterDependencies(entryNode.dependencies, includeExternals);
|
|
709
|
+
rootDependencies.forEach((dependency, index) => {
|
|
710
|
+
const lines = renderDependency(dependency, graph, visited, "", index === rootDependencies.length - 1, includeExternals, cwd);
|
|
711
|
+
rootLines.push(...lines);
|
|
712
|
+
});
|
|
713
|
+
return rootLines.join("\n");
|
|
714
|
+
}
|
|
715
|
+
function renderDependency(dependency, graph, visited, prefix, isLast, includeExternals, cwd) {
|
|
716
|
+
const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
|
|
717
|
+
const label = formatDependencyLabel(dependency, graph, cwd);
|
|
718
|
+
if (dependency.kind !== "source") return [`${branch}${label}`];
|
|
719
|
+
if (visited.has(dependency.target)) return [`${branch}${label} (circular)`];
|
|
720
|
+
const childNode = graph.nodes.get(dependency.target);
|
|
721
|
+
if (childNode === void 0) return [`${branch}${label}`];
|
|
722
|
+
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
723
|
+
const nextVisited = new Set(visited);
|
|
724
|
+
nextVisited.add(dependency.target);
|
|
725
|
+
const childLines = [`${branch}${label}`];
|
|
726
|
+
const childDependencies = filterDependencies(childNode.dependencies, includeExternals);
|
|
727
|
+
childDependencies.forEach((childDependency, index) => {
|
|
728
|
+
const isChildLast = index === childDependencies.length - 1;
|
|
729
|
+
childLines.push(...renderDependency(childDependency, graph, nextVisited, nextPrefix, isChildLast, includeExternals, cwd));
|
|
730
|
+
});
|
|
731
|
+
return childLines;
|
|
732
|
+
}
|
|
733
|
+
function filterDependencies(dependencies, includeExternals) {
|
|
734
|
+
return dependencies.filter((dependency) => {
|
|
735
|
+
if (dependency.kind === "source" || dependency.kind === "missing") return true;
|
|
736
|
+
return includeExternals;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function formatDependencyLabel(dependency, _graph, cwd) {
|
|
740
|
+
const prefixes = [];
|
|
741
|
+
if (dependency.isTypeOnly) prefixes.push("type");
|
|
742
|
+
if (dependency.referenceKind === "require") prefixes.push("require");
|
|
743
|
+
else if (dependency.referenceKind === "dynamic-import") prefixes.push("dynamic");
|
|
744
|
+
else if (dependency.referenceKind === "export") prefixes.push("re-export");
|
|
745
|
+
else if (dependency.referenceKind === "import-equals") prefixes.push("import=");
|
|
746
|
+
const annotation = prefixes.length > 0 ? `[${prefixes.join(", ")}] ` : "";
|
|
747
|
+
if (dependency.kind === "source") return `${annotation}${toDisplayPath(dependency.target, cwd)}`;
|
|
748
|
+
if (dependency.kind === "missing") return `${annotation}${dependency.specifier} [missing]`;
|
|
749
|
+
if (dependency.kind === "builtin") return `${annotation}${dependency.target} [builtin]`;
|
|
750
|
+
return `${annotation}${dependency.target} [external]`;
|
|
751
|
+
}
|
|
752
|
+
//#endregion
|
|
753
|
+
export { graphToSerializableReactTree as a, getReactUsageRoots as i, printReactUsageTree as n, analyzeDependencies as o, analyzeReactUsage as r, graphToSerializableTree as s, printDependencyTree as t };
|
|
754
|
+
|
|
755
|
+
//# sourceMappingURL=tree-Cp8CNmeY.mjs.map
|