foresthouse 1.0.0-dev.2 → 1.0.0-dev.21

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.
@@ -0,0 +1,2484 @@
1
+ import { builtinModules } from "node:module";
2
+ import { execFileSync } from "node:child_process";
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import ts from "typescript";
7
+ import { ResolverFactory } from "oxc-resolver";
8
+ import { parseSync, visitorKeys } from "oxc-parser";
9
+ import process$1 from "node:process";
10
+ //#region src/utils/to-display-path.ts
11
+ function toDisplayPath(filePath, cwd) {
12
+ const relativePath = path.relative(cwd, filePath);
13
+ if (relativePath === "") return ".";
14
+ const normalizedPath = relativePath.split(path.sep).join("/");
15
+ return normalizedPath.startsWith("..") ? filePath : normalizedPath;
16
+ }
17
+ //#endregion
18
+ //#region src/analyzers/base.ts
19
+ var BaseAnalyzer = class {
20
+ constructor(entryFile, options) {
21
+ this.entryFile = entryFile;
22
+ this.options = options;
23
+ }
24
+ analyze() {
25
+ return this.doAnalyze();
26
+ }
27
+ };
28
+ //#endregion
29
+ //#region src/analyzers/deps/index.ts
30
+ const PNPM_WORKSPACE_FILE$1 = "pnpm-workspace.yaml";
31
+ function analyzePackageDependencies(directory) {
32
+ return new PackageDependencyAnalyzer(directory).analyze();
33
+ }
34
+ var PackageDependencyAnalyzer = class extends BaseAnalyzer {
35
+ inputPath;
36
+ constructor(directory) {
37
+ super(directory, {});
38
+ this.inputPath = path.resolve(process.cwd(), directory);
39
+ }
40
+ doAnalyze() {
41
+ const rootPackageDir = findNearestPackageDirectory(resolveInputDirectory(this.inputPath));
42
+ const workspaceRootDir = findWorkspaceRoot(rootPackageDir) ?? rootPackageDir;
43
+ const workspacePackages = discoverWorkspacePackages(workspaceRootDir);
44
+ const nodes = /* @__PURE__ */ new Map();
45
+ visitPackage(rootPackageDir, workspaceRootDir, workspacePackages, nodes);
46
+ return {
47
+ repositoryRoot: workspaceRootDir,
48
+ rootId: rootPackageDir,
49
+ nodes
50
+ };
51
+ }
52
+ };
53
+ function visitPackage(packageDir, repositoryRoot, workspacePackages, nodes) {
54
+ if (nodes.has(packageDir)) return;
55
+ const manifest = readPackageManifest(packageDir);
56
+ const packageName = resolvePackageName(manifest, packageDir);
57
+ const dependencies = packageDir === repositoryRoot && workspacePackages.size > 0 ? collectRepositoryRootDependencies(manifest, workspacePackages, repositoryRoot) : collectManifestDependencies(manifest, workspacePackages, repositoryRoot);
58
+ nodes.set(packageDir, {
59
+ packageDir,
60
+ packageName,
61
+ dependencies
62
+ });
63
+ dependencies.forEach((dependency) => {
64
+ if (dependency.kind === "workspace") visitPackage(dependency.target, repositoryRoot, workspacePackages, nodes);
65
+ });
66
+ }
67
+ function resolveInputDirectory(resolvedPath) {
68
+ const stats = fs.existsSync(resolvedPath) ? fs.statSync(resolvedPath) : null;
69
+ if (stats === null) throw new Error(`Directory does not exist: ${resolvedPath}`);
70
+ if (stats.isDirectory()) return resolvedPath;
71
+ if (stats.isFile() && path.basename(resolvedPath) === "package.json") return path.dirname(resolvedPath);
72
+ throw new Error(`Expected a package directory: ${resolvedPath}`);
73
+ }
74
+ function findNearestPackageDirectory(startDirectory) {
75
+ let currentDirectory = startDirectory;
76
+ while (true) {
77
+ const packageJsonPath = path.join(currentDirectory, "package.json");
78
+ if (fs.existsSync(packageJsonPath)) return currentDirectory;
79
+ const parentDirectory = path.dirname(currentDirectory);
80
+ if (parentDirectory === currentDirectory) break;
81
+ currentDirectory = parentDirectory;
82
+ }
83
+ throw new Error(`No package.json found from ${startDirectory}`);
84
+ }
85
+ function findWorkspaceRoot(packageDir) {
86
+ let currentDirectory = packageDir;
87
+ while (true) {
88
+ const packageJsonPath = path.join(currentDirectory, "package.json");
89
+ if (fs.existsSync(packageJsonPath)) {
90
+ const manifest = readPackageManifest(currentDirectory);
91
+ const workspacePatterns = resolveWorkspacePatterns(currentDirectory, manifest);
92
+ if (workspacePatterns.length > 0 && currentDirectory === packageDir) return currentDirectory;
93
+ if (workspacePatterns.length > 0 && isWorkspaceMatch(currentDirectory, workspacePatterns, packageDir)) return currentDirectory;
94
+ }
95
+ const parentDirectory = path.dirname(currentDirectory);
96
+ if (parentDirectory === currentDirectory) return;
97
+ currentDirectory = parentDirectory;
98
+ }
99
+ }
100
+ function discoverWorkspacePackages(repositoryRoot) {
101
+ const workspacePatterns = resolveWorkspacePatterns(repositoryRoot, readPackageManifest(repositoryRoot));
102
+ if (workspacePatterns.length === 0) return /* @__PURE__ */ new Map();
103
+ const workspacePackageDirs = /* @__PURE__ */ new Set();
104
+ workspacePatterns.forEach((pattern) => {
105
+ expandWorkspacePattern(repositoryRoot, pattern).forEach((packageDir) => {
106
+ workspacePackageDirs.add(packageDir);
107
+ });
108
+ });
109
+ const workspacePackages = /* @__PURE__ */ new Map();
110
+ Array.from(workspacePackageDirs).sort((left, right) => left.localeCompare(right)).forEach((packageDir) => {
111
+ const workspaceName = resolvePackageName(readPackageManifest(packageDir), packageDir);
112
+ if (workspacePackages.has(workspaceName)) throw new Error(`Duplicate workspace package name: ${workspaceName}`);
113
+ workspacePackages.set(workspaceName, {
114
+ name: workspaceName,
115
+ packageDir,
116
+ label: toDisplayPath(packageDir, repositoryRoot)
117
+ });
118
+ });
119
+ return workspacePackages;
120
+ }
121
+ function collectManifestDependencies(manifest, workspacePackages, repositoryRoot) {
122
+ const dependencyEntries = Object.entries(manifest.dependencies ?? {});
123
+ const workspaceDependencies = [];
124
+ const externalDependencies = [];
125
+ dependencyEntries.forEach(([dependencyName, specifier]) => {
126
+ const workspacePackage = workspacePackages.get(dependencyName);
127
+ if (workspacePackage !== void 0) {
128
+ workspaceDependencies.push({
129
+ kind: "workspace",
130
+ name: dependencyName,
131
+ specifier,
132
+ target: workspacePackage.packageDir
133
+ });
134
+ return;
135
+ }
136
+ externalDependencies.push({
137
+ kind: "external",
138
+ name: dependencyName,
139
+ specifier
140
+ });
141
+ });
142
+ workspaceDependencies.sort((left, right) => {
143
+ const leftLabel = toDisplayPath(left.target, repositoryRoot);
144
+ const rightLabel = toDisplayPath(right.target, repositoryRoot);
145
+ return leftLabel.localeCompare(rightLabel);
146
+ });
147
+ externalDependencies.sort((left, right) => left.name.localeCompare(right.name));
148
+ return [...workspaceDependencies, ...externalDependencies];
149
+ }
150
+ function collectRepositoryRootDependencies(manifest, workspacePackages, _repositoryRoot) {
151
+ const topLevelWorkspaceDependencies = Array.from(workspacePackages.values()).sort((left, right) => left.label.localeCompare(right.label)).map((workspacePackage) => ({
152
+ kind: "workspace",
153
+ name: workspacePackage.name,
154
+ target: workspacePackage.packageDir
155
+ }));
156
+ const externalDependencies = Object.entries(manifest.dependencies ?? {}).filter(([dependencyName]) => !workspacePackages.has(dependencyName)).map(([dependencyName, specifier]) => ({
157
+ kind: "external",
158
+ name: dependencyName,
159
+ specifier
160
+ })).sort((left, right) => left.name.localeCompare(right.name));
161
+ return [...topLevelWorkspaceDependencies, ...externalDependencies];
162
+ }
163
+ function readPackageManifest(packageDir) {
164
+ const packageJsonPath = path.join(packageDir, "package.json");
165
+ if (!fs.existsSync(packageJsonPath)) throw new Error(`Missing package.json in ${packageDir}`);
166
+ const manifestText = fs.readFileSync(packageJsonPath, "utf8");
167
+ try {
168
+ const parsed = JSON.parse(manifestText);
169
+ if (!isRecord(parsed)) throw new Error("package.json must contain an object");
170
+ return parsed;
171
+ } catch (error) {
172
+ const message = error instanceof Error ? error.message : "Unknown JSON parse error";
173
+ throw new Error(`Failed to read ${packageJsonPath}: ${message}`);
174
+ }
175
+ }
176
+ function resolvePackageName(manifest, packageDir) {
177
+ if (typeof manifest.name === "string" && manifest.name.trim().length > 0) return manifest.name;
178
+ throw new Error(`Package at ${packageDir} is missing a valid name`);
179
+ }
180
+ function getWorkspacePatterns(manifest) {
181
+ if (Array.isArray(manifest.workspaces)) return manifest.workspaces.filter((pattern) => typeof pattern === "string" && pattern.length > 0);
182
+ if (isRecord(manifest.workspaces) && Array.isArray(manifest.workspaces.packages)) return manifest.workspaces.packages.filter((pattern) => typeof pattern === "string" && pattern.length > 0);
183
+ return [];
184
+ }
185
+ function resolveWorkspacePatterns(repositoryRoot, manifest) {
186
+ const manifestPatterns = getWorkspacePatterns(manifest);
187
+ if (manifestPatterns.length > 0) return manifestPatterns;
188
+ return readPnpmWorkspacePatterns(repositoryRoot);
189
+ }
190
+ function readPnpmWorkspacePatterns(repositoryRoot) {
191
+ const pnpmWorkspacePath = path.join(repositoryRoot, PNPM_WORKSPACE_FILE$1);
192
+ if (!fs.existsSync(pnpmWorkspacePath)) return [];
193
+ const lines = fs.readFileSync(pnpmWorkspacePath, "utf8").split(/\r?\n/);
194
+ const patterns = [];
195
+ let inPackagesBlock = false;
196
+ for (const line of lines) {
197
+ const trimmedLine = line.trim();
198
+ if (trimmedLine.length === 0 || trimmedLine.startsWith("#")) continue;
199
+ if (!inPackagesBlock) {
200
+ if (trimmedLine === "packages:") inPackagesBlock = true;
201
+ continue;
202
+ }
203
+ if (!line.startsWith(" ") && !line.startsWith(" ")) break;
204
+ if (!trimmedLine.startsWith("- ")) continue;
205
+ const pattern = unquoteWorkspacePattern(trimmedLine.slice(2).trim());
206
+ if (pattern.length > 0) patterns.push(pattern);
207
+ }
208
+ return patterns;
209
+ }
210
+ function unquoteWorkspacePattern(pattern) {
211
+ if (pattern.startsWith("\"") && pattern.endsWith("\"") || pattern.startsWith("'") && pattern.endsWith("'")) return pattern.slice(1, -1);
212
+ return pattern;
213
+ }
214
+ function isWorkspaceMatch(repositoryRoot, patterns, packageDir) {
215
+ return patterns.some((pattern) => expandWorkspacePattern(repositoryRoot, pattern).includes(packageDir));
216
+ }
217
+ function expandWorkspacePattern(repositoryRoot, pattern) {
218
+ return matchWorkspaceSegments(repositoryRoot, normalizeWorkspacePattern(pattern), 0).filter((packageDir) => fs.existsSync(path.join(packageDir, "package.json")));
219
+ }
220
+ function normalizeWorkspacePattern(pattern) {
221
+ return pattern.split(/[\\/]+/).filter((segment) => segment.length > 0 && segment !== ".");
222
+ }
223
+ function matchWorkspaceSegments(currentDirectory, segments, index) {
224
+ if (index >= segments.length) return [currentDirectory];
225
+ const segment = segments[index];
226
+ if (segment === void 0) return [currentDirectory];
227
+ if (segment === "**") {
228
+ const results = new Set(matchWorkspaceSegments(currentDirectory, segments, index + 1));
229
+ listSubdirectories(currentDirectory).forEach((subdirectory) => {
230
+ matchWorkspaceSegments(subdirectory, segments, index).forEach((match) => {
231
+ results.add(match);
232
+ });
233
+ });
234
+ return [...results];
235
+ }
236
+ const matcher = createWorkspaceSegmentMatcher(segment);
237
+ const results = [];
238
+ listSubdirectories(currentDirectory).forEach((subdirectory) => {
239
+ if (!matcher(path.basename(subdirectory))) return;
240
+ results.push(...matchWorkspaceSegments(subdirectory, segments, index + 1));
241
+ });
242
+ return results;
243
+ }
244
+ function listSubdirectories(directory) {
245
+ return fs.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git").map((entry) => path.join(directory, entry.name));
246
+ }
247
+ function createWorkspaceSegmentMatcher(segment) {
248
+ if (!segment.includes("*")) return (name) => name === segment;
249
+ const escapedSegment = escapeRegExp(segment).replaceAll("*", "[^/]*");
250
+ const pattern = new RegExp(`^${escapedSegment}$`);
251
+ return (name) => pattern.test(name);
252
+ }
253
+ function escapeRegExp(value) {
254
+ return value.replaceAll(/[|\\{}()[\]^$+?.]/g, "\\$&");
255
+ }
256
+ function isRecord(value) {
257
+ return typeof value === "object" && value !== null;
258
+ }
259
+ //#endregion
260
+ //#region src/analyzers/deps/pnpm-lock.ts
261
+ const PNPM_LOCKFILE$1 = "pnpm-lock.yaml";
262
+ const DEPENDENCY_SECTIONS = new Set(["dependencies", "optionalDependencies"]);
263
+ function loadPnpmLockImporterResolutions(repositoryRoot) {
264
+ const lockfilePath = path.join(repositoryRoot, PNPM_LOCKFILE$1);
265
+ if (!fs.existsSync(lockfilePath)) return;
266
+ return parsePnpmLockImporterResolutions(fs.readFileSync(lockfilePath, "utf8"));
267
+ }
268
+ function parsePnpmLockImporterResolutions(source) {
269
+ const importers = /* @__PURE__ */ new Map();
270
+ const lines = source.split(/\r?\n/u);
271
+ let inImporters = false;
272
+ let currentImporter;
273
+ let currentSection;
274
+ let currentDependencyName;
275
+ lines.forEach((line) => {
276
+ const trimmedLine = line.trim();
277
+ if (trimmedLine.length === 0 || trimmedLine.startsWith("#")) return;
278
+ const indentation = line.length - line.trimStart().length;
279
+ if (indentation === 0) {
280
+ if (trimmedLine === "importers:") {
281
+ inImporters = true;
282
+ currentImporter = void 0;
283
+ currentSection = void 0;
284
+ currentDependencyName = void 0;
285
+ return;
286
+ }
287
+ inImporters = false;
288
+ return;
289
+ }
290
+ if (!inImporters) return;
291
+ if (indentation === 2 && trimmedLine.endsWith(":")) {
292
+ currentImporter = parseYamlScalar(trimmedLine.slice(0, -1));
293
+ currentSection = void 0;
294
+ currentDependencyName = void 0;
295
+ if (!importers.has(currentImporter)) importers.set(currentImporter, /* @__PURE__ */ new Map());
296
+ return;
297
+ }
298
+ if (currentImporter === void 0) return;
299
+ if (indentation === 4 && trimmedLine.endsWith(":")) {
300
+ const sectionName = parseYamlScalar(trimmedLine.slice(0, -1));
301
+ currentSection = DEPENDENCY_SECTIONS.has(sectionName) ? sectionName : void 0;
302
+ currentDependencyName = void 0;
303
+ return;
304
+ }
305
+ if (currentSection === void 0) return;
306
+ if (indentation === 6) {
307
+ if (trimmedLine.endsWith(":")) {
308
+ currentDependencyName = parseYamlScalar(trimmedLine.slice(0, -1));
309
+ upsertDependency(importers, currentImporter, currentDependencyName, {});
310
+ return;
311
+ }
312
+ const pair = splitYamlKeyValue(trimmedLine);
313
+ if (pair === void 0) return;
314
+ currentDependencyName = void 0;
315
+ upsertDependency(importers, currentImporter, parseYamlScalar(pair.key), { ...parsePnpmVersion(parseYamlScalar(pair.value)) });
316
+ return;
317
+ }
318
+ if (indentation === 8 && currentDependencyName !== void 0) {
319
+ const pair = splitYamlKeyValue(trimmedLine);
320
+ if (pair === void 0) return;
321
+ if (pair.key === "specifier") {
322
+ upsertDependency(importers, currentImporter, currentDependencyName, { specifier: parseYamlScalar(pair.value) });
323
+ return;
324
+ }
325
+ if (pair.key === "version") upsertDependency(importers, currentImporter, currentDependencyName, { ...parsePnpmVersion(parseYamlScalar(pair.value)) });
326
+ }
327
+ });
328
+ return importers;
329
+ }
330
+ function upsertDependency(importers, importerPath, dependencyName, nextValue) {
331
+ const importer = importers.get(importerPath);
332
+ if (importer === void 0) return;
333
+ importer.set(dependencyName, {
334
+ ...importer.get(dependencyName),
335
+ ...nextValue
336
+ });
337
+ }
338
+ function splitYamlKeyValue(line) {
339
+ const separatorIndex = line.indexOf(":");
340
+ if (separatorIndex <= 0) return;
341
+ return {
342
+ key: line.slice(0, separatorIndex).trim(),
343
+ value: line.slice(separatorIndex + 1).trim()
344
+ };
345
+ }
346
+ function parseYamlScalar(value) {
347
+ if (value.length >= 2) {
348
+ const firstCharacter = value[0];
349
+ const lastCharacter = value[value.length - 1];
350
+ if (firstCharacter === "\"" && lastCharacter === "\"" || firstCharacter === "'" && lastCharacter === "'") return value.slice(1, -1);
351
+ }
352
+ return value;
353
+ }
354
+ function parsePnpmVersion(value) {
355
+ const firstParenthesisIndex = value.indexOf("(");
356
+ if (firstParenthesisIndex < 0) return { version: value };
357
+ return {
358
+ version: value.slice(0, firstParenthesisIndex),
359
+ peerSuffix: value.slice(firstParenthesisIndex)
360
+ };
361
+ }
362
+ //#endregion
363
+ //#region src/analyzers/deps/diff.ts
364
+ const PNPM_LOCKFILE = "pnpm-lock.yaml";
365
+ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
366
+ function analyzePackageDependencyDiff(directory, diff) {
367
+ const resolvedInputPath = path.resolve(process.cwd(), directory);
368
+ const repositoryRoot = findGitRepositoryRoot(resolvedInputPath);
369
+ const inputPathWithinRepository = resolveRepositoryInputPath(resolvedInputPath, repositoryRoot);
370
+ const comparison = resolveGitDiffComparison(repositoryRoot, diff);
371
+ const beforeGraph = loadGitTreeGraph(repositoryRoot, comparison.beforeTree, inputPathWithinRepository);
372
+ const afterGraph = comparison.afterTree === void 0 ? loadWorkingTreeGraph(repositoryRoot, inputPathWithinRepository) : loadGitTreeGraph(repositoryRoot, comparison.afterTree, inputPathWithinRepository);
373
+ if (beforeGraph === void 0 && afterGraph === void 0) throw new Error(`No package.json found from ${resolvedInputPath}`);
374
+ const changedPackagePaths = collectChangedPackagePaths(repositoryRoot, comparison, beforeGraph, afterGraph);
375
+ return {
376
+ repositoryRoot,
377
+ root: diffPackageNode({
378
+ beforePath: beforeGraph?.rootPath,
379
+ afterPath: afterGraph?.rootPath,
380
+ kind: "root"
381
+ }, beforeGraph, afterGraph, changedPackagePaths, /* @__PURE__ */ new Set())
382
+ };
383
+ }
384
+ function resolveRepositoryInputPath(resolvedInputPath, repositoryRoot) {
385
+ const existingPath = findNearestExistingPath(resolvedInputPath);
386
+ const existingRealPath = fs.realpathSync.native(existingPath);
387
+ const repositoryRealPath = fs.realpathSync.native(repositoryRoot);
388
+ const relativeFromExistingPath = path.relative(existingPath, resolvedInputPath);
389
+ const relativeToRepository = path.relative(repositoryRealPath, existingRealPath);
390
+ const normalizedPath = path.normalize(path.join(relativeToRepository, relativeFromExistingPath));
391
+ return normalizedPath === "." ? "" : normalizedPath;
392
+ }
393
+ function findGitRepositoryRoot(resolvedInputPath) {
394
+ const searchPath = findNearestExistingPath(resolvedInputPath);
395
+ try {
396
+ return runGit(searchPath, ["rev-parse", "--show-toplevel"]).trim();
397
+ } catch (error) {
398
+ throw new Error(`Git diff mode requires a Git repository: ${getCommandErrorMessage(error)}`);
399
+ }
400
+ }
401
+ function findNearestExistingPath(resolvedInputPath) {
402
+ let currentPath = resolvedInputPath;
403
+ while (!fs.existsSync(currentPath)) {
404
+ const parentPath = path.dirname(currentPath);
405
+ if (parentPath === currentPath) return resolvedInputPath;
406
+ currentPath = parentPath;
407
+ }
408
+ if (fs.statSync(currentPath).isFile()) return path.dirname(currentPath);
409
+ return currentPath;
410
+ }
411
+ function resolveGitDiffComparison(repositoryRoot, diff) {
412
+ if (diff.includes("...")) {
413
+ const [baseRef, headRef] = splitDiffRange(diff, "...");
414
+ try {
415
+ return {
416
+ beforeTree: resolveGitTree(repositoryRoot, runGit(repositoryRoot, [
417
+ "merge-base",
418
+ baseRef,
419
+ headRef
420
+ ]).trim()),
421
+ afterTree: resolveGitTree(repositoryRoot, headRef)
422
+ };
423
+ } catch (error) {
424
+ throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
425
+ }
426
+ }
427
+ if (diff.includes("..")) {
428
+ const [beforeRef, afterRef] = splitDiffRange(diff, "..");
429
+ try {
430
+ return {
431
+ beforeTree: resolveGitTree(repositoryRoot, beforeRef),
432
+ afterTree: resolveGitTree(repositoryRoot, afterRef)
433
+ };
434
+ } catch (error) {
435
+ throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
436
+ }
437
+ }
438
+ try {
439
+ return {
440
+ beforeTree: resolveGitTree(repositoryRoot, diff),
441
+ afterTree: void 0
442
+ };
443
+ } catch (error) {
444
+ throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
445
+ }
446
+ }
447
+ function splitDiffRange(diff, separator) {
448
+ const [left, right, ...extra] = diff.split(separator);
449
+ if (left === void 0 || right === void 0 || left.length === 0 || right.length === 0 || extra.length > 0) throw new Error(`Invalid Git diff spec \`${diff}\``);
450
+ return [left, right];
451
+ }
452
+ function resolveGitTree(repositoryRoot, reference) {
453
+ return runGit(repositoryRoot, [
454
+ "rev-parse",
455
+ "--verify",
456
+ `${reference}^{tree}`
457
+ ]).trim();
458
+ }
459
+ function loadWorkingTreeGraph(repositoryRoot, inputPathWithinRepository) {
460
+ const packageGraph = tryAnalyzePackageGraph(resolveSnapshotInputPath(repositoryRoot, inputPathWithinRepository));
461
+ return packageGraph === void 0 ? void 0 : toComparableGraph(packageGraph);
462
+ }
463
+ function loadGitTreeGraph(repositoryRoot, tree, inputPathWithinRepository) {
464
+ const snapshotRoot = materializeGitTreeSnapshot(repositoryRoot, tree);
465
+ try {
466
+ const packageGraph = tryAnalyzePackageGraph(resolveSnapshotInputPath(snapshotRoot, inputPathWithinRepository));
467
+ return packageGraph === void 0 ? void 0 : toComparableGraph(packageGraph);
468
+ } finally {
469
+ fs.rmSync(snapshotRoot, {
470
+ recursive: true,
471
+ force: true
472
+ });
473
+ }
474
+ }
475
+ function materializeGitTreeSnapshot(repositoryRoot, tree) {
476
+ const snapshotRoot = fs.mkdtempSync(path.join(os.tmpdir(), "foresthouse-deps-diff-"));
477
+ const trackedFiles = runGit(repositoryRoot, [
478
+ "ls-tree",
479
+ "-r",
480
+ "-z",
481
+ "--name-only",
482
+ tree
483
+ ]).split("\0").filter((filePath) => filePath.length > 0);
484
+ trackedFiles.forEach((filePath) => {
485
+ ensureSnapshotDirectory(snapshotRoot, filePath);
486
+ });
487
+ trackedFiles.filter(isManifestSnapshotFile).forEach((filePath) => {
488
+ const fileContent = runGit(repositoryRoot, [
489
+ "cat-file",
490
+ "-p",
491
+ `${tree}:${filePath}`
492
+ ], { trim: false });
493
+ const absolutePath = path.join(snapshotRoot, ...filePath.split("/"));
494
+ fs.writeFileSync(absolutePath, fileContent);
495
+ });
496
+ return snapshotRoot;
497
+ }
498
+ function ensureSnapshotDirectory(snapshotRoot, filePath) {
499
+ const parentDirectory = path.dirname(filePath);
500
+ if (parentDirectory === ".") return;
501
+ fs.mkdirSync(path.join(snapshotRoot, ...parentDirectory.split("/")), { recursive: true });
502
+ }
503
+ function isManifestSnapshotFile(filePath) {
504
+ const fileName = path.posix.basename(filePath);
505
+ return fileName === "package.json" || fileName === PNPM_LOCKFILE || fileName === PNPM_WORKSPACE_FILE;
506
+ }
507
+ function resolveSnapshotInputPath(snapshotRoot, inputPathWithinRepository) {
508
+ if (inputPathWithinRepository === "") return snapshotRoot;
509
+ return path.join(snapshotRoot, inputPathWithinRepository);
510
+ }
511
+ function tryAnalyzePackageGraph(inputPath) {
512
+ if (!fs.existsSync(inputPath)) return;
513
+ try {
514
+ return analyzePackageDependencies(inputPath);
515
+ } catch (error) {
516
+ if (error instanceof Error && error.message.startsWith("No package.json found from ")) return;
517
+ throw error;
518
+ }
519
+ }
520
+ function toComparableGraph(graph) {
521
+ const pnpmLockResolutions = loadPnpmLockImporterResolutions(graph.repositoryRoot);
522
+ const nodes = /* @__PURE__ */ new Map();
523
+ graph.nodes.forEach((node, packageDir) => {
524
+ const packagePath = toDisplayPath(packageDir, graph.repositoryRoot);
525
+ const packageLockResolutions = pnpmLockResolutions?.get(packagePath);
526
+ const dependenciesByKey = /* @__PURE__ */ new Map();
527
+ node.dependencies.forEach((dependency) => {
528
+ dependenciesByKey.set(createDependencyKey(dependency), toComparableDependency(dependency, graph.repositoryRoot, packageLockResolutions?.get(dependency.name)));
529
+ });
530
+ nodes.set(packagePath, {
531
+ path: packagePath,
532
+ packageName: node.packageName,
533
+ dependenciesByKey
534
+ });
535
+ });
536
+ return {
537
+ rootPath: toDisplayPath(graph.rootId, graph.repositoryRoot),
538
+ nodes
539
+ };
540
+ }
541
+ function createDependencyKey(dependency) {
542
+ return `${dependency.kind}:${dependency.name}`;
543
+ }
544
+ function toComparableDependency(dependency, repositoryRoot, lockResolution) {
545
+ if (dependency.kind === "external") {
546
+ const resolvedVersion = resolveResolvedVersion(lockResolution);
547
+ const peerContext = resolvePeerContext(lockResolution);
548
+ return {
549
+ kind: "external",
550
+ key: createDependencyKey(dependency),
551
+ name: dependency.name,
552
+ specifier: dependency.specifier,
553
+ ...resolvedVersion === void 0 ? {} : { resolvedVersion },
554
+ ...peerContext === void 0 ? {} : { peerContext }
555
+ };
556
+ }
557
+ return {
558
+ kind: "workspace",
559
+ key: createDependencyKey(dependency),
560
+ name: dependency.name,
561
+ specifier: dependency.specifier,
562
+ targetPath: toDisplayPath(dependency.target, repositoryRoot)
563
+ };
564
+ }
565
+ function resolveResolvedVersion(lockResolution) {
566
+ if (lockResolution === void 0) return;
567
+ const version = lockResolution.version?.trim();
568
+ if (version === void 0 || version.length === 0 || version.startsWith("link:") || version.startsWith("file:") || version.startsWith("workspace:")) return;
569
+ return version;
570
+ }
571
+ function resolvePeerContext(lockResolution) {
572
+ const peerSuffix = lockResolution?.peerSuffix?.trim();
573
+ return peerSuffix === void 0 || peerSuffix.length === 0 ? void 0 : peerSuffix;
574
+ }
575
+ function diffPackageNode(location, beforeGraph, afterGraph, changedPackagePaths, ancestry) {
576
+ const nodeKey = `${location.beforePath ?? ""}->${location.afterPath ?? ""}`;
577
+ const beforeNode = location.beforePath === void 0 ? void 0 : beforeGraph?.nodes.get(location.beforePath);
578
+ const afterNode = location.afterPath === void 0 ? void 0 : afterGraph?.nodes.get(location.afterPath);
579
+ if (beforeNode === void 0 && afterNode === void 0) throw new Error("Unable to resolve package diff node.");
580
+ const packageName = afterNode?.packageName ?? beforeNode?.packageName ?? location.afterPath ?? location.beforePath ?? ".";
581
+ const packagePath = location.afterPath ?? location.beforePath ?? ".";
582
+ if (ancestry.has(nodeKey)) return {
583
+ kind: "circular",
584
+ label: location.kind === "root" ? packageName : packagePath,
585
+ packageName,
586
+ path: packagePath,
587
+ change: "unchanged",
588
+ dependencies: []
589
+ };
590
+ const nextAncestry = new Set(ancestry);
591
+ nextAncestry.add(nodeKey);
592
+ const dependencies = collectDependencyDiffs(beforeNode, afterNode, beforeGraph, afterGraph, changedPackagePaths, nextAncestry);
593
+ const change = resolveNodeChange(location, beforeNode, afterNode, dependencies, changedPackagePaths);
594
+ return {
595
+ kind: location.kind,
596
+ label: location.kind === "root" ? packageName : packagePath,
597
+ packageName,
598
+ path: packagePath,
599
+ change,
600
+ ...beforeNode !== void 0 && afterNode !== void 0 && beforeNode.packageName !== afterNode.packageName ? {
601
+ beforePackageName: beforeNode.packageName,
602
+ afterPackageName: afterNode.packageName
603
+ } : {},
604
+ dependencies
605
+ };
606
+ }
607
+ function collectDependencyDiffs(beforeNode, afterNode, beforeGraph, afterGraph, changedPackagePaths, ancestry) {
608
+ const dependencyKeys = new Set([...Array.from(beforeNode?.dependenciesByKey.keys() ?? []), ...Array.from(afterNode?.dependenciesByKey.keys() ?? [])]);
609
+ return Array.from(dependencyKeys).sort((left, right) => compareDependencyLabels(beforeNode?.dependenciesByKey.get(left) ?? afterNode?.dependenciesByKey.get(left), beforeNode?.dependenciesByKey.get(right) ?? afterNode?.dependenciesByKey.get(right))).flatMap((dependencyKey) => {
610
+ const dependency = diffDependency(beforeNode?.dependenciesByKey.get(dependencyKey), afterNode?.dependenciesByKey.get(dependencyKey), beforeGraph, afterGraph, changedPackagePaths, ancestry);
611
+ return dependency === void 0 ? [] : [dependency];
612
+ });
613
+ }
614
+ function compareDependencyLabels(left, right) {
615
+ const leftLabel = left === void 0 ? "" : getDependencySortLabel(left);
616
+ const rightLabel = right === void 0 ? "" : getDependencySortLabel(right);
617
+ return leftLabel.localeCompare(rightLabel);
618
+ }
619
+ function getDependencySortLabel(dependency) {
620
+ if (dependency.kind === "external") return `${dependency.name}@${dependency.specifier}`;
621
+ return dependency.targetPath;
622
+ }
623
+ function diffDependency(beforeDependency, afterDependency, beforeGraph, afterGraph, changedPackagePaths, ancestry) {
624
+ const change = resolveDependencyChange(beforeDependency, afterDependency);
625
+ if (beforeDependency?.kind === "workspace" || afterDependency?.kind === "workspace") {
626
+ const workspaceDependency = afterDependency?.kind === "workspace" ? afterDependency : beforeDependency?.kind === "workspace" ? beforeDependency : void 0;
627
+ if (workspaceDependency === void 0) return;
628
+ const node = diffPackageNode({
629
+ beforePath: beforeDependency?.kind === "workspace" ? beforeDependency.targetPath : void 0,
630
+ afterPath: afterDependency?.kind === "workspace" ? afterDependency.targetPath : void 0,
631
+ kind: "workspace"
632
+ }, beforeGraph, afterGraph, changedPackagePaths, ancestry);
633
+ if (change === "unchanged" && !hasVisibleChanges(node)) return;
634
+ return {
635
+ kind: "workspace",
636
+ name: workspaceDependency.name,
637
+ change,
638
+ ...beforeDependency === void 0 ? {} : { before: toDiffState(beforeDependency) },
639
+ ...afterDependency === void 0 ? {} : { after: toDiffState(afterDependency) },
640
+ node
641
+ };
642
+ }
643
+ if (change === "unchanged") return;
644
+ const dependency = afterDependency ?? beforeDependency;
645
+ if (dependency === void 0 || dependency.kind !== "external") return;
646
+ return {
647
+ kind: "external",
648
+ name: dependency.name,
649
+ change,
650
+ specifierChanged: didExternalSpecifierChange(beforeDependency, afterDependency),
651
+ resolvedVersionChanged: didExternalResolvedVersionChange(beforeDependency, afterDependency),
652
+ peerContextChanged: didExternalPeerContextChange(beforeDependency, afterDependency),
653
+ ...beforeDependency === void 0 ? {} : { before: toDiffState(beforeDependency) },
654
+ ...afterDependency === void 0 ? {} : { after: toDiffState(afterDependency) }
655
+ };
656
+ }
657
+ function didExternalSpecifierChange(beforeDependency, afterDependency) {
658
+ return beforeDependency?.kind === "external" && afterDependency?.kind === "external" && beforeDependency.specifier !== afterDependency.specifier;
659
+ }
660
+ function didExternalResolvedVersionChange(beforeDependency, afterDependency) {
661
+ return beforeDependency?.kind === "external" && afterDependency?.kind === "external" && beforeDependency.resolvedVersion !== afterDependency.resolvedVersion;
662
+ }
663
+ function didExternalPeerContextChange(beforeDependency, afterDependency) {
664
+ return beforeDependency?.kind === "external" && afterDependency?.kind === "external" && beforeDependency.peerContext !== afterDependency.peerContext;
665
+ }
666
+ function resolveDependencyChange(beforeDependency, afterDependency) {
667
+ if (beforeDependency === void 0 && afterDependency !== void 0) return "added";
668
+ if (beforeDependency !== void 0 && afterDependency === void 0) return "removed";
669
+ if (beforeDependency === void 0 || afterDependency === void 0) return "unchanged";
670
+ if (beforeDependency.kind !== afterDependency.kind) return "changed";
671
+ if (beforeDependency.kind === "external" && afterDependency.kind === "external") return beforeDependency.specifier === afterDependency.specifier && beforeDependency.resolvedVersion === afterDependency.resolvedVersion && beforeDependency.peerContext === afterDependency.peerContext ? "unchanged" : "changed";
672
+ if (beforeDependency.kind === "workspace" && afterDependency.kind === "workspace") return beforeDependency.specifier === afterDependency.specifier && beforeDependency.targetPath === afterDependency.targetPath ? "unchanged" : "changed";
673
+ return "changed";
674
+ }
675
+ function toDiffState(dependency) {
676
+ if (dependency.kind === "external") return {
677
+ target: `${dependency.name}@${dependency.specifier}`,
678
+ specifier: dependency.specifier,
679
+ ...dependency.resolvedVersion === void 0 ? {} : { resolvedVersion: dependency.resolvedVersion },
680
+ ...dependency.peerContext === void 0 ? {} : { peerContext: dependency.peerContext }
681
+ };
682
+ return dependency.specifier === void 0 ? { target: dependency.targetPath } : {
683
+ target: dependency.targetPath,
684
+ specifier: dependency.specifier
685
+ };
686
+ }
687
+ function hasVisibleChanges(node) {
688
+ return node.change !== "unchanged" || node.dependencies.length > 0;
689
+ }
690
+ function resolveNodeChange(location, beforeNode, afterNode, dependencies, changedPackagePaths) {
691
+ if (beforeNode === void 0 && afterNode !== void 0) return "added";
692
+ if (beforeNode !== void 0 && afterNode === void 0) return "removed";
693
+ if (beforeNode === void 0 || afterNode === void 0) return "unchanged";
694
+ if (location.beforePath !== location.afterPath) return "changed";
695
+ if (beforeNode.packageName !== afterNode.packageName) return "changed";
696
+ const packagePath = location.afterPath ?? location.beforePath;
697
+ if (packagePath !== void 0 && changedPackagePaths.has(packagePath)) return "changed";
698
+ return dependencies.length > 0 ? "changed" : "unchanged";
699
+ }
700
+ function collectChangedPackagePaths(repositoryRoot, comparison, beforeGraph, afterGraph) {
701
+ const changedFiles = listChangedRepositoryPaths(repositoryRoot, comparison);
702
+ const packagePaths = Array.from(new Set([...Array.from(beforeGraph?.nodes.keys() ?? []), ...Array.from(afterGraph?.nodes.keys() ?? [])])).sort((left, right) => right.length - left.length);
703
+ const changedPackagePaths = /* @__PURE__ */ new Set();
704
+ changedFiles.forEach((filePath) => {
705
+ const ownerPackagePath = packagePaths.find((packagePath) => isFileInPackage(filePath, packagePath));
706
+ if (ownerPackagePath !== void 0) changedPackagePaths.add(ownerPackagePath);
707
+ });
708
+ return changedPackagePaths;
709
+ }
710
+ function listChangedRepositoryPaths(repositoryRoot, comparison) {
711
+ const trackedChanges = comparison.afterTree === void 0 ? runGit(repositoryRoot, [
712
+ "diff",
713
+ "--name-only",
714
+ "-z",
715
+ comparison.beforeTree,
716
+ "--"
717
+ ]) : runGit(repositoryRoot, [
718
+ "diff",
719
+ "--name-only",
720
+ "-z",
721
+ comparison.beforeTree,
722
+ comparison.afterTree
723
+ ]);
724
+ const changedPaths = new Set(trackedChanges.split("\0").filter((filePath) => filePath.length > 0));
725
+ if (comparison.afterTree === void 0) runGit(repositoryRoot, [
726
+ "ls-files",
727
+ "--others",
728
+ "--exclude-standard",
729
+ "-z"
730
+ ]).split("\0").filter((filePath) => filePath.length > 0).forEach((filePath) => {
731
+ changedPaths.add(filePath);
732
+ });
733
+ return Array.from(changedPaths);
734
+ }
735
+ function isFileInPackage(filePath, packagePath) {
736
+ if (packagePath === ".") return true;
737
+ return filePath === packagePath || filePath.startsWith(`${packagePath}/`);
738
+ }
739
+ function runGit(repositoryRoot, args, options = {}) {
740
+ const output = execFileSync("git", [
741
+ "-C",
742
+ repositoryRoot,
743
+ ...args
744
+ ], { encoding: "utf8" });
745
+ return options.trim === false ? output : output.trimEnd();
746
+ }
747
+ function getCommandErrorMessage(error) {
748
+ if (!(error instanceof Error)) return "Unknown Git error";
749
+ const stderr = Reflect.get(error, "stderr");
750
+ if (typeof stderr === "string" && stderr.trim().length > 0) return stderr.trim();
751
+ if (Buffer.isBuffer(stderr) && stderr.byteLength > 0) return stderr.toString("utf8").trim();
752
+ return error.message;
753
+ }
754
+ //#endregion
755
+ //#region src/typescript/config.ts
756
+ function loadCompilerOptions(searchFrom, explicitConfigPath) {
757
+ const configPath = explicitConfigPath === void 0 ? findNearestConfig(searchFrom) : path.resolve(searchFrom, explicitConfigPath);
758
+ if (configPath === void 0) return { compilerOptions: defaultCompilerOptions() };
759
+ const readResult = ts.readConfigFile(configPath, ts.sys.readFile);
760
+ if (readResult.error !== void 0) throw new Error(`Failed to read TypeScript config at ${configPath}: ${formatDiagnostic(readResult.error)}`);
761
+ const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath), defaultCompilerOptions(), configPath);
762
+ if (parsed.errors.length > 0) {
763
+ const [firstError] = parsed.errors;
764
+ if (firstError === void 0) throw new Error(`Failed to parse TypeScript config at ${configPath}.`);
765
+ throw new Error(`Failed to parse TypeScript config at ${configPath}: ${formatDiagnostic(firstError)}`);
766
+ }
767
+ return {
768
+ path: configPath,
769
+ compilerOptions: parsed.options
770
+ };
771
+ }
772
+ function findNearestConfig(searchFrom) {
773
+ let currentDirectory = path.resolve(searchFrom);
774
+ while (true) {
775
+ if (!isInsideNodeModules$1(currentDirectory)) {
776
+ const tsconfigPath = path.join(currentDirectory, "tsconfig.json");
777
+ if (ts.sys.fileExists(tsconfigPath)) return tsconfigPath;
778
+ const jsconfigPath = path.join(currentDirectory, "jsconfig.json");
779
+ if (ts.sys.fileExists(jsconfigPath)) return jsconfigPath;
780
+ }
781
+ const parentDirectory = path.dirname(currentDirectory);
782
+ if (parentDirectory === currentDirectory) return;
783
+ currentDirectory = parentDirectory;
784
+ }
785
+ }
786
+ function defaultCompilerOptions() {
787
+ return {
788
+ allowJs: true,
789
+ jsx: ts.JsxEmit.ReactJSX,
790
+ module: ts.ModuleKind.NodeNext,
791
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
792
+ target: ts.ScriptTarget.ESNext,
793
+ resolveJsonModule: true,
794
+ esModuleInterop: true
795
+ };
796
+ }
797
+ function formatDiagnostic(diagnostic) {
798
+ return ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
799
+ }
800
+ function isInsideNodeModules$1(filePath) {
801
+ return filePath.includes(`${path.sep}node_modules${path.sep}`);
802
+ }
803
+ //#endregion
804
+ //#region src/utils/is-source-code-file.ts
805
+ const SOURCE_EXTENSIONS = new Set([
806
+ ".js",
807
+ ".jsx",
808
+ ".ts",
809
+ ".tsx",
810
+ ".mjs",
811
+ ".cjs",
812
+ ".mts",
813
+ ".cts"
814
+ ]);
815
+ function isSourceCodeFile(filePath) {
816
+ return SOURCE_EXTENSIONS.has(path.extname(filePath).toLowerCase());
817
+ }
818
+ //#endregion
819
+ //#region src/utils/normalize-file-path.ts
820
+ function normalizeFilePath(filePath) {
821
+ return path.normalize(filePath);
822
+ }
823
+ //#endregion
824
+ //#region src/analyzers/import/entry.ts
825
+ function resolveExistingPath(cwd, entryFile) {
826
+ const normalizedPath = normalizeFilePath(path.resolve(cwd, entryFile));
827
+ if (!fs.existsSync(normalizedPath)) throw new Error(`Entry file not found: ${entryFile}`);
828
+ if (!isSourceCodeFile(normalizedPath)) throw new Error(`Entry file must be a JS/TS source file: ${entryFile}`);
829
+ return normalizedPath;
830
+ }
831
+ //#endregion
832
+ //#region src/typescript/program.ts
833
+ function createProgram(entryFile, compilerOptions, currentDirectory) {
834
+ const host = ts.createCompilerHost(compilerOptions, true);
835
+ host.getCurrentDirectory = () => currentDirectory;
836
+ if (ts.sys.realpath !== void 0) host.realpath = ts.sys.realpath;
837
+ return ts.createProgram({
838
+ rootNames: [entryFile],
839
+ options: compilerOptions,
840
+ host
841
+ });
842
+ }
843
+ function createSourceFile(filePath) {
844
+ const sourceText = fs.readFileSync(filePath, "utf8");
845
+ return ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filePath));
846
+ }
847
+ function getScriptKind(filePath) {
848
+ switch (path.extname(filePath).toLowerCase()) {
849
+ case ".js":
850
+ case ".mjs":
851
+ case ".cjs": return ts.ScriptKind.JS;
852
+ case ".jsx": return ts.ScriptKind.JSX;
853
+ case ".tsx": return ts.ScriptKind.TSX;
854
+ case ".json": return ts.ScriptKind.JSON;
855
+ default: return ts.ScriptKind.TS;
856
+ }
857
+ }
858
+ //#endregion
859
+ //#region src/analyzers/import/unused.ts
860
+ function collectUnusedImports(sourceFile, checker) {
861
+ const importUsage = /* @__PURE__ */ new Map();
862
+ const symbolToImportDeclaration = /* @__PURE__ */ new Map();
863
+ const importedLocalNames = /* @__PURE__ */ new Set();
864
+ sourceFile.statements.forEach((statement) => {
865
+ if (!ts.isImportDeclaration(statement) || statement.importClause === void 0) return;
866
+ const identifiers = getImportBindingIdentifiers(statement.importClause);
867
+ if (identifiers.length === 0) return;
868
+ importUsage.set(statement, {
869
+ canTrack: false,
870
+ used: false
871
+ });
872
+ identifiers.forEach((identifier) => {
873
+ importedLocalNames.add(identifier.text);
874
+ const symbol = tryGetSymbolAtLocation(checker, identifier);
875
+ if (symbol === void 0) return;
876
+ symbolToImportDeclaration.set(symbol, statement);
877
+ const state = importUsage.get(statement);
878
+ if (state !== void 0) state.canTrack = true;
879
+ });
880
+ });
881
+ function visit(node) {
882
+ if (ts.isImportDeclaration(node)) return;
883
+ if (ts.isIdentifier(node) && importedLocalNames.has(node.text) && isReferenceIdentifier(node)) {
884
+ const symbol = tryGetSymbolAtLocation(checker, node);
885
+ const declaration = symbol === void 0 ? void 0 : symbolToImportDeclaration.get(symbol);
886
+ if (declaration !== void 0) {
887
+ const state = importUsage.get(declaration);
888
+ if (state !== void 0) state.used = true;
889
+ }
890
+ }
891
+ ts.forEachChild(node, visit);
892
+ }
893
+ visit(sourceFile);
894
+ return new Map([...importUsage.entries()].map(([declaration, state]) => [declaration, state.canTrack && !state.used]));
895
+ }
896
+ function getImportBindingIdentifiers(importClause) {
897
+ const identifiers = [];
898
+ if (importClause.name !== void 0) identifiers.push(importClause.name);
899
+ const namedBindings = importClause.namedBindings;
900
+ if (namedBindings === void 0) return identifiers;
901
+ if (ts.isNamespaceImport(namedBindings)) {
902
+ identifiers.push(namedBindings.name);
903
+ return identifiers;
904
+ }
905
+ namedBindings.elements.forEach((element) => {
906
+ identifiers.push(element.name);
907
+ });
908
+ return identifiers;
909
+ }
910
+ function isReferenceIdentifier(node) {
911
+ const parent = node.parent;
912
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node) return false;
913
+ if (ts.isQualifiedName(parent) && parent.right === node) return false;
914
+ if (ts.isPropertyAssignment(parent) && parent.name === node) return false;
915
+ if (ts.isBindingElement(parent) && parent.propertyName === node) return false;
916
+ if (ts.isJsxAttribute(parent) && parent.name === node) return false;
917
+ if (ts.isExportSpecifier(parent)) return parent.propertyName === node || parent.propertyName === void 0;
918
+ return true;
919
+ }
920
+ function tryGetSymbolAtLocation(checker, node) {
921
+ try {
922
+ return checker.getSymbolAtLocation(node);
923
+ } catch {
924
+ return;
925
+ }
926
+ }
927
+ //#endregion
928
+ //#region src/analyzers/import/references.ts
929
+ function collectModuleReferences(sourceFile, checker) {
930
+ const references = /* @__PURE__ */ new Map();
931
+ const unusedImports = collectUnusedImports(sourceFile, checker);
932
+ function addReference(specifier, referenceKind, isTypeOnly, unused) {
933
+ const key = `${referenceKind}:${isTypeOnly ? "type" : "value"}:${specifier}`;
934
+ const existing = references.get(key);
935
+ if (existing !== void 0) {
936
+ if (existing.unused && !unused) references.set(key, {
937
+ ...existing,
938
+ unused: false
939
+ });
940
+ return;
941
+ }
942
+ references.set(key, {
943
+ specifier,
944
+ referenceKind,
945
+ isTypeOnly,
946
+ unused
947
+ });
948
+ }
949
+ function visit(node) {
950
+ if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "import", node.importClause?.isTypeOnly ?? false, unusedImports.get(node) ?? false);
951
+ else if (ts.isExportDeclaration(node) && node.moduleSpecifier !== void 0 && ts.isStringLiteralLike(node.moduleSpecifier)) addReference(node.moduleSpecifier.text, "export", node.isTypeOnly ?? false, false);
952
+ else if (ts.isImportEqualsDeclaration(node)) {
953
+ const moduleReference = node.moduleReference;
954
+ if (ts.isExternalModuleReference(moduleReference) && moduleReference.expression !== void 0 && ts.isStringLiteralLike(moduleReference.expression)) addReference(moduleReference.expression.text, "import-equals", false, false);
955
+ } else if (ts.isCallExpression(node)) {
956
+ if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length === 1) {
957
+ const [argument] = node.arguments;
958
+ if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "dynamic-import", false, false);
959
+ }
960
+ if (ts.isIdentifier(node.expression) && node.expression.text === "require" && node.arguments.length === 1) {
961
+ const [argument] = node.arguments;
962
+ if (argument !== void 0 && ts.isStringLiteralLike(argument)) addReference(argument.text, "require", false, false);
963
+ }
964
+ }
965
+ ts.forEachChild(node, visit);
966
+ }
967
+ visit(sourceFile);
968
+ return [...references.values()];
969
+ }
970
+ //#endregion
971
+ //#region src/analyzers/import/resolver.ts
972
+ const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
973
+ function resolveDependency(reference, containingFile, options) {
974
+ const specifier = reference.specifier;
975
+ if (BUILTIN_MODULES.has(specifier)) return createEdge(reference, "builtin", specifier);
976
+ const containingConfig = options.getConfigForFile(containingFile);
977
+ const oxcResolution = resolveWithOxc(reference, specifier, containingFile, options);
978
+ if (oxcResolution !== void 0) return oxcResolution;
979
+ const host = createResolutionHost(containingConfig, options.cwd);
980
+ const resolution = ts.resolveModuleName(specifier, containingFile, containingConfig.compilerOptions, host).resolvedModule;
981
+ if (resolution !== void 0) {
982
+ const resolvedPath = normalizeFilePath(resolution.resolvedFileName);
983
+ const realPath = resolveRealPath(resolvedPath);
984
+ const sourcePath = pickSourcePath(resolvedPath, realPath);
985
+ if (sourcePath !== void 0) {
986
+ const boundary = classifyBoundary(specifier, sourcePath, options);
987
+ if (boundary !== void 0) return createEdge(reference, "boundary", sourcePath, boundary);
988
+ if (!resolution.isExternalLibraryImport || !isInsideNodeModules(sourcePath) || realPath !== void 0 && !isInsideNodeModules(realPath)) return createEdge(reference, "source", sourcePath);
989
+ }
990
+ if (resolution.isExternalLibraryImport || isInsideNodeModules(resolvedPath) || realPath !== void 0 && isInsideNodeModules(realPath)) return createEdge(reference, "external", specifier);
991
+ }
992
+ if (!specifier.startsWith(".") && !path.isAbsolute(specifier)) return createEdge(reference, "external", specifier);
993
+ return createEdge(reference, "missing", specifier);
994
+ }
995
+ function resolveWithOxc(reference, specifier, containingFile, options) {
996
+ try {
997
+ const result = options.getResolverForFile(containingFile).resolveFileSync(containingFile, specifier);
998
+ if (result.builtin !== void 0) return createEdge(reference, "builtin", result.builtin.resolved);
999
+ if (result.path !== void 0) return classifyResolvedPath(reference, specifier, result.path, options);
1000
+ } catch {
1001
+ return;
1002
+ }
1003
+ }
1004
+ function createEdge(reference, kind, target, boundary) {
1005
+ return {
1006
+ specifier: reference.specifier,
1007
+ referenceKind: reference.referenceKind,
1008
+ isTypeOnly: reference.isTypeOnly,
1009
+ unused: reference.unused,
1010
+ kind,
1011
+ target,
1012
+ ...boundary === void 0 ? {} : { boundary }
1013
+ };
1014
+ }
1015
+ function createResolutionHost(config, cwd) {
1016
+ return {
1017
+ fileExists: ts.sys.fileExists,
1018
+ readFile: ts.sys.readFile,
1019
+ directoryExists: ts.sys.directoryExists,
1020
+ getCurrentDirectory: () => config.path === void 0 ? cwd : path.dirname(config.path),
1021
+ getDirectories: ts.sys.getDirectories,
1022
+ ...ts.sys.realpath === void 0 ? {} : { realpath: ts.sys.realpath }
1023
+ };
1024
+ }
1025
+ function resolveRealPath(resolvedPath) {
1026
+ if (ts.sys.realpath === void 0) return;
1027
+ try {
1028
+ return normalizeFilePath(ts.sys.realpath(resolvedPath));
1029
+ } catch {
1030
+ return;
1031
+ }
1032
+ }
1033
+ function classifyResolvedPath(reference, specifier, resolvedPathValue, options) {
1034
+ const resolvedPath = normalizeFilePath(resolvedPathValue);
1035
+ const realPath = resolveRealPath(resolvedPath);
1036
+ const sourcePath = pickSourcePath(resolvedPath, realPath);
1037
+ if (sourcePath !== void 0) {
1038
+ const boundary = classifyBoundary(specifier, sourcePath, options);
1039
+ if (boundary !== void 0) return createEdge(reference, "boundary", sourcePath, boundary);
1040
+ if (!isInsideNodeModules(sourcePath) || realPath !== void 0 && !isInsideNodeModules(realPath)) return createEdge(reference, "source", sourcePath);
1041
+ }
1042
+ return createEdge(reference, "external", specifier);
1043
+ }
1044
+ function pickSourcePath(resolvedPath, realPath) {
1045
+ const candidates = [realPath, resolvedPath];
1046
+ for (const candidate of candidates) if (candidate !== void 0 && isSourceCodeFile(candidate) && !candidate.endsWith(".d.ts")) return candidate;
1047
+ }
1048
+ function classifyBoundary(specifier, sourcePath, options) {
1049
+ const targetConfigPath = options.getConfigForFile(sourcePath).path;
1050
+ const entryConfigPath = options.entryConfigPath;
1051
+ if (options.projectOnly && entryConfigPath !== void 0 && targetConfigPath !== entryConfigPath) return "project";
1052
+ if (!options.expandWorkspaces && isWorkspaceLikeImport(specifier) && entryConfigPath !== void 0 && targetConfigPath !== entryConfigPath) return "workspace";
1053
+ }
1054
+ function isWorkspaceLikeImport(specifier) {
1055
+ return !specifier.startsWith(".") && !path.isAbsolute(specifier);
1056
+ }
1057
+ function isInsideNodeModules(filePath) {
1058
+ return filePath.includes(`${path.sep}node_modules${path.sep}`);
1059
+ }
1060
+ //#endregion
1061
+ //#region src/analyzers/import/graph.ts
1062
+ function buildDependencyGraph(entryPath, options) {
1063
+ return new DependencyGraphBuilder(entryPath, options).build();
1064
+ }
1065
+ var DependencyGraphBuilder = class {
1066
+ nodes = /* @__PURE__ */ new Map();
1067
+ configCache = /* @__PURE__ */ new Map();
1068
+ programCache = /* @__PURE__ */ new Map();
1069
+ checkerCache = /* @__PURE__ */ new Map();
1070
+ resolverCache = /* @__PURE__ */ new Map();
1071
+ constructor(entryPath, options) {
1072
+ this.entryPath = entryPath;
1073
+ this.options = options;
1074
+ const entryConfig = {
1075
+ compilerOptions: options.entryCompilerOptions,
1076
+ ...options.entryConfigPath === void 0 ? {} : { path: options.entryConfigPath }
1077
+ };
1078
+ this.configCache.set(path.dirname(this.entryPath), entryConfig);
1079
+ }
1080
+ build() {
1081
+ this.visitFile(this.entryPath);
1082
+ return this.nodes;
1083
+ }
1084
+ visitFile(filePath) {
1085
+ const normalizedPath = normalizeFilePath(filePath);
1086
+ if (this.nodes.has(normalizedPath)) return;
1087
+ const config = this.getConfigForFile(normalizedPath);
1088
+ const program = this.getProgramForFile(normalizedPath, config);
1089
+ const checker = this.getCheckerForFile(normalizedPath, config);
1090
+ const dependencies = collectModuleReferences(program.getSourceFile(normalizedPath) ?? createSourceFile(normalizedPath), checker).map((reference) => resolveDependency(reference, normalizedPath, {
1091
+ cwd: this.options.cwd,
1092
+ expandWorkspaces: this.options.expandWorkspaces,
1093
+ projectOnly: this.options.projectOnly,
1094
+ getConfigForFile: (targetPath) => this.getConfigForFile(targetPath),
1095
+ getResolverForFile: (targetPath) => this.getResolverForFile(targetPath),
1096
+ ...this.options.entryConfigPath === void 0 ? {} : { entryConfigPath: this.options.entryConfigPath }
1097
+ }));
1098
+ this.nodes.set(normalizedPath, {
1099
+ id: normalizedPath,
1100
+ dependencies
1101
+ });
1102
+ for (const dependency of dependencies) if (dependency.kind === "source") this.visitFile(dependency.target);
1103
+ }
1104
+ getConfigForFile(filePath) {
1105
+ const directory = path.dirname(filePath);
1106
+ const cached = this.configCache.get(directory);
1107
+ if (cached !== void 0) return cached;
1108
+ const loaded = loadCompilerOptions(directory);
1109
+ this.configCache.set(directory, loaded);
1110
+ return loaded;
1111
+ }
1112
+ getProgramForFile(filePath, config) {
1113
+ const cacheKey = this.getProgramCacheKey(filePath, config);
1114
+ const cached = this.programCache.get(cacheKey);
1115
+ if (cached !== void 0) return cached;
1116
+ const currentDirectory = config.path === void 0 ? path.dirname(filePath) : path.dirname(config.path);
1117
+ const program = createProgram(filePath, config.compilerOptions, currentDirectory);
1118
+ this.programCache.set(cacheKey, program);
1119
+ return program;
1120
+ }
1121
+ getCheckerForFile(filePath, config) {
1122
+ const cacheKey = this.getProgramCacheKey(filePath, config);
1123
+ const cached = this.checkerCache.get(cacheKey);
1124
+ if (cached !== void 0) return cached;
1125
+ const checker = this.getProgramForFile(filePath, config).getTypeChecker();
1126
+ this.checkerCache.set(cacheKey, checker);
1127
+ return checker;
1128
+ }
1129
+ getProgramCacheKey(filePath, config) {
1130
+ return config.path ?? `default:${path.dirname(filePath)}`;
1131
+ }
1132
+ getResolverForFile(filePath) {
1133
+ const config = this.getConfigForFile(filePath);
1134
+ const cacheKey = this.getProgramCacheKey(filePath, config);
1135
+ const cached = this.resolverCache.get(cacheKey);
1136
+ if (cached !== void 0) return cached;
1137
+ const resolver = new ResolverFactory({
1138
+ builtinModules: true,
1139
+ conditionNames: [
1140
+ "import",
1141
+ "require",
1142
+ "default"
1143
+ ],
1144
+ extensions: [
1145
+ ".ts",
1146
+ ".tsx",
1147
+ ".js",
1148
+ ".jsx",
1149
+ ".mts",
1150
+ ".cts",
1151
+ ".mjs",
1152
+ ".cjs",
1153
+ ".json"
1154
+ ],
1155
+ mainFields: [
1156
+ "types",
1157
+ "module",
1158
+ "main"
1159
+ ],
1160
+ tsconfig: config.path === void 0 ? "auto" : { configFile: config.path }
1161
+ });
1162
+ this.resolverCache.set(cacheKey, resolver);
1163
+ return resolver;
1164
+ }
1165
+ };
1166
+ //#endregion
1167
+ //#region src/analyzers/import/index.ts
1168
+ function analyzeDependencies(entryFile, options = {}) {
1169
+ return new ImportAnalyzer(entryFile, options).analyze();
1170
+ }
1171
+ var ImportAnalyzer = class extends BaseAnalyzer {
1172
+ cwd;
1173
+ entryPath;
1174
+ constructor(entryFile, options) {
1175
+ super(entryFile, options);
1176
+ this.cwd = path.resolve(options.cwd ?? process.cwd());
1177
+ this.entryPath = resolveExistingPath(this.cwd, entryFile);
1178
+ }
1179
+ doAnalyze() {
1180
+ const { compilerOptions, path: configPath } = loadCompilerOptions(path.dirname(this.entryPath), this.options.configPath);
1181
+ const nodes = buildDependencyGraph(this.entryPath, {
1182
+ cwd: this.cwd,
1183
+ entryCompilerOptions: compilerOptions,
1184
+ expandWorkspaces: this.options.expandWorkspaces ?? true,
1185
+ projectOnly: this.options.projectOnly ?? false,
1186
+ ...configPath === void 0 ? {} : { entryConfigPath: configPath }
1187
+ });
1188
+ return {
1189
+ cwd: this.cwd,
1190
+ entryId: this.entryPath,
1191
+ nodes,
1192
+ ...configPath === void 0 ? {} : { configPath }
1193
+ };
1194
+ }
1195
+ };
1196
+ //#endregion
1197
+ //#region src/analyzers/react/bindings.ts
1198
+ function collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName, reExportBindingsByName, exportAllBindings) {
1199
+ switch (statement.type) {
1200
+ case "ImportDeclaration":
1201
+ collectImportBindings(statement, sourceDependencies, importsByLocalName);
1202
+ return;
1203
+ case "ExportNamedDeclaration":
1204
+ collectNamedExports(statement, sourceDependencies, symbolsByName, exportsByName, reExportBindingsByName);
1205
+ return;
1206
+ case "ExportAllDeclaration":
1207
+ collectExportAllBindings(statement, sourceDependencies, exportAllBindings);
1208
+ return;
1209
+ case "ExportDefaultDeclaration":
1210
+ collectDefaultExport(statement, symbolsByName, exportsByName);
1211
+ return;
1212
+ default: return;
1213
+ }
1214
+ }
1215
+ function collectImportBindings(declaration, sourceDependencies, importsByLocalName) {
1216
+ if (declaration.importKind === "type") return;
1217
+ const sourceSpecifier = declaration.source.value;
1218
+ const sourcePath = sourceDependencies.get(declaration.source.value);
1219
+ declaration.specifiers.forEach((specifier) => {
1220
+ const binding = getImportBinding(specifier, sourceSpecifier, sourcePath);
1221
+ if (binding === void 0) return;
1222
+ importsByLocalName.set(binding.localName, {
1223
+ importedName: binding.importedName,
1224
+ sourceSpecifier: binding.sourceSpecifier,
1225
+ ...binding.sourcePath === void 0 ? {} : { sourcePath: binding.sourcePath }
1226
+ });
1227
+ });
1228
+ }
1229
+ function getImportBinding(specifier, sourceSpecifier, sourcePath) {
1230
+ if (specifier.type === "ImportSpecifier") {
1231
+ if (specifier.importKind === "type") return;
1232
+ return {
1233
+ localName: specifier.local.name,
1234
+ importedName: toModuleExportName(specifier.imported),
1235
+ sourceSpecifier,
1236
+ ...sourcePath === void 0 ? {} : { sourcePath }
1237
+ };
1238
+ }
1239
+ if (specifier.type === "ImportDefaultSpecifier") return {
1240
+ localName: specifier.local.name,
1241
+ importedName: "default",
1242
+ sourceSpecifier,
1243
+ ...sourcePath === void 0 ? {} : { sourcePath }
1244
+ };
1245
+ }
1246
+ function collectNamedExports(declaration, sourceDependencies, symbolsByName, exportsByName, reExportBindingsByName) {
1247
+ if (declaration.exportKind === "type") return;
1248
+ if (declaration.declaration !== null) {
1249
+ if (declaration.declaration.type === "FunctionDeclaration") {
1250
+ const name = declaration.declaration.id?.name;
1251
+ if (name !== void 0) addExportBinding(name, name, symbolsByName, exportsByName);
1252
+ } else if (declaration.declaration.type === "VariableDeclaration") declaration.declaration.declarations.forEach((declarator) => {
1253
+ if (declarator.id.type === "Identifier") addExportBinding(declarator.id.name, declarator.id.name, symbolsByName, exportsByName);
1254
+ });
1255
+ return;
1256
+ }
1257
+ if (declaration.source !== null) {
1258
+ const sourceSpecifier = declaration.source.value;
1259
+ const sourcePath = sourceDependencies.get(sourceSpecifier);
1260
+ declaration.specifiers.forEach((specifier) => {
1261
+ if (specifier.exportKind === "type") return;
1262
+ reExportBindingsByName.set(toModuleExportName(specifier.exported), {
1263
+ importedName: toModuleExportName(specifier.local),
1264
+ sourceSpecifier,
1265
+ ...sourcePath === void 0 ? {} : { sourcePath }
1266
+ });
1267
+ });
1268
+ return;
1269
+ }
1270
+ declaration.specifiers.forEach((specifier) => {
1271
+ if (specifier.exportKind === "type") return;
1272
+ addExportBinding(toModuleExportName(specifier.local), toModuleExportName(specifier.exported), symbolsByName, exportsByName);
1273
+ });
1274
+ }
1275
+ function collectExportAllBindings(declaration, sourceDependencies, exportAllBindings) {
1276
+ if (declaration.exportKind === "type") return;
1277
+ const sourceSpecifier = declaration.source.value;
1278
+ const sourcePath = sourceDependencies.get(sourceSpecifier);
1279
+ exportAllBindings.push({
1280
+ importedName: "*",
1281
+ sourceSpecifier,
1282
+ ...sourcePath === void 0 ? {} : { sourcePath }
1283
+ });
1284
+ }
1285
+ function collectDefaultExport(declaration, symbolsByName, exportsByName) {
1286
+ if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") {
1287
+ const localName = declaration.declaration.id?.name;
1288
+ if (localName !== void 0) addExportBinding(localName, "default", symbolsByName, exportsByName);
1289
+ return;
1290
+ }
1291
+ if (declaration.declaration.type === "Identifier") {
1292
+ addExportBinding(declaration.declaration.name, "default", symbolsByName, exportsByName);
1293
+ return;
1294
+ }
1295
+ if (declaration.declaration.type === "ArrowFunctionExpression") addExportBinding("default", "default", symbolsByName, exportsByName);
1296
+ }
1297
+ function addExportBinding(localName, exportedName, symbolsByName, exportsByName) {
1298
+ const symbol = symbolsByName.get(localName);
1299
+ if (symbol === void 0) return;
1300
+ symbol.exportNames.add(exportedName);
1301
+ exportsByName.set(exportedName, symbol.id);
1302
+ }
1303
+ function toModuleExportName(name) {
1304
+ return name.type === "Literal" ? name.value : name.name;
1305
+ }
1306
+ //#endregion
1307
+ //#region src/analyzers/react/walk.ts
1308
+ const FUNCTION_NODE_TYPES = new Set([
1309
+ "FunctionDeclaration",
1310
+ "FunctionExpression",
1311
+ "ArrowFunctionExpression",
1312
+ "TSDeclareFunction",
1313
+ "TSEmptyBodyFunctionExpression"
1314
+ ]);
1315
+ function walkReactUsageTree(root, visit) {
1316
+ walkNode(root, visit, true);
1317
+ }
1318
+ function walkNode(node, visit, allowNestedFunctions = false) {
1319
+ visit(node);
1320
+ const keys = visitorKeys[node.type];
1321
+ if (keys === void 0) return;
1322
+ keys.forEach((key) => {
1323
+ const value = node[key];
1324
+ walkChild(value, visit, allowNestedFunctions);
1325
+ });
1326
+ }
1327
+ function walkChild(value, visit, allowNestedFunctions) {
1328
+ if (Array.isArray(value)) {
1329
+ value.forEach((entry) => {
1330
+ walkChild(entry, visit, allowNestedFunctions);
1331
+ });
1332
+ return;
1333
+ }
1334
+ if (!isNode(value)) return;
1335
+ if (!allowNestedFunctions && FUNCTION_NODE_TYPES.has(value.type)) return;
1336
+ walkNode(value, visit, false);
1337
+ }
1338
+ function isNode(value) {
1339
+ return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
1340
+ }
1341
+ function classifyReactSymbol(name, declaration) {
1342
+ if (isHookName(name) && isFunctionLikeDeclaration(declaration)) return "hook";
1343
+ if (isComponentName(name) && (isFunctionLikeDeclaration(declaration) && returnsReactElement(declaration) || isStyledComponentDeclaration(declaration))) return "component";
1344
+ }
1345
+ function containsReactElementLikeExpression(expression) {
1346
+ let found = false;
1347
+ walkNode(expression, (node) => {
1348
+ if (node.type === "JSXElement" || node.type === "JSXFragment" || node.type === "CallExpression" && isReactCreateElementCall(node)) found = true;
1349
+ });
1350
+ return found;
1351
+ }
1352
+ function getComponentReferenceName(node) {
1353
+ const name = getJsxName(node.openingElement.name);
1354
+ return name !== void 0 && isComponentName(name) ? name : void 0;
1355
+ }
1356
+ function getBuiltinReferenceName(node) {
1357
+ const name = getJsxName(node.openingElement.name);
1358
+ return name !== void 0 && isIntrinsicElementName(name) ? name : void 0;
1359
+ }
1360
+ function getHookReferenceName(node) {
1361
+ const calleeName = getIdentifierName(node.callee);
1362
+ return calleeName !== void 0 && isHookName(calleeName) ? calleeName : void 0;
1363
+ }
1364
+ function getCreateElementComponentReferenceName(node) {
1365
+ if (!isReactCreateElementCall(node)) return;
1366
+ const [firstArgument] = node.arguments;
1367
+ if (firstArgument === void 0 || firstArgument.type !== "Identifier") return;
1368
+ return isComponentName(firstArgument.name) ? firstArgument.name : void 0;
1369
+ }
1370
+ function getStyledComponentReferenceName(node) {
1371
+ const reference = getStyledFactoryReference(node.type === "CallExpression" ? node.callee : node.tag);
1372
+ return reference?.kind === "component" ? reference.name : void 0;
1373
+ }
1374
+ function getStyledBuiltinReferenceName(node) {
1375
+ const reference = getStyledFactoryReference(node.type === "CallExpression" ? node.callee : node.tag);
1376
+ return reference?.kind === "builtin" ? reference.name : void 0;
1377
+ }
1378
+ function isHookName(name) {
1379
+ return /^use[A-Z0-9]/.test(name);
1380
+ }
1381
+ function isComponentName(name) {
1382
+ return /^[A-Z]/.test(name);
1383
+ }
1384
+ function isIntrinsicElementName(name) {
1385
+ return /^[a-z]/.test(name);
1386
+ }
1387
+ function isFunctionLikeDeclaration(declaration) {
1388
+ return declaration.type === "ArrowFunctionExpression" || declaration.type === "FunctionDeclaration" || declaration.type === "FunctionExpression";
1389
+ }
1390
+ function returnsReactElement(declaration) {
1391
+ if (declaration.type === "ArrowFunctionExpression" && declaration.expression) return containsReactElementLikeExpression(declaration.body);
1392
+ const body = declaration.body;
1393
+ if (body === null) return false;
1394
+ let found = false;
1395
+ walkReactUsageTree(body, (node) => {
1396
+ if (node.type !== "ReturnStatement" || node.argument === null) return;
1397
+ if (containsReactElementLikeExpression(node.argument)) found = true;
1398
+ });
1399
+ return found;
1400
+ }
1401
+ function isStyledComponentDeclaration(expression) {
1402
+ if (expression.type === "CallExpression") return getStyledFactoryReference(expression.callee) !== void 0;
1403
+ if (expression.type === "TaggedTemplateExpression") return getStyledFactoryReference(expression.tag) !== void 0;
1404
+ return false;
1405
+ }
1406
+ function getStyledFactoryReference(expression) {
1407
+ const unwrapped = unwrapExpression(expression);
1408
+ if (unwrapped.type === "MemberExpression" && !unwrapped.computed) {
1409
+ if (unwrapped.object.type !== "Identifier" || unwrapped.object.name !== "styled" || unwrapped.property.type !== "Identifier") return;
1410
+ const name = unwrapped.property.name;
1411
+ if (isIntrinsicElementName(name)) return {
1412
+ kind: "builtin",
1413
+ name
1414
+ };
1415
+ if (isComponentName(name)) return {
1416
+ kind: "component",
1417
+ name
1418
+ };
1419
+ return;
1420
+ }
1421
+ if (unwrapped.type !== "CallExpression") return;
1422
+ const callee = unwrapExpression(unwrapped.callee);
1423
+ if (callee.type !== "Identifier" || callee.name !== "styled") return;
1424
+ const [firstArgument] = unwrapped.arguments;
1425
+ if (firstArgument?.type !== "Identifier") return;
1426
+ return isComponentName(firstArgument.name) ? {
1427
+ kind: "component",
1428
+ name: firstArgument.name
1429
+ } : void 0;
1430
+ }
1431
+ function isReactCreateElementCall(node) {
1432
+ const callee = unwrapExpression(node.callee);
1433
+ if (callee.type !== "MemberExpression" || callee.computed) return false;
1434
+ return callee.object.type === "Identifier" && callee.object.name === "React" && callee.property.name === "createElement";
1435
+ }
1436
+ function getJsxName(name) {
1437
+ if (name.type === "JSXIdentifier") return name.name;
1438
+ }
1439
+ function getIdentifierName(expression) {
1440
+ const unwrapped = unwrapExpression(expression);
1441
+ return unwrapped.type === "Identifier" ? unwrapped.name : void 0;
1442
+ }
1443
+ function unwrapExpression(expression) {
1444
+ let current = expression;
1445
+ while (true) {
1446
+ if (current.type === "ParenthesizedExpression" || current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
1447
+ current = current.expression;
1448
+ continue;
1449
+ }
1450
+ return current;
1451
+ }
1452
+ }
1453
+ //#endregion
1454
+ //#region src/analyzers/react/entries.ts
1455
+ function collectEntryUsages(program, filePath, sourceText, includeBuiltins) {
1456
+ const entries = /* @__PURE__ */ new Map();
1457
+ program.body.forEach((statement) => {
1458
+ collectStatementEntryUsages(statement, filePath, sourceText, entries, includeBuiltins);
1459
+ });
1460
+ return [...entries.values()].sort(comparePendingReactUsageEntries);
1461
+ }
1462
+ function collectStatementEntryUsages(statement, filePath, sourceText, entries, includeBuiltins) {
1463
+ collectNodeEntryUsages(statement, filePath, sourceText, entries, includeBuiltins, false);
1464
+ }
1465
+ function collectNodeEntryUsages(node, filePath, sourceText, entries, includeBuiltins, hasComponentAncestor) {
1466
+ if (FUNCTION_NODE_TYPES.has(node.type)) return;
1467
+ let nextHasComponentAncestor = hasComponentAncestor;
1468
+ if (node.type === "JSXElement") {
1469
+ const referenceName = getComponentReferenceName(node);
1470
+ if (referenceName !== void 0) {
1471
+ if (!hasComponentAncestor) addPendingReactUsageEntry(entries, referenceName, "component", createReactUsageLocation(filePath, sourceText, node.start));
1472
+ nextHasComponentAncestor = true;
1473
+ } else if (includeBuiltins) {
1474
+ const builtinName = getBuiltinReferenceName(node);
1475
+ if (builtinName !== void 0) {
1476
+ if (!hasComponentAncestor) addPendingReactUsageEntry(entries, builtinName, "builtin", createReactUsageLocation(filePath, sourceText, node.start));
1477
+ nextHasComponentAncestor = true;
1478
+ }
1479
+ }
1480
+ } else if (node.type === "CallExpression") {
1481
+ const hookReference = getHookReferenceName(node);
1482
+ if (hookReference !== void 0) addPendingReactUsageEntry(entries, hookReference, "hook", createReactUsageLocation(filePath, sourceText, node.start));
1483
+ const referenceName = getCreateElementComponentReferenceName(node);
1484
+ if (referenceName !== void 0) {
1485
+ if (!hasComponentAncestor) addPendingReactUsageEntry(entries, referenceName, "component", createReactUsageLocation(filePath, sourceText, node.start));
1486
+ nextHasComponentAncestor = true;
1487
+ }
1488
+ }
1489
+ const keys = visitorKeys[node.type];
1490
+ if (keys === void 0) return;
1491
+ keys.forEach((key) => {
1492
+ const value = node[key];
1493
+ collectEntryUsageChild(value, filePath, sourceText, entries, includeBuiltins, nextHasComponentAncestor);
1494
+ });
1495
+ }
1496
+ function collectEntryUsageChild(value, filePath, sourceText, entries, includeBuiltins, hasComponentAncestor) {
1497
+ if (Array.isArray(value)) {
1498
+ value.forEach((entry) => {
1499
+ collectEntryUsageChild(entry, filePath, sourceText, entries, includeBuiltins, hasComponentAncestor);
1500
+ });
1501
+ return;
1502
+ }
1503
+ if (!isNode(value)) return;
1504
+ collectNodeEntryUsages(value, filePath, sourceText, entries, includeBuiltins, hasComponentAncestor);
1505
+ }
1506
+ function addPendingReactUsageEntry(entries, referenceName, kind, location) {
1507
+ const key = `${location.filePath}:${location.line}:${location.column}:${kind}:${referenceName}`;
1508
+ entries.set(key, {
1509
+ referenceName,
1510
+ kind,
1511
+ location
1512
+ });
1513
+ }
1514
+ function createReactUsageLocation(filePath, sourceText, offset) {
1515
+ return {
1516
+ filePath,
1517
+ ...offsetToLineAndColumn(sourceText, offset)
1518
+ };
1519
+ }
1520
+ function offsetToLineAndColumn(sourceText, offset) {
1521
+ let line = 1;
1522
+ let column = 1;
1523
+ for (let index = 0; index < offset && index < sourceText.length; index += 1) {
1524
+ if (sourceText[index] === "\n") {
1525
+ line += 1;
1526
+ column = 1;
1527
+ continue;
1528
+ }
1529
+ column += 1;
1530
+ }
1531
+ return {
1532
+ line,
1533
+ column
1534
+ };
1535
+ }
1536
+ function comparePendingReactUsageEntries(left, right) {
1537
+ 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);
1538
+ }
1539
+ //#endregion
1540
+ //#region src/analyzers/react/symbols.ts
1541
+ function collectTopLevelReactSymbols(statement, filePath, symbolsByName) {
1542
+ switch (statement.type) {
1543
+ case "FunctionDeclaration":
1544
+ addFunctionSymbol(statement, filePath, symbolsByName);
1545
+ return;
1546
+ case "VariableDeclaration":
1547
+ statement.declarations.forEach((declarator) => {
1548
+ addVariableSymbol(declarator, filePath, symbolsByName);
1549
+ });
1550
+ return;
1551
+ case "ExportNamedDeclaration":
1552
+ if (statement.declaration !== null) collectTopLevelReactSymbols(statement.declaration, filePath, symbolsByName);
1553
+ return;
1554
+ case "ExportDefaultDeclaration":
1555
+ addDefaultExportSymbol(statement, filePath, symbolsByName);
1556
+ return;
1557
+ default: return;
1558
+ }
1559
+ }
1560
+ function collectTopLevelDynamicComponentCandidates(statement, filePath, symbolsByName, dynamicComponentCandidatesByName) {
1561
+ switch (statement.type) {
1562
+ case "VariableDeclaration":
1563
+ statement.declarations.forEach((declarator) => {
1564
+ addDynamicComponentCandidate(declarator, filePath, symbolsByName, dynamicComponentCandidatesByName);
1565
+ });
1566
+ return;
1567
+ case "ExportNamedDeclaration":
1568
+ if (statement.declaration !== null) collectTopLevelDynamicComponentCandidates(statement.declaration, filePath, symbolsByName, dynamicComponentCandidatesByName);
1569
+ return;
1570
+ default: return;
1571
+ }
1572
+ }
1573
+ function addFunctionSymbol(declaration, filePath, symbolsByName) {
1574
+ const name = declaration.id?.name;
1575
+ if (name === void 0) return;
1576
+ const kind = classifyReactSymbol(name, declaration);
1577
+ if (kind === void 0) return;
1578
+ symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration.id?.start ?? declaration.start, getAnalysisRoot(declaration)));
1579
+ }
1580
+ function addVariableSymbol(declarator, filePath, symbolsByName) {
1581
+ if (declarator.id.type !== "Identifier" || declarator.init === null) return;
1582
+ const name = declarator.id.name;
1583
+ const kind = classifyReactSymbol(name, declarator.init);
1584
+ if (kind === void 0) return;
1585
+ symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declarator.init.start, getAnalysisRoot(declarator.init)));
1586
+ }
1587
+ function addDefaultExportSymbol(declaration, filePath, symbolsByName) {
1588
+ if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") addFunctionSymbol(declaration.declaration, filePath, symbolsByName);
1589
+ else if (declaration.declaration.type === "ArrowFunctionExpression") {
1590
+ const name = "default";
1591
+ const kind = declaration.declaration.body ? classifyReactSymbol(name, declaration.declaration) : void 0;
1592
+ if (kind !== void 0) symbolsByName.set(name, createPendingSymbol(filePath, name, kind, declaration.declaration.start, getAnalysisRoot(declaration.declaration)));
1593
+ }
1594
+ }
1595
+ function addDynamicComponentCandidate(declarator, filePath, symbolsByName, dynamicComponentCandidatesByName) {
1596
+ if (declarator.id.type !== "Identifier" || declarator.init === null) return;
1597
+ const name = declarator.id.name;
1598
+ if (!isPotentialDynamicComponentName(name) || symbolsByName.has(name) || !isDynamicComponentInitializer(declarator.init)) return;
1599
+ dynamicComponentCandidatesByName.set(name, createPendingSymbol(filePath, name, "component", declarator.init.start, getAnalysisRoot(declarator.init)));
1600
+ }
1601
+ function createPendingSymbol(filePath, name, kind, declarationOffset, analysisRoot) {
1602
+ return {
1603
+ id: `${filePath}#${kind}:${name}`,
1604
+ name,
1605
+ kind,
1606
+ filePath,
1607
+ declarationOffset,
1608
+ analysisRoot,
1609
+ exportNames: /* @__PURE__ */ new Set(),
1610
+ componentReferences: /* @__PURE__ */ new Set(),
1611
+ hookReferences: /* @__PURE__ */ new Set(),
1612
+ builtinReferences: /* @__PURE__ */ new Set()
1613
+ };
1614
+ }
1615
+ function getAnalysisRoot(declaration) {
1616
+ if (declaration.type === "FunctionDeclaration" || declaration.type === "FunctionExpression") {
1617
+ if (declaration.body === null) throw new Error(`Expected React symbol "${declaration.id?.name ?? "anonymous"}" to have a body.`);
1618
+ return declaration.body;
1619
+ }
1620
+ if (declaration.type === "ArrowFunctionExpression") return declaration.body;
1621
+ return declaration;
1622
+ }
1623
+ function isDynamicComponentInitializer(expression) {
1624
+ return expression.type === "CallExpression" || expression.type === "TaggedTemplateExpression";
1625
+ }
1626
+ function isPotentialDynamicComponentName(name) {
1627
+ return /^[A-Z][A-Za-z0-9]*$/.test(name);
1628
+ }
1629
+ //#endregion
1630
+ //#region src/analyzers/react/usage.ts
1631
+ function analyzeSymbolUsages(symbol, includeBuiltins) {
1632
+ walkReactUsageTree(symbol.analysisRoot, (node) => {
1633
+ if (node.type === "JSXElement") {
1634
+ const name = getComponentReferenceName(node);
1635
+ if (name !== void 0) symbol.componentReferences.add(name);
1636
+ if (includeBuiltins) {
1637
+ const builtinName = getBuiltinReferenceName(node);
1638
+ if (builtinName !== void 0) symbol.builtinReferences.add(builtinName);
1639
+ }
1640
+ return;
1641
+ }
1642
+ if (node.type === "CallExpression") {
1643
+ const hookReference = getHookReferenceName(node);
1644
+ if (hookReference !== void 0) symbol.hookReferences.add(hookReference);
1645
+ const componentReference = getCreateElementComponentReferenceName(node);
1646
+ if (componentReference !== void 0) symbol.componentReferences.add(componentReference);
1647
+ const styledComponentReference = getStyledComponentReferenceName(node);
1648
+ if (styledComponentReference !== void 0) symbol.componentReferences.add(styledComponentReference);
1649
+ if (includeBuiltins) {
1650
+ const styledBuiltinReference = getStyledBuiltinReferenceName(node);
1651
+ if (styledBuiltinReference !== void 0) symbol.builtinReferences.add(styledBuiltinReference);
1652
+ }
1653
+ }
1654
+ if (node.type === "TaggedTemplateExpression") {
1655
+ const styledComponentReference = getStyledComponentReferenceName(node);
1656
+ if (styledComponentReference !== void 0) symbol.componentReferences.add(styledComponentReference);
1657
+ if (includeBuiltins) {
1658
+ const styledBuiltinReference = getStyledBuiltinReferenceName(node);
1659
+ if (styledBuiltinReference !== void 0) symbol.builtinReferences.add(styledBuiltinReference);
1660
+ }
1661
+ }
1662
+ });
1663
+ }
1664
+ //#endregion
1665
+ //#region src/analyzers/react/file.ts
1666
+ function analyzeReactFile(program, filePath, sourceText, includeNestedRenderEntries, sourceDependencies, includeBuiltins) {
1667
+ const symbolsByName = /* @__PURE__ */ new Map();
1668
+ const dynamicComponentCandidatesByName = /* @__PURE__ */ new Map();
1669
+ program.body.forEach((statement) => {
1670
+ collectTopLevelReactSymbols(statement, filePath, symbolsByName);
1671
+ });
1672
+ program.body.forEach((statement) => {
1673
+ collectTopLevelDynamicComponentCandidates(statement, filePath, symbolsByName, dynamicComponentCandidatesByName);
1674
+ });
1675
+ const allSymbolsByName = new Map([...dynamicComponentCandidatesByName, ...symbolsByName]);
1676
+ const importsByLocalName = /* @__PURE__ */ new Map();
1677
+ const exportsByName = /* @__PURE__ */ new Map();
1678
+ const reExportBindingsByName = /* @__PURE__ */ new Map();
1679
+ const exportAllBindings = [];
1680
+ const directEntryUsages = includeNestedRenderEntries ? collectEntryUsages(program, filePath, sourceText, includeBuiltins) : [];
1681
+ program.body.forEach((statement) => {
1682
+ collectImportsAndExports(statement, sourceDependencies, allSymbolsByName, importsByLocalName, exportsByName, reExportBindingsByName, exportAllBindings);
1683
+ });
1684
+ allSymbolsByName.forEach((symbol) => {
1685
+ analyzeSymbolUsages(symbol, includeBuiltins);
1686
+ });
1687
+ const allSymbolsById = new Map([...allSymbolsByName.values()].map((symbol) => [symbol.id, symbol]));
1688
+ return {
1689
+ filePath,
1690
+ importsByLocalName,
1691
+ exportsByName,
1692
+ reExportBindingsByName,
1693
+ exportAllBindings,
1694
+ entryUsages: directEntryUsages.length > 0 ? directEntryUsages : includeNestedRenderEntries ? collectComponentDeclarationEntryUsages(symbolsByName, sourceText) : [],
1695
+ allSymbolsById,
1696
+ allSymbolsByName,
1697
+ symbolsById: new Map([...symbolsByName.values()].map((symbol) => [symbol.id, symbol])),
1698
+ symbolsByName
1699
+ };
1700
+ }
1701
+ function collectComponentDeclarationEntryUsages(symbolsByName, sourceText) {
1702
+ const componentSymbols = [...symbolsByName.values()].filter((symbol) => symbol.kind === "component");
1703
+ if (componentSymbols.length === 0) return [];
1704
+ const exportedComponentSymbols = componentSymbols.filter((symbol) => symbol.exportNames.size > 0);
1705
+ return (exportedComponentSymbols.length > 0 ? exportedComponentSymbols : componentSymbols).sort((left, right) => {
1706
+ return left.declarationOffset - right.declarationOffset || left.name.localeCompare(right.name);
1707
+ }).map((symbol) => ({
1708
+ referenceName: symbol.name,
1709
+ kind: "component",
1710
+ location: createReactUsageLocation(symbol.filePath, sourceText, symbol.declarationOffset)
1711
+ }));
1712
+ }
1713
+ //#endregion
1714
+ //#region src/analyzers/react/references.ts
1715
+ function resolveReactReference(fileAnalysis, fileAnalyses, name, kind) {
1716
+ if (kind === "builtin") return getBuiltinNodeId(name);
1717
+ const localSymbol = fileAnalysis.allSymbolsByName.get(name);
1718
+ if (localSymbol !== void 0 && localSymbol.kind === kind) return localSymbol.id;
1719
+ const importBinding = fileAnalysis.importsByLocalName.get(name);
1720
+ if (importBinding === void 0) return;
1721
+ if (importBinding.sourcePath === void 0) return kind === "hook" ? getExternalHookNodeId(importBinding, name) : void 0;
1722
+ const sourceFileAnalysis = fileAnalyses.get(importBinding.sourcePath);
1723
+ if (sourceFileAnalysis === void 0) return;
1724
+ const targetId = resolveExportedSymbol(sourceFileAnalysis, importBinding.importedName, kind, fileAnalyses, /* @__PURE__ */ new Set());
1725
+ if (targetId === void 0) return;
1726
+ return targetId;
1727
+ }
1728
+ function resolveExportedSymbol(fileAnalysis, exportName, kind, fileAnalyses, visited) {
1729
+ const visitKey = `${fileAnalysis.filePath}:${exportName}:${kind}`;
1730
+ if (visited.has(visitKey)) return;
1731
+ visited.add(visitKey);
1732
+ const directTargetId = fileAnalysis.exportsByName.get(exportName);
1733
+ if (directTargetId !== void 0) {
1734
+ if (fileAnalysis.allSymbolsById.get(directTargetId)?.kind === kind) return directTargetId;
1735
+ }
1736
+ const reExportBinding = fileAnalysis.reExportBindingsByName.get(exportName);
1737
+ if (reExportBinding?.sourcePath !== void 0) {
1738
+ const reExportSourceAnalysis = fileAnalyses.get(reExportBinding.sourcePath);
1739
+ if (reExportSourceAnalysis !== void 0) {
1740
+ const reExportTargetId = resolveExportedSymbol(reExportSourceAnalysis, reExportBinding.importedName, kind, fileAnalyses, visited);
1741
+ if (reExportTargetId !== void 0) return reExportTargetId;
1742
+ }
1743
+ }
1744
+ for (const exportAllBinding of fileAnalysis.exportAllBindings) {
1745
+ if (exportAllBinding.sourcePath === void 0) continue;
1746
+ const exportAllSourceAnalysis = fileAnalyses.get(exportAllBinding.sourcePath);
1747
+ if (exportAllSourceAnalysis === void 0) continue;
1748
+ const exportAllTargetId = resolveExportedSymbol(exportAllSourceAnalysis, exportName, kind, fileAnalyses, visited);
1749
+ if (exportAllTargetId !== void 0) return exportAllTargetId;
1750
+ }
1751
+ }
1752
+ function addExternalHookNodes(fileAnalyses, nodes) {
1753
+ for (const fileAnalysis of fileAnalyses.values()) fileAnalysis.importsByLocalName.forEach((binding, localName) => {
1754
+ if (binding.sourcePath !== void 0) return;
1755
+ if (!isHookName(localName) && !isHookName(binding.importedName)) return;
1756
+ const externalNode = createExternalHookNode(binding, localName);
1757
+ if (!nodes.has(externalNode.id)) nodes.set(externalNode.id, externalNode);
1758
+ });
1759
+ }
1760
+ function addBuiltinNodes(fileAnalyses, nodes) {
1761
+ for (const fileAnalysis of fileAnalyses.values()) {
1762
+ fileAnalysis.entryUsages.forEach((entry) => {
1763
+ if (entry.kind !== "builtin") return;
1764
+ const builtinNode = createBuiltinNode(entry.referenceName);
1765
+ if (!nodes.has(builtinNode.id)) nodes.set(builtinNode.id, builtinNode);
1766
+ });
1767
+ fileAnalysis.allSymbolsById.forEach((symbol) => {
1768
+ symbol.builtinReferences.forEach((name) => {
1769
+ const builtinNode = createBuiltinNode(name);
1770
+ if (!nodes.has(builtinNode.id)) nodes.set(builtinNode.id, builtinNode);
1771
+ });
1772
+ });
1773
+ }
1774
+ }
1775
+ function createExternalHookNode(binding, localName) {
1776
+ const name = getExternalHookName(binding, localName);
1777
+ return {
1778
+ id: getExternalHookNodeId(binding, localName),
1779
+ name,
1780
+ kind: "hook",
1781
+ filePath: binding.sourceSpecifier,
1782
+ exportNames: [binding.importedName],
1783
+ usages: []
1784
+ };
1785
+ }
1786
+ function createBuiltinNode(name) {
1787
+ return {
1788
+ id: getBuiltinNodeId(name),
1789
+ name,
1790
+ kind: "builtin",
1791
+ filePath: "html",
1792
+ exportNames: [],
1793
+ usages: []
1794
+ };
1795
+ }
1796
+ function getExternalHookNodeId(binding, localName) {
1797
+ return `external:${binding.sourceSpecifier}#hook:${getExternalHookName(binding, localName)}`;
1798
+ }
1799
+ function getBuiltinNodeId(name) {
1800
+ return `builtin:${name}`;
1801
+ }
1802
+ function getExternalHookName(binding, localName) {
1803
+ return binding.importedName === "default" ? localName : binding.importedName;
1804
+ }
1805
+ function compareReactNodeIds(leftId, rightId, nodes) {
1806
+ const left = nodes.get(leftId);
1807
+ const right = nodes.get(rightId);
1808
+ if (left === void 0 || right === void 0) return leftId.localeCompare(rightId);
1809
+ return compareReactNodes(left, right);
1810
+ }
1811
+ function compareReactUsageEntries(left, right, nodes) {
1812
+ 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);
1813
+ }
1814
+ function compareReactNodes(left, right) {
1815
+ return left.filePath.localeCompare(right.filePath) || left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind);
1816
+ }
1817
+ //#endregion
1818
+ //#region src/analyzers/react/index.ts
1819
+ function analyzeReactUsage(entryFile, options = {}) {
1820
+ return new ReactAnalyzer(normalizeEntryFiles(entryFile), options).analyze();
1821
+ }
1822
+ var ReactAnalyzer = class extends BaseAnalyzer {
1823
+ constructor(entryFiles, options) {
1824
+ const [firstEntryFile] = entryFiles;
1825
+ if (firstEntryFile === void 0) throw new Error("At least one React entry file is required.");
1826
+ super(firstEntryFile, options);
1827
+ this.entryFiles = entryFiles;
1828
+ }
1829
+ doAnalyze() {
1830
+ const dependencyGraphs = this.entryFiles.map((entryFile) => analyzeDependencies(entryFile, this.options));
1831
+ const dependencyGraph = mergeDependencyGraphs(dependencyGraphs);
1832
+ const entryIds = dependencyGraphs.map((graph) => graph.entryId);
1833
+ const fileAnalyses = this.collectFileAnalyses(dependencyGraph);
1834
+ const nodes = this.createNodes(fileAnalyses);
1835
+ this.attachUsages(fileAnalyses, nodes);
1836
+ const entries = this.collectEntries(fileAnalyses, nodes);
1837
+ return {
1838
+ cwd: dependencyGraph.cwd,
1839
+ entryId: dependencyGraph.entryId,
1840
+ entryIds,
1841
+ nodes,
1842
+ entries
1843
+ };
1844
+ }
1845
+ collectFileAnalyses(dependencyGraph) {
1846
+ const reachableFiles = new Set([...dependencyGraph.entryIds, ...dependencyGraph.nodes.keys()]);
1847
+ const fileAnalyses = /* @__PURE__ */ new Map();
1848
+ for (const filePath of [...reachableFiles].sort()) {
1849
+ if (!isSourceCodeFile(filePath) || filePath.endsWith(".d.ts")) continue;
1850
+ const sourceText = fs.readFileSync(filePath, "utf8");
1851
+ const parseResult = parseSync(filePath, sourceText, {
1852
+ astType: "ts",
1853
+ sourceType: "unambiguous"
1854
+ });
1855
+ const dependencyNode = dependencyGraph.nodes.get(filePath);
1856
+ const sourceDependencies = /* @__PURE__ */ new Map();
1857
+ dependencyNode?.dependencies.forEach((dependency) => {
1858
+ if (dependency.kind === "source") sourceDependencies.set(dependency.specifier, dependency.target);
1859
+ });
1860
+ fileAnalyses.set(filePath, analyzeReactFile(parseResult.program, filePath, sourceText, dependencyGraph.entryIds.includes(filePath), sourceDependencies, this.options.includeBuiltins === true));
1861
+ }
1862
+ return fileAnalyses;
1863
+ }
1864
+ createNodes(fileAnalyses) {
1865
+ const nodes = /* @__PURE__ */ new Map();
1866
+ for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.allSymbolsById.values()) nodes.set(symbol.id, {
1867
+ id: symbol.id,
1868
+ name: symbol.name,
1869
+ kind: symbol.kind,
1870
+ filePath: symbol.filePath,
1871
+ exportNames: [...symbol.exportNames].sort(),
1872
+ usages: []
1873
+ });
1874
+ addExternalHookNodes(fileAnalyses, nodes);
1875
+ if (this.options.includeBuiltins === true) addBuiltinNodes(fileAnalyses, nodes);
1876
+ return nodes;
1877
+ }
1878
+ attachUsages(fileAnalyses, nodes) {
1879
+ for (const fileAnalysis of fileAnalyses.values()) for (const symbol of fileAnalysis.allSymbolsById.values()) {
1880
+ const usages = /* @__PURE__ */ new Map();
1881
+ symbol.componentReferences.forEach((referenceName) => {
1882
+ const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "component");
1883
+ if (targetId !== void 0 && targetId !== symbol.id) usages.set(`render:${targetId}:${referenceName}`, {
1884
+ kind: "render",
1885
+ target: targetId,
1886
+ referenceName
1887
+ });
1888
+ });
1889
+ symbol.hookReferences.forEach((referenceName) => {
1890
+ const targetId = resolveReactReference(fileAnalysis, fileAnalyses, referenceName, "hook");
1891
+ if (targetId !== void 0 && targetId !== symbol.id) usages.set(`hook:${targetId}:${referenceName}`, {
1892
+ kind: "hook-call",
1893
+ target: targetId,
1894
+ referenceName
1895
+ });
1896
+ });
1897
+ if (this.options.includeBuiltins === true) symbol.builtinReferences.forEach((referenceName) => {
1898
+ const targetId = getBuiltinNodeId(referenceName);
1899
+ usages.set(`render:${targetId}:${referenceName}`, {
1900
+ kind: "render",
1901
+ target: targetId,
1902
+ referenceName
1903
+ });
1904
+ });
1905
+ const node = nodes.get(symbol.id);
1906
+ if (node === void 0) continue;
1907
+ const sortedUsages = [...usages.values()].sort((left, right) => compareReactNodeIds(left.target, right.target, nodes));
1908
+ nodes.set(symbol.id, {
1909
+ ...node,
1910
+ usages: sortedUsages
1911
+ });
1912
+ }
1913
+ }
1914
+ collectEntries(fileAnalyses, nodes) {
1915
+ const entriesByKey = /* @__PURE__ */ new Map();
1916
+ for (const fileAnalysis of fileAnalyses.values()) for (const entry of fileAnalysis.entryUsages) {
1917
+ const targetId = resolveReactReference(fileAnalysis, fileAnalyses, entry.referenceName, entry.kind);
1918
+ if (targetId === void 0) continue;
1919
+ const key = `${entry.location.filePath}:${entry.location.line}:${entry.location.column}:${targetId}`;
1920
+ entriesByKey.set(key, {
1921
+ target: targetId,
1922
+ referenceName: entry.referenceName,
1923
+ location: entry.location
1924
+ });
1925
+ }
1926
+ return [...entriesByKey.values()].sort((left, right) => compareReactUsageEntries(left, right, nodes));
1927
+ }
1928
+ };
1929
+ function normalizeEntryFiles(entryFile) {
1930
+ const entryFiles = Array.isArray(entryFile) ? entryFile : [entryFile];
1931
+ const dedupedEntryFiles = [...new Set(entryFiles)];
1932
+ if (dedupedEntryFiles.length === 0) throw new Error("At least one React entry file is required.");
1933
+ return dedupedEntryFiles;
1934
+ }
1935
+ function mergeDependencyGraphs(graphs) {
1936
+ const firstGraph = graphs[0];
1937
+ if (firstGraph === void 0) throw new Error("At least one dependency graph is required.");
1938
+ const nodes = /* @__PURE__ */ new Map();
1939
+ for (const graph of graphs) for (const [nodeId, node] of graph.nodes) if (!nodes.has(nodeId)) nodes.set(nodeId, node);
1940
+ const uniqueConfigPaths = [...new Set(graphs.map((graph) => graph.configPath))];
1941
+ const configPath = uniqueConfigPaths.length === 1 ? uniqueConfigPaths[0] : void 0;
1942
+ return {
1943
+ cwd: firstGraph.cwd,
1944
+ entryId: firstGraph.entryId,
1945
+ entryIds: graphs.map((graph) => graph.entryId),
1946
+ nodes,
1947
+ ...configPath === void 0 ? {} : { configPath }
1948
+ };
1949
+ }
1950
+ //#endregion
1951
+ //#region src/analyzers/react/queries.ts
1952
+ function getReactUsageEntries(graph, filter = "all") {
1953
+ return graph.entries.filter((entry) => {
1954
+ const targetNode = graph.nodes.get(entry.target);
1955
+ return targetNode !== void 0 && matchesReactFilter(targetNode, filter);
1956
+ });
1957
+ }
1958
+ function getReactUsageRoots(graph, filter = "all") {
1959
+ const entries = getReactUsageEntries(graph, filter);
1960
+ if (entries.length > 0) return [...new Set(entries.map((entry) => entry.target))];
1961
+ const filteredNodes = getFilteredReactUsageNodes(graph, filter);
1962
+ const inboundCounts = /* @__PURE__ */ new Map();
1963
+ filteredNodes.forEach((node) => {
1964
+ inboundCounts.set(node.id, 0);
1965
+ });
1966
+ filteredNodes.forEach((node) => {
1967
+ getFilteredUsages(node, graph, filter).forEach((usage) => {
1968
+ inboundCounts.set(usage.target, (inboundCounts.get(usage.target) ?? 0) + 1);
1969
+ });
1970
+ });
1971
+ const roots = filteredNodes.filter((node) => (inboundCounts.get(node.id) ?? 0) === 0).map((node) => node.id);
1972
+ if (roots.length > 0) return roots.sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
1973
+ return filteredNodes.map((node) => node.id).sort((left, right) => compareReactNodeIds(left, right, graph.nodes));
1974
+ }
1975
+ function getFilteredUsages(node, graph, filter = "all") {
1976
+ return node.usages.filter((usage) => {
1977
+ const targetNode = graph.nodes.get(usage.target);
1978
+ return targetNode !== void 0 && matchesReactFilter(targetNode, filter);
1979
+ });
1980
+ }
1981
+ function getFilteredReactUsageNodes(graph, filter) {
1982
+ return [...graph.nodes.values()].filter((node) => matchesReactFilter(node, filter)).sort((left, right) => {
1983
+ return left.filePath.localeCompare(right.filePath) || left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind);
1984
+ });
1985
+ }
1986
+ function matchesReactFilter(node, filter) {
1987
+ return filter === "all" || node.kind === filter;
1988
+ }
1989
+ //#endregion
1990
+ //#region src/color.ts
1991
+ const ANSI_RESET = "\x1B[0m";
1992
+ const ANSI_COMPONENT = "\x1B[36m";
1993
+ const ANSI_HOOK = "\x1B[35m";
1994
+ const ANSI_BUILTIN = "\x1B[34m";
1995
+ const ANSI_MUTED = "\x1B[38;5;244m";
1996
+ const ANSI_UNUSED = "\x1B[38;5;214m";
1997
+ const ANSI_DIFF_ADDED = "\x1B[32m";
1998
+ const ANSI_DIFF_REMOVED = "\x1B[31m";
1999
+ const ANSI_DIFF_CHANGED = "\x1B[33m";
2000
+ function resolveColorSupport(mode = "auto", options = {}) {
2001
+ if (mode === true) return true;
2002
+ if (mode === false) return false;
2003
+ const forceColor = "forceColor" in options ? options.forceColor : process$1.env.FORCE_COLOR;
2004
+ if (forceColor !== void 0) return forceColor !== "0";
2005
+ if (("noColor" in options ? options.noColor : process$1.env.NO_COLOR) !== void 0) return false;
2006
+ return ("isTTY" in options ? options.isTTY : process$1.stdout.isTTY) === true;
2007
+ }
2008
+ function colorizeUnusedMarker(text, enabled) {
2009
+ if (!enabled) return text;
2010
+ return text.replaceAll("(unused)", `${ANSI_UNUSED}(unused)${ANSI_RESET}`);
2011
+ }
2012
+ function formatReactSymbolLabel(name, kind, enabled) {
2013
+ const label = `${formatReactSymbolName(name, kind)} [${kind}]`;
2014
+ if (!enabled) return label;
2015
+ return `${getReactSymbolColor(kind)}${label}${ANSI_RESET}`;
2016
+ }
2017
+ function formatReactSymbolName(name, kind) {
2018
+ if (kind === "component") return `<${name} />`;
2019
+ if (kind === "hook") return `${name}()`;
2020
+ return `<${name}>`;
2021
+ }
2022
+ function colorizeReactLabel(text, kind, enabled) {
2023
+ if (!enabled) return text;
2024
+ return `${getReactSymbolColor(kind)}${text}${ANSI_RESET}`;
2025
+ }
2026
+ function colorizeMuted(text, enabled) {
2027
+ if (!enabled) return text;
2028
+ return `${ANSI_MUTED}${text}${ANSI_RESET}`;
2029
+ }
2030
+ function colorizePackageDiff(text, change, enabled) {
2031
+ if (!enabled) return text;
2032
+ return `${getPackageDiffColor(change)}${text}${ANSI_RESET}`;
2033
+ }
2034
+ function getReactSymbolColor(kind) {
2035
+ if (kind === "component") return ANSI_COMPONENT;
2036
+ if (kind === "hook") return ANSI_HOOK;
2037
+ return ANSI_BUILTIN;
2038
+ }
2039
+ function getPackageDiffColor(change) {
2040
+ if (change === "added") return ANSI_DIFF_ADDED;
2041
+ if (change === "removed") return ANSI_DIFF_REMOVED;
2042
+ return ANSI_DIFF_CHANGED;
2043
+ }
2044
+ //#endregion
2045
+ //#region src/output/ascii/deps.ts
2046
+ function printPackageDependencyTree(graph, _options = {}) {
2047
+ const rootNode = graph.nodes.get(graph.rootId);
2048
+ if (rootNode === void 0) return toDisplayPath(graph.rootId, graph.repositoryRoot);
2049
+ const lines = [rootNode.packageName];
2050
+ const visited = new Set([graph.rootId]);
2051
+ rootNode.dependencies.forEach((dependency, index) => {
2052
+ lines.push(...renderDependency$1(dependency, graph, visited, "", index === rootNode.dependencies.length - 1));
2053
+ });
2054
+ return lines.join("\n");
2055
+ }
2056
+ function printPackageDependencyDiffTree(graph, options = {}) {
2057
+ const color = resolveColorSupport(options.color);
2058
+ const lines = [formatDiffRootLabel(graph.root, color)];
2059
+ graph.root.dependencies.forEach((dependency, index) => {
2060
+ lines.push(...renderDiffDependency(dependency, "", index === graph.root.dependencies.length - 1, color));
2061
+ });
2062
+ return lines.join("\n");
2063
+ }
2064
+ function renderDependency$1(dependency, graph, visited, prefix, isLast) {
2065
+ const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
2066
+ const label = formatDependencyLabel$1(dependency, graph.repositoryRoot);
2067
+ if (dependency.kind === "external") return [`${branch}${label}`];
2068
+ if (visited.has(dependency.target)) return [`${branch}${label} (circular)`];
2069
+ const childNode = graph.nodes.get(dependency.target);
2070
+ if (childNode === void 0) return [`${branch}${label}`];
2071
+ const childLines = [`${branch}${label}`];
2072
+ const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
2073
+ const nextVisited = new Set(visited);
2074
+ nextVisited.add(dependency.target);
2075
+ childNode.dependencies.forEach((childDependency, index) => {
2076
+ childLines.push(...renderDependency$1(childDependency, graph, nextVisited, nextPrefix, index === childNode.dependencies.length - 1));
2077
+ });
2078
+ return childLines;
2079
+ }
2080
+ function formatDependencyLabel$1(dependency, repositoryRoot) {
2081
+ if (dependency.kind === "external") return `${dependency.name}@${dependency.specifier}`;
2082
+ const workspaceLabel = toDisplayPath(dependency.target, repositoryRoot);
2083
+ if (dependency.specifier === void 0) return workspaceLabel;
2084
+ return `${workspaceLabel} (${dependency.specifier})`;
2085
+ }
2086
+ function renderDiffDependency(dependency, prefix, isLast, color) {
2087
+ const line = `${`${prefix}${isLast ? "└─ " : "├─ "}`}${formatDiffDependencyLine(dependency, color)}`;
2088
+ if (dependency.kind === "external") return [line];
2089
+ if (dependency.node.kind === "circular") return [`${line} (circular)`];
2090
+ const childLines = [line];
2091
+ const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
2092
+ dependency.node.dependencies.forEach((childDependency, index) => {
2093
+ childLines.push(...renderDiffDependency(childDependency, nextPrefix, index === dependency.node.dependencies.length - 1, color));
2094
+ });
2095
+ return childLines;
2096
+ }
2097
+ function formatDiffRootLabel(node, color) {
2098
+ if (node.change === "changed" && node.beforePackageName !== void 0 && node.afterPackageName !== void 0) return colorizeDiffText(`${toMarker(node.change)} ${node.beforePackageName} -> ${node.afterPackageName}`, node.change, color);
2099
+ if (node.change === "unchanged") return node.packageName;
2100
+ return colorizeDiffText(`${toMarker(node.change)} ${node.packageName}`, node.change, color);
2101
+ }
2102
+ function formatDiffDependencyLine(dependency, color) {
2103
+ const marker = resolveVisibleDependencyMarker(dependency);
2104
+ const label = formatDiffDependencyLabel(dependency);
2105
+ if (marker === "unchanged") return label;
2106
+ return colorizeDiffText(`${toMarker(marker)} ${label}`, marker, color);
2107
+ }
2108
+ function resolveVisibleDependencyMarker(dependency) {
2109
+ if (dependency.change !== "unchanged") return dependency.change;
2110
+ if (dependency.kind === "workspace" && dependency.node.change !== "unchanged") return dependency.node.change;
2111
+ return "unchanged";
2112
+ }
2113
+ function formatDiffDependencyLabel(dependency) {
2114
+ if (dependency.kind === "external") return formatExternalDiffLabel(dependency);
2115
+ return formatWorkspaceDiffLabel(dependency);
2116
+ }
2117
+ function formatExternalDiffLabel(dependency) {
2118
+ const beforeResolution = dependency.before?.resolvedVersion;
2119
+ const afterResolution = dependency.after?.resolvedVersion;
2120
+ if (dependency.change !== "changed") return dependency.after?.target ?? dependency.before?.target ?? dependency.name;
2121
+ const previousSpecifier = dependency.before?.specifier ?? "none";
2122
+ const nextSpecifier = dependency.after?.specifier ?? "none";
2123
+ if (dependency.resolvedVersionChanged) {
2124
+ const resolutionLabel = formatVersionChange(beforeResolution, afterResolution);
2125
+ return previousSpecifier === nextSpecifier ? `${dependency.name}@${nextSpecifier} (${resolutionLabel})` : `${dependency.name}@${previousSpecifier} -> ${nextSpecifier} (${resolutionLabel})`;
2126
+ }
2127
+ if (previousSpecifier === nextSpecifier) return `${dependency.name}@${nextSpecifier}`;
2128
+ return `${dependency.name}@${previousSpecifier} -> ${nextSpecifier}`;
2129
+ }
2130
+ function formatWorkspaceDiffLabel(dependency) {
2131
+ if (dependency.change !== "changed") return formatWorkspaceState(dependency.after ?? dependency.before, dependency.node.path);
2132
+ const previousTarget = dependency.before?.target ?? dependency.node.path;
2133
+ const nextTarget = dependency.after?.target ?? dependency.node.path;
2134
+ const targetLabel = previousTarget === nextTarget ? nextTarget : `${previousTarget} -> ${nextTarget}`;
2135
+ const previousSpecifier = dependency.before?.specifier;
2136
+ const nextSpecifier = dependency.after?.specifier;
2137
+ if (previousSpecifier === nextSpecifier) return formatWorkspaceState({
2138
+ target: targetLabel,
2139
+ ...nextSpecifier === void 0 ? {} : { specifier: nextSpecifier }
2140
+ }, dependency.node.path);
2141
+ return formatWorkspaceState({
2142
+ target: targetLabel,
2143
+ specifier: `${previousSpecifier ?? "none"} -> ${nextSpecifier ?? "none"}`
2144
+ }, dependency.node.path);
2145
+ }
2146
+ function formatWorkspaceState(state, fallbackTarget) {
2147
+ const target = state?.target ?? fallbackTarget;
2148
+ if (state?.specifier === void 0) return target;
2149
+ return `${target} (${state.specifier})`;
2150
+ }
2151
+ function formatVersionChange(beforeResolvedVersion, afterResolvedVersion) {
2152
+ return `${beforeResolvedVersion ?? "none"} -> ${afterResolvedVersion ?? "none"}`;
2153
+ }
2154
+ function toMarker(change) {
2155
+ if (change === "added") return "+";
2156
+ if (change === "removed") return "-";
2157
+ return "~";
2158
+ }
2159
+ function colorizeDiffText(text, change, color) {
2160
+ if (change === "unchanged") return text;
2161
+ return colorizePackageDiff(text, change, color);
2162
+ }
2163
+ //#endregion
2164
+ //#region src/output/ascii/import.ts
2165
+ function printDependencyTree(graph, options = {}) {
2166
+ const cwd = options.cwd ?? graph.cwd;
2167
+ const color = resolveColorSupport(options.color);
2168
+ const includeExternals = options.includeExternals ?? false;
2169
+ const omitUnused = options.omitUnused ?? false;
2170
+ const rootLines = [toDisplayPath(graph.entryId, cwd)];
2171
+ const visited = new Set([graph.entryId]);
2172
+ const entryNode = graph.nodes.get(graph.entryId);
2173
+ if (entryNode === void 0) return rootLines.join("\n");
2174
+ const rootDependencies = filterDependencies(entryNode.dependencies, includeExternals, omitUnused);
2175
+ rootDependencies.forEach((dependency, index) => {
2176
+ rootLines.push(...renderDependency(dependency, graph, visited, "", index === rootDependencies.length - 1, includeExternals, omitUnused, color, cwd));
2177
+ });
2178
+ return rootLines.join("\n");
2179
+ }
2180
+ function renderDependency(dependency, graph, visited, prefix, isLast, includeExternals, omitUnused, color, cwd) {
2181
+ const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
2182
+ const label = formatDependencyLabel(dependency, cwd, color);
2183
+ if (dependency.kind !== "source") return [`${branch}${label}`];
2184
+ if (visited.has(dependency.target)) return [`${branch}${label} (circular)`];
2185
+ const childNode = graph.nodes.get(dependency.target);
2186
+ if (childNode === void 0) return [`${branch}${label}`];
2187
+ const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
2188
+ const nextVisited = new Set(visited);
2189
+ nextVisited.add(dependency.target);
2190
+ const childLines = [`${branch}${label}`];
2191
+ const childDependencies = filterDependencies(childNode.dependencies, includeExternals, omitUnused);
2192
+ childDependencies.forEach((childDependency, index) => {
2193
+ childLines.push(...renderDependency(childDependency, graph, nextVisited, nextPrefix, index === childDependencies.length - 1, includeExternals, omitUnused, color, cwd));
2194
+ });
2195
+ return childLines;
2196
+ }
2197
+ function filterDependencies(dependencies, includeExternals, omitUnused) {
2198
+ return dependencies.filter((dependency) => {
2199
+ if (omitUnused && dependency.unused) return false;
2200
+ if (dependency.kind === "source" || dependency.kind === "missing" || dependency.kind === "boundary") return true;
2201
+ return includeExternals;
2202
+ });
2203
+ }
2204
+ function formatDependencyLabel(dependency, cwd, color) {
2205
+ const prefixes = [];
2206
+ if (dependency.isTypeOnly) prefixes.push("type");
2207
+ if (dependency.referenceKind === "require") prefixes.push("require");
2208
+ else if (dependency.referenceKind === "dynamic-import") prefixes.push("dynamic");
2209
+ else if (dependency.referenceKind === "export") prefixes.push("re-export");
2210
+ else if (dependency.referenceKind === "import-equals") prefixes.push("import=");
2211
+ const annotation = prefixes.length > 0 ? `[${prefixes.join(", ")}] ` : "";
2212
+ if (dependency.kind === "source") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${toDisplayPath(dependency.target, cwd)}`, dependency.unused), color);
2213
+ if (dependency.kind === "missing") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.specifier} [missing]`, dependency.unused), color);
2214
+ if (dependency.kind === "boundary") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${toDisplayPath(dependency.target, cwd)} [${dependency.boundary === "project" ? "project boundary" : "workspace boundary"}]`, dependency.unused), color);
2215
+ if (dependency.kind === "builtin") return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.target} [builtin]`, dependency.unused), color);
2216
+ return colorizeUnusedMarker(withUnusedSuffix(`${annotation}${dependency.target} [external]`, dependency.unused), color);
2217
+ }
2218
+ function withUnusedSuffix(label, unused) {
2219
+ return unused ? `${label} (unused)` : label;
2220
+ }
2221
+ //#endregion
2222
+ //#region src/output/ascii/react.ts
2223
+ function printReactUsageTree(graph, options = {}) {
2224
+ const cwd = options.cwd ?? graph.cwd;
2225
+ const color = resolveColorSupport(options.color);
2226
+ const filter = options.filter ?? "all";
2227
+ const entries = getReactUsageEntries(graph, filter);
2228
+ if (entries.length > 0) return renderReactUsageEntries(graph, entries, cwd, filter, color);
2229
+ const roots = getReactUsageRoots(graph, filter);
2230
+ if (roots.length === 0) return "No React symbols found.";
2231
+ const lines = [];
2232
+ roots.forEach((rootId, index) => {
2233
+ const root = graph.nodes.get(rootId);
2234
+ if (root === void 0) return;
2235
+ lines.push(formatReactNodeLabel(root, cwd, color));
2236
+ const usages = getFilteredUsages(root, graph, filter);
2237
+ usages.forEach((usage, usageIndex) => {
2238
+ lines.push(...renderUsage(usage, graph, cwd, filter, color, new Set([root.id]), "", usageIndex === usages.length - 1));
2239
+ });
2240
+ if (index < roots.length - 1) lines.push("");
2241
+ });
2242
+ return lines.join("\n");
2243
+ }
2244
+ function renderReactUsageEntries(graph, entries, cwd, filter, color) {
2245
+ const lines = [];
2246
+ entries.forEach((entry, index) => {
2247
+ const root = graph.nodes.get(entry.target);
2248
+ if (root === void 0) return;
2249
+ lines.push(formatReactEntryLabel(entry, cwd));
2250
+ lines.push(formatReactNodeLabel(root, cwd, color, entry.referenceName));
2251
+ const usages = getFilteredUsages(root, graph, filter);
2252
+ usages.forEach((usage, usageIndex) => {
2253
+ lines.push(...renderUsage(usage, graph, cwd, filter, color, new Set([root.id]), "", usageIndex === usages.length - 1));
2254
+ });
2255
+ if (index < entries.length - 1) lines.push("");
2256
+ });
2257
+ return lines.join("\n");
2258
+ }
2259
+ function renderUsage(usage, graph, cwd, filter, color, visited, prefix, isLast) {
2260
+ const branch = `${prefix}${isLast ? "└─ " : "├─ "}`;
2261
+ const target = graph.nodes.get(usage.target);
2262
+ if (target === void 0) return [`${branch}${usage.target}`];
2263
+ if (visited.has(target.id)) return [`${branch}${formatReactNodeLabel(target, cwd, color, usage.referenceName)} (circular)`];
2264
+ const childLines = [`${branch}${formatReactNodeLabel(target, cwd, color, usage.referenceName)}`];
2265
+ const nextVisited = new Set(visited);
2266
+ nextVisited.add(target.id);
2267
+ const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
2268
+ const childUsages = getFilteredUsages(target, graph, filter);
2269
+ childUsages.forEach((childUsage, index) => {
2270
+ childLines.push(...renderUsage(childUsage, graph, cwd, filter, color, nextVisited, nextPrefix, index === childUsages.length - 1));
2271
+ });
2272
+ return childLines;
2273
+ }
2274
+ function formatReactNodeLabel(node, cwd, color, referenceName) {
2275
+ return `${referenceName !== void 0 && referenceName !== node.name ? `${colorizeReactLabel(formatReactSymbolName(node.name, node.kind), node.kind, color)} ${colorizeMuted(`as ${referenceName}`, color)} ${colorizeReactLabel(`[${node.kind}]`, node.kind, color)}` : formatReactSymbolLabel(node.name, node.kind, color)} (${formatReactNodeFilePath$1(node, cwd)})`;
2276
+ }
2277
+ function formatReactEntryLabel(entry, cwd) {
2278
+ return `${toDisplayPath(entry.location.filePath, cwd)}:${entry.location.line}:${entry.location.column}`;
2279
+ }
2280
+ function formatReactNodeFilePath$1(node, cwd) {
2281
+ return node.kind === "builtin" ? "html" : toDisplayPath(node.filePath, cwd);
2282
+ }
2283
+ //#endregion
2284
+ //#region src/output/json/deps.ts
2285
+ function graphToSerializablePackageTree(graph) {
2286
+ return serializePackageNode(graph.rootId, graph, /* @__PURE__ */ new Set());
2287
+ }
2288
+ function diffGraphToSerializablePackageTree(graph) {
2289
+ return serializePackageDiffNode(graph.root);
2290
+ }
2291
+ function serializePackageNode(packageDir, graph, visited) {
2292
+ const packageNode = graph.nodes.get(packageDir);
2293
+ const packagePath = toDisplayPath(packageDir, graph.repositoryRoot);
2294
+ if (packageNode === void 0) return {
2295
+ kind: "missing",
2296
+ label: packagePath,
2297
+ path: packagePath,
2298
+ dependencies: []
2299
+ };
2300
+ if (visited.has(packageDir)) return {
2301
+ kind: "circular",
2302
+ label: packageNode.packageName,
2303
+ packageName: packageNode.packageName,
2304
+ path: packagePath,
2305
+ dependencies: []
2306
+ };
2307
+ visited.add(packageDir);
2308
+ return {
2309
+ kind: packageDir === graph.rootId ? "root" : "workspace",
2310
+ label: packageDir === graph.rootId ? packageNode.packageName : packagePath,
2311
+ packageName: packageNode.packageName,
2312
+ path: packagePath,
2313
+ dependencies: packageNode.dependencies.map((dependency) => serializeDependency(dependency, graph, new Set(visited)))
2314
+ };
2315
+ }
2316
+ function serializeDependency(dependency, graph, visited) {
2317
+ if (dependency.kind === "external") return {
2318
+ kind: "external",
2319
+ name: dependency.name,
2320
+ specifier: dependency.specifier,
2321
+ target: `${dependency.name}@${dependency.specifier}`
2322
+ };
2323
+ return {
2324
+ kind: "workspace",
2325
+ name: dependency.name,
2326
+ ...dependency.specifier === void 0 ? {} : { specifier: dependency.specifier },
2327
+ target: toDisplayPath(dependency.target, graph.repositoryRoot),
2328
+ node: serializePackageNode(dependency.target, graph, visited)
2329
+ };
2330
+ }
2331
+ function serializePackageDiffNode(node) {
2332
+ return {
2333
+ kind: node.kind,
2334
+ label: node.label,
2335
+ packageName: node.packageName,
2336
+ path: node.path,
2337
+ change: node.change,
2338
+ ...node.beforePackageName === void 0 ? {} : { beforePackageName: node.beforePackageName },
2339
+ ...node.afterPackageName === void 0 ? {} : { afterPackageName: node.afterPackageName },
2340
+ dependencies: node.dependencies.map((dependency) => serializePackageDiffDependency(dependency))
2341
+ };
2342
+ }
2343
+ function serializePackageDiffDependency(dependency) {
2344
+ return {
2345
+ kind: dependency.kind,
2346
+ name: dependency.name,
2347
+ change: dependency.change,
2348
+ ...dependency.kind === "external" ? {
2349
+ specifierChanged: dependency.specifierChanged,
2350
+ resolvedVersionChanged: dependency.resolvedVersionChanged,
2351
+ peerContextChanged: dependency.peerContextChanged
2352
+ } : {},
2353
+ ...dependency.before === void 0 ? {} : {
2354
+ beforeTarget: dependency.before.target,
2355
+ ...dependency.before.specifier === void 0 ? {} : { beforeSpecifier: dependency.before.specifier },
2356
+ ...dependency.before.resolvedVersion === void 0 ? {} : { beforeResolvedVersion: dependency.before.resolvedVersion },
2357
+ ...dependency.before.peerContext === void 0 ? {} : { beforePeerContext: dependency.before.peerContext }
2358
+ },
2359
+ ...dependency.after === void 0 ? {} : {
2360
+ afterTarget: dependency.after.target,
2361
+ ...dependency.after.specifier === void 0 ? {} : { afterSpecifier: dependency.after.specifier },
2362
+ ...dependency.after.resolvedVersion === void 0 ? {} : { afterResolvedVersion: dependency.after.resolvedVersion },
2363
+ ...dependency.after.peerContext === void 0 ? {} : { afterPeerContext: dependency.after.peerContext }
2364
+ },
2365
+ ...dependency.before === void 0 ? {} : { before: dependency.before },
2366
+ ...dependency.after === void 0 ? {} : { after: dependency.after },
2367
+ ...dependency.after === void 0 ? dependency.before === void 0 ? {} : { target: dependency.before.target } : { target: dependency.after.target },
2368
+ ...dependency.kind === "workspace" ? { node: serializePackageDiffNode(dependency.node) } : {}
2369
+ };
2370
+ }
2371
+ //#endregion
2372
+ //#region src/output/json/import.ts
2373
+ function graphToSerializableTree(graph, options = {}) {
2374
+ const visited = /* @__PURE__ */ new Set();
2375
+ return serializeNode(graph.entryId, graph, visited, options.omitUnused ?? false);
2376
+ }
2377
+ function serializeNode(filePath, graph, visited, omitUnused) {
2378
+ const node = graph.nodes.get(filePath);
2379
+ const displayPath = toDisplayPath(filePath, graph.cwd);
2380
+ if (node === void 0) return {
2381
+ path: displayPath,
2382
+ kind: "missing",
2383
+ dependencies: []
2384
+ };
2385
+ if (visited.has(filePath)) return {
2386
+ path: displayPath,
2387
+ kind: "circular",
2388
+ dependencies: []
2389
+ };
2390
+ visited.add(filePath);
2391
+ const dependencies = node.dependencies.filter((dependency) => !omitUnused || !dependency.unused).map((dependency) => {
2392
+ if (dependency.kind !== "source") return {
2393
+ specifier: dependency.specifier,
2394
+ referenceKind: dependency.referenceKind,
2395
+ isTypeOnly: dependency.isTypeOnly,
2396
+ unused: dependency.unused,
2397
+ kind: dependency.kind,
2398
+ ...dependency.boundary === void 0 ? {} : { boundary: dependency.boundary },
2399
+ target: serializeDependencyTarget(dependency, graph.cwd)
2400
+ };
2401
+ return {
2402
+ specifier: dependency.specifier,
2403
+ referenceKind: dependency.referenceKind,
2404
+ isTypeOnly: dependency.isTypeOnly,
2405
+ unused: dependency.unused,
2406
+ kind: dependency.kind,
2407
+ target: toDisplayPath(dependency.target, graph.cwd),
2408
+ node: serializeNode(dependency.target, graph, new Set(visited), omitUnused)
2409
+ };
2410
+ });
2411
+ return {
2412
+ path: displayPath,
2413
+ kind: filePath === graph.entryId ? "entry" : "source",
2414
+ dependencies
2415
+ };
2416
+ }
2417
+ function serializeDependencyTarget(dependency, cwd) {
2418
+ if (dependency.kind === "missing" || dependency.kind === "external") return dependency.target;
2419
+ if (dependency.kind === "builtin") return dependency.target;
2420
+ return toDisplayPath(dependency.target, cwd);
2421
+ }
2422
+ //#endregion
2423
+ //#region src/output/json/react.ts
2424
+ function graphToSerializableReactTree(graph, options = {}) {
2425
+ const filter = options.filter ?? "all";
2426
+ const entries = getReactUsageEntries(graph, filter);
2427
+ 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()));
2428
+ return {
2429
+ kind: "react-usage",
2430
+ entries: entries.map((entry) => serializeReactUsageEntry(entry, graph, filter)),
2431
+ roots
2432
+ };
2433
+ }
2434
+ function serializeReactUsageNode(nodeId, graph, filter, visited) {
2435
+ const node = graph.nodes.get(nodeId);
2436
+ if (node === void 0) return {
2437
+ id: nodeId,
2438
+ name: nodeId,
2439
+ symbolKind: "circular",
2440
+ filePath: "",
2441
+ exportNames: [],
2442
+ usages: []
2443
+ };
2444
+ if (visited.has(nodeId)) return {
2445
+ id: node.id,
2446
+ name: node.name,
2447
+ symbolKind: "circular",
2448
+ filePath: formatReactNodeFilePath(node.filePath, node.kind, graph.cwd),
2449
+ exportNames: node.exportNames,
2450
+ usages: []
2451
+ };
2452
+ const nextVisited = new Set(visited);
2453
+ nextVisited.add(nodeId);
2454
+ return {
2455
+ id: node.id,
2456
+ name: node.name,
2457
+ symbolKind: node.kind,
2458
+ filePath: formatReactNodeFilePath(node.filePath, node.kind, graph.cwd),
2459
+ exportNames: node.exportNames,
2460
+ usages: getFilteredUsages(node, graph, filter).map((usage) => ({
2461
+ kind: usage.kind,
2462
+ targetId: usage.target,
2463
+ referenceName: usage.referenceName,
2464
+ node: serializeReactUsageNode(usage.target, graph, filter, nextVisited)
2465
+ }))
2466
+ };
2467
+ }
2468
+ function serializeReactUsageEntry(entry, graph, filter) {
2469
+ return {
2470
+ targetId: entry.target,
2471
+ referenceName: entry.referenceName,
2472
+ filePath: toDisplayPath(entry.location.filePath, graph.cwd),
2473
+ line: entry.location.line,
2474
+ column: entry.location.column,
2475
+ node: serializeReactUsageNode(entry.target, graph, filter, /* @__PURE__ */ new Set())
2476
+ };
2477
+ }
2478
+ function formatReactNodeFilePath(filePath, kind, cwd) {
2479
+ return kind === "builtin" ? "html" : toDisplayPath(filePath, cwd);
2480
+ }
2481
+ //#endregion
2482
+ export { printReactUsageTree as a, printPackageDependencyTree as c, getReactUsageRoots as d, analyzeReactUsage as f, analyzePackageDependencies as g, analyzePackageDependencyDiff as h, graphToSerializablePackageTree as i, getFilteredUsages as l, isSourceCodeFile as m, graphToSerializableTree as n, printDependencyTree as o, analyzeDependencies as p, diffGraphToSerializablePackageTree as r, printPackageDependencyDiffTree as s, graphToSerializableReactTree as t, getReactUsageEntries as u };
2483
+
2484
+ //# sourceMappingURL=react-DWxB8qZ6.mjs.map