foresthouse 1.1.0 → 1.2.0-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -1
- package/dist/cli.mjs +9 -65
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +84 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{react-xFA5HmqF.mjs → react-D0TO9XR2.mjs} +764 -41
- package/dist/react-D0TO9XR2.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/react-xFA5HmqF.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { builtinModules } from "node:module";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
@@ -363,11 +363,12 @@ function parsePnpmVersion(value) {
|
|
|
363
363
|
//#region src/analyzers/deps/diff.ts
|
|
364
364
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
365
365
|
const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
366
|
+
const GIT_EXEC_MAX_BUFFER$1 = 64 * 1024 * 1024;
|
|
366
367
|
function analyzePackageDependencyDiff(directory, diff) {
|
|
367
368
|
const resolvedInputPath = path.resolve(process.cwd(), directory);
|
|
368
|
-
const repositoryRoot = findGitRepositoryRoot(resolvedInputPath);
|
|
369
|
+
const repositoryRoot = findGitRepositoryRoot$1(resolvedInputPath);
|
|
369
370
|
const inputPathWithinRepository = resolveRepositoryInputPath(resolvedInputPath, repositoryRoot);
|
|
370
|
-
const comparison = resolveGitDiffComparison(repositoryRoot, diff);
|
|
371
|
+
const comparison = resolveGitDiffComparison$1(repositoryRoot, diff);
|
|
371
372
|
const beforeGraph = loadGitTreeGraph(repositoryRoot, comparison.beforeTree, inputPathWithinRepository);
|
|
372
373
|
const afterGraph = comparison.afterTree === void 0 ? loadWorkingTreeGraph(repositoryRoot, inputPathWithinRepository) : loadGitTreeGraph(repositoryRoot, comparison.afterTree, inputPathWithinRepository);
|
|
373
374
|
if (beforeGraph === void 0 && afterGraph === void 0) throw new Error(`No package.json found from ${resolvedInputPath}`);
|
|
@@ -382,7 +383,7 @@ function analyzePackageDependencyDiff(directory, diff) {
|
|
|
382
383
|
};
|
|
383
384
|
}
|
|
384
385
|
function resolveRepositoryInputPath(resolvedInputPath, repositoryRoot) {
|
|
385
|
-
const existingPath = findNearestExistingPath(resolvedInputPath);
|
|
386
|
+
const existingPath = findNearestExistingPath$1(resolvedInputPath);
|
|
386
387
|
const existingRealPath = fs.realpathSync.native(existingPath);
|
|
387
388
|
const repositoryRealPath = fs.realpathSync.native(repositoryRoot);
|
|
388
389
|
const relativeFromExistingPath = path.relative(existingPath, resolvedInputPath);
|
|
@@ -390,15 +391,15 @@ function resolveRepositoryInputPath(resolvedInputPath, repositoryRoot) {
|
|
|
390
391
|
const normalizedPath = path.normalize(path.join(relativeToRepository, relativeFromExistingPath));
|
|
391
392
|
return normalizedPath === "." ? "" : normalizedPath;
|
|
392
393
|
}
|
|
393
|
-
function findGitRepositoryRoot(resolvedInputPath) {
|
|
394
|
-
const searchPath = findNearestExistingPath(resolvedInputPath);
|
|
394
|
+
function findGitRepositoryRoot$1(resolvedInputPath) {
|
|
395
|
+
const searchPath = findNearestExistingPath$1(resolvedInputPath);
|
|
395
396
|
try {
|
|
396
|
-
return runGit(searchPath, ["rev-parse", "--show-toplevel"]).trim();
|
|
397
|
+
return runGit$1(searchPath, ["rev-parse", "--show-toplevel"]).trim();
|
|
397
398
|
} catch (error) {
|
|
398
|
-
throw new Error(`Git diff mode requires a Git repository: ${getCommandErrorMessage(error)}`);
|
|
399
|
+
throw new Error(`Git diff mode requires a Git repository: ${getCommandErrorMessage$1(error)}`);
|
|
399
400
|
}
|
|
400
401
|
}
|
|
401
|
-
function findNearestExistingPath(resolvedInputPath) {
|
|
402
|
+
function findNearestExistingPath$1(resolvedInputPath) {
|
|
402
403
|
let currentPath = resolvedInputPath;
|
|
403
404
|
while (!fs.existsSync(currentPath)) {
|
|
404
405
|
const parentPath = path.dirname(currentPath);
|
|
@@ -408,49 +409,49 @@ function findNearestExistingPath(resolvedInputPath) {
|
|
|
408
409
|
if (fs.statSync(currentPath).isFile()) return path.dirname(currentPath);
|
|
409
410
|
return currentPath;
|
|
410
411
|
}
|
|
411
|
-
function resolveGitDiffComparison(repositoryRoot, diff) {
|
|
412
|
+
function resolveGitDiffComparison$1(repositoryRoot, diff) {
|
|
412
413
|
if (diff.includes("...")) {
|
|
413
|
-
const [baseRef, headRef] = splitDiffRange(diff, "...");
|
|
414
|
+
const [baseRef, headRef] = splitDiffRange$1(diff, "...");
|
|
414
415
|
try {
|
|
415
416
|
return {
|
|
416
|
-
beforeTree: resolveGitTree(repositoryRoot, runGit(repositoryRoot, [
|
|
417
|
+
beforeTree: resolveGitTree$1(repositoryRoot, runGit$1(repositoryRoot, [
|
|
417
418
|
"merge-base",
|
|
418
419
|
baseRef,
|
|
419
420
|
headRef
|
|
420
421
|
]).trim()),
|
|
421
|
-
afterTree: resolveGitTree(repositoryRoot, headRef)
|
|
422
|
+
afterTree: resolveGitTree$1(repositoryRoot, headRef)
|
|
422
423
|
};
|
|
423
424
|
} catch (error) {
|
|
424
|
-
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
425
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage$1(error)}`);
|
|
425
426
|
}
|
|
426
427
|
}
|
|
427
428
|
if (diff.includes("..")) {
|
|
428
|
-
const [beforeRef, afterRef] = splitDiffRange(diff, "..");
|
|
429
|
+
const [beforeRef, afterRef] = splitDiffRange$1(diff, "..");
|
|
429
430
|
try {
|
|
430
431
|
return {
|
|
431
|
-
beforeTree: resolveGitTree(repositoryRoot, beforeRef),
|
|
432
|
-
afterTree: resolveGitTree(repositoryRoot, afterRef)
|
|
432
|
+
beforeTree: resolveGitTree$1(repositoryRoot, beforeRef),
|
|
433
|
+
afterTree: resolveGitTree$1(repositoryRoot, afterRef)
|
|
433
434
|
};
|
|
434
435
|
} catch (error) {
|
|
435
|
-
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
436
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage$1(error)}`);
|
|
436
437
|
}
|
|
437
438
|
}
|
|
438
439
|
try {
|
|
439
440
|
return {
|
|
440
|
-
beforeTree: resolveGitTree(repositoryRoot, diff),
|
|
441
|
+
beforeTree: resolveGitTree$1(repositoryRoot, diff),
|
|
441
442
|
afterTree: void 0
|
|
442
443
|
};
|
|
443
444
|
} catch (error) {
|
|
444
|
-
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
445
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage$1(error)}`);
|
|
445
446
|
}
|
|
446
447
|
}
|
|
447
|
-
function splitDiffRange(diff, separator) {
|
|
448
|
+
function splitDiffRange$1(diff, separator) {
|
|
448
449
|
const [left, right, ...extra] = diff.split(separator);
|
|
449
450
|
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
451
|
return [left, right];
|
|
451
452
|
}
|
|
452
|
-
function resolveGitTree(repositoryRoot, reference) {
|
|
453
|
-
return runGit(repositoryRoot, [
|
|
453
|
+
function resolveGitTree$1(repositoryRoot, reference) {
|
|
454
|
+
return runGit$1(repositoryRoot, [
|
|
454
455
|
"rev-parse",
|
|
455
456
|
"--verify",
|
|
456
457
|
`${reference}^{tree}`
|
|
@@ -458,13 +459,13 @@ function resolveGitTree(repositoryRoot, reference) {
|
|
|
458
459
|
}
|
|
459
460
|
function loadWorkingTreeGraph(repositoryRoot, inputPathWithinRepository) {
|
|
460
461
|
const packageGraph = tryAnalyzePackageGraph(resolveSnapshotInputPath(repositoryRoot, inputPathWithinRepository));
|
|
461
|
-
return packageGraph === void 0 ? void 0 : toComparableGraph(packageGraph);
|
|
462
|
+
return packageGraph === void 0 ? void 0 : toComparableGraph$1(packageGraph);
|
|
462
463
|
}
|
|
463
464
|
function loadGitTreeGraph(repositoryRoot, tree, inputPathWithinRepository) {
|
|
464
|
-
const snapshotRoot = materializeGitTreeSnapshot(repositoryRoot, tree);
|
|
465
|
+
const snapshotRoot = materializeGitTreeSnapshot$1(repositoryRoot, tree);
|
|
465
466
|
try {
|
|
466
467
|
const packageGraph = tryAnalyzePackageGraph(resolveSnapshotInputPath(snapshotRoot, inputPathWithinRepository));
|
|
467
|
-
return packageGraph === void 0 ? void 0 : toComparableGraph(packageGraph);
|
|
468
|
+
return packageGraph === void 0 ? void 0 : toComparableGraph$1(packageGraph);
|
|
468
469
|
} finally {
|
|
469
470
|
fs.rmSync(snapshotRoot, {
|
|
470
471
|
recursive: true,
|
|
@@ -472,9 +473,9 @@ function loadGitTreeGraph(repositoryRoot, tree, inputPathWithinRepository) {
|
|
|
472
473
|
});
|
|
473
474
|
}
|
|
474
475
|
}
|
|
475
|
-
function materializeGitTreeSnapshot(repositoryRoot, tree) {
|
|
476
|
+
function materializeGitTreeSnapshot$1(repositoryRoot, tree) {
|
|
476
477
|
const snapshotRoot = fs.mkdtempSync(path.join(os.tmpdir(), "foresthouse-deps-diff-"));
|
|
477
|
-
const trackedFiles = runGit(repositoryRoot, [
|
|
478
|
+
const trackedFiles = runGit$1(repositoryRoot, [
|
|
478
479
|
"ls-tree",
|
|
479
480
|
"-r",
|
|
480
481
|
"-z",
|
|
@@ -485,7 +486,7 @@ function materializeGitTreeSnapshot(repositoryRoot, tree) {
|
|
|
485
486
|
ensureSnapshotDirectory(snapshotRoot, filePath);
|
|
486
487
|
});
|
|
487
488
|
trackedFiles.filter(isManifestSnapshotFile).forEach((filePath) => {
|
|
488
|
-
const fileContent = runGit(repositoryRoot, [
|
|
489
|
+
const fileContent = runGit$1(repositoryRoot, [
|
|
489
490
|
"cat-file",
|
|
490
491
|
"-p",
|
|
491
492
|
`${tree}:${filePath}`
|
|
@@ -517,7 +518,7 @@ function tryAnalyzePackageGraph(inputPath) {
|
|
|
517
518
|
throw error;
|
|
518
519
|
}
|
|
519
520
|
}
|
|
520
|
-
function toComparableGraph(graph) {
|
|
521
|
+
function toComparableGraph$1(graph) {
|
|
521
522
|
const pnpmLockResolutions = loadPnpmLockImporterResolutions(graph.repositoryRoot);
|
|
522
523
|
const nodes = /* @__PURE__ */ new Map();
|
|
523
524
|
graph.nodes.forEach((node, packageDir) => {
|
|
@@ -708,13 +709,13 @@ function collectChangedPackagePaths(repositoryRoot, comparison, beforeGraph, aft
|
|
|
708
709
|
return changedPackagePaths;
|
|
709
710
|
}
|
|
710
711
|
function listChangedRepositoryPaths(repositoryRoot, comparison) {
|
|
711
|
-
const trackedChanges = comparison.afterTree === void 0 ? runGit(repositoryRoot, [
|
|
712
|
+
const trackedChanges = comparison.afterTree === void 0 ? runGit$1(repositoryRoot, [
|
|
712
713
|
"diff",
|
|
713
714
|
"--name-only",
|
|
714
715
|
"-z",
|
|
715
716
|
comparison.beforeTree,
|
|
716
717
|
"--"
|
|
717
|
-
]) : runGit(repositoryRoot, [
|
|
718
|
+
]) : runGit$1(repositoryRoot, [
|
|
718
719
|
"diff",
|
|
719
720
|
"--name-only",
|
|
720
721
|
"-z",
|
|
@@ -722,7 +723,7 @@ function listChangedRepositoryPaths(repositoryRoot, comparison) {
|
|
|
722
723
|
comparison.afterTree
|
|
723
724
|
]);
|
|
724
725
|
const changedPaths = new Set(trackedChanges.split("\0").filter((filePath) => filePath.length > 0));
|
|
725
|
-
if (comparison.afterTree === void 0) runGit(repositoryRoot, [
|
|
726
|
+
if (comparison.afterTree === void 0) runGit$1(repositoryRoot, [
|
|
726
727
|
"ls-files",
|
|
727
728
|
"--others",
|
|
728
729
|
"--exclude-standard",
|
|
@@ -736,13 +737,14 @@ function isFileInPackage(filePath, packagePath) {
|
|
|
736
737
|
if (packagePath === ".") return true;
|
|
737
738
|
return filePath === packagePath || filePath.startsWith(`${packagePath}/`);
|
|
738
739
|
}
|
|
739
|
-
function runGit(repositoryRoot, args, options = {}) {
|
|
740
|
+
function runGit$1(repositoryRoot, args, options = {}) {
|
|
740
741
|
const output = execFileSync("git", [
|
|
741
742
|
"-C",
|
|
742
743
|
repositoryRoot,
|
|
743
744
|
...args
|
|
744
745
|
], {
|
|
745
746
|
encoding: "utf8",
|
|
747
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER$1,
|
|
746
748
|
stdio: [
|
|
747
749
|
"ignore",
|
|
748
750
|
"pipe",
|
|
@@ -751,7 +753,7 @@ function runGit(repositoryRoot, args, options = {}) {
|
|
|
751
753
|
});
|
|
752
754
|
return options.trim === false ? output : output.trimEnd();
|
|
753
755
|
}
|
|
754
|
-
function getCommandErrorMessage(error) {
|
|
756
|
+
function getCommandErrorMessage$1(error) {
|
|
755
757
|
if (!(error instanceof Error)) return "Unknown Git error";
|
|
756
758
|
const stderr = Reflect.get(error, "stderr");
|
|
757
759
|
if (typeof stderr === "string" && stderr.trim().length > 0) return stderr.trim();
|
|
@@ -1523,6 +1525,67 @@ var MultiEntryImportAnalyzer = class extends BaseAnalyzer {
|
|
|
1523
1525
|
}
|
|
1524
1526
|
};
|
|
1525
1527
|
//#endregion
|
|
1528
|
+
//#region src/app/react-entry-files.ts
|
|
1529
|
+
const NEXT_JS_ENTRY_ROOTS = [
|
|
1530
|
+
"pages",
|
|
1531
|
+
"app",
|
|
1532
|
+
path.join("src", "pages"),
|
|
1533
|
+
path.join("src", "app")
|
|
1534
|
+
];
|
|
1535
|
+
function resolveReactEntryFiles(options) {
|
|
1536
|
+
if (options.entryFile !== void 0) return [options.entryFile];
|
|
1537
|
+
if (!options.nextjs) throw new Error("Missing React entry file. Use `foresthouse react <entry-file>` or `foresthouse react --nextjs`.");
|
|
1538
|
+
return discoverNextJsPageEntries(options.cwd);
|
|
1539
|
+
}
|
|
1540
|
+
function discoverNextJsPageEntries(cwd) {
|
|
1541
|
+
const effectiveCwd = path.resolve(cwd ?? process.cwd());
|
|
1542
|
+
const entries = /* @__PURE__ */ new Set();
|
|
1543
|
+
collectPagesRouterEntries(path.join(effectiveCwd, "pages"), effectiveCwd, entries);
|
|
1544
|
+
collectAppRouterEntries(path.join(effectiveCwd, "app"), effectiveCwd, entries);
|
|
1545
|
+
collectPagesRouterEntries(path.join(effectiveCwd, "src", "pages"), effectiveCwd, entries);
|
|
1546
|
+
collectAppRouterEntries(path.join(effectiveCwd, "src", "app"), effectiveCwd, entries);
|
|
1547
|
+
const resolvedEntries = [...entries].sort();
|
|
1548
|
+
if (resolvedEntries.length > 0) return resolvedEntries;
|
|
1549
|
+
const searchedRoots = NEXT_JS_ENTRY_ROOTS.map((root) => `\`${root}/\``).join(", ");
|
|
1550
|
+
throw new Error(`No Next.js page entries found. Searched ${searchedRoots} relative to ${effectiveCwd}.`);
|
|
1551
|
+
}
|
|
1552
|
+
function collectPagesRouterEntries(rootDirectory, cwd, entries) {
|
|
1553
|
+
walkDirectory(rootDirectory, (filePath, relativePath) => {
|
|
1554
|
+
if (!isSourceCodeFile(filePath) || filePath.endsWith(".d.ts")) return;
|
|
1555
|
+
if (relativePath.split(path.sep)[0] === "api") return;
|
|
1556
|
+
if (path.basename(filePath).startsWith("_")) return;
|
|
1557
|
+
entries.add(path.relative(cwd, filePath));
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
function collectAppRouterEntries(rootDirectory, cwd, entries) {
|
|
1561
|
+
walkDirectory(rootDirectory, (filePath) => {
|
|
1562
|
+
if (!isSourceCodeFile(filePath) || filePath.endsWith(".d.ts")) return;
|
|
1563
|
+
const extension = path.extname(filePath);
|
|
1564
|
+
if (path.basename(filePath, extension) !== "page") return;
|
|
1565
|
+
entries.add(path.relative(cwd, filePath));
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
function walkDirectory(rootDirectory, visitor) {
|
|
1569
|
+
if (!fs.existsSync(rootDirectory)) return;
|
|
1570
|
+
const stack = [rootDirectory];
|
|
1571
|
+
while (stack.length > 0) {
|
|
1572
|
+
const directory = stack.pop();
|
|
1573
|
+
if (directory === void 0) continue;
|
|
1574
|
+
const directoryEntries = fs.readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
1575
|
+
for (let index = directoryEntries.length - 1; index >= 0; index -= 1) {
|
|
1576
|
+
const entry = directoryEntries[index];
|
|
1577
|
+
if (entry === void 0) continue;
|
|
1578
|
+
const entryPath = path.join(directory, entry.name);
|
|
1579
|
+
if (entry.isDirectory()) {
|
|
1580
|
+
stack.push(entryPath);
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
if (!entry.isFile()) continue;
|
|
1584
|
+
visitor(entryPath, path.relative(rootDirectory, entryPath));
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
//#endregion
|
|
1526
1589
|
//#region src/analyzers/react/bindings.ts
|
|
1527
1590
|
function collectImportsAndExports(statement, sourceDependencies, symbolsByName, importsByLocalName, exportsByName, reExportBindingsByName, exportAllBindings) {
|
|
1528
1591
|
switch (statement.type) {
|
|
@@ -1536,7 +1599,7 @@ function collectImportsAndExports(statement, sourceDependencies, symbolsByName,
|
|
|
1536
1599
|
collectExportAllBindings(statement, sourceDependencies, exportAllBindings);
|
|
1537
1600
|
return;
|
|
1538
1601
|
case "ExportDefaultDeclaration":
|
|
1539
|
-
collectDefaultExport(statement, symbolsByName, exportsByName);
|
|
1602
|
+
collectDefaultExport(statement, symbolsByName, importsByLocalName, exportsByName, reExportBindingsByName);
|
|
1540
1603
|
return;
|
|
1541
1604
|
default: return;
|
|
1542
1605
|
}
|
|
@@ -1571,6 +1634,12 @@ function getImportBinding(specifier, sourceSpecifier, sourcePath) {
|
|
|
1571
1634
|
sourceSpecifier,
|
|
1572
1635
|
...sourcePath === void 0 ? {} : { sourcePath }
|
|
1573
1636
|
};
|
|
1637
|
+
if (specifier.type === "ImportNamespaceSpecifier") return {
|
|
1638
|
+
localName: specifier.local.name,
|
|
1639
|
+
importedName: "*",
|
|
1640
|
+
sourceSpecifier,
|
|
1641
|
+
...sourcePath === void 0 ? {} : { sourcePath }
|
|
1642
|
+
};
|
|
1574
1643
|
}
|
|
1575
1644
|
function collectNamedExports(declaration, sourceDependencies, symbolsByName, exportsByName, reExportBindingsByName) {
|
|
1576
1645
|
if (declaration.exportKind === "type") return;
|
|
@@ -1611,14 +1680,19 @@ function collectExportAllBindings(declaration, sourceDependencies, exportAllBind
|
|
|
1611
1680
|
...sourcePath === void 0 ? {} : { sourcePath }
|
|
1612
1681
|
});
|
|
1613
1682
|
}
|
|
1614
|
-
function collectDefaultExport(declaration, symbolsByName, exportsByName) {
|
|
1683
|
+
function collectDefaultExport(declaration, symbolsByName, importsByLocalName, exportsByName, reExportBindingsByName) {
|
|
1615
1684
|
if (declaration.declaration.type === "FunctionDeclaration" || declaration.declaration.type === "FunctionExpression") {
|
|
1616
1685
|
const localName = declaration.declaration.id?.name;
|
|
1617
1686
|
if (localName !== void 0) addExportBinding(localName, "default", symbolsByName, exportsByName);
|
|
1618
1687
|
return;
|
|
1619
1688
|
}
|
|
1620
1689
|
if (declaration.declaration.type === "Identifier") {
|
|
1621
|
-
|
|
1690
|
+
const localName = declaration.declaration.name;
|
|
1691
|
+
addExportBinding(localName, "default", symbolsByName, exportsByName);
|
|
1692
|
+
if (!exportsByName.has("default")) {
|
|
1693
|
+
const importBinding = importsByLocalName.get(localName);
|
|
1694
|
+
if (importBinding !== void 0) reExportBindingsByName.set("default", importBinding);
|
|
1695
|
+
}
|
|
1622
1696
|
return;
|
|
1623
1697
|
}
|
|
1624
1698
|
if (declaration.declaration.type === "ArrowFunctionExpression") addExportBinding("default", "default", symbolsByName, exportsByName);
|
|
@@ -1682,6 +1756,14 @@ function getComponentReferenceName(node) {
|
|
|
1682
1756
|
const name = getJsxName(node.openingElement.name);
|
|
1683
1757
|
return name !== void 0 && isComponentName(name) ? name : void 0;
|
|
1684
1758
|
}
|
|
1759
|
+
function getMemberExpressionComponentReferenceName(node) {
|
|
1760
|
+
const name = node.openingElement.name;
|
|
1761
|
+
if (name.type !== "JSXMemberExpression") return;
|
|
1762
|
+
if (name.object.type !== "JSXIdentifier") return;
|
|
1763
|
+
const objectName = name.object.name;
|
|
1764
|
+
const propertyName = name.property.name;
|
|
1765
|
+
if (isComponentName(objectName)) return `${objectName}.${propertyName}`;
|
|
1766
|
+
}
|
|
1685
1767
|
function getBuiltinReferenceName(node) {
|
|
1686
1768
|
const name = getJsxName(node.openingElement.name);
|
|
1687
1769
|
return name !== void 0 && isIntrinsicElementName(name) ? name : void 0;
|
|
@@ -1796,7 +1878,7 @@ function collectNodeEntryUsages(node, filePath, sourceText, entries, includeBuil
|
|
|
1796
1878
|
if (FUNCTION_NODE_TYPES.has(node.type)) return;
|
|
1797
1879
|
let nextHasComponentAncestor = hasComponentAncestor;
|
|
1798
1880
|
if (node.type === "JSXElement") {
|
|
1799
|
-
const referenceName = getComponentReferenceName(node);
|
|
1881
|
+
const referenceName = getComponentReferenceName(node) ?? getMemberExpressionComponentReferenceName(node);
|
|
1800
1882
|
if (referenceName !== void 0) {
|
|
1801
1883
|
if (!hasComponentAncestor) addPendingReactUsageEntry(entries, referenceName, "component", createReactUsageLocation(filePath, sourceText, node.start));
|
|
1802
1884
|
nextHasComponentAncestor = true;
|
|
@@ -1982,6 +2064,10 @@ function analyzeSymbolUsages(symbol, includeBuiltins) {
|
|
|
1982
2064
|
if (node.type === "JSXElement") {
|
|
1983
2065
|
const name = getComponentReferenceName(node);
|
|
1984
2066
|
if (name !== void 0) symbol.componentReferences.add(name);
|
|
2067
|
+
else {
|
|
2068
|
+
const memberName = getMemberExpressionComponentReferenceName(node);
|
|
2069
|
+
if (memberName !== void 0) symbol.componentReferences.add(memberName);
|
|
2070
|
+
}
|
|
1985
2071
|
if (includeBuiltins) {
|
|
1986
2072
|
const builtinName = getBuiltinReferenceName(node);
|
|
1987
2073
|
if (builtinName !== void 0) symbol.builtinReferences.add(builtinName);
|
|
@@ -2063,6 +2149,8 @@ function collectComponentDeclarationEntryUsages(symbolsByName, sourceText) {
|
|
|
2063
2149
|
//#region src/analyzers/react/references.ts
|
|
2064
2150
|
function resolveReactReference(fileAnalysis, fileAnalyses, name, kind) {
|
|
2065
2151
|
if (kind === "builtin") return getBuiltinNodeId(name);
|
|
2152
|
+
const dotIndex = name.indexOf(".");
|
|
2153
|
+
if (dotIndex !== -1) return resolveNamespaceMemberReference(fileAnalysis, fileAnalyses, name.slice(0, dotIndex), name.slice(dotIndex + 1), kind);
|
|
2066
2154
|
const localSymbol = fileAnalysis.allSymbolsByName.get(name);
|
|
2067
2155
|
if (localSymbol !== void 0 && localSymbol.kind === kind) return localSymbol.id;
|
|
2068
2156
|
const importBinding = fileAnalysis.importsByLocalName.get(name);
|
|
@@ -2074,6 +2162,13 @@ function resolveReactReference(fileAnalysis, fileAnalyses, name, kind) {
|
|
|
2074
2162
|
if (targetId === void 0) return;
|
|
2075
2163
|
return targetId;
|
|
2076
2164
|
}
|
|
2165
|
+
function resolveNamespaceMemberReference(fileAnalysis, fileAnalyses, namespaceName, propertyName, kind) {
|
|
2166
|
+
const importBinding = fileAnalysis.importsByLocalName.get(namespaceName);
|
|
2167
|
+
if (importBinding === void 0 || importBinding.importedName !== "*" || importBinding.sourcePath === void 0) return;
|
|
2168
|
+
const sourceFileAnalysis = fileAnalyses.get(importBinding.sourcePath);
|
|
2169
|
+
if (sourceFileAnalysis === void 0) return;
|
|
2170
|
+
return resolveExportedSymbol(sourceFileAnalysis, propertyName, kind, fileAnalyses, /* @__PURE__ */ new Set());
|
|
2171
|
+
}
|
|
2077
2172
|
function resolveExportedSymbol(fileAnalysis, exportName, kind, fileAnalyses, visited) {
|
|
2078
2173
|
const visitKey = `${fileAnalysis.filePath}:${exportName}:${kind}`;
|
|
2079
2174
|
if (visited.has(visitKey)) return;
|
|
@@ -2322,6 +2417,513 @@ function matchesReactFilter(node, filter) {
|
|
|
2322
2417
|
return filter === "all" || node.kind === filter;
|
|
2323
2418
|
}
|
|
2324
2419
|
//#endregion
|
|
2420
|
+
//#region src/analyzers/react/diff.ts
|
|
2421
|
+
function analyzeReactUsageDiff(options) {
|
|
2422
|
+
const originalCwd = resolveOriginalCwd(options.cwd);
|
|
2423
|
+
const repositoryRoot = findGitRepositoryRoot(resolveDiffInputPath(options, originalCwd));
|
|
2424
|
+
const comparison = resolveGitDiffComparison(repositoryRoot, options.diff ?? "HEAD");
|
|
2425
|
+
const [beforeGraph, afterGraph] = loadBothGraphs({
|
|
2426
|
+
repositoryRoot,
|
|
2427
|
+
originalCwd,
|
|
2428
|
+
cwdWithinRepository: resolvePathWithinRepository(originalCwd, repositoryRoot, "Working directory"),
|
|
2429
|
+
entryFile: options.entryFile,
|
|
2430
|
+
nextjs: options.nextjs,
|
|
2431
|
+
filter: options.filter,
|
|
2432
|
+
includeBuiltins: options.includeBuiltins,
|
|
2433
|
+
analyzeOptions: toAnalyzeOptions(options)
|
|
2434
|
+
}, comparison);
|
|
2435
|
+
if (beforeGraph === void 0 && afterGraph === void 0) throw new Error("No React entry files found for the requested diff. Check the entry path or Next.js discovery roots.");
|
|
2436
|
+
const entries = diffEntries(beforeGraph, afterGraph);
|
|
2437
|
+
return {
|
|
2438
|
+
kind: "react-usage-diff",
|
|
2439
|
+
repositoryRoot,
|
|
2440
|
+
cwd: originalCwd,
|
|
2441
|
+
entries,
|
|
2442
|
+
roots: entries.length > 0 ? entries.map((entry) => entry.node) : diffRoots(beforeGraph, afterGraph)
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
const GIT_EXEC_MAX_BUFFER = 64 * 1024 * 1024;
|
|
2446
|
+
function resolveOriginalCwd(cwd) {
|
|
2447
|
+
return path.resolve(cwd ?? process.cwd());
|
|
2448
|
+
}
|
|
2449
|
+
function resolveDiffInputPath(options, originalCwd) {
|
|
2450
|
+
if (options.entryFile === void 0) return originalCwd;
|
|
2451
|
+
return path.isAbsolute(options.entryFile) ? path.resolve(options.entryFile) : path.resolve(originalCwd, options.entryFile);
|
|
2452
|
+
}
|
|
2453
|
+
function toAnalyzeOptions(options) {
|
|
2454
|
+
return {
|
|
2455
|
+
...options.configPath === void 0 ? {} : { configPath: options.configPath },
|
|
2456
|
+
expandWorkspaces: options.expandWorkspaces,
|
|
2457
|
+
projectOnly: options.projectOnly,
|
|
2458
|
+
includeBuiltins: options.includeBuiltins
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
function loadBothGraphs(context, comparison) {
|
|
2462
|
+
const snapshotsToCleanup = [];
|
|
2463
|
+
try {
|
|
2464
|
+
let beforeRoot;
|
|
2465
|
+
let afterRoot;
|
|
2466
|
+
if (comparison.afterTree === void 0) {
|
|
2467
|
+
beforeRoot = materializeGitTreeSnapshot(context.repositoryRoot, comparison.beforeTree);
|
|
2468
|
+
snapshotsToCleanup.push(beforeRoot);
|
|
2469
|
+
afterRoot = context.repositoryRoot;
|
|
2470
|
+
} else {
|
|
2471
|
+
[beforeRoot, afterRoot] = materializeTwoGitTreeSnapshots(context.repositoryRoot, comparison.beforeTree, comparison.afterTree);
|
|
2472
|
+
snapshotsToCleanup.push(beforeRoot, afterRoot);
|
|
2473
|
+
}
|
|
2474
|
+
return [tryAnalyzeComparableReactGraph(context, beforeRoot), tryAnalyzeComparableReactGraph(context, afterRoot)];
|
|
2475
|
+
} finally {
|
|
2476
|
+
for (const dir of snapshotsToCleanup) fs.rm(dir, {
|
|
2477
|
+
recursive: true,
|
|
2478
|
+
force: true
|
|
2479
|
+
}, () => {});
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
function tryAnalyzeComparableReactGraph(context, workingRoot) {
|
|
2483
|
+
const snapshotCwd = resolveSnapshotCwd(workingRoot, context.cwdWithinRepository);
|
|
2484
|
+
const entryFiles = resolveSnapshotEntryFiles(context, snapshotCwd, workingRoot);
|
|
2485
|
+
if (entryFiles === void 0 || entryFiles.length === 0) return;
|
|
2486
|
+
return toComparableGraph(analyzeReactUsage(entryFiles, {
|
|
2487
|
+
...context.analyzeOptions,
|
|
2488
|
+
cwd: snapshotCwd,
|
|
2489
|
+
...context.analyzeOptions.configPath === void 0 ? {} : { configPath: resolveSnapshotConfigPath(context.analyzeOptions.configPath, workingRoot, context.repositoryRoot) }
|
|
2490
|
+
}), context.repositoryRoot, context.originalCwd, context.filter);
|
|
2491
|
+
}
|
|
2492
|
+
function resolveSnapshotCwd(workingRoot, cwdWithinRepository) {
|
|
2493
|
+
return cwdWithinRepository.length === 0 ? workingRoot : path.join(workingRoot, cwdWithinRepository);
|
|
2494
|
+
}
|
|
2495
|
+
function resolveSnapshotEntryFiles(context, snapshotCwd, workingRoot) {
|
|
2496
|
+
if (context.entryFile !== void 0) {
|
|
2497
|
+
const entryFile = resolveSnapshotEntryPath(context.entryFile, workingRoot, context.repositoryRoot);
|
|
2498
|
+
return doesResolvedPathExist(entryFile, snapshotCwd) ? [entryFile] : void 0;
|
|
2499
|
+
}
|
|
2500
|
+
if (!context.nextjs) throw new Error("Missing React entry file. Use `foresthouse react <entry-file>` or `foresthouse react --nextjs`.");
|
|
2501
|
+
try {
|
|
2502
|
+
return resolveReactEntryFiles({
|
|
2503
|
+
command: "react",
|
|
2504
|
+
entryFile: void 0,
|
|
2505
|
+
diff: void 0,
|
|
2506
|
+
cwd: snapshotCwd,
|
|
2507
|
+
configPath: void 0,
|
|
2508
|
+
expandWorkspaces: true,
|
|
2509
|
+
projectOnly: false,
|
|
2510
|
+
json: false,
|
|
2511
|
+
filter: context.filter,
|
|
2512
|
+
nextjs: true,
|
|
2513
|
+
includeBuiltins: context.includeBuiltins
|
|
2514
|
+
});
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
if (error instanceof Error && error.message.startsWith("No Next.js page entries found.")) return;
|
|
2517
|
+
throw error;
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
function resolveSnapshotEntryPath(entryFile, workingRoot, repositoryRoot) {
|
|
2521
|
+
if (!path.isAbsolute(entryFile)) return entryFile;
|
|
2522
|
+
const entryWithinRepository = resolvePathWithinRepository(entryFile, repositoryRoot, "React entry file");
|
|
2523
|
+
return entryWithinRepository.length === 0 ? workingRoot : path.join(workingRoot, entryWithinRepository);
|
|
2524
|
+
}
|
|
2525
|
+
function resolveSnapshotConfigPath(configPath, workingRoot, repositoryRoot) {
|
|
2526
|
+
if (!path.isAbsolute(configPath)) return configPath;
|
|
2527
|
+
const configWithinRepository = resolvePathWithinRepository(configPath, repositoryRoot, "TypeScript config");
|
|
2528
|
+
return configWithinRepository.length === 0 ? workingRoot : path.join(workingRoot, configWithinRepository);
|
|
2529
|
+
}
|
|
2530
|
+
function doesResolvedPathExist(entryFile, cwd) {
|
|
2531
|
+
const resolvedEntryPath = path.isAbsolute(entryFile) ? entryFile : path.resolve(cwd, entryFile);
|
|
2532
|
+
return fs.existsSync(resolvedEntryPath);
|
|
2533
|
+
}
|
|
2534
|
+
function toComparableGraph(graph, _repositoryRoot, originalCwd, filter) {
|
|
2535
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
2536
|
+
graph.nodes.forEach((node) => {
|
|
2537
|
+
const comparableNode = toComparableNode(node, graph, originalCwd, filter);
|
|
2538
|
+
nodes.set(comparableNode.id, comparableNode);
|
|
2539
|
+
});
|
|
2540
|
+
const entries = indexEntries(getReactUsageEntries(graph, filter).map((entry) => toComparableEntry(entry, graph, originalCwd)));
|
|
2541
|
+
return {
|
|
2542
|
+
entriesByKey: new Map(entries.map((entry) => [entry.key, entry])),
|
|
2543
|
+
rootIds: getReactUsageRoots(graph, filter).map((rootId) => {
|
|
2544
|
+
const node = graph.nodes.get(rootId);
|
|
2545
|
+
return node === void 0 ? void 0 : createComparableNodeId(node, graph.cwd);
|
|
2546
|
+
}).filter((rootId) => rootId !== void 0),
|
|
2547
|
+
nodes
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
function toComparableNode(node, graph, originalCwd, filter) {
|
|
2551
|
+
const usages = indexUsages(getFilteredUsages(node, graph, filter).map((usage) => toComparableEdge(usage, graph)));
|
|
2552
|
+
return {
|
|
2553
|
+
id: createComparableNodeId(node, graph.cwd),
|
|
2554
|
+
name: node.name,
|
|
2555
|
+
symbolKind: node.kind,
|
|
2556
|
+
filePath: toDisplayFilePath(node.filePath, graph.cwd, originalCwd),
|
|
2557
|
+
exportNames: [...node.exportNames].sort(),
|
|
2558
|
+
usagesByKey: new Map(usages.map((usage) => [usage.key, usage]))
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
function toComparableEdge(usage, graph) {
|
|
2562
|
+
const targetNode = graph.nodes.get(usage.target);
|
|
2563
|
+
return {
|
|
2564
|
+
kind: usage.kind,
|
|
2565
|
+
targetId: targetNode === void 0 ? usage.target : createComparableNodeId(targetNode, graph.cwd),
|
|
2566
|
+
referenceName: usage.referenceName
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
function toComparableEntry(entry, graph, originalCwd) {
|
|
2570
|
+
const targetNode = graph.nodes.get(entry.target);
|
|
2571
|
+
return {
|
|
2572
|
+
targetId: targetNode === void 0 ? entry.target : createComparableNodeId(targetNode, graph.cwd),
|
|
2573
|
+
referenceName: entry.referenceName,
|
|
2574
|
+
filePath: toDisplayFilePath(entry.location.filePath, graph.cwd, originalCwd),
|
|
2575
|
+
line: entry.location.line,
|
|
2576
|
+
column: entry.location.column
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
function indexUsages(usages) {
|
|
2580
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2581
|
+
return [...usages].sort(compareComparableEdgeState).map((usage) => {
|
|
2582
|
+
const signature = `${usage.kind}:${usage.targetId}`;
|
|
2583
|
+
const occurrence = counts.get(signature) ?? 0;
|
|
2584
|
+
counts.set(signature, occurrence + 1);
|
|
2585
|
+
return {
|
|
2586
|
+
...usage,
|
|
2587
|
+
key: `${signature}#${occurrence}`
|
|
2588
|
+
};
|
|
2589
|
+
});
|
|
2590
|
+
}
|
|
2591
|
+
function indexEntries(entries) {
|
|
2592
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2593
|
+
return [...entries].sort(compareComparableEntryState).map((entry) => {
|
|
2594
|
+
const signature = `${entry.filePath}:${entry.targetId}`;
|
|
2595
|
+
const occurrence = counts.get(signature) ?? 0;
|
|
2596
|
+
counts.set(signature, occurrence + 1);
|
|
2597
|
+
return {
|
|
2598
|
+
...entry,
|
|
2599
|
+
key: `${signature}#${occurrence}`
|
|
2600
|
+
};
|
|
2601
|
+
});
|
|
2602
|
+
}
|
|
2603
|
+
function createComparableNodeId(node, analysisCwd) {
|
|
2604
|
+
return `${node.kind}:${toComparablePath(node.filePath, analysisCwd)}#${node.name}`;
|
|
2605
|
+
}
|
|
2606
|
+
function toComparablePath(filePath, analysisCwd) {
|
|
2607
|
+
if (!path.isAbsolute(filePath)) return normalizePathSeparators(filePath);
|
|
2608
|
+
const relativePath = path.relative(fs.realpathSync.native(analysisCwd), fs.realpathSync.native(filePath));
|
|
2609
|
+
if (relativePath === "") return ".";
|
|
2610
|
+
return normalizePathSeparators(relativePath);
|
|
2611
|
+
}
|
|
2612
|
+
function toDisplayFilePath(filePath, analysisCwd, originalCwd) {
|
|
2613
|
+
if (!path.isAbsolute(filePath)) return normalizePathSeparators(filePath);
|
|
2614
|
+
const relativePath = path.relative(fs.realpathSync.native(analysisCwd), fs.realpathSync.native(filePath));
|
|
2615
|
+
if (relativePath === "") return ".";
|
|
2616
|
+
return toDisplayPath(path.resolve(originalCwd, relativePath), originalCwd);
|
|
2617
|
+
}
|
|
2618
|
+
function diffEntries(beforeGraph, afterGraph) {
|
|
2619
|
+
const entryKeys = new Set([...Array.from(beforeGraph?.entriesByKey.keys() ?? []), ...Array.from(afterGraph?.entriesByKey.keys() ?? [])]);
|
|
2620
|
+
return Array.from(entryKeys).sort((left, right) => compareComparableEntryState(beforeGraph?.entriesByKey.get(left) ?? afterGraph?.entriesByKey.get(left), beforeGraph?.entriesByKey.get(right) ?? afterGraph?.entriesByKey.get(right))).flatMap((entryKey) => {
|
|
2621
|
+
const beforeEntry = beforeGraph?.entriesByKey.get(entryKey);
|
|
2622
|
+
const afterEntry = afterGraph?.entriesByKey.get(entryKey);
|
|
2623
|
+
const node = diffNode(beforeEntry?.targetId, afterEntry?.targetId, beforeGraph, afterGraph, /* @__PURE__ */ new Set());
|
|
2624
|
+
const change = resolveEntryChange(beforeEntry, afterEntry);
|
|
2625
|
+
if (change === "unchanged" && !hasVisibleNodeChanges(node)) return [];
|
|
2626
|
+
const currentEntry = afterEntry ?? beforeEntry;
|
|
2627
|
+
if (currentEntry === void 0) return [];
|
|
2628
|
+
return [{
|
|
2629
|
+
key: currentEntry.key,
|
|
2630
|
+
change,
|
|
2631
|
+
targetId: node.id,
|
|
2632
|
+
referenceName: afterEntry?.referenceName ?? beforeEntry?.referenceName ?? node.name,
|
|
2633
|
+
...beforeEntry === void 0 ? {} : {
|
|
2634
|
+
beforeReferenceName: beforeEntry.referenceName,
|
|
2635
|
+
beforeFilePath: beforeEntry.filePath,
|
|
2636
|
+
beforeLine: beforeEntry.line,
|
|
2637
|
+
beforeColumn: beforeEntry.column
|
|
2638
|
+
},
|
|
2639
|
+
...afterEntry === void 0 ? {} : {
|
|
2640
|
+
afterReferenceName: afterEntry.referenceName,
|
|
2641
|
+
afterFilePath: afterEntry.filePath,
|
|
2642
|
+
afterLine: afterEntry.line,
|
|
2643
|
+
afterColumn: afterEntry.column
|
|
2644
|
+
},
|
|
2645
|
+
node
|
|
2646
|
+
}];
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2649
|
+
function diffRoots(beforeGraph, afterGraph) {
|
|
2650
|
+
const rootIds = new Set([...Array.from(beforeGraph?.rootIds ?? []), ...Array.from(afterGraph?.rootIds ?? [])]);
|
|
2651
|
+
return Array.from(rootIds).sort((left, right) => compareComparableNodeState(beforeGraph?.nodes.get(left) ?? afterGraph?.nodes.get(left), beforeGraph?.nodes.get(right) ?? afterGraph?.nodes.get(right))).flatMap((rootId) => {
|
|
2652
|
+
const node = diffNode(beforeGraph?.nodes.has(rootId) === true ? rootId : void 0, afterGraph?.nodes.has(rootId) === true ? rootId : void 0, beforeGraph, afterGraph, /* @__PURE__ */ new Set());
|
|
2653
|
+
return hasVisibleNodeChanges(node) ? [node] : [];
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
function diffNode(beforeNodeId, afterNodeId, beforeGraph, afterGraph, ancestry) {
|
|
2657
|
+
const beforeNode = beforeNodeId === void 0 ? void 0 : beforeGraph?.nodes.get(beforeNodeId);
|
|
2658
|
+
const afterNode = afterNodeId === void 0 ? void 0 : afterGraph?.nodes.get(afterNodeId);
|
|
2659
|
+
if (beforeNode === void 0 && afterNode === void 0) throw new Error("Unable to resolve React diff node.");
|
|
2660
|
+
const ancestryKey = `${beforeNodeId ?? ""}->${afterNodeId ?? ""}`;
|
|
2661
|
+
if (ancestry.has(ancestryKey)) {
|
|
2662
|
+
const node = afterNode ?? beforeNode;
|
|
2663
|
+
if (node === void 0) throw new Error("Unable to resolve circular React diff node.");
|
|
2664
|
+
return {
|
|
2665
|
+
id: node.id,
|
|
2666
|
+
name: node.name,
|
|
2667
|
+
symbolKind: node.symbolKind,
|
|
2668
|
+
circular: true,
|
|
2669
|
+
filePath: node.filePath,
|
|
2670
|
+
change: "unchanged",
|
|
2671
|
+
exportNames: node.exportNames,
|
|
2672
|
+
usages: []
|
|
2673
|
+
};
|
|
2674
|
+
}
|
|
2675
|
+
const nextAncestry = new Set(ancestry);
|
|
2676
|
+
nextAncestry.add(ancestryKey);
|
|
2677
|
+
const usages = collectUsageDiffs(beforeNode, afterNode, beforeGraph, afterGraph, nextAncestry);
|
|
2678
|
+
const directChange = resolveNodeDirectChange(beforeNode, afterNode);
|
|
2679
|
+
const node = afterNode ?? beforeNode;
|
|
2680
|
+
if (node === void 0) throw new Error("Unable to resolve React diff node.");
|
|
2681
|
+
return {
|
|
2682
|
+
id: node.id,
|
|
2683
|
+
name: node.name,
|
|
2684
|
+
symbolKind: node.symbolKind,
|
|
2685
|
+
filePath: node.filePath,
|
|
2686
|
+
change: directChange === "unchanged" && usages.length > 0 ? "changed" : directChange,
|
|
2687
|
+
exportNames: node.exportNames,
|
|
2688
|
+
...beforeNode === void 0 ? {} : { beforeExportNames: beforeNode.exportNames },
|
|
2689
|
+
...afterNode === void 0 ? {} : { afterExportNames: afterNode.exportNames },
|
|
2690
|
+
usages
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
function collectUsageDiffs(beforeNode, afterNode, beforeGraph, afterGraph, ancestry) {
|
|
2694
|
+
const usageKeys = new Set([...Array.from(beforeNode?.usagesByKey.keys() ?? []), ...Array.from(afterNode?.usagesByKey.keys() ?? [])]);
|
|
2695
|
+
return Array.from(usageKeys).sort((left, right) => compareComparableEdgeState(beforeNode?.usagesByKey.get(left) ?? afterNode?.usagesByKey.get(left), beforeNode?.usagesByKey.get(right) ?? afterNode?.usagesByKey.get(right))).flatMap((usageKey) => {
|
|
2696
|
+
const beforeUsage = beforeNode?.usagesByKey.get(usageKey);
|
|
2697
|
+
const afterUsage = afterNode?.usagesByKey.get(usageKey);
|
|
2698
|
+
const node = diffNode(beforeUsage?.targetId, afterUsage?.targetId, beforeGraph, afterGraph, ancestry);
|
|
2699
|
+
const change = resolveEdgeChange(beforeUsage, afterUsage);
|
|
2700
|
+
if (change === "unchanged" && !hasVisibleNodeChanges(node)) return [];
|
|
2701
|
+
return [{
|
|
2702
|
+
key: afterUsage?.key ?? beforeUsage?.key ?? usageKey,
|
|
2703
|
+
kind: afterUsage?.kind ?? beforeUsage?.kind ?? "render",
|
|
2704
|
+
change,
|
|
2705
|
+
targetId: node.id,
|
|
2706
|
+
referenceName: afterUsage?.referenceName ?? beforeUsage?.referenceName ?? node.name,
|
|
2707
|
+
...beforeUsage === void 0 ? {} : { beforeReferenceName: beforeUsage.referenceName },
|
|
2708
|
+
...afterUsage === void 0 ? {} : { afterReferenceName: afterUsage.referenceName },
|
|
2709
|
+
node
|
|
2710
|
+
}];
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
function resolveEntryChange(beforeEntry, afterEntry) {
|
|
2714
|
+
if (beforeEntry === void 0 && afterEntry !== void 0) return "added";
|
|
2715
|
+
if (beforeEntry !== void 0 && afterEntry === void 0) return "removed";
|
|
2716
|
+
if (beforeEntry === void 0 || afterEntry === void 0) return "unchanged";
|
|
2717
|
+
return beforeEntry.targetId === afterEntry.targetId && beforeEntry.referenceName === afterEntry.referenceName && beforeEntry.filePath === afterEntry.filePath && beforeEntry.line === afterEntry.line && beforeEntry.column === afterEntry.column ? "unchanged" : "changed";
|
|
2718
|
+
}
|
|
2719
|
+
function resolveNodeDirectChange(beforeNode, afterNode) {
|
|
2720
|
+
if (beforeNode === void 0 && afterNode !== void 0) return "added";
|
|
2721
|
+
if (beforeNode !== void 0 && afterNode === void 0) return "removed";
|
|
2722
|
+
if (beforeNode === void 0 || afterNode === void 0) return "unchanged";
|
|
2723
|
+
return beforeNode.name === afterNode.name && beforeNode.symbolKind === afterNode.symbolKind && beforeNode.filePath === afterNode.filePath && areStringArraysEqual(beforeNode.exportNames, afterNode.exportNames) ? "unchanged" : "changed";
|
|
2724
|
+
}
|
|
2725
|
+
function resolveEdgeChange(beforeUsage, afterUsage) {
|
|
2726
|
+
if (beforeUsage === void 0 && afterUsage !== void 0) return "added";
|
|
2727
|
+
if (beforeUsage !== void 0 && afterUsage === void 0) return "removed";
|
|
2728
|
+
if (beforeUsage === void 0 || afterUsage === void 0) return "unchanged";
|
|
2729
|
+
return beforeUsage.kind === afterUsage.kind && beforeUsage.targetId === afterUsage.targetId && beforeUsage.referenceName === afterUsage.referenceName ? "unchanged" : "changed";
|
|
2730
|
+
}
|
|
2731
|
+
function hasVisibleNodeChanges(node) {
|
|
2732
|
+
return node.change !== "unchanged" || node.usages.length > 0;
|
|
2733
|
+
}
|
|
2734
|
+
function compareComparableNodeState(left, right) {
|
|
2735
|
+
return (left?.filePath ?? "").localeCompare(right?.filePath ?? "") || (left?.name ?? "").localeCompare(right?.name ?? "") || (left?.symbolKind ?? "").localeCompare(right?.symbolKind ?? "");
|
|
2736
|
+
}
|
|
2737
|
+
function compareComparableEdgeState(left, right) {
|
|
2738
|
+
return (left?.targetId ?? "").localeCompare(right?.targetId ?? "") || (left?.referenceName ?? "").localeCompare(right?.referenceName ?? "") || (left?.kind ?? "").localeCompare(right?.kind ?? "");
|
|
2739
|
+
}
|
|
2740
|
+
function compareComparableEntryState(left, right) {
|
|
2741
|
+
return (left?.filePath ?? "").localeCompare(right?.filePath ?? "") || (left?.line ?? 0) - (right?.line ?? 0) || (left?.column ?? 0) - (right?.column ?? 0) || (left?.referenceName ?? "").localeCompare(right?.referenceName ?? "") || (left?.targetId ?? "").localeCompare(right?.targetId ?? "");
|
|
2742
|
+
}
|
|
2743
|
+
function areStringArraysEqual(left, right) {
|
|
2744
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
2745
|
+
}
|
|
2746
|
+
function resolvePathWithinRepository(resolvedPath, repositoryRoot, label) {
|
|
2747
|
+
const absolutePath = path.resolve(resolvedPath);
|
|
2748
|
+
const existingPath = findNearestExistingPath(absolutePath);
|
|
2749
|
+
const existingRealPath = fs.realpathSync.native(existingPath);
|
|
2750
|
+
const repositoryRealPath = fs.realpathSync.native(repositoryRoot);
|
|
2751
|
+
const relativeFromExistingPath = path.relative(existingPath, absolutePath);
|
|
2752
|
+
const relativePath = path.normalize(path.join(path.relative(repositoryRealPath, existingRealPath), relativeFromExistingPath));
|
|
2753
|
+
if (relativePath === "") return "";
|
|
2754
|
+
if (relativePath.startsWith("..")) throw new Error(`${label} must be inside the Git repository when using --diff.`);
|
|
2755
|
+
return normalizePathSeparators(relativePath);
|
|
2756
|
+
}
|
|
2757
|
+
function findGitRepositoryRoot(resolvedInputPath) {
|
|
2758
|
+
const searchPath = findNearestExistingPath(resolvedInputPath);
|
|
2759
|
+
try {
|
|
2760
|
+
return runGit(searchPath, ["rev-parse", "--show-toplevel"]).trim();
|
|
2761
|
+
} catch (error) {
|
|
2762
|
+
throw new Error(`Git diff mode requires a Git repository: ${getCommandErrorMessage(error)}`);
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
function findNearestExistingPath(resolvedInputPath) {
|
|
2766
|
+
let currentPath = resolvedInputPath;
|
|
2767
|
+
while (!fs.existsSync(currentPath)) {
|
|
2768
|
+
const parentPath = path.dirname(currentPath);
|
|
2769
|
+
if (parentPath === currentPath) return resolvedInputPath;
|
|
2770
|
+
currentPath = parentPath;
|
|
2771
|
+
}
|
|
2772
|
+
if (fs.statSync(currentPath).isFile()) return path.dirname(currentPath);
|
|
2773
|
+
return currentPath;
|
|
2774
|
+
}
|
|
2775
|
+
function resolveGitDiffComparison(repositoryRoot, diff) {
|
|
2776
|
+
if (diff.includes("...")) {
|
|
2777
|
+
const [baseRef, headRef] = splitDiffRange(diff, "...");
|
|
2778
|
+
try {
|
|
2779
|
+
return {
|
|
2780
|
+
beforeTree: resolveGitTree(repositoryRoot, runGit(repositoryRoot, [
|
|
2781
|
+
"merge-base",
|
|
2782
|
+
baseRef,
|
|
2783
|
+
headRef
|
|
2784
|
+
]).trim()),
|
|
2785
|
+
afterTree: resolveGitTree(repositoryRoot, headRef)
|
|
2786
|
+
};
|
|
2787
|
+
} catch (error) {
|
|
2788
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
if (diff.includes("..")) {
|
|
2792
|
+
const [beforeRef, afterRef] = splitDiffRange(diff, "..");
|
|
2793
|
+
try {
|
|
2794
|
+
return {
|
|
2795
|
+
beforeTree: resolveGitTree(repositoryRoot, beforeRef),
|
|
2796
|
+
afterTree: resolveGitTree(repositoryRoot, afterRef)
|
|
2797
|
+
};
|
|
2798
|
+
} catch (error) {
|
|
2799
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
try {
|
|
2803
|
+
return {
|
|
2804
|
+
beforeTree: resolveGitTree(repositoryRoot, diff),
|
|
2805
|
+
afterTree: void 0
|
|
2806
|
+
};
|
|
2807
|
+
} catch (error) {
|
|
2808
|
+
throw new Error(`Failed to resolve Git diff spec \`${diff}\`: ${getCommandErrorMessage(error)}`);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
function splitDiffRange(diff, separator) {
|
|
2812
|
+
const [left, right, ...extra] = diff.split(separator);
|
|
2813
|
+
if (left === void 0 || right === void 0 || left.length === 0 || right.length === 0 || extra.length > 0) throw new Error(`Invalid Git diff spec \`${diff}\``);
|
|
2814
|
+
return [left, right];
|
|
2815
|
+
}
|
|
2816
|
+
function resolveGitTree(repositoryRoot, reference) {
|
|
2817
|
+
return runGit(repositoryRoot, [
|
|
2818
|
+
"rev-parse",
|
|
2819
|
+
"--verify",
|
|
2820
|
+
`${reference}^{tree}`
|
|
2821
|
+
]).trim();
|
|
2822
|
+
}
|
|
2823
|
+
function materializeGitTreeSnapshot(repositoryRoot, tree) {
|
|
2824
|
+
const snapshotRoot = fs.mkdtempSync(path.join(os.tmpdir(), "foresthouse-react-diff-"));
|
|
2825
|
+
execSync(`git -C ${JSON.stringify(repositoryRoot)} archive --format=tar ${tree} | tar -x -C ${JSON.stringify(snapshotRoot)}`, {
|
|
2826
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER,
|
|
2827
|
+
stdio: [
|
|
2828
|
+
"ignore",
|
|
2829
|
+
"pipe",
|
|
2830
|
+
"pipe"
|
|
2831
|
+
]
|
|
2832
|
+
});
|
|
2833
|
+
return snapshotRoot;
|
|
2834
|
+
}
|
|
2835
|
+
function materializeTwoGitTreeSnapshots(repositoryRoot, beforeTree, afterTree) {
|
|
2836
|
+
const afterSnap = materializeGitTreeSnapshot(repositoryRoot, afterTree);
|
|
2837
|
+
try {
|
|
2838
|
+
return [cloneSnapshotWithOverlay(repositoryRoot, afterSnap, beforeTree, afterTree), afterSnap];
|
|
2839
|
+
} catch {
|
|
2840
|
+
return [materializeGitTreeSnapshot(repositoryRoot, beforeTree), afterSnap];
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
function cloneSnapshotWithOverlay(repositoryRoot, sourceSnap, targetTree, sourceTree) {
|
|
2844
|
+
const targetSnap = fs.mkdtempSync(path.join(os.tmpdir(), "foresthouse-react-diff-"));
|
|
2845
|
+
fs.rmSync(targetSnap, { recursive: true });
|
|
2846
|
+
if (process.platform === "darwin") execFileSync("cp", [
|
|
2847
|
+
"-cR",
|
|
2848
|
+
sourceSnap,
|
|
2849
|
+
targetSnap
|
|
2850
|
+
], { stdio: "ignore" });
|
|
2851
|
+
else execFileSync("cp", [
|
|
2852
|
+
"-a",
|
|
2853
|
+
"--reflink=auto",
|
|
2854
|
+
sourceSnap,
|
|
2855
|
+
targetSnap
|
|
2856
|
+
], { stdio: "ignore" });
|
|
2857
|
+
const diffOutput = execFileSync("git", [
|
|
2858
|
+
"-C",
|
|
2859
|
+
repositoryRoot,
|
|
2860
|
+
"diff",
|
|
2861
|
+
"--name-status",
|
|
2862
|
+
"--no-renames",
|
|
2863
|
+
"-z",
|
|
2864
|
+
targetTree,
|
|
2865
|
+
sourceTree
|
|
2866
|
+
], {
|
|
2867
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER,
|
|
2868
|
+
encoding: "utf8"
|
|
2869
|
+
});
|
|
2870
|
+
if (diffOutput.length === 0) return targetSnap;
|
|
2871
|
+
const parts = diffOutput.split("\0");
|
|
2872
|
+
const filesToDelete = [];
|
|
2873
|
+
const filesToRestore = [];
|
|
2874
|
+
for (let i = 0; i + 1 < parts.length; i += 2) {
|
|
2875
|
+
const status = parts[i];
|
|
2876
|
+
const file = parts[i + 1];
|
|
2877
|
+
if (file === void 0) break;
|
|
2878
|
+
if (status === "A") filesToDelete.push(file);
|
|
2879
|
+
else if (status === "D" || status === "M") filesToRestore.push(file);
|
|
2880
|
+
}
|
|
2881
|
+
for (const file of filesToDelete) fs.rmSync(path.join(targetSnap, file), { force: true });
|
|
2882
|
+
if (filesToRestore.length > 0) {
|
|
2883
|
+
const archive = execFileSync("git", [
|
|
2884
|
+
"-C",
|
|
2885
|
+
repositoryRoot,
|
|
2886
|
+
"archive",
|
|
2887
|
+
"--format=tar",
|
|
2888
|
+
targetTree,
|
|
2889
|
+
"--",
|
|
2890
|
+
...filesToRestore
|
|
2891
|
+
], { maxBuffer: GIT_EXEC_MAX_BUFFER });
|
|
2892
|
+
execFileSync("tar", [
|
|
2893
|
+
"-x",
|
|
2894
|
+
"-C",
|
|
2895
|
+
targetSnap
|
|
2896
|
+
], { input: archive });
|
|
2897
|
+
}
|
|
2898
|
+
return targetSnap;
|
|
2899
|
+
}
|
|
2900
|
+
function runGit(repositoryRoot, args, options = {}) {
|
|
2901
|
+
const output = execFileSync("git", [
|
|
2902
|
+
"-C",
|
|
2903
|
+
repositoryRoot,
|
|
2904
|
+
...args
|
|
2905
|
+
], {
|
|
2906
|
+
encoding: "utf8",
|
|
2907
|
+
maxBuffer: GIT_EXEC_MAX_BUFFER,
|
|
2908
|
+
stdio: [
|
|
2909
|
+
"ignore",
|
|
2910
|
+
"pipe",
|
|
2911
|
+
"pipe"
|
|
2912
|
+
]
|
|
2913
|
+
});
|
|
2914
|
+
return options.trim === false ? output : output.trimEnd();
|
|
2915
|
+
}
|
|
2916
|
+
function getCommandErrorMessage(error) {
|
|
2917
|
+
if (!(error instanceof Error)) return "Unknown Git error";
|
|
2918
|
+
const stderr = Reflect.get(error, "stderr");
|
|
2919
|
+
if (typeof stderr === "string" && stderr.trim().length > 0) return stderr.trim();
|
|
2920
|
+
if (Buffer.isBuffer(stderr) && stderr.byteLength > 0) return stderr.toString("utf8").trim();
|
|
2921
|
+
return error.message;
|
|
2922
|
+
}
|
|
2923
|
+
function normalizePathSeparators(filePath) {
|
|
2924
|
+
return filePath.split(path.sep).join("/");
|
|
2925
|
+
}
|
|
2926
|
+
//#endregion
|
|
2325
2927
|
//#region src/color.ts
|
|
2326
2928
|
const ANSI_RESET = "\x1B[0m";
|
|
2327
2929
|
const ANSI_COMPONENT = "\x1B[36m";
|
|
@@ -2579,6 +3181,20 @@ function printReactUsageTree(graph, options = {}) {
|
|
|
2579
3181
|
});
|
|
2580
3182
|
return lines.join("\n");
|
|
2581
3183
|
}
|
|
3184
|
+
function printReactUsageDiffTree(graph, options = {}) {
|
|
3185
|
+
const color = resolveColorSupport(options.color);
|
|
3186
|
+
if (graph.entries.length > 0) return renderReactDiffEntries(graph.entries, color);
|
|
3187
|
+
if (graph.roots.length === 0) return "No React changes found.";
|
|
3188
|
+
const lines = [];
|
|
3189
|
+
graph.roots.forEach((root, index) => {
|
|
3190
|
+
lines.push(formatDiffLine(root.change, formatReactDiffNodeLabel(root, color), color));
|
|
3191
|
+
root.usages.forEach((usage, usageIndex) => {
|
|
3192
|
+
lines.push(...renderDiffUsage(usage, color, "", usageIndex === root.usages.length - 1));
|
|
3193
|
+
});
|
|
3194
|
+
if (index < graph.roots.length - 1) lines.push("");
|
|
3195
|
+
});
|
|
3196
|
+
return lines.join("\n");
|
|
3197
|
+
}
|
|
2582
3198
|
function renderReactUsageEntries(graph, entries, cwd, filter, color) {
|
|
2583
3199
|
const lines = [];
|
|
2584
3200
|
entries.forEach((entry, index) => {
|
|
@@ -2618,6 +3234,63 @@ function formatReactEntryLabel(entry, cwd) {
|
|
|
2618
3234
|
function formatReactNodeFilePath$1(node, cwd) {
|
|
2619
3235
|
return node.kind === "builtin" ? "html" : toDisplayPath(node.filePath, cwd);
|
|
2620
3236
|
}
|
|
3237
|
+
function renderReactDiffEntries(entries, color) {
|
|
3238
|
+
const lines = [];
|
|
3239
|
+
entries.forEach((entry, index) => {
|
|
3240
|
+
lines.push(formatDiffLine(resolveVisibleEntryChange(entry), formatReactDiffEntryLabel(entry), color));
|
|
3241
|
+
lines.push(formatDiffLine(entry.node.change, formatReactDiffNodeLabel(entry.node, color, entry.beforeReferenceName, entry.afterReferenceName), color));
|
|
3242
|
+
entry.node.usages.forEach((usage, usageIndex) => {
|
|
3243
|
+
lines.push(...renderDiffUsage(usage, color, "", usageIndex === entry.node.usages.length - 1));
|
|
3244
|
+
});
|
|
3245
|
+
if (index < entries.length - 1) lines.push("");
|
|
3246
|
+
});
|
|
3247
|
+
return lines.join("\n");
|
|
3248
|
+
}
|
|
3249
|
+
function renderDiffUsage(usage, color, prefix, isLast) {
|
|
3250
|
+
const line = `${`${prefix}${isLast ? "└─ " : "├─ "}`}${formatDiffLine(resolveVisibleEdgeChange(usage), formatReactDiffNodeLabel(usage.node, color, usage.beforeReferenceName, usage.afterReferenceName), color)}`;
|
|
3251
|
+
if (usage.node.circular === true) return [`${line} (circular)`];
|
|
3252
|
+
const childLines = [line];
|
|
3253
|
+
const nextPrefix = `${prefix}${isLast ? " " : "│ "}`;
|
|
3254
|
+
usage.node.usages.forEach((childUsage, index) => {
|
|
3255
|
+
childLines.push(...renderDiffUsage(childUsage, color, nextPrefix, index === usage.node.usages.length - 1));
|
|
3256
|
+
});
|
|
3257
|
+
return childLines;
|
|
3258
|
+
}
|
|
3259
|
+
function formatReactDiffEntryLabel(entry) {
|
|
3260
|
+
const beforeLocation = entry.beforeFilePath === void 0 || entry.beforeLine === void 0 || entry.beforeColumn === void 0 ? void 0 : `${entry.beforeFilePath}:${entry.beforeLine}:${entry.beforeColumn}`;
|
|
3261
|
+
const afterLocation = entry.afterFilePath === void 0 || entry.afterLine === void 0 || entry.afterColumn === void 0 ? void 0 : `${entry.afterFilePath}:${entry.afterLine}:${entry.afterColumn}`;
|
|
3262
|
+
if (entry.change === "changed" && beforeLocation !== void 0 && afterLocation !== void 0) return `${beforeLocation} -> ${afterLocation}`;
|
|
3263
|
+
return afterLocation ?? beforeLocation ?? entry.referenceName;
|
|
3264
|
+
}
|
|
3265
|
+
function formatReactDiffNodeLabel(node, color, beforeReferenceName, afterReferenceName) {
|
|
3266
|
+
return `${formatReactDiffSymbolLabel(node.name, node.symbolKind, color, beforeReferenceName, afterReferenceName)} (${node.filePath})`;
|
|
3267
|
+
}
|
|
3268
|
+
function formatReactDiffSymbolLabel(name, kind, color, beforeReferenceName, afterReferenceName) {
|
|
3269
|
+
if (![beforeReferenceName, afterReferenceName].filter((referenceName) => referenceName !== void 0).some((referenceName) => referenceName !== name)) return formatReactSymbolLabel(name, kind, color);
|
|
3270
|
+
const aliasText = formatReactDiffAlias(name, beforeReferenceName, afterReferenceName);
|
|
3271
|
+
return `${colorizeReactLabel(formatReactSymbolName(name, kind), kind, color)} ${colorizeMuted(aliasText, color)} ${colorizeReactLabel(`[${kind}]`, kind, color)}`;
|
|
3272
|
+
}
|
|
3273
|
+
function formatReactDiffAlias(name, beforeReferenceName, afterReferenceName) {
|
|
3274
|
+
if (beforeReferenceName !== void 0 && afterReferenceName !== void 0 && beforeReferenceName !== afterReferenceName) return `as ${beforeReferenceName} -> ${afterReferenceName}`;
|
|
3275
|
+
const currentReferenceName = afterReferenceName ?? beforeReferenceName;
|
|
3276
|
+
if (currentReferenceName === void 0 || currentReferenceName === name) return "as self";
|
|
3277
|
+
return `as ${currentReferenceName}`;
|
|
3278
|
+
}
|
|
3279
|
+
function resolveVisibleEntryChange(entry) {
|
|
3280
|
+
return entry.change === "unchanged" ? entry.node.change : entry.change;
|
|
3281
|
+
}
|
|
3282
|
+
function resolveVisibleEdgeChange(usage) {
|
|
3283
|
+
return usage.change === "unchanged" ? usage.node.change : usage.change;
|
|
3284
|
+
}
|
|
3285
|
+
function formatDiffLine(change, text, color) {
|
|
3286
|
+
if (change === "unchanged") return text;
|
|
3287
|
+
return colorizePackageDiff(`${toDiffMarker(change)} ${text}`, change, color);
|
|
3288
|
+
}
|
|
3289
|
+
function toDiffMarker(change) {
|
|
3290
|
+
if (change === "added") return "+";
|
|
3291
|
+
if (change === "removed") return "-";
|
|
3292
|
+
return "~";
|
|
3293
|
+
}
|
|
2621
3294
|
//#endregion
|
|
2622
3295
|
//#region src/output/json/deps.ts
|
|
2623
3296
|
function graphToSerializablePackageTree(graph) {
|
|
@@ -2775,6 +3448,13 @@ function graphToSerializableReactTree(graph, options = {}) {
|
|
|
2775
3448
|
roots
|
|
2776
3449
|
};
|
|
2777
3450
|
}
|
|
3451
|
+
function diffGraphToSerializableReactTree(graph) {
|
|
3452
|
+
return {
|
|
3453
|
+
kind: graph.kind,
|
|
3454
|
+
entries: graph.entries.map((entry) => serializeReactDiffEntry(entry)),
|
|
3455
|
+
roots: graph.roots.map((root) => serializeReactDiffNode(root))
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
2778
3458
|
function serializeReactUsageNode(nodeId, graph, filter, visited) {
|
|
2779
3459
|
const node = graph.nodes.get(nodeId);
|
|
2780
3460
|
if (node === void 0) return {
|
|
@@ -2822,7 +3502,50 @@ function serializeReactUsageEntry(entry, graph, filter) {
|
|
|
2822
3502
|
function formatReactNodeFilePath(filePath, kind, cwd) {
|
|
2823
3503
|
return kind === "builtin" ? "html" : toDisplayPath(filePath, cwd);
|
|
2824
3504
|
}
|
|
3505
|
+
function serializeReactDiffNode(node) {
|
|
3506
|
+
return {
|
|
3507
|
+
id: node.id,
|
|
3508
|
+
name: node.name,
|
|
3509
|
+
symbolKind: node.symbolKind,
|
|
3510
|
+
...node.circular === true ? { circular: true } : {},
|
|
3511
|
+
filePath: node.filePath,
|
|
3512
|
+
change: node.change,
|
|
3513
|
+
exportNames: node.exportNames,
|
|
3514
|
+
...node.beforeExportNames === void 0 ? {} : { beforeExportNames: node.beforeExportNames },
|
|
3515
|
+
...node.afterExportNames === void 0 ? {} : { afterExportNames: node.afterExportNames },
|
|
3516
|
+
usages: node.usages.map((usage) => serializeReactDiffEdge(usage))
|
|
3517
|
+
};
|
|
3518
|
+
}
|
|
3519
|
+
function serializeReactDiffEdge(usage) {
|
|
3520
|
+
return {
|
|
3521
|
+
key: usage.key,
|
|
3522
|
+
kind: usage.kind,
|
|
3523
|
+
change: usage.change,
|
|
3524
|
+
targetId: usage.targetId,
|
|
3525
|
+
referenceName: usage.referenceName,
|
|
3526
|
+
...usage.beforeReferenceName === void 0 ? {} : { beforeReferenceName: usage.beforeReferenceName },
|
|
3527
|
+
...usage.afterReferenceName === void 0 ? {} : { afterReferenceName: usage.afterReferenceName },
|
|
3528
|
+
node: serializeReactDiffNode(usage.node)
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
function serializeReactDiffEntry(entry) {
|
|
3532
|
+
return {
|
|
3533
|
+
key: entry.key,
|
|
3534
|
+
change: entry.change,
|
|
3535
|
+
targetId: entry.targetId,
|
|
3536
|
+
referenceName: entry.referenceName,
|
|
3537
|
+
...entry.beforeReferenceName === void 0 ? {} : { beforeReferenceName: entry.beforeReferenceName },
|
|
3538
|
+
...entry.afterReferenceName === void 0 ? {} : { afterReferenceName: entry.afterReferenceName },
|
|
3539
|
+
...entry.beforeFilePath === void 0 ? {} : { beforeFilePath: entry.beforeFilePath },
|
|
3540
|
+
...entry.beforeLine === void 0 ? {} : { beforeLine: entry.beforeLine },
|
|
3541
|
+
...entry.beforeColumn === void 0 ? {} : { beforeColumn: entry.beforeColumn },
|
|
3542
|
+
...entry.afterFilePath === void 0 ? {} : { afterFilePath: entry.afterFilePath },
|
|
3543
|
+
...entry.afterLine === void 0 ? {} : { afterLine: entry.afterLine },
|
|
3544
|
+
...entry.afterColumn === void 0 ? {} : { afterColumn: entry.afterColumn },
|
|
3545
|
+
node: serializeReactDiffNode(entry.node)
|
|
3546
|
+
};
|
|
3547
|
+
}
|
|
2825
3548
|
//#endregion
|
|
2826
|
-
export {
|
|
3549
|
+
export { analyzeDependencies as _, graphToSerializablePackageTree as a, printDependencyTree as c, analyzeReactUsageDiff as d, getFilteredUsages as f, resolveReactEntryFiles as g, analyzeReactUsage as h, diffGraphToSerializablePackageTree as i, printPackageDependencyDiffTree as l, getReactUsageRoots as m, graphToSerializableReactTree as n, printReactUsageDiffTree as o, getReactUsageEntries as p, graphToSerializableTree as r, printReactUsageTree as s, diffGraphToSerializableReactTree as t, printPackageDependencyTree as u, analyzePackageDependencyDiff as v, analyzePackageDependencies as y };
|
|
2827
3550
|
|
|
2828
|
-
//# sourceMappingURL=react-
|
|
3551
|
+
//# sourceMappingURL=react-D0TO9XR2.mjs.map
|