foresthouse 1.0.0-dev.9 → 1.0.1-dev.1
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/README.md +111 -42
- package/dist/cli.mjs +126 -9
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +102 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/react-BPPq-E6H.mjs +2523 -0
- package/dist/react-BPPq-E6H.mjs.map +1 -0
- package/package.json +7 -2
- package/dist/react-BHPy_fw5.mjs +0 -1130
- package/dist/react-BHPy_fw5.mjs.map +0 -1
package/dist/react-BHPy_fw5.mjs
DELETED
|
@@ -1,1130 +0,0 @@
|
|
|
1
|
-
import { builtinModules } from "node:module";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { parseSync, visitorKeys } from "oxc-parser";
|
|
6
|
-
import process$1 from "node:process";
|
|
7
|
-
//#region src/typescript/config.ts
|
|
8
|
-
function loadCompilerOptions(searchFrom, explicitConfigPath) {
|
|
9
|
-
const configPath = explicitConfigPath === void 0 ? findNearestConfig(searchFrom) : path.resolve(searchFrom, explicitConfigPath);
|
|
10
|
-
if (configPath === void 0) return { compilerOptions: defaultCompilerOptions() };
|
|
11
|
-
const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
12
|
-
if (readResult.error !== void 0) throw new Error(`Failed to read TypeScript config at ${configPath}: ${formatDiagnostic(readResult.error)}`);
|
|
13
|
-
const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath), defaultCompilerOptions(), configPath);
|
|
14
|
-
if (parsed.errors.length > 0) {
|
|
15
|
-
const [firstError] = parsed.errors;
|
|
16
|
-
if (firstError === void 0) throw new Error(`Failed to parse TypeScript config at ${configPath}.`);
|
|
17
|
-
throw new Error(`Failed to parse TypeScript config at ${configPath}: ${formatDiagnostic(firstError)}`);
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
path: configPath,
|
|
21
|
-
compilerOptions: parsed.options
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function findNearestConfig(searchFrom) {
|
|
25
|
-
let currentDirectory = path.resolve(searchFrom);
|
|
26
|
-
while (true) {
|
|
27
|
-
const tsconfigPath = path.join(currentDirectory, "tsconfig.json");
|
|
28
|
-
if (ts.sys.fileExists(tsconfigPath)) return tsconfigPath;
|
|
29
|
-
const jsconfigPath = path.join(currentDirectory, "jsconfig.json");
|
|
30
|
-
if (ts.sys.fileExists(jsconfigPath)) return jsconfigPath;
|
|
31
|
-
const parentDirectory = path.dirname(currentDirectory);
|
|
32
|
-
if (parentDirectory === currentDirectory) return;
|
|
33
|
-
currentDirectory = parentDirectory;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function defaultCompilerOptions() {
|
|
37
|
-
return {
|
|
38
|
-
allowJs: true,
|
|
39
|
-
jsx: ts.JsxEmit.ReactJSX,
|
|
40
|
-
module: ts.ModuleKind.NodeNext,
|
|
41
|
-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
42
|
-
target: ts.ScriptTarget.ESNext,
|
|
43
|
-
resolveJsonModule: true,
|
|
44
|
-
esModuleInterop: true
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
function formatDiagnostic(diagnostic) {
|
|
48
|
-
return ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
49
|
-
}
|
|
50
|
-
//#endregion
|
|
51
|
-
//#region src/analyzers/base.ts
|
|
52
|
-
var BaseAnalyzer = class {
|
|
53
|
-
constructor(entryFile, options) {
|
|
54
|
-
this.entryFile = entryFile;
|
|
55
|
-
this.options = options;
|
|
56
|
-
}
|
|
57
|
-
analyze() {
|
|
58
|
-
return this.doAnalyze();
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/utils/is-source-code-file.ts
|
|
63
|
-
const SOURCE_EXTENSIONS = new Set([
|
|
64
|
-
".js",
|
|
65
|
-
".jsx",
|
|
66
|
-
".ts",
|
|
67
|
-
".tsx",
|
|
68
|
-
".mjs",
|
|
69
|
-
".cjs",
|
|
70
|
-
".mts",
|
|
71
|
-
".cts"
|
|
72
|
-
]);
|
|
73
|
-
function isSourceCodeFile(filePath) {
|
|
74
|
-
return SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
|
|
75
|
-
}
|
|
76
|
-
//#endregion
|
|
77
|
-
//#region src/utils/normalize-file-path.ts
|
|
78
|
-
function normalizeFilePath(filePath) {
|
|
79
|
-
return path.normalize(filePath);
|
|
80
|
-
}
|
|
81
|
-
//#endregion
|
|
82
|
-
//#region src/analyzers/import/entry.ts
|
|
83
|
-
function resolveExistingPath(cwd, entryFile) {
|
|
84
|
-
const normalizedPath = normalizeFilePath(path.resolve(cwd, entryFile));
|
|
85
|
-
if (!fs.existsSync(normalizedPath)) throw new Error(`Entry file not found: ${entryFile}`);
|
|
86
|
-
if (!isSourceCodeFile(normalizedPath)) throw new Error(`Entry file must be a JS/TS source file: ${entryFile}`);
|
|
87
|
-
return normalizedPath;
|
|
88
|
-
}
|
|
89
|
-
//#endregion
|
|
90
|
-
//#region src/typescript/program.ts
|
|
91
|
-
function createProgram(entryFile, compilerOptions, cwd) {
|
|
92
|
-
const host = ts.createCompilerHost(compilerOptions, true);
|
|
93
|
-
host.getCurrentDirectory = () => cwd;
|
|
94
|
-
if (ts.sys.realpath !== void 0) host.realpath = ts.sys.realpath;
|
|
95
|
-
return ts.createProgram({
|
|
96
|
-
rootNames: [entryFile],
|
|
97
|
-
options: compilerOptions,
|
|
98
|
-
host
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
function createSourceFile(filePath) {
|
|
102
|
-
const sourceText = fs.readFileSync(filePath, "utf8");
|
|
103
|
-
return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filePath));
|
|
104
|
-
}
|
|
105
|
-
function createModuleResolutionHost(cwd) {
|
|
106
|
-
return {
|
|
107
|
-
fileExists: ts.sys.fileExists,
|
|
108
|
-
readFile: ts.sys.readFile,
|
|
109
|
-
directoryExists: ts.sys.directoryExists,
|
|
110
|
-
getCurrentDirectory: () => cwd,
|
|
111
|
-
getDirectories: ts.sys.getDirectories,
|
|
112
|
-
...ts.sys.realpath === void 0 ? {} : { realpath: ts.sys.realpath }
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
function getScriptKind(filePath) {
|
|
116
|
-
switch (path.extname(filePath).toLowerCase()) {
|
|
117
|
-
case ".js":
|
|
118
|
-
case ".mjs":
|
|
119
|
-
case ".cjs": return ts.ScriptKind.JS;
|
|
120
|
-
case ".jsx": return ts.ScriptKind.JSX;
|
|
121
|
-
case ".tsx": return ts.ScriptKind.TSX;
|
|
122
|
-
case ".json": return ts.ScriptKind.JSON;
|
|
123
|
-
default: return ts.ScriptKind.TS;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
//#endregion
|
|
127
|
-
//#region src/analyzers/import/unused.ts
|
|
128
|
-
function collectUnusedImports(sourceFile, checker) {
|
|
129
|
-
const importUsage = /* @__PURE__ */ new Map();
|
|
130
|
-
const symbolToImportDeclaration = /* @__PURE__ */ new Map();
|
|
131
|
-
const importedLocalNames = /* @__PURE__ */ new Set();
|
|
132
|
-
sourceFile.statements.forEach((statement) => {
|
|
133
|
-
if (!ts.isImportDeclaration(statement) || statement.importClause === void 0) return;
|
|
134
|
-
const identifiers = getImportBindingIdentifiers(statement.importClause);
|
|
135
|
-
if (identifiers.length === 0) return;
|
|
136
|
-
importUsage.set(statement, {
|
|
137
|
-
canTrack: false,
|
|
138
|
-
used: false
|
|
139
|
-
});
|
|
140
|
-
identifiers.forEach((identifier) => {
|
|
141
|
-
importedLocalNames.add(identifier.text);
|
|
142
|
-
const symbol = tryGetSymbolAtLocation(checker, identifier);
|
|
143
|
-
if (symbol === void 0) return;
|
|
144
|
-
symbolToImportDeclaration.set(symbol, statement);
|
|
145
|
-
const state = importUsage.get(statement);
|
|
146
|
-
if (state !== void 0) state.canTrack = true;
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
function visit(node) {
|
|
150
|
-
if (ts.isImportDeclaration(node)) return;
|
|
151
|
-
if (ts.isIdentifier(node) && importedLocalNames.has(node.text) && isReferenceIdentifier(node)) {
|
|
152
|
-
const symbol = tryGetSymbolAtLocation(checker, node);
|
|
153
|
-
const declaration = symbol === void 0 ? void 0 : symbolToImportDeclaration.get(symbol);
|
|
154
|
-
if (declaration !== void 0) {
|
|
155
|
-
const state = importUsage.get(declaration);
|
|
156
|
-
if (state !== void 0) state.used = true;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
ts.forEachChild(node, visit);
|
|
160
|
-
}
|
|
161
|
-
visit(sourceFile);
|
|
162
|
-
return new Map([...importUsage.entries()].map(([declaration, state]) => [declaration, state.canTrack && !state.used]));
|
|
163
|
-
}
|
|
164
|
-
function getImportBindingIdentifiers(importClause) {
|
|
165
|
-
const identifiers = [];
|
|
166
|
-
if (importClause.name !== void 0) identifiers.push(importClause.name);
|
|
167
|
-
const namedBindings = importClause.namedBindings;
|
|
168
|
-
if (namedBindings === void 0) return identifiers;
|
|
169
|
-
if (ts.isNamespaceImport(namedBindings)) {
|
|
170
|
-
identifiers.push(namedBindings.name);
|
|
171
|
-
return identifiers;
|
|
172
|
-
}
|
|
173
|
-
namedBindings.elements.forEach((element) => {
|
|
174
|
-
identifiers.push(element.name);
|
|
175
|
-
});
|
|
176
|
-
return identifiers;
|
|
177
|
-
}
|
|
178
|
-
function isReferenceIdentifier(node) {
|
|
179
|
-
const parent = node.parent;
|
|
180
|
-
if (ts.isPropertyAccessExpression(parent) && parent.name === node) return false;
|
|
181
|
-
if (ts.isQualifiedName(parent) && parent.right === node) return false;
|
|
182
|
-
if (ts.isPropertyAssignment(parent) && parent.name === node) return false;
|
|
183
|
-
if (ts.isBindingElement(parent) && parent.propertyName === node) return false;
|
|
184
|
-
if (ts.isJsxAttribute(parent) && parent.name === node) return false;
|
|
185
|
-
if (ts.isExportSpecifier(parent)) return parent.propertyName === node || parent.propertyName === void 0;
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
function tryGetSymbolAtLocation(checker, node) {
|
|
189
|
-
try {
|
|
190
|
-
return checker.getSymbolAtLocation(node);
|
|
191
|
-
} catch {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
//#endregion
|
|
196
|
-
//#region src/analyzers/import/references.ts
|
|
197
|
-
function collectModuleReferences(sourceFile, checker) {
|
|
198
|
-
const references = /* @__PURE__ */ new Map();
|
|
199
|
-
const unusedImports = collectUnusedImports(sourceFile, checker);
|
|
200
|
-
function addReference(specifier, referenceKind, isTypeOnly, unused) {
|
|
201
|
-
const key = `${referenceKind}:${isTypeOnly ? "type" : "value"}:${specifier}`;
|
|
202
|
-
const existing = references.get(key);
|
|
203
|
-
if (existing !== void 0) {
|
|
204
|
-
if (existing.unused && !unused) references.set(key, {
|
|
205
|
-
...existing,
|
|
206
|
-
unused: false
|
|
207
|
-
});
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
references.set(key, {
|
|
211
|
-
specifier,
|
|
212
|
-
referenceKind,
|
|
213
|
-
isTypeOnly,
|
|
214
|
-
unused
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
function visit(node) {
|
|
218
|
-
if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "import", node.importClause?.isTypeOnly ?? false, unusedImports.get(node) ?? false);
|
|
219
|
-
else if (ts.isExportDeclaration(node) && node.moduleSpecifier !== void 0 && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "export", node.isTypeOnly ?? false, false);
|
|
220
|
-
else if (ts.isImportEqualsDeclaration(node)) {
|
|
221
|
-
const moduleReference = node.moduleReference;
|
|
222
|
-
if (ts.isExternalModuleReference(moduleReference) && moduleReference.expression !== void 0 && ts.isStringLiteralLike(moduleReference.expression)) addReference(moduleReference.expression.text, "import-equals", false, false);
|
|
223
|
-
} else if (ts.isCallExpression(node)) {
|
|
224
|
-
if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1) {
|
|
225
|
-
const [argument] = node.arguments;
|
|
226
|
-
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "dynamic-import", false, false);
|
|
227
|
-
}
|
|
228
|
-
if (ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1) {
|
|
229
|
-
const [argument] = node.arguments;
|
|
230
|
-
if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "require", false, false);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
ts.forEachChild(node, visit);
|
|
234
|
-
}
|
|
235
|
-
visit(sourceFile);
|
|
236
|
-
return [...references.values()];
|
|
237
|
-
}
|
|
238
|
-
//#endregion
|
|
239
|
-
//#region src/analyzers/import/resolver.ts
|
|
240
|
-
const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
|
|
241
|
-
function resolveDependency(reference, containingFile, compilerOptions, host) {
|
|
242
|
-
const specifier = reference.specifier;
|
|
243
|
-
if (BUILTIN_MODULES.has(specifier)) return createEdge(reference, "builtin", specifier);
|
|
244
|
-
const resolution = ts.resolveModuleName(specifier, containingFile, compilerOptions, host).resolvedModule;
|
|
245
|
-
if (resolution !== void 0) {
|
|
246
|
-
const resolvedPath = normalizeFilePath(resolution.resolvedFileName);
|
|
247
|
-
if (resolution.isExternalLibraryImport || resolvedPath.includes(`${path.sep}node_modules${path.sep}`)) return createEdge(reference, "external", specifier);
|
|
248
|
-
if (isSourceCodeFile(resolvedPath) && !resolvedPath.endsWith(".d.ts")) return createEdge(reference, "source", resolvedPath);
|
|
249
|
-
}
|
|
250
|
-
if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return createEdge(reference, "external", specifier);
|
|
251
|
-
return createEdge(reference, "missing", specifier);
|
|
252
|
-
}
|
|
253
|
-
function createEdge(reference, kind, target) {
|
|
254
|
-
return {
|
|
255
|
-
specifier: reference.specifier,
|
|
256
|
-
referenceKind: reference.referenceKind,
|
|
257
|
-
isTypeOnly: reference.isTypeOnly,
|
|
258
|
-
unused: reference.unused,
|
|
259
|
-
kind,
|
|
260
|
-
target
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
//#endregion
|
|
264
|
-
//#region src/analyzers/import/graph.ts
|
|
265
|
-
function buildDependencyGraph(entryPath, compilerOptions, cwd) {
|
|
266
|
-
return new DependencyGraphBuilder(entryPath, compilerOptions, cwd).build();
|
|
267
|
-
}
|
|
268
|
-
var DependencyGraphBuilder = class {
|
|
269
|
-
host;
|
|
270
|
-
nodes = /* @__PURE__ */ new Map();
|
|
271
|
-
program;
|
|
272
|
-
checker;
|
|
273
|
-
constructor(entryPath, compilerOptions, cwd) {
|
|
274
|
-
this.entryPath = entryPath;
|
|
275
|
-
this.compilerOptions = compilerOptions;
|
|
276
|
-
this.host = createModuleResolutionHost(cwd);
|
|
277
|
-
this.program = createProgram(entryPath, compilerOptions, cwd);
|
|
278
|
-
this.checker = this.program.getTypeChecker();
|
|
279
|
-
}
|
|
280
|
-
build() {
|
|
281
|
-
this.visitFile(this.entryPath);
|
|
282
|
-
return this.nodes;
|
|
283
|
-
}
|
|
284
|
-
visitFile(filePath) {
|
|
285
|
-
const normalizedPath = normalizeFilePath(filePath);
|
|
286
|
-
if (this.nodes.has(normalizedPath)) return;
|
|
287
|
-
const dependencies = collectModuleReferences(this.program.getSourceFile(normalizedPath) ?? createSourceFile(normalizedPath), this.checker).map((reference) => resolveDependency(reference, normalizedPath, this.compilerOptions, this.host));
|
|
288
|
-
this.nodes.set(normalizedPath, {
|
|
289
|
-
id: normalizedPath,
|
|
290
|
-
dependencies
|
|
291
|
-
});
|
|
292
|
-
for (const dependency of dependencies) if (dependency.kind === "source") this.visitFile(dependency.target);
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
//#endregion
|
|
296
|
-
//#region src/analyzers/import/index.ts
|
|
297
|
-
function analyzeDependencies(entryFile, options = {}) {
|
|
298
|
-
return new ImportAnalyzer(entryFile, options).analyze();
|
|
299
|
-
}
|
|
300
|
-
var ImportAnalyzer = class extends BaseAnalyzer {
|
|
301
|
-
cwd;
|
|
302
|
-
entryPath;
|
|
303
|
-
constructor(entryFile, options) {
|
|
304
|
-
super(entryFile, options);
|
|
305
|
-
this.cwd = path.resolve(options.cwd ?? process.cwd());
|
|
306
|
-
this.entryPath = resolveExistingPath(this.cwd, entryFile);
|
|
307
|
-
}
|
|
308
|
-
doAnalyze() {
|
|
309
|
-
const { compilerOptions, path: configPath } = loadCompilerOptions(path.dirname(this.entryPath), this.options.configPath);
|
|
310
|
-
const nodes = buildDependencyGraph(this.entryPath, compilerOptions, this.cwd);
|
|
311
|
-
return {
|
|
312
|
-
cwd: this.cwd,
|
|
313
|
-
entryId: this.entryPath,
|
|
314
|
-
nodes,
|
|
315
|
-
...configPath === void 0 ? {} : { configPath }
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
//#endregion
|
|
320
|
-
//#region src/analyzers/react/bindings.ts
|
|
321
|
-
function collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName) {
|
|
322
|
-
switch (statement.type) {
|
|
323
|
-
case "ImportDeclaration":
|
|
324
|
-
collectImportBindings(statement, sourceDependencies, importsByLocalName);
|
|
325
|
-
return;
|
|
326
|
-
case "ExportNamedDeclaration":
|
|
327
|
-
collectNamedExports(statement, symbolsByName, exportsByName);
|
|
328
|
-
return;
|
|
329
|
-
case "ExportDefaultDeclaration":
|
|
330
|
-
collectDefaultExport(statement, symbolsByName, exportsByName);
|
|
331
|
-
return;
|
|
332
|
-
default: return;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function collectImportBindings(declaration, sourceDependencies, importsByLocalName) {
|
|
336
|
-
if (declaration.importKind === "type") return;
|
|
337
|
-
const sourceSpecifier = declaration.source.value;
|
|
338
|
-
const sourcePath = sourceDependencies.get(declaration.source.value);
|
|
339
|
-
declaration.specifiers.forEach((specifier) => {
|
|
340
|
-
const binding = getImportBinding(specifier, sourceSpecifier, sourcePath);
|
|
341
|
-
if (binding === void 0) return;
|
|
342
|
-
importsByLocalName.set(binding.localName, {
|
|
343
|
-
importedName: binding.importedName,
|
|
344
|
-
sourceSpecifier: binding.sourceSpecifier,
|
|
345
|
-
...binding.sourcePath === void 0 ? {} : { sourcePath: binding.sourcePath }
|
|
346
|
-
});
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
function getImportBinding(specifier, sourceSpecifier, sourcePath) {
|
|
350
|
-
if (specifier.type === "ImportSpecifier") {
|
|
351
|
-
if (specifier.importKind === "type") return;
|
|
352
|
-
return {
|
|
353
|
-
localName: specifier.local.name,
|
|
354
|
-
importedName: toModuleExportName(specifier.imported),
|
|
355
|
-
sourceSpecifier,
|
|
356
|
-
...sourcePath === void 0 ? {} : { sourcePath }
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
if (specifier.type === "ImportDefaultSpecifier") return {
|
|
360
|
-
localName: specifier.local.name,
|
|
361
|
-
importedName: "default",
|
|
362
|
-
sourceSpecifier,
|
|
363
|
-
...sourcePath === void 0 ? {} : { sourcePath }
|
|
364
|
-
};
|
|
365
|
-
}
|
|
366
|
-
function collectNamedExports(declaration, symbolsByName, exportsByName) {
|
|
367
|
-
if (declaration.exportKind === "type") return;
|
|
368
|
-
if (declaration.declaration !== null) {
|
|
369
|
-
if (declaration.declaration.type === "FunctionDeclaration") {
|
|
370
|
-
const name = declaration.declaration.id?.name;
|
|
371
|
-
if (name !== void 0) addExportBinding(name, name, symbolsByName, exportsByName);
|
|
372
|
-
} else if (declaration.declaration.type === "VariableDeclaration") declaration.declaration.declarations.forEach((declarator) => {
|
|
373
|
-
if (declarator.id.type === "Identifier") addExportBinding(declarator.id.name, declarator.id.name, symbolsByName, exportsByName);
|
|
374
|
-
});
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
if (declaration.source !== null) return;
|
|
378
|
-
declaration.specifiers.forEach((specifier) => {
|
|
379
|
-
if (specifier.exportKind === "type") return;
|
|
380
|
-
addExportBinding(toModuleExportName(specifier.local), toModuleExportName(specifier.exported), symbolsByName, exportsByName);
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
function collectDefaultExport(declaration, symbolsByName, exportsByName) {
|
|
384
|
-
if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") {
|
|
385
|
-
const localName = declaration.declaration.id?.name;
|
|
386
|
-
if (localName !== void 0) addExportBinding(localName, "default", symbolsByName, exportsByName);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
if (declaration.declaration.type === "Identifier") {
|
|
390
|
-
addExportBinding(declaration.declaration.name, "default", symbolsByName, exportsByName);
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
if (declaration.declaration.type === "ArrowFunctionExpression") addExportBinding("default", "default", symbolsByName, exportsByName);
|
|
394
|
-
}
|
|
395
|
-
function addExportBinding(localName, exportedName, symbolsByName, exportsByName) {
|
|
396
|
-
const symbol = symbolsByName.get(localName);
|
|
397
|
-
if (symbol === void 0) return;
|
|
398
|
-
symbol.exportNames.add(exportedName);
|
|
399
|
-
exportsByName.set(exportedName, symbol.id);
|
|
400
|
-
}
|
|
401
|
-
function toModuleExportName(name) {
|
|
402
|
-
return name.type === "Literal" ? name.value : name.name;
|
|
403
|
-
}
|
|
404
|
-
//#endregion
|
|
405
|
-
//#region src/analyzers/react/walk.ts
|
|
406
|
-
const FUNCTION_NODE_TYPES = new Set([
|
|
407
|
-
"FunctionDeclaration",
|
|
408
|
-
"FunctionExpression",
|
|
409
|
-
"ArrowFunctionExpression",
|
|
410
|
-
"TSDeclareFunction",
|
|
411
|
-
"TSEmptyBodyFunctionExpression"
|
|
412
|
-
]);
|
|
413
|
-
function walkReactUsageTree(root, visit) {
|
|
414
|
-
walkNode(root, visit, true);
|
|
415
|
-
}
|
|
416
|
-
function walkNode(node, visit, allowNestedFunctions = false) {
|
|
417
|
-
visit(node);
|
|
418
|
-
const keys = visitorKeys[node.type];
|
|
419
|
-
if (keys === void 0) return;
|
|
420
|
-
keys.forEach((key) => {
|
|
421
|
-
const value = node[key];
|
|
422
|
-
walkChild(value, visit, allowNestedFunctions);
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
function walkChild(value, visit, allowNestedFunctions) {
|
|
426
|
-
if (Array.isArray(value)) {
|
|
427
|
-
value.forEach((entry) => {
|
|
428
|
-
walkChild(entry, visit, allowNestedFunctions);
|
|
429
|
-
});
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
if (!isNode(value)) return;
|
|
433
|
-
if (!allowNestedFunctions && FUNCTION_NODE_TYPES.has(value.type)) return;
|
|
434
|
-
walkNode(value, visit, false);
|
|
435
|
-
}
|
|
436
|
-
function isNode(value) {
|
|
437
|
-
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
438
|
-
}
|
|
439
|
-
function classifyReactSymbol(name, declaration) {
|
|
440
|
-
if (isHookName(name)) return "hook";
|
|
441
|
-
if (isComponentName(name) && returnsReactElement(declaration)) return "component";
|
|
442
|
-
}
|
|
443
|
-
function containsReactElementLikeExpression(expression) {
|
|
444
|
-
let found = false;
|
|
445
|
-
walkNode(expression, (node) => {
|
|
446
|
-
if (node.type === "JSXElement" || node.type === "JSXFragment" || node.type === "CallExpression" && isReactCreateElementCall(node)) found = true;
|
|
447
|
-
});
|
|
448
|
-
return found;
|
|
449
|
-
}
|
|
450
|
-
function getComponentReferenceName(node) {
|
|
451
|
-
const name = getJsxName(node.openingElement.name);
|
|
452
|
-
return name !== void 0 && isComponentName(name) ? name : void 0;
|
|
453
|
-
}
|
|
454
|
-
function getHookReferenceName(node) {
|
|
455
|
-
const calleeName = getIdentifierName(node.callee);
|
|
456
|
-
return calleeName !== void 0 && isHookName(calleeName) ? calleeName : void 0;
|
|
457
|
-
}
|
|
458
|
-
function getCreateElementComponentReferenceName(node) {
|
|
459
|
-
if (!isReactCreateElementCall(node)) return;
|
|
460
|
-
const [firstArgument] = node.arguments;
|
|
461
|
-
if (firstArgument === void 0 || firstArgument.type !== "Identifier") return;
|
|
462
|
-
return isComponentName(firstArgument.name) ? firstArgument.name : void 0;
|
|
463
|
-
}
|
|
464
|
-
function isHookName(name) {
|
|
465
|
-
return /^use[A-Z0-9]/.test(name);
|
|
466
|
-
}
|
|
467
|
-
function isComponentName(name) {
|
|
468
|
-
return /^[A-Z]/.test(name);
|
|
469
|
-
}
|
|
470
|
-
function returnsReactElement(declaration) {
|
|
471
|
-
if (declaration.type === "ArrowFunctionExpression" && declaration.expression) return containsReactElementLikeExpression(declaration.body);
|
|
472
|
-
const body = declaration.body;
|
|
473
|
-
if (body === null) return false;
|
|
474
|
-
let found = false;
|
|
475
|
-
walkReactUsageTree(body, (node) => {
|
|
476
|
-
if (node.type !== "ReturnStatement" || node.argument === null) return;
|
|
477
|
-
if (containsReactElementLikeExpression(node.argument)) found = true;
|
|
478
|
-
});
|
|
479
|
-
return found;
|
|
480
|
-
}
|
|
481
|
-
function isReactCreateElementCall(node) {
|
|
482
|
-
const callee = unwrapExpression(node.callee);
|
|
483
|
-
if (callee.type !== "MemberExpression" || callee.computed) return false;
|
|
484
|
-
return callee.object.type === "Identifier" && callee.object.name === "React" && callee.property.name === "createElement";
|
|
485
|
-
}
|
|
486
|
-
function getJsxName(name) {
|
|
487
|
-
if (name.type === "JSXIdentifier") return name.name;
|
|
488
|
-
}
|
|
489
|
-
function getIdentifierName(expression) {
|
|
490
|
-
const unwrapped = unwrapExpression(expression);
|
|
491
|
-
return unwrapped.type === "Identifier" ? unwrapped.name : void 0;
|
|
492
|
-
}
|
|
493
|
-
function unwrapExpression(expression) {
|
|
494
|
-
let current = expression;
|
|
495
|
-
while (true) {
|
|
496
|
-
if (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
497
|
-
current = current.expression;
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
return current;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
//#endregion
|
|
504
|
-
//#region src/analyzers/react/entries.ts
|
|
505
|
-
function collectEntryUsages(program, filePath, sourceText, includeNestedFunctions) {
|
|
506
|
-
const entries = /* @__PURE__ */ new Map();
|
|
507
|
-
program.body.forEach((statement) => {
|
|
508
|
-
collectStatementEntryUsages(statement, filePath, sourceText, entries, includeNestedFunctions);
|
|
509
|
-
});
|
|
510
|
-
return [...entries.values()].sort(comparePendingReactUsageEntries);
|
|
511
|
-
}
|
|
512
|
-
function collectStatementEntryUsages(statement, filePath, sourceText, entries, includeNestedFunctions) {
|
|
513
|
-
collectNodeEntryUsages(statement, filePath, sourceText, entries, false, includeNestedFunctions);
|
|
514
|
-
}
|
|
515
|
-
function collectNodeEntryUsages(node, filePath, sourceText, entries, hasComponentAncestor, includeNestedFunctions) {
|
|
516
|
-
if (!includeNestedFunctions && FUNCTION_NODE_TYPES.has(node.type)) return;
|
|
517
|
-
let nextHasComponentAncestor = hasComponentAncestor;
|
|
518
|
-
if (node.type === "JSXElement") {
|
|
519
|
-
const referenceName = getComponentReferenceName(node);
|
|
520
|
-
if (referenceName !== void 0) {
|
|
521
|
-
if (!hasComponentAncestor) addPendingReactUsageEntry(entries, referenceName, "component", createReactUsageLocation(filePath, sourceText, node.start));
|
|
522
|
-
nextHasComponentAncestor = true;
|
|
523
|
-
}
|
|
524
|
-
} else if (node.type === "CallExpression") {
|
|
525
|
-
const hookReference = getHookReferenceName(node);
|
|
526
|
-
if (hookReference !== void 0) addPendingReactUsageEntry(entries, hookReference, "hook", createReactUsageLocation(filePath, sourceText, node.start));
|
|
527
|
-
const referenceName = getCreateElementComponentReferenceName(node);
|
|
528
|
-
if (referenceName !== void 0) {
|
|
529
|
-
if (!hasComponentAncestor) addPendingReactUsageEntry(entries, referenceName, "component", createReactUsageLocation(filePath, sourceText, node.start));
|
|
530
|
-
nextHasComponentAncestor = true;
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
const keys = visitorKeys[node.type];
|
|
534
|
-
if (keys === void 0) return;
|
|
535
|
-
keys.forEach((key) => {
|
|
536
|
-
const value = node[key];
|
|
537
|
-
collectEntryUsageChild(value, filePath, sourceText, entries, nextHasComponentAncestor, includeNestedFunctions);
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
function collectEntryUsageChild(value, filePath, sourceText, entries, hasComponentAncestor, includeNestedFunctions) {
|
|
541
|
-
if (Array.isArray(value)) {
|
|
542
|
-
value.forEach((entry) => {
|
|
543
|
-
collectEntryUsageChild(entry, filePath, sourceText, entries, hasComponentAncestor, includeNestedFunctions);
|
|
544
|
-
});
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (!isNode(value)) return;
|
|
548
|
-
collectNodeEntryUsages(value, filePath, sourceText, entries, hasComponentAncestor, includeNestedFunctions);
|
|
549
|
-
}
|
|
550
|
-
function addPendingReactUsageEntry(entries, referenceName, kind, location) {
|
|
551
|
-
const key = `${location.filePath}:${location.line}:${location.column}:${kind}:${referenceName}`;
|
|
552
|
-
entries.set(key, {
|
|
553
|
-
referenceName,
|
|
554
|
-
kind,
|
|
555
|
-
location
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
function createReactUsageLocation(filePath, sourceText, offset) {
|
|
559
|
-
return {
|
|
560
|
-
filePath,
|
|
561
|
-
...offsetToLineAndColumn(sourceText, offset)
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
function offsetToLineAndColumn(sourceText, offset) {
|
|
565
|
-
let line = 1;
|
|
566
|
-
let column = 1;
|
|
567
|
-
for (let index = 0; index < offset && index < sourceText.length; index += 1) {
|
|
568
|
-
if (sourceText[index] === "\n") {
|
|
569
|
-
line += 1;
|
|
570
|
-
column = 1;
|
|
571
|
-
continue;
|
|
572
|
-
}
|
|
573
|
-
column += 1;
|
|
574
|
-
}
|
|
575
|
-
return {
|
|
576
|
-
line,
|
|
577
|
-
column
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
function comparePendingReactUsageEntries(left, right) {
|
|
581
|
-
return left.location.filePath.localeCompare(right.location.filePath) || left.location.line - right.location.line || left.location.column - right.location.column || left.kind.localeCompare(right.kind) || left.referenceName.localeCompare(right.referenceName);
|
|
582
|
-
}
|
|
583
|
-
//#endregion
|
|
584
|
-
//#region src/analyzers/react/symbols.ts
|
|
585
|
-
function collectTopLevelReactSymbols(statement, filePath, symbolsByName) {
|
|
586
|
-
switch (statement.type) {
|
|
587
|
-
case "FunctionDeclaration":
|
|
588
|
-
addFunctionSymbol(statement, filePath, symbolsByName);
|
|
589
|
-
return;
|
|
590
|
-
case "VariableDeclaration":
|
|
591
|
-
statement.declarations.forEach((declarator) => {
|
|
592
|
-
addVariableSymbol(declarator, filePath, symbolsByName);
|
|
593
|
-
});
|
|
594
|
-
return;
|
|
595
|
-
case "ExportNamedDeclaration":
|
|
596
|
-
if (statement.declaration !== null) collectTopLevelReactSymbols(statement.declaration, filePath, symbolsByName);
|
|
597
|
-
return;
|
|
598
|
-
case "ExportDefaultDeclaration":
|
|
599
|
-
addDefaultExportSymbol(statement, filePath, symbolsByName);
|
|
600
|
-
return;
|
|
601
|
-
default: return;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
function addFunctionSymbol(declaration, filePath, symbolsByName) {
|
|
605
|
-
const name = declaration.id?.name;
|
|
606
|
-
if (name === void 0) return;
|
|
607
|
-
const kind = classifyReactSymbol(name, declaration);
|
|
608
|
-
if (kind === void 0) return;
|
|
609
|
-
symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration));
|
|
610
|
-
}
|
|
611
|
-
function addVariableSymbol(declarator, filePath, symbolsByName) {
|
|
612
|
-
if (declarator.id.type !== "Identifier" || declarator.init === null) return;
|
|
613
|
-
if (declarator.init.type !== "ArrowFunctionExpression" && declarator.init.type !== "FunctionExpression") return;
|
|
614
|
-
const name = declarator.id.name;
|
|
615
|
-
const kind = classifyReactSymbol(name, declarator.init);
|
|
616
|
-
if (kind === void 0) return;
|
|
617
|
-
symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declarator.init));
|
|
618
|
-
}
|
|
619
|
-
function addDefaultExportSymbol(declaration, filePath, symbolsByName) {
|
|
620
|
-
if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") addFunctionSymbol(declaration.declaration, filePath, symbolsByName);
|
|
621
|
-
else if (declaration.declaration.type === "ArrowFunctionExpression") {
|
|
622
|
-
const name = "default";
|
|
623
|
-
const kind = declaration.declaration.body ? classifyReactSymbol(name, declaration.declaration) : void 0;
|
|
624
|
-
if (kind !== void 0) symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration.declaration));
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function createPendingSymbol(filePath, name, kind, declaration) {
|
|
628
|
-
return {
|
|
629
|
-
id: `${filePath}#${kind}:${name}`,
|
|
630
|
-
name,
|
|
631
|
-
kind,
|
|
632
|
-
filePath,
|
|
633
|
-
declaration,
|
|
634
|
-
exportNames: /* @__PURE__ */ new Set(),
|
|
635
|
-
componentReferences: /* @__PURE__ */ new Set(),
|
|
636
|
-
hookReferences: /* @__PURE__ */ new Set()
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
//#endregion
|
|
640
|
-
//#region src/analyzers/react/usage.ts
|
|
641
|
-
function analyzeSymbolUsages(symbol) {
|
|
642
|
-
const root = symbol.declaration.type === "ArrowFunctionExpression" ? symbol.declaration.body : symbol.declaration.body;
|
|
643
|
-
if (root === null) return;
|
|
644
|
-
walkReactUsageTree(root, (node) => {
|
|
645
|
-
if (node.type === "JSXElement") {
|
|
646
|
-
const name = getComponentReferenceName(node);
|
|
647
|
-
if (name !== void 0) symbol.componentReferences.add(name);
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
if (node.type === "CallExpression") {
|
|
651
|
-
const hookReference = getHookReferenceName(node);
|
|
652
|
-
if (hookReference !== void 0) symbol.hookReferences.add(hookReference);
|
|
653
|
-
const componentReference = getCreateElementComponentReferenceName(node);
|
|
654
|
-
if (componentReference !== void 0) symbol.componentReferences.add(componentReference);
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
//#endregion
|
|
659
|
-
//#region src/analyzers/react/file.ts
|
|
660
|
-
function analyzeReactFile(program, filePath, sourceText, includeNestedRenderEntries, sourceDependencies) {
|
|
661
|
-
const symbolsByName = /* @__PURE__ */ new Map();
|
|
662
|
-
program.body.forEach((statement) => {
|
|
663
|
-
collectTopLevelReactSymbols(statement, filePath, symbolsByName);
|
|
664
|
-
});
|
|
665
|
-
const importsByLocalName = /* @__PURE__ */ new Map();
|
|
666
|
-
const exportsByName = /* @__PURE__ */ new Map();
|
|
667
|
-
const entryUsages = collectEntryUsages(program, filePath, sourceText, includeNestedRenderEntries);
|
|
668
|
-
program.body.forEach((statement) => {
|
|
669
|
-
collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName);
|
|
670
|
-
});
|
|
671
|
-
symbolsByName.forEach((symbol) => {
|
|
672
|
-
analyzeSymbolUsages(symbol);
|
|
673
|
-
});
|
|
674
|
-
return {
|
|
675
|
-
filePath,
|
|
676
|
-
importsByLocalName,
|
|
677
|
-
exportsByName,
|
|
678
|
-
entryUsages,
|
|
679
|
-
symbolsById: new Map([...symbolsByName.values()].map((symbol) => [symbol.id, symbol])),
|
|
680
|
-
symbolsByName
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
//#endregion
|
|
684
|
-
//#region src/analyzers/react/references.ts
|
|
685
|
-
function resolveReactReference(fileAnalysis, fileAnalyses, name, kind) {
|
|
686
|
-
const localSymbol = fileAnalysis.symbolsByName.get(name);
|
|
687
|
-
if (localSymbol !== void 0 && localSymbol.kind === kind) return localSymbol.id;
|
|
688
|
-
const importBinding = fileAnalysis.importsByLocalName.get(name);
|
|
689
|
-
if (importBinding === void 0) return;
|
|
690
|
-
if (importBinding.sourcePath === void 0) return kind === "hook" ? getExternalHookNodeId(importBinding, name) : void 0;
|
|
691
|
-
const sourceFileAnalysis = fileAnalyses.get(importBinding.sourcePath);
|
|
692
|
-
if (sourceFileAnalysis === void 0) return;
|
|
693
|
-
const targetId = sourceFileAnalysis.exportsByName.get(importBinding.importedName);
|
|
694
|
-
if (targetId === void 0) return;
|
|
695
|
-
return sourceFileAnalysis.symbolsById.get(targetId)?.kind === kind ? targetId : void 0;
|
|
696
|
-
}
|
|
697
|
-
function addExternalHookNodes(fileAnalyses, nodes) {
|
|
698
|
-
for (const fileAnalysis of fileAnalyses.values()) fileAnalysis.importsByLocalName.forEach((binding, localName) => {
|
|
699
|
-
if (binding.sourcePath !== void 0) return;
|
|
700
|
-
if (!isHookName(localName) && !isHookName(binding.importedName)) return;
|
|
701
|
-
const externalNode = createExternalHookNode(binding, localName);
|
|
702
|
-
if (!nodes.has(externalNode.id)) nodes.set(externalNode.id, externalNode);
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
function createExternalHookNode(binding, localName) {
|
|
706
|
-
const name = getExternalHookName(binding, localName);
|
|
707
|
-
return {
|
|
708
|
-
id: getExternalHookNodeId(binding, localName),
|
|
709
|
-
name,
|
|
710
|
-
kind: "hook",
|
|
711
|
-
filePath: binding.sourceSpecifier,
|
|
712
|
-
exportNames: [binding.importedName],
|
|
713
|
-
usages: []
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
function getExternalHookNodeId(binding, localName) {
|
|
717
|
-
return `external:${binding.sourceSpecifier}#hook:${getExternalHookName(binding, localName)}`;
|
|
718
|
-
}
|
|
719
|
-
function getExternalHookName(binding, localName) {
|
|
720
|
-
return binding.importedName === "default" ? localName : binding.importedName;
|
|
721
|
-
}
|
|
722
|
-
function compareReactNodeIds(leftId, rightId, nodes) {
|
|
723
|
-
const left = nodes.get(leftId);
|
|
724
|
-
const right = nodes.get(rightId);
|
|
725
|
-
if (left === void 0 || right === void 0) return leftId.localeCompare(rightId);
|
|
726
|
-
return compareReactNodes(left, right);
|
|
727
|
-
}
|
|
728
|
-
function compareReactUsageEntries(left, right, nodes) {
|
|
729
|
-
return left.location.filePath.localeCompare(right.location.filePath) || left.location.line - right.location.line || left.location.column - right.location.column || left.referenceName.localeCompare(right.referenceName) || compareReactNodeIds(left.target, right.target, nodes);
|
|
730
|
-
}
|
|
731
|
-
function compareReactNodes(left, right) {
|
|
732
|
-
return left.filePath.localeCompare(right.filePath) || left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind);
|
|
733
|
-
}
|
|
734
|
-
//#endregion
|
|
735
|
-
//#region src/analyzers/react/index.ts
|
|
736
|
-
function analyzeReactUsage(entryFile, options = {}) {
|
|
737
|
-
return new ReactAnalyzer(entryFile, options).analyze();
|
|
738
|
-
}
|
|
739
|
-
var ReactAnalyzer = class extends BaseAnalyzer {
|
|
740
|
-
doAnalyze() {
|
|
741
|
-
const dependencyGraph = analyzeDependencies(this.entryFile, this.options);
|
|
742
|
-
const fileAnalyses = this.collectFileAnalyses(dependencyGraph);
|
|
743
|
-
const nodes = this.createNodes(fileAnalyses);
|
|
744
|
-
this.attachUsages(fileAnalyses, nodes);
|
|
745
|
-
const entries = this.collectEntries(fileAnalyses, nodes);
|
|
746
|
-
return {
|
|
747
|
-
cwd: dependencyGraph.cwd,
|
|
748
|
-
entryId: dependencyGraph.entryId,
|
|
749
|
-
nodes,
|
|
750
|
-
entries
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
collectFileAnalyses(dependencyGraph) {
|
|
754
|
-
const reachableFiles = new Set([dependencyGraph.entryId, ...dependencyGraph.nodes.keys()]);
|
|
755
|
-
const fileAnalyses = /* @__PURE__ */ new Map();
|
|
756
|
-
for (const filePath of [...reachableFiles].sort()) {
|
|
757
|
-
if (!isSourceCodeFile(filePath) || filePath.endsWith(".d.ts")) continue;
|
|
758
|
-
const sourceText = fs.readFileSync(filePath, "utf8");
|
|
759
|
-
const parseResult = parseSync(filePath, sourceText, {
|
|
760
|
-
astType: "ts",
|
|
761
|
-
sourceType: "unambiguous"
|
|
762
|
-
});
|
|
763
|
-
const dependencyNode = dependencyGraph.nodes.get(filePath);
|
|
764
|
-
const sourceDependencies = /* @__PURE__ */ new Map();
|
|
765
|
-
dependencyNode?.dependencies.forEach((dependency) => {
|
|
766
|
-
if (dependency.kind === "source") sourceDependencies.set(dependency.specifier, dependency.target);
|
|
767
|
-
});
|
|
768
|
-
fileAnalyses.set(filePath, analyzeReactFile(parseResult.program, filePath, sourceText, filePath === dependencyGraph.entryId, sourceDependencies));
|
|
769
|
-
}
|
|
770
|
-
return fileAnalyses;
|
|
771
|
-
}
|
|
772
|
-
createNodes(fileAnalyses) {
|
|
773
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
774
|
-
for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.symbolsById.values()) nodes.set(symbol.id, {
|
|
775
|
-
id: symbol.id,
|
|
776
|
-
name: symbol.name,
|
|
777
|
-
kind: symbol.kind,
|
|
778
|
-
filePath: symbol.filePath,
|
|
779
|
-
exportNames: [...symbol.exportNames].sort(),
|
|
780
|
-
usages: []
|
|
781
|
-
});
|
|
782
|
-
addExternalHookNodes(fileAnalyses, nodes);
|
|
783
|
-
return nodes;
|
|
784
|
-
}
|
|
785
|
-
attachUsages(fileAnalyses, nodes) {
|
|
786
|
-
for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.symbolsById.values()) {
|
|
787
|
-
const usages = /* @__PURE__ */ new Map();
|
|
788
|
-
symbol.componentReferences.forEach((referenceName) => {
|
|
789
|
-
const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "component");
|
|
790
|
-
if (targetId !== void 0 && targetId !== symbol.id) usages.set(`render:${targetId}:${referenceName}`, {
|
|
791
|
-
kind: "render",
|
|
792
|
-
target: targetId,
|
|
793
|
-
referenceName
|
|
794
|
-
});
|
|
795
|
-
});
|
|
796
|
-
symbol.hookReferences.forEach((referenceName) => {
|
|
797
|
-
const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "hook");
|
|
798
|
-
if (targetId !== void 0 && targetId !== symbol.id) usages.set(`hook:${targetId}:${referenceName}`, {
|
|
799
|
-
kind: "hook-call",
|
|
800
|
-
target: targetId,
|
|
801
|
-
referenceName
|
|
802
|
-
});
|
|
803
|
-
});
|
|
804
|
-
const node = nodes.get(symbol.id);
|
|
805
|
-
if (node === void 0) continue;
|
|
806
|
-
const sortedUsages = [...usages.values()].sort((left, right) => compareReactNodeIds(left.target, right.target, nodes));
|
|
807
|
-
nodes.set(symbol.id, {
|
|
808
|
-
...node,
|
|
809
|
-
usages: sortedUsages
|
|
810
|
-
});
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
collectEntries(fileAnalyses, nodes) {
|
|
814
|
-
const entriesByKey = /* @__PURE__ */ new Map();
|
|
815
|
-
for (const fileAnalysis of fileAnalyses.values()) for (const entry of fileAnalysis.entryUsages) {
|
|
816
|
-
const targetId = resolveReactReference(fileAnalysis, fileAnalyses, entry.referenceName, entry.kind);
|
|
817
|
-
if (targetId === void 0) continue;
|
|
818
|
-
const key = `${entry.location.filePath}:${entry.location.line}:${entry.location.column}:${targetId}`;
|
|
819
|
-
entriesByKey.set(key, {
|
|
820
|
-
target: targetId,
|
|
821
|
-
referenceName: entry.referenceName,
|
|
822
|
-
location: entry.location
|
|
823
|
-
});
|
|
824
|
-
}
|
|
825
|
-
return [...entriesByKey.values()].sort((left, right) => compareReactUsageEntries(left, right, nodes));
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
//#endregion
|
|
829
|
-
//#region src/analyzers/react/queries.ts
|
|
830
|
-
function getReactUsageEntries(graph, filter = "all") {
|
|
831
|
-
return graph.entries.filter((entry) => {
|
|
832
|
-
const targetNode = graph.nodes.get(entry.target);
|
|
833
|
-
return targetNode !== void 0 && matchesReactFilter(targetNode, filter);
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
function getReactUsageRoots(graph, filter = "all") {
|
|
837
|
-
const entries = getReactUsageEntries(graph, filter);
|
|
838
|
-
if (entries.length > 0) return [...new Set(entries.map((entry) => entry.target))];
|
|
839
|
-
const filteredNodes = getFilteredReactUsageNodes(graph, filter);
|
|
840
|
-
const inboundCounts = /* @__PURE__ */ new Map();
|
|
841
|
-
filteredNodes.forEach((node) => {
|
|
842
|
-
inboundCounts.set(node.id, 0);
|
|
843
|
-
});
|
|
844
|
-
filteredNodes.forEach((node) => {
|
|
845
|
-
getFilteredUsages(node, graph, filter).forEach((usage) => {
|
|
846
|
-
inboundCounts.set(usage.target, (inboundCounts.get(usage.target) ?? 0) + 1);
|
|
847
|
-
});
|
|
848
|
-
});
|
|
849
|
-
const roots = filteredNodes.filter((node) => (inboundCounts.get(node.id) ?? 0) === 0).map((node) => node.id);
|
|
850
|
-
if (roots.length > 0) return roots.sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
|
|
851
|
-
return filteredNodes.map((node) => node.id).sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
|
|
852
|
-
}
|
|
853
|
-
function getFilteredUsages(node, graph, filter = "all") {
|
|
854
|
-
return node.usages.filter((usage) => {
|
|
855
|
-
const targetNode = graph.nodes.get(usage.target);
|
|
856
|
-
return targetNode !== void 0 && matchesReactFilter(targetNode, filter);
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
function getFilteredReactUsageNodes(graph, filter) {
|
|
860
|
-
return [...graph.nodes.values()].filter((node) => matchesReactFilter(node, filter)).sort((left, right) => {
|
|
861
|
-
return left.filePath.localeCompare(right.filePath) || left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind);
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
function matchesReactFilter(node, filter) {
|
|
865
|
-
return filter === "all" || node.kind === filter;
|
|
866
|
-
}
|
|
867
|
-
//#endregion
|
|
868
|
-
//#region src/color.ts
|
|
869
|
-
const ANSI_RESET = "\x1B[0m";
|
|
870
|
-
const ANSI_COMPONENT = "\x1B[36m";
|
|
871
|
-
const ANSI_HOOK = "\x1B[35m";
|
|
872
|
-
const ANSI_MUTED = "\x1B[38;5;244m";
|
|
873
|
-
const ANSI_UNUSED = "\x1B[38;5;214m";
|
|
874
|
-
function resolveColorSupport(mode = "auto", options = {}) {
|
|
875
|
-
if (mode === true) return true;
|
|
876
|
-
if (mode === false) return false;
|
|
877
|
-
const forceColor = "forceColor" in options ? options.forceColor : process$1.env.FORCE_COLOR;
|
|
878
|
-
if (forceColor !== void 0) return forceColor !== "0";
|
|
879
|
-
if (("noColor" in options ? options.noColor : process$1.env.NO_COLOR) !== void 0) return false;
|
|
880
|
-
return ("isTTY" in options ? options.isTTY : process$1.stdout.isTTY) === true;
|
|
881
|
-
}
|
|
882
|
-
function colorizeUnusedMarker(text, enabled) {
|
|
883
|
-
if (!enabled) return text;
|
|
884
|
-
return text.replaceAll("(unused)", `${ANSI_UNUSED}(unused)${ANSI_RESET}`);
|
|
885
|
-
}
|
|
886
|
-
function formatReactSymbolLabel(name, kind, enabled) {
|
|
887
|
-
const label = `${name} [${kind}]`;
|
|
888
|
-
if (!enabled) return label;
|
|
889
|
-
return `${getReactSymbolColor(kind)}${label}${ANSI_RESET}`;
|
|
890
|
-
}
|
|
891
|
-
function colorizeReactLabel(text, kind, enabled) {
|
|
892
|
-
if (!enabled) return text;
|
|
893
|
-
return `${getReactSymbolColor(kind)}${text}${ANSI_RESET}`;
|
|
894
|
-
}
|
|
895
|
-
function colorizeMuted(text, enabled) {
|
|
896
|
-
if (!enabled) return text;
|
|
897
|
-
return `${ANSI_MUTED}${text}${ANSI_RESET}`;
|
|
898
|
-
}
|
|
899
|
-
function getReactSymbolColor(kind) {
|
|
900
|
-
return kind === "component" ? ANSI_COMPONENT : ANSI_HOOK;
|
|
901
|
-
}
|
|
902
|
-
//#endregion
|
|
903
|
-
//#region src/utils/to-display-path.ts
|
|
904
|
-
function toDisplayPath(filePath, cwd) {
|
|
905
|
-
const relativePath = path.relative(cwd, filePath);
|
|
906
|
-
if (relativePath === "") return ".";
|
|
907
|
-
const normalizedPath = relativePath.split(path.sep).join("/");
|
|
908
|
-
return normalizedPath.startsWith("..") ? filePath : normalizedPath;
|
|
909
|
-
}
|
|
910
|
-
//#endregion
|
|
911
|
-
//#region src/output/ascii/import.ts
|
|
912
|
-
function printDependencyTree(graph, options = {}) {
|
|
913
|
-
const cwd = options.cwd ?? graph.cwd;
|
|
914
|
-
const color = resolveColorSupport(options.color);
|
|
915
|
-
const includeExternals = options.includeExternals ?? false;
|
|
916
|
-
const omitUnused = options.omitUnused ?? false;
|
|
917
|
-
const rootLines = [toDisplayPath(graph.entryId, cwd)];
|
|
918
|
-
const visited = new Set([graph.entryId]);
|
|
919
|
-
const entryNode = graph.nodes.get(graph.entryId);
|
|
920
|
-
if (entryNode === void 0) return rootLines.join("\n");
|
|
921
|
-
const rootDependencies = filterDependencies(entryNode.dependencies, includeExternals, omitUnused);
|
|
922
|
-
rootDependencies.forEach((dependency, index) => {
|
|
923
|
-
rootLines.push(...renderDependency(dependency, graph, visited, "", index === rootDependencies.length - 1, includeExternals, omitUnused, color, cwd));
|
|
924
|
-
});
|
|
925
|
-
return rootLines.join("\n");
|
|
926
|
-
}
|
|
927
|
-
function renderDependency(dependency, graph, visited, prefix, isLast, includeExternals, omitUnused, color, cwd) {
|
|
928
|
-
const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
|
|
929
|
-
const label = formatDependencyLabel(dependency, cwd, color);
|
|
930
|
-
if (dependency.kind !== "source") return [`${branch}${label}`];
|
|
931
|
-
if (visited.has(dependency.target)) return [`${branch}${label} (circular)`];
|
|
932
|
-
const childNode = graph.nodes.get(dependency.target);
|
|
933
|
-
if (childNode === void 0) return [`${branch}${label}`];
|
|
934
|
-
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
935
|
-
const nextVisited = new Set(visited);
|
|
936
|
-
nextVisited.add(dependency.target);
|
|
937
|
-
const childLines = [`${branch}${label}`];
|
|
938
|
-
const childDependencies = filterDependencies(childNode.dependencies, includeExternals, omitUnused);
|
|
939
|
-
childDependencies.forEach((childDependency, index) => {
|
|
940
|
-
childLines.push(...renderDependency(childDependency, graph, nextVisited, nextPrefix, index === childDependencies.length - 1, includeExternals, omitUnused, color, cwd));
|
|
941
|
-
});
|
|
942
|
-
return childLines;
|
|
943
|
-
}
|
|
944
|
-
function filterDependencies(dependencies, includeExternals, omitUnused) {
|
|
945
|
-
return dependencies.filter((dependency) => {
|
|
946
|
-
if (omitUnused && dependency.unused) return false;
|
|
947
|
-
if (dependency.kind === "source" || dependency.kind === "missing") return true;
|
|
948
|
-
return includeExternals;
|
|
949
|
-
});
|
|
950
|
-
}
|
|
951
|
-
function formatDependencyLabel(dependency, cwd, color) {
|
|
952
|
-
const prefixes = [];
|
|
953
|
-
if (dependency.isTypeOnly) prefixes.push("type");
|
|
954
|
-
if (dependency.referenceKind === "require") prefixes.push("require");
|
|
955
|
-
else if (dependency.referenceKind === "dynamic-import") prefixes.push("dynamic");
|
|
956
|
-
else if (dependency.referenceKind === "export") prefixes.push("re-export");
|
|
957
|
-
else if (dependency.referenceKind === "import-equals") prefixes.push("import=");
|
|
958
|
-
const annotation = prefixes.length > 0 ? `[${prefixes.join(", ")}] ` : "";
|
|
959
|
-
if (dependency.kind === "source") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${toDisplayPath(dependency.target, cwd)}`, dependency.unused), color);
|
|
960
|
-
if (dependency.kind === "missing") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.specifier} [missing]`, dependency.unused), color);
|
|
961
|
-
if (dependency.kind === "builtin") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.target} [builtin]`, dependency.unused), color);
|
|
962
|
-
return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.target} [external]`, dependency.unused), color);
|
|
963
|
-
}
|
|
964
|
-
function withUnusedSuffix(label, unused) {
|
|
965
|
-
return unused ? `${label} (unused)` : label;
|
|
966
|
-
}
|
|
967
|
-
//#endregion
|
|
968
|
-
//#region src/output/ascii/react.ts
|
|
969
|
-
function printReactUsageTree(graph, options = {}) {
|
|
970
|
-
const cwd = options.cwd ?? graph.cwd;
|
|
971
|
-
const color = resolveColorSupport(options.color);
|
|
972
|
-
const filter = options.filter ?? "all";
|
|
973
|
-
const entries = getReactUsageEntries(graph, filter);
|
|
974
|
-
if (entries.length > 0) return renderReactUsageEntries(graph, entries, cwd, filter, color);
|
|
975
|
-
const roots = getReactUsageRoots(graph, filter);
|
|
976
|
-
if (roots.length === 0) return "No React symbols found.";
|
|
977
|
-
const lines = [];
|
|
978
|
-
roots.forEach((rootId, index) => {
|
|
979
|
-
const root = graph.nodes.get(rootId);
|
|
980
|
-
if (root === void 0) return;
|
|
981
|
-
lines.push(formatReactNodeLabel(root, cwd, color));
|
|
982
|
-
const usages = getFilteredUsages(root, graph, filter);
|
|
983
|
-
usages.forEach((usage, usageIndex) => {
|
|
984
|
-
lines.push(...renderUsage(usage, graph, cwd, filter, color, new Set([root.id]), "", usageIndex === usages.length - 1));
|
|
985
|
-
});
|
|
986
|
-
if (index < roots.length - 1) lines.push("");
|
|
987
|
-
});
|
|
988
|
-
return lines.join("\n");
|
|
989
|
-
}
|
|
990
|
-
function renderReactUsageEntries(graph, entries, cwd, filter, color) {
|
|
991
|
-
const lines = [];
|
|
992
|
-
entries.forEach((entry, index) => {
|
|
993
|
-
const root = graph.nodes.get(entry.target);
|
|
994
|
-
if (root === void 0) return;
|
|
995
|
-
lines.push(formatReactEntryLabel(entry, cwd));
|
|
996
|
-
lines.push(formatReactNodeLabel(root, cwd, color, entry.referenceName));
|
|
997
|
-
const usages = getFilteredUsages(root, graph, filter);
|
|
998
|
-
usages.forEach((usage, usageIndex) => {
|
|
999
|
-
lines.push(...renderUsage(usage, graph, cwd, filter, color, new Set([root.id]), "", usageIndex === usages.length - 1));
|
|
1000
|
-
});
|
|
1001
|
-
if (index < entries.length - 1) lines.push("");
|
|
1002
|
-
});
|
|
1003
|
-
return lines.join("\n");
|
|
1004
|
-
}
|
|
1005
|
-
function renderUsage(usage, graph, cwd, filter, color, visited, prefix, isLast) {
|
|
1006
|
-
const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
|
|
1007
|
-
const target = graph.nodes.get(usage.target);
|
|
1008
|
-
if (target === void 0) return [`${branch}${usage.target}`];
|
|
1009
|
-
if (visited.has(target.id)) return [`${branch}${formatReactNodeLabel(target, cwd, color, usage.referenceName)} (circular)`];
|
|
1010
|
-
const childLines = [`${branch}${formatReactNodeLabel(target, cwd, color, usage.referenceName)}`];
|
|
1011
|
-
const nextVisited = new Set(visited);
|
|
1012
|
-
nextVisited.add(target.id);
|
|
1013
|
-
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
1014
|
-
const childUsages = getFilteredUsages(target, graph, filter);
|
|
1015
|
-
childUsages.forEach((childUsage, index) => {
|
|
1016
|
-
childLines.push(...renderUsage(childUsage, graph, cwd, filter, color, nextVisited, nextPrefix, index === childUsages.length - 1));
|
|
1017
|
-
});
|
|
1018
|
-
return childLines;
|
|
1019
|
-
}
|
|
1020
|
-
function formatReactNodeLabel(node, cwd, color, referenceName) {
|
|
1021
|
-
return `${referenceName !== void 0 && referenceName !== node.name ? `${colorizeReactLabel(node.name, node.kind, color)} ${colorizeMuted(`as ${referenceName}`, color)} ${colorizeReactLabel(`[${node.kind}]`, node.kind, color)}` : formatReactSymbolLabel(node.name, node.kind, color)} (${toDisplayPath(node.filePath, cwd)})`;
|
|
1022
|
-
}
|
|
1023
|
-
function formatReactEntryLabel(entry, cwd) {
|
|
1024
|
-
return `${toDisplayPath(entry.location.filePath, cwd)}:${entry.location.line}:${entry.location.column}`;
|
|
1025
|
-
}
|
|
1026
|
-
//#endregion
|
|
1027
|
-
//#region src/output/json/import.ts
|
|
1028
|
-
function graphToSerializableTree(graph, options = {}) {
|
|
1029
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1030
|
-
return serializeNode(graph.entryId, graph, visited, options.omitUnused ?? false);
|
|
1031
|
-
}
|
|
1032
|
-
function serializeNode(filePath, graph, visited, omitUnused) {
|
|
1033
|
-
const node = graph.nodes.get(filePath);
|
|
1034
|
-
const displayPath = toDisplayPath(filePath, graph.cwd);
|
|
1035
|
-
if (node === void 0) return {
|
|
1036
|
-
path: displayPath,
|
|
1037
|
-
kind: "missing",
|
|
1038
|
-
dependencies: []
|
|
1039
|
-
};
|
|
1040
|
-
if (visited.has(filePath)) return {
|
|
1041
|
-
path: displayPath,
|
|
1042
|
-
kind: "circular",
|
|
1043
|
-
dependencies: []
|
|
1044
|
-
};
|
|
1045
|
-
visited.add(filePath);
|
|
1046
|
-
const dependencies = node.dependencies.filter((dependency) => !omitUnused || !dependency.unused).map((dependency) => {
|
|
1047
|
-
if (dependency.kind !== "source") return {
|
|
1048
|
-
specifier: dependency.specifier,
|
|
1049
|
-
referenceKind: dependency.referenceKind,
|
|
1050
|
-
isTypeOnly: dependency.isTypeOnly,
|
|
1051
|
-
unused: dependency.unused,
|
|
1052
|
-
kind: dependency.kind,
|
|
1053
|
-
target: dependency.kind === "missing" ? dependency.target : toDisplayPath(dependency.target, graph.cwd)
|
|
1054
|
-
};
|
|
1055
|
-
return {
|
|
1056
|
-
specifier: dependency.specifier,
|
|
1057
|
-
referenceKind: dependency.referenceKind,
|
|
1058
|
-
isTypeOnly: dependency.isTypeOnly,
|
|
1059
|
-
unused: dependency.unused,
|
|
1060
|
-
kind: dependency.kind,
|
|
1061
|
-
target: toDisplayPath(dependency.target, graph.cwd),
|
|
1062
|
-
node: serializeNode(dependency.target, graph, new Set(visited), omitUnused)
|
|
1063
|
-
};
|
|
1064
|
-
});
|
|
1065
|
-
return {
|
|
1066
|
-
path: displayPath,
|
|
1067
|
-
kind: filePath === graph.entryId ? "entry" : "source",
|
|
1068
|
-
dependencies
|
|
1069
|
-
};
|
|
1070
|
-
}
|
|
1071
|
-
//#endregion
|
|
1072
|
-
//#region src/output/json/react.ts
|
|
1073
|
-
function graphToSerializableReactTree(graph, options = {}) {
|
|
1074
|
-
const filter = options.filter ?? "all";
|
|
1075
|
-
const entries = getReactUsageEntries(graph, filter);
|
|
1076
|
-
const roots = entries.length > 0 ? entries.map((entry) => serializeReactUsageNode(entry.target, graph, filter, /* @__PURE__ */ new Set())) : getReactUsageRoots(graph, filter).map((rootId) => serializeReactUsageNode(rootId, graph, filter, /* @__PURE__ */ new Set()));
|
|
1077
|
-
return {
|
|
1078
|
-
kind: "react-usage",
|
|
1079
|
-
entries: entries.map((entry) => serializeReactUsageEntry(entry, graph, filter)),
|
|
1080
|
-
roots
|
|
1081
|
-
};
|
|
1082
|
-
}
|
|
1083
|
-
function serializeReactUsageNode(nodeId, graph, filter, visited) {
|
|
1084
|
-
const node = graph.nodes.get(nodeId);
|
|
1085
|
-
if (node === void 0) return {
|
|
1086
|
-
id: nodeId,
|
|
1087
|
-
name: nodeId,
|
|
1088
|
-
symbolKind: "circular",
|
|
1089
|
-
filePath: "",
|
|
1090
|
-
exportNames: [],
|
|
1091
|
-
usages: []
|
|
1092
|
-
};
|
|
1093
|
-
if (visited.has(nodeId)) return {
|
|
1094
|
-
id: node.id,
|
|
1095
|
-
name: node.name,
|
|
1096
|
-
symbolKind: "circular",
|
|
1097
|
-
filePath: toDisplayPath(node.filePath, graph.cwd),
|
|
1098
|
-
exportNames: node.exportNames,
|
|
1099
|
-
usages: []
|
|
1100
|
-
};
|
|
1101
|
-
const nextVisited = new Set(visited);
|
|
1102
|
-
nextVisited.add(nodeId);
|
|
1103
|
-
return {
|
|
1104
|
-
id: node.id,
|
|
1105
|
-
name: node.name,
|
|
1106
|
-
symbolKind: node.kind,
|
|
1107
|
-
filePath: toDisplayPath(node.filePath, graph.cwd),
|
|
1108
|
-
exportNames: node.exportNames,
|
|
1109
|
-
usages: getFilteredUsages(node, graph, filter).map((usage) => ({
|
|
1110
|
-
kind: usage.kind,
|
|
1111
|
-
targetId: usage.target,
|
|
1112
|
-
referenceName: usage.referenceName,
|
|
1113
|
-
node: serializeReactUsageNode(usage.target, graph, filter, nextVisited)
|
|
1114
|
-
}))
|
|
1115
|
-
};
|
|
1116
|
-
}
|
|
1117
|
-
function serializeReactUsageEntry(entry, graph, filter) {
|
|
1118
|
-
return {
|
|
1119
|
-
targetId: entry.target,
|
|
1120
|
-
referenceName: entry.referenceName,
|
|
1121
|
-
filePath: toDisplayPath(entry.location.filePath, graph.cwd),
|
|
1122
|
-
line: entry.location.line,
|
|
1123
|
-
column: entry.location.column,
|
|
1124
|
-
node: serializeReactUsageNode(entry.target, graph, filter, /* @__PURE__ */ new Set())
|
|
1125
|
-
};
|
|
1126
|
-
}
|
|
1127
|
-
//#endregion
|
|
1128
|
-
export { getFilteredUsages as a, analyzeReactUsage as c, printDependencyTree as i, analyzeDependencies as l, graphToSerializableTree as n, getReactUsageEntries as o, printReactUsageTree as r, getReactUsageRoots as s, graphToSerializableReactTree as t };
|
|
1129
|
-
|
|
1130
|
-
//# sourceMappingURL=react-BHPy_fw5.mjs.map
|