deslop-js 0.0.8 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -254,7 +254,9 @@ const PLATFORM_SUFFIXES = [
254
254
  ".desktop",
255
255
  ".windows",
256
256
  ".macos",
257
- ".any"
257
+ ".any",
258
+ ".server",
259
+ ".client"
258
260
  ];
259
261
  const REACT_NATIVE_PLATFORM_EXTENSIONS = [
260
262
  ".web.ts",
@@ -605,6 +607,10 @@ const extractNamedExportDeclaration = (node, sourceText, exports) => {
605
607
  }
606
608
  };
607
609
  const extractDefaultExportDeclaration = (node, sourceText, exports) => {
610
+ const defaultExpression = node.declaration;
611
+ let defaultExportLocalName;
612
+ if (defaultExpression?.type === "Identifier" && typeof defaultExpression.name === "string") defaultExportLocalName = defaultExpression.name;
613
+ else if ((defaultExpression?.type === "FunctionDeclaration" || defaultExpression?.type === "ClassDeclaration") && defaultExpression.id?.name) defaultExportLocalName = defaultExpression.id.name;
608
614
  exports.push({
609
615
  name: "default",
610
616
  isDefault: true,
@@ -615,7 +621,8 @@ const extractDefaultExportDeclaration = (node, sourceText, exports) => {
615
621
  reExportOriginalName: void 0,
616
622
  isNamespaceReExport: false,
617
623
  line: getLineFromOffset(sourceText, node.start),
618
- column: getColumnFromOffset(sourceText, node.start)
624
+ column: getColumnFromOffset(sourceText, node.start),
625
+ defaultExportLocalName
619
626
  });
620
627
  };
621
628
  const extractExportAllDeclaration = (node, sourceText, exports) => {
@@ -1096,6 +1103,12 @@ const collectWorkspacePatterns = (rootDir) => {
1096
1103
  const packageLines = extractPnpmWorkspacePackages((0, node_fs.readFileSync)(pnpmWorkspacePath, "utf-8"));
1097
1104
  patterns.push(...packageLines);
1098
1105
  } catch {}
1106
+ const lernaJsonPath = (0, node_path.join)(rootDir, "lerna.json");
1107
+ if ((0, node_fs.existsSync)(lernaJsonPath)) try {
1108
+ const content = (0, node_fs.readFileSync)(lernaJsonPath, "utf-8");
1109
+ const lernaJson = JSON.parse(content);
1110
+ if (Array.isArray(lernaJson.packages)) patterns.push(...lernaJson.packages.filter((pattern) => typeof pattern === "string" && !pattern.startsWith("!")));
1111
+ } catch {}
1099
1112
  return patterns;
1100
1113
  };
1101
1114
  const extractPnpmWorkspacePackages = (yamlContent) => {
@@ -1657,6 +1670,54 @@ const resolveSourcePath = (distPath, directory) => {
1657
1670
  }
1658
1671
  };
1659
1672
 
1673
+ //#endregion
1674
+ //#region src/utils/find-monorepo-root.ts
1675
+ const MONOREPO_ROOT_MARKERS = [
1676
+ "pnpm-workspace.yaml",
1677
+ "pnpm-workspace.yml",
1678
+ "lerna.json",
1679
+ "nx.json",
1680
+ "turbo.json",
1681
+ "rush.json"
1682
+ ];
1683
+ const LOCKFILE_MARKERS = [
1684
+ "pnpm-lock.yaml",
1685
+ "yarn.lock",
1686
+ "package-lock.json",
1687
+ "bun.lockb",
1688
+ "bun.lock"
1689
+ ];
1690
+ const MAX_MONOREPO_WALK_DEPTH = 5;
1691
+ const findMonorepoRoot = (rootDir) => {
1692
+ let currentDirectory = (0, node_path.resolve)(rootDir);
1693
+ let walkedDepth = 0;
1694
+ while (walkedDepth < MAX_MONOREPO_WALK_DEPTH) {
1695
+ const parentDirectory = (0, node_path.dirname)(currentDirectory);
1696
+ if (parentDirectory === currentDirectory) break;
1697
+ currentDirectory = parentDirectory;
1698
+ walkedDepth++;
1699
+ if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, ".git"))) {
1700
+ for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
1701
+ const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
1702
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
1703
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
1704
+ if (JSON.parse(content).workspaces) return currentDirectory;
1705
+ } catch {}
1706
+ for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
1707
+ return;
1708
+ }
1709
+ for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
1710
+ const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
1711
+ if ((0, node_fs.existsSync)(packageJsonPath)) try {
1712
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
1713
+ if (JSON.parse(content).workspaces) return currentDirectory;
1714
+ } catch {
1715
+ continue;
1716
+ }
1717
+ for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
1718
+ }
1719
+ };
1720
+
1660
1721
  //#endregion
1661
1722
  //#region src/collect/entries.ts
1662
1723
  const collectSourceFiles = async (config) => {
@@ -1762,6 +1823,14 @@ const resolveEntries = async (config) => {
1762
1823
  for (const entryPath of allDiscoveredEntries) if (entryPath.endsWith(".html") && (0, node_fs.existsSync)(entryPath)) htmlScriptEntries.push(...extractScriptTagsFromHtmlFile(entryPath));
1763
1824
  const angularEntries = extractAngularEntryPoints(absoluteRoot);
1764
1825
  for (const workspacePackage of entryEligiblePackages) angularEntries.push(...extractAngularEntryPoints(workspacePackage.directory));
1826
+ const browserExtensionEntries = extractBrowserExtensionEntries(absoluteRoot);
1827
+ for (const workspacePackage of entryEligiblePackages) browserExtensionEntries.push(...extractBrowserExtensionEntries(workspacePackage.directory));
1828
+ const webWorkerEntries = extractWebWorkerEntries(absoluteRoot);
1829
+ for (const workspacePackage of entryEligiblePackages) webWorkerEntries.push(...extractWebWorkerEntries(workspacePackage.directory));
1830
+ const tsConfigIncludeEntries = extractTsConfigIncludeFilesEntries(absoluteRoot);
1831
+ for (const workspacePackage of entryEligiblePackages) tsConfigIncludeEntries.push(...extractTsConfigIncludeFilesEntries(workspacePackage.directory));
1832
+ const wranglerEntries = extractWranglerEntries(absoluteRoot);
1833
+ for (const workspacePackage of entryEligiblePackages) wranglerEntries.push(...extractWranglerEntries(workspacePackage.directory));
1765
1834
  const testSetupEntries = extractTestSetupFiles(absoluteRoot);
1766
1835
  for (const workspacePackage of entryEligiblePackages) testSetupEntries.push(...extractTestSetupFiles(workspacePackage.directory));
1767
1836
  const pluginFileEntries = extractNextConfigPluginFiles(absoluteRoot);
@@ -1783,6 +1852,10 @@ const resolveEntries = async (config) => {
1783
1852
  ...bundlerConfigEntries,
1784
1853
  ...htmlScriptEntries,
1785
1854
  ...angularEntries,
1855
+ ...browserExtensionEntries,
1856
+ ...webWorkerEntries,
1857
+ ...tsConfigIncludeEntries,
1858
+ ...wranglerEntries,
1786
1859
  ...pluginFileEntries,
1787
1860
  ...toolingDiscovery.entryFiles,
1788
1861
  ...ciEntries
@@ -2426,6 +2499,231 @@ const extractScriptTagsFromHtmlFile = (htmlFilePath) => {
2426
2499
  } catch {}
2427
2500
  return entries;
2428
2501
  };
2502
+ const TSCONFIG_FILENAME_GLOBS = ["tsconfig.json", "tsconfig.*.json"];
2503
+ const stripJsoncCommentsLocal = (sourceText) => {
2504
+ let result = "";
2505
+ let insideString = false;
2506
+ let index = 0;
2507
+ while (index < sourceText.length) {
2508
+ const ch = sourceText[index];
2509
+ if (insideString) {
2510
+ if (ch === "\\" && index + 1 < sourceText.length) {
2511
+ result += sourceText[index] + sourceText[index + 1];
2512
+ index += 2;
2513
+ continue;
2514
+ }
2515
+ if (ch === "\"") insideString = false;
2516
+ result += ch;
2517
+ index++;
2518
+ continue;
2519
+ }
2520
+ if (ch === "\"") {
2521
+ insideString = true;
2522
+ result += ch;
2523
+ index++;
2524
+ continue;
2525
+ }
2526
+ if (ch === "/" && index + 1 < sourceText.length) {
2527
+ if (sourceText[index + 1] === "/") {
2528
+ while (index < sourceText.length && sourceText[index] !== "\n") index++;
2529
+ continue;
2530
+ }
2531
+ if (sourceText[index + 1] === "*") {
2532
+ index += 2;
2533
+ while (index + 1 < sourceText.length && !(sourceText[index] === "*" && sourceText[index + 1] === "/")) index++;
2534
+ index += 2;
2535
+ continue;
2536
+ }
2537
+ }
2538
+ result += ch;
2539
+ index++;
2540
+ }
2541
+ return result.replace(/,(\s*[}\]])/g, "$1");
2542
+ };
2543
+ const extractTsConfigIncludeFilesEntries = (directory) => {
2544
+ const entries = [];
2545
+ const tsconfigPaths = fast_glob.default.sync(TSCONFIG_FILENAME_GLOBS, {
2546
+ cwd: directory,
2547
+ absolute: true,
2548
+ onlyFiles: true,
2549
+ ignore: [
2550
+ "**/node_modules/**",
2551
+ "**/dist/**",
2552
+ "**/build/**"
2553
+ ],
2554
+ deep: 1
2555
+ });
2556
+ for (const tsconfigPath of tsconfigPaths) try {
2557
+ const cleaned = stripJsoncCommentsLocal((0, node_fs.readFileSync)(tsconfigPath, "utf-8"));
2558
+ const tsconfigJson = JSON.parse(cleaned);
2559
+ const tsconfigDir = (0, node_path.dirname)(tsconfigPath);
2560
+ const collectPaths = (rawList) => {
2561
+ if (!Array.isArray(rawList)) return;
2562
+ for (const item of rawList) {
2563
+ if (typeof item !== "string") continue;
2564
+ if (item.includes("*") || item.includes("?")) continue;
2565
+ const candidatePath = (0, node_path.resolve)(tsconfigDir, item);
2566
+ if ((0, node_fs.existsSync)(candidatePath)) entries.push(candidatePath);
2567
+ }
2568
+ };
2569
+ collectPaths(tsconfigJson.include);
2570
+ collectPaths(tsconfigJson.files);
2571
+ } catch {}
2572
+ return entries;
2573
+ };
2574
+ const WRANGLER_TOML_MAIN_PATTERN = /^\s*main\s*=\s*['"]([^'"\n]+)['"]/m;
2575
+ const WRANGLER_JSON_MAIN_PATTERN = /"main"\s*:\s*"([^"]+)"/;
2576
+ const WRANGLER_SERVICE_BINDINGS_PATTERN = /entry_point\s*=\s*['"]([^'"\n]+)['"]/g;
2577
+ const extractWranglerEntries = (directory) => {
2578
+ const entries = [];
2579
+ const wranglerPaths = fast_glob.default.sync([
2580
+ "wrangler.toml",
2581
+ "wrangler.json",
2582
+ "wrangler.jsonc"
2583
+ ], {
2584
+ cwd: directory,
2585
+ absolute: true,
2586
+ onlyFiles: true,
2587
+ ignore: ["**/node_modules/**"],
2588
+ deep: 1
2589
+ });
2590
+ for (const wranglerPath of wranglerPaths) try {
2591
+ const content = (0, node_fs.readFileSync)(wranglerPath, "utf-8");
2592
+ const wranglerDir = (0, node_path.dirname)(wranglerPath);
2593
+ const mainMatch = wranglerPath.endsWith(".toml") ? content.match(WRANGLER_TOML_MAIN_PATTERN) : content.match(WRANGLER_JSON_MAIN_PATTERN);
2594
+ if (mainMatch?.[1]) {
2595
+ const candidatePath = (0, node_path.resolve)(wranglerDir, mainMatch[1]);
2596
+ if ((0, node_fs.existsSync)(candidatePath)) entries.push(candidatePath);
2597
+ else {
2598
+ const sourceCandidate = resolveSourcePath(candidatePath, wranglerDir);
2599
+ if (sourceCandidate) entries.push(sourceCandidate);
2600
+ }
2601
+ }
2602
+ let entryPointMatch;
2603
+ WRANGLER_SERVICE_BINDINGS_PATTERN.lastIndex = 0;
2604
+ while ((entryPointMatch = WRANGLER_SERVICE_BINDINGS_PATTERN.exec(content)) !== null) {
2605
+ const candidatePath = (0, node_path.resolve)(wranglerDir, entryPointMatch[1]);
2606
+ if ((0, node_fs.existsSync)(candidatePath)) entries.push(candidatePath);
2607
+ }
2608
+ } catch {}
2609
+ return entries;
2610
+ };
2611
+ const WORKER_FILE_GLOBS = [
2612
+ "**/*.worker.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
2613
+ "**/*.sw.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
2614
+ "**/sw.{ts,tsx,js,jsx,mts,mjs,cts,cjs}",
2615
+ "**/service-worker.{ts,tsx,js,jsx,mts,mjs,cts,cjs}"
2616
+ ];
2617
+ const extractWebWorkerEntries = (directory) => {
2618
+ return fast_glob.default.sync(WORKER_FILE_GLOBS, {
2619
+ cwd: directory,
2620
+ absolute: true,
2621
+ onlyFiles: true,
2622
+ ignore: [
2623
+ "**/node_modules/**",
2624
+ "**/dist/**",
2625
+ "**/build/**",
2626
+ "**/.next/**",
2627
+ "**/out/**"
2628
+ ],
2629
+ deep: 8
2630
+ });
2631
+ };
2632
+ const collectBrowserExtensionManifestPaths = (manifest) => {
2633
+ const candidatePaths = [];
2634
+ if (typeof manifest !== "object" || manifest === null) return candidatePaths;
2635
+ const manifestRecord = manifest;
2636
+ const background = manifestRecord.background;
2637
+ if (typeof background === "object" && background !== null) {
2638
+ const backgroundRecord = background;
2639
+ if (typeof backgroundRecord.service_worker === "string") candidatePaths.push(backgroundRecord.service_worker);
2640
+ if (typeof backgroundRecord.page === "string") candidatePaths.push(backgroundRecord.page);
2641
+ if (typeof backgroundRecord.scripts === "string") candidatePaths.push(backgroundRecord.scripts);
2642
+ if (Array.isArray(backgroundRecord.scripts)) {
2643
+ for (const scriptPath of backgroundRecord.scripts) if (typeof scriptPath === "string") candidatePaths.push(scriptPath);
2644
+ }
2645
+ }
2646
+ const contentScripts = manifestRecord.content_scripts;
2647
+ if (Array.isArray(contentScripts)) for (const contentScript of contentScripts) {
2648
+ if (typeof contentScript !== "object" || contentScript === null) continue;
2649
+ const contentScriptRecord = contentScript;
2650
+ if (Array.isArray(contentScriptRecord.js)) {
2651
+ for (const scriptPath of contentScriptRecord.js) if (typeof scriptPath === "string") candidatePaths.push(scriptPath);
2652
+ }
2653
+ if (Array.isArray(contentScriptRecord.css)) {
2654
+ for (const stylePath of contentScriptRecord.css) if (typeof stylePath === "string") candidatePaths.push(stylePath);
2655
+ }
2656
+ }
2657
+ const action = manifestRecord.action ?? manifestRecord.browser_action ?? manifestRecord.page_action;
2658
+ if (typeof action === "object" && action !== null) {
2659
+ const actionRecord = action;
2660
+ if (typeof actionRecord.default_popup === "string") candidatePaths.push(actionRecord.default_popup);
2661
+ }
2662
+ if (typeof manifestRecord.devtools_page === "string") candidatePaths.push(manifestRecord.devtools_page);
2663
+ if (typeof manifestRecord.options_page === "string") candidatePaths.push(manifestRecord.options_page);
2664
+ if (typeof manifestRecord.options_ui === "object" && manifestRecord.options_ui !== null) {
2665
+ const optionsRecord = manifestRecord.options_ui;
2666
+ if (typeof optionsRecord.page === "string") candidatePaths.push(optionsRecord.page);
2667
+ }
2668
+ if (typeof manifestRecord.sandbox === "object" && manifestRecord.sandbox !== null) {
2669
+ const sandboxRecord = manifestRecord.sandbox;
2670
+ if (Array.isArray(sandboxRecord.pages)) {
2671
+ for (const pagePath of sandboxRecord.pages) if (typeof pagePath === "string") candidatePaths.push(pagePath);
2672
+ }
2673
+ }
2674
+ return candidatePaths;
2675
+ };
2676
+ const isLikelyBrowserExtensionManifest = (manifest) => {
2677
+ if (typeof manifest !== "object" || manifest === null) return false;
2678
+ return typeof manifest.manifest_version === "number";
2679
+ };
2680
+ const extractBrowserExtensionEntries = (directory) => {
2681
+ const entries = [];
2682
+ const manifestPaths = fast_glob.default.sync([
2683
+ "manifest.json",
2684
+ "manifest.*.json",
2685
+ "src/manifest.json",
2686
+ "src/manifest.*.json",
2687
+ "public/manifest.json",
2688
+ "public/manifest.*.json",
2689
+ "static/manifest.json"
2690
+ ], {
2691
+ cwd: directory,
2692
+ absolute: true,
2693
+ onlyFiles: true,
2694
+ ignore: [
2695
+ "**/node_modules/**",
2696
+ "**/dist/**",
2697
+ "**/build/**"
2698
+ ],
2699
+ deep: 3
2700
+ });
2701
+ for (const manifestPath of manifestPaths) try {
2702
+ const content = (0, node_fs.readFileSync)(manifestPath, "utf-8");
2703
+ const manifest = JSON.parse(content);
2704
+ if (!isLikelyBrowserExtensionManifest(manifest)) continue;
2705
+ const manifestDir = (0, node_path.dirname)(manifestPath);
2706
+ const candidatePaths = collectBrowserExtensionManifestPaths(manifest);
2707
+ const resolutionRoots = [
2708
+ manifestDir,
2709
+ (0, node_path.resolve)(manifestDir, ".."),
2710
+ directory
2711
+ ];
2712
+ for (const candidatePath of candidatePaths) for (const resolutionRoot of resolutionRoots) {
2713
+ const candidateAbsolutePath = (0, node_path.resolve)(resolutionRoot, candidatePath);
2714
+ if ((0, node_fs.existsSync)(candidateAbsolutePath)) {
2715
+ entries.push(candidateAbsolutePath);
2716
+ break;
2717
+ }
2718
+ const sourceFile = resolveSourcePath(candidateAbsolutePath, resolutionRoot);
2719
+ if (sourceFile) {
2720
+ entries.push(sourceFile);
2721
+ break;
2722
+ }
2723
+ }
2724
+ } catch {}
2725
+ return entries;
2726
+ };
2429
2727
  const ANGULAR_ENTRY_KEYS = [
2430
2728
  "main",
2431
2729
  "polyfills",
@@ -3156,8 +3454,14 @@ const FRAMEWORK_PATTERNS = [
3156
3454
  alwaysUsed: ["tsr.config.json", "app.config.{ts,js}"]
3157
3455
  },
3158
3456
  {
3159
- enablers: ["vite", "rolldown-vite"],
3160
- enablerPrefixes: ["@vitejs/"],
3457
+ enablers: [
3458
+ "vite",
3459
+ "rolldown-vite",
3460
+ "vite-plus",
3461
+ "@voidzero-dev/vite-plus-core",
3462
+ "@voidzero-dev/vite-plus-test"
3463
+ ],
3464
+ enablerPrefixes: ["@vitejs/", "@voidzero-dev/vite-plus"],
3161
3465
  entryPatterns: [
3162
3466
  "src/main.{ts,tsx,js,jsx}",
3163
3467
  "src/index.{ts,tsx,js,jsx}",
@@ -3445,10 +3749,25 @@ const detectBunTestRunner = (directory) => {
3445
3749
  return false;
3446
3750
  }
3447
3751
  };
3752
+ const readPackageJsonDependencies = (packageJsonPath) => {
3753
+ try {
3754
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
3755
+ const packageJson = JSON.parse(content);
3756
+ return {
3757
+ ...packageJson.dependencies,
3758
+ ...packageJson.devDependencies,
3759
+ ...packageJson.optionalDependencies
3760
+ };
3761
+ } catch {
3762
+ return {};
3763
+ }
3764
+ };
3448
3765
  const discoverTestRunnerEntryPoints = (rootDir, workspacePackages) => {
3449
3766
  const allEntries = [];
3450
3767
  const allAlwaysUsed = [];
3451
3768
  const directoriesToCheck = [rootDir, ...workspacePackages.map((workspacePackage) => workspacePackage.directory)];
3769
+ const monorepoRoot = findMonorepoRoot(rootDir);
3770
+ const monorepoRootDeps = monorepoRoot && monorepoRoot !== rootDir ? readPackageJsonDependencies((0, node_path.join)(monorepoRoot, "package.json")) : {};
3452
3771
  for (const directory of directoriesToCheck) {
3453
3772
  const packageJsonPath = (0, node_path.join)(directory, "package.json");
3454
3773
  if (!(0, node_fs.existsSync)(packageJsonPath)) continue;
@@ -3473,16 +3792,25 @@ const discoverTestRunnerEntryPoints = (rootDir, workspacePackages) => {
3473
3792
  })) return true;
3474
3793
  return runner.configFileActivators.some((configFile) => (0, node_fs.existsSync)((0, node_path.join)(checkDirectory, configFile)));
3475
3794
  };
3476
- for (const runner of TEST_FRAMEWORK_PATTERNS) if (isRunnerEnabled(runner, allDependencies, directory)) {
3477
- const isVitestRunner = runner.enablers.includes("vitest");
3478
- const isJestRunner = runner.enablers.includes("jest");
3479
- let customPatterns = [];
3480
- if (isVitestRunner) customPatterns = extractVitestIncludePatterns(directory);
3481
- else if (isJestRunner) customPatterns = extractJestTestMatchPatterns(directory);
3482
- if (customPatterns.length > 0) activatedPatterns.push(...customPatterns);
3483
- else activatedPatterns.push(...runner.entryPatterns);
3484
- activatedFixturePatterns.push(...runner.fixturePatterns);
3485
- activatedAlwaysUsed.push(...runner.alwaysUsed);
3795
+ for (const runner of TEST_FRAMEWORK_PATTERNS) {
3796
+ const enabledLocally = isRunnerEnabled(runner, allDependencies, directory);
3797
+ const enabledViaMonorepo = !enabledLocally && monorepoRoot && (isRunnerEnabled(runner, monorepoRootDeps, monorepoRoot) || runner.configFileActivators.some((configFile) => (0, node_fs.existsSync)((0, node_path.join)(monorepoRoot, configFile))));
3798
+ if (enabledLocally || enabledViaMonorepo) {
3799
+ const isVitestRunner = runner.enablers.includes("vitest");
3800
+ const isJestRunner = runner.enablers.includes("jest");
3801
+ let customPatterns = [];
3802
+ if (isVitestRunner) {
3803
+ customPatterns = extractVitestIncludePatterns(directory);
3804
+ if (customPatterns.length === 0 && monorepoRoot) customPatterns = extractVitestIncludePatterns(monorepoRoot);
3805
+ } else if (isJestRunner) {
3806
+ customPatterns = extractJestTestMatchPatterns(directory);
3807
+ if (customPatterns.length === 0 && monorepoRoot) customPatterns = extractJestTestMatchPatterns(monorepoRoot);
3808
+ }
3809
+ if (customPatterns.length > 0) activatedPatterns.push(...customPatterns);
3810
+ else activatedPatterns.push(...runner.entryPatterns);
3811
+ activatedFixturePatterns.push(...runner.fixturePatterns);
3812
+ activatedAlwaysUsed.push(...runner.alwaysUsed);
3813
+ }
3486
3814
  }
3487
3815
  if (activatedPatterns.length === 0 && directory !== rootDir) {
3488
3816
  const rootPackageJsonPath = (0, node_path.join)(rootDir, "package.json");
@@ -3547,6 +3875,45 @@ const isToolingPluginEnabled = (plugin, dependencies) => {
3547
3875
  }
3548
3876
  return false;
3549
3877
  };
3878
+ const FRAMEWORK_SCRIPT_BINARIES = {
3879
+ next: ["next"],
3880
+ nuxt: ["nuxt"],
3881
+ astro: ["astro"],
3882
+ gatsby: ["gatsby"],
3883
+ remix: ["remix"],
3884
+ "@sveltejs/kit": ["svelte-kit", "vite-svelte-kit"],
3885
+ "@docusaurus/core": ["docusaurus"],
3886
+ "@angular/core": ["ng"],
3887
+ "@nestjs/core": ["nest"],
3888
+ storybook: [
3889
+ "storybook",
3890
+ "start-storybook",
3891
+ "build-storybook"
3892
+ ]
3893
+ };
3894
+ const detectFrameworkFromScripts = (scripts) => {
3895
+ const enabledEnablers = /* @__PURE__ */ new Set();
3896
+ if (!scripts || typeof scripts !== "object") return enabledEnablers;
3897
+ for (const scriptValue of Object.values(scripts)) {
3898
+ if (typeof scriptValue !== "string") continue;
3899
+ const tokenized = scriptValue.split(/[\s|&;]+/);
3900
+ for (const token of tokenized) {
3901
+ const cleaned = token.replace(/^.*\//, "");
3902
+ for (const [enabler, binaries] of Object.entries(FRAMEWORK_SCRIPT_BINARIES)) if (binaries.includes(cleaned)) enabledEnablers.add(enabler);
3903
+ }
3904
+ }
3905
+ return enabledEnablers;
3906
+ };
3907
+ const readPackageScripts = (directory) => {
3908
+ const packageJsonPath = (0, node_path.join)(directory, "package.json");
3909
+ if (!(0, node_fs.existsSync)(packageJsonPath)) return void 0;
3910
+ try {
3911
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
3912
+ return JSON.parse(content).scripts;
3913
+ } catch {
3914
+ return;
3915
+ }
3916
+ };
3550
3917
  const discoverToolingEntryPoints = (rootDir, workspacePackages) => {
3551
3918
  const allEntries = [];
3552
3919
  const allAlwaysUsed = [];
@@ -3562,6 +3929,8 @@ const discoverToolingEntryPoints = (rootDir, workspacePackages) => {
3562
3929
  ...rootPackageJson.optionalDependencies
3563
3930
  };
3564
3931
  } catch {}
3932
+ const monorepoRoot = findMonorepoRoot(rootDir);
3933
+ const monorepoRootDeps = monorepoRoot && monorepoRoot !== rootDir ? readPackageJsonDependencies((0, node_path.join)(monorepoRoot, "package.json")) : {};
3565
3934
  for (const directory of directoriesToCheck) {
3566
3935
  const packageJsonPath = (0, node_path.join)(directory, "package.json");
3567
3936
  if (!(0, node_fs.existsSync)(packageJsonPath)) continue;
@@ -3577,7 +3946,10 @@ const discoverToolingEntryPoints = (rootDir, workspacePackages) => {
3577
3946
  } catch {
3578
3947
  continue;
3579
3948
  }
3580
- const mergedDependencies = directory === rootDir ? rootDependencies : workspaceDependencies;
3949
+ const scriptDetectedEnablers = detectFrameworkFromScripts(readPackageScripts(directory));
3950
+ const mergedDependencies = { ...workspaceDependencies };
3951
+ if (directory === rootDir) Object.assign(mergedDependencies, rootDependencies);
3952
+ else for (const enabler of scriptDetectedEnablers) if (enabler in monorepoRootDeps || enabler in rootDependencies) mergedDependencies[enabler] = "*";
3581
3953
  const activatedPatterns = [];
3582
3954
  const activatedAlwaysUsed = [];
3583
3955
  for (const plugin of FRAMEWORK_PATTERNS) if (isToolingPluginEnabled(plugin, mergedDependencies)) {
@@ -3715,6 +4087,17 @@ const COMMON_RESOLVER_OPTIONS = {
3715
4087
  ".cjs": [".cts", ".cjs"]
3716
4088
  }
3717
4089
  };
4090
+ const WEBPACK_CONFIG_GLOBS = [
4091
+ "webpack.config.{js,ts,mjs,cjs}",
4092
+ "**/webpack*.config.{js,ts,mjs,cjs}",
4093
+ "**/webpack.config*.{js,ts,mjs,cjs}",
4094
+ "**/webpack*.config*.babel.{js,ts}"
4095
+ ];
4096
+ const WEBPACK_ALIAS_BLOCK_PATTERN = /alias\s*:\s*\{([\s\S]*?)\}/g;
4097
+ const WEBPACK_ALIAS_ENTRY_PATTERN = /["']?([@\w$./-]+)["']?\s*:\s*(?:path\.(?:resolve|join)\(\s*__dirname\s*,\s*((?:["'][^"']+["']\s*,?\s*)+)\)|["']([^"']+)["'])/g;
4098
+ const WEBPACK_MODULES_BLOCK_PATTERN = /modules\s*:\s*\[([\s\S]*?)\]/g;
4099
+ const WEBPACK_PATH_CALL_PATTERN = /path\.(?:resolve|join)\(\s*__dirname\s*,\s*((?:["'][^"']+["']\s*,?\s*)+)\)/g;
4100
+ const WEBPACK_STRING_LITERAL_PATTERN = /["']([^"']+)["']/g;
3718
4101
  const TSCONFIG_FILENAMES = [
3719
4102
  "tsconfig.json",
3720
4103
  "tsconfig.web.json",
@@ -3764,6 +4147,108 @@ const resolveScssPartial = (specifier, fromDirectory) => {
3764
4147
  candidates.push((0, node_path.join)(basePath, `_index.sass`));
3765
4148
  for (const candidate of candidates) if (cachedExistsSync(candidate)) return candidate;
3766
4149
  };
4150
+ const isInsideDirectory = (filePath, directory) => {
4151
+ const relativePath = (0, node_path.relative)(directory, filePath);
4152
+ return relativePath === "" || !relativePath.startsWith("..") && !(0, node_path.isAbsolute)(relativePath);
4153
+ };
4154
+ const extractQuotedSegments = (value) => {
4155
+ const segments = [];
4156
+ let segmentMatch;
4157
+ WEBPACK_STRING_LITERAL_PATTERN.lastIndex = 0;
4158
+ while ((segmentMatch = WEBPACK_STRING_LITERAL_PATTERN.exec(value)) !== null) segments.push(segmentMatch[1]);
4159
+ return segments;
4160
+ };
4161
+ const resolveWebpackPathValue = (value, configDirectory) => {
4162
+ if ((0, node_path.isAbsolute)(value)) return value;
4163
+ return (0, node_path.resolve)(configDirectory, value);
4164
+ };
4165
+ const findWebpackConfigScope = (configPath, rootDir) => {
4166
+ let currentDirectory = (0, node_path.dirname)(configPath);
4167
+ const absoluteRoot = (0, node_path.resolve)(rootDir);
4168
+ while (currentDirectory.length >= absoluteRoot.length) {
4169
+ if (cachedExistsSync((0, node_path.join)(currentDirectory, "package.json"))) return currentDirectory;
4170
+ const parentDirectory = (0, node_path.dirname)(currentDirectory);
4171
+ if (parentDirectory === currentDirectory) break;
4172
+ currentDirectory = parentDirectory;
4173
+ }
4174
+ return absoluteRoot;
4175
+ };
4176
+ const extractWebpackAliases = (content, configDirectory) => {
4177
+ const aliases = [];
4178
+ let aliasBlockMatch;
4179
+ WEBPACK_ALIAS_BLOCK_PATTERN.lastIndex = 0;
4180
+ while ((aliasBlockMatch = WEBPACK_ALIAS_BLOCK_PATTERN.exec(content)) !== null) {
4181
+ const aliasBlock = aliasBlockMatch[1];
4182
+ let aliasEntryMatch;
4183
+ WEBPACK_ALIAS_ENTRY_PATTERN.lastIndex = 0;
4184
+ while ((aliasEntryMatch = WEBPACK_ALIAS_ENTRY_PATTERN.exec(aliasBlock)) !== null) {
4185
+ const rawName = aliasEntryMatch[1];
4186
+ const pathCallSegments = aliasEntryMatch[2];
4187
+ const stringTarget = aliasEntryMatch[3];
4188
+ if (!pathCallSegments && !stringTarget) continue;
4189
+ const isExact = rawName.endsWith("$");
4190
+ const name = isExact ? rawName.slice(0, -1) : rawName.replace(/\/$/, "");
4191
+ let targetDirectory;
4192
+ if (pathCallSegments) targetDirectory = (0, node_path.resolve)(configDirectory, ...extractQuotedSegments(pathCallSegments));
4193
+ else if (stringTarget) targetDirectory = resolveWebpackPathValue(stringTarget, configDirectory);
4194
+ else continue;
4195
+ aliases.push({
4196
+ name,
4197
+ targetDirectory,
4198
+ isExact
4199
+ });
4200
+ }
4201
+ }
4202
+ return aliases;
4203
+ };
4204
+ const extractWebpackModuleDirectories = (content, configDirectory) => {
4205
+ const moduleDirectories = [];
4206
+ let modulesBlockMatch;
4207
+ WEBPACK_MODULES_BLOCK_PATTERN.lastIndex = 0;
4208
+ while ((modulesBlockMatch = WEBPACK_MODULES_BLOCK_PATTERN.exec(content)) !== null) {
4209
+ const modulesBlock = modulesBlockMatch[1];
4210
+ let pathCallMatch;
4211
+ WEBPACK_PATH_CALL_PATTERN.lastIndex = 0;
4212
+ while ((pathCallMatch = WEBPACK_PATH_CALL_PATTERN.exec(modulesBlock)) !== null) moduleDirectories.push((0, node_path.resolve)(configDirectory, ...extractQuotedSegments(pathCallMatch[1])));
4213
+ let stringMatch;
4214
+ WEBPACK_STRING_LITERAL_PATTERN.lastIndex = 0;
4215
+ while ((stringMatch = WEBPACK_STRING_LITERAL_PATTERN.exec(modulesBlock)) !== null) {
4216
+ const moduleDirectory = stringMatch[1];
4217
+ if (moduleDirectory === "node_modules") continue;
4218
+ moduleDirectories.push(resolveWebpackPathValue(moduleDirectory, configDirectory));
4219
+ }
4220
+ }
4221
+ return [...new Set(moduleDirectories)];
4222
+ };
4223
+ const loadWebpackResolverConfigs = (rootDir) => {
4224
+ const configPaths = fast_glob.default.sync(WEBPACK_CONFIG_GLOBS, {
4225
+ cwd: rootDir,
4226
+ absolute: true,
4227
+ onlyFiles: true,
4228
+ ignore: [
4229
+ "**/node_modules/**",
4230
+ "**/dist/**",
4231
+ "**/build/**"
4232
+ ],
4233
+ deep: 4
4234
+ });
4235
+ const configs = [];
4236
+ for (const configPath of configPaths) try {
4237
+ const content = cachedReadFileSync(configPath);
4238
+ const configDirectory = (0, node_path.dirname)(configPath);
4239
+ const aliases = extractWebpackAliases(content, configDirectory);
4240
+ const moduleDirectories = extractWebpackModuleDirectories(content, configDirectory);
4241
+ if (aliases.length === 0 && moduleDirectories.length === 0) continue;
4242
+ configs.push({
4243
+ scopeDirectory: findWebpackConfigScope(configPath, rootDir),
4244
+ aliases,
4245
+ moduleDirectories
4246
+ });
4247
+ } catch {
4248
+ continue;
4249
+ }
4250
+ return configs;
4251
+ };
3767
4252
  const createResolver = (config, workspacePackages = [], options = {}) => {
3768
4253
  const resolverCache = /* @__PURE__ */ new Map();
3769
4254
  const resolveResultCache = /* @__PURE__ */ new Map();
@@ -3800,6 +4285,7 @@ const createResolver = (config, workspacePackages = [], options = {}) => {
3800
4285
  };
3801
4286
  const workspaceNameToDirectory = /* @__PURE__ */ new Map();
3802
4287
  for (const workspacePackage of workspacePackages) workspaceNameToDirectory.set(workspacePackage.name, workspacePackage.directory);
4288
+ const webpackResolverConfigs = (options.monorepoRoot && options.monorepoRoot !== config.rootDir ? [config.rootDir, options.monorepoRoot] : [config.rootDir]).flatMap(loadWebpackResolverConfigs).sort((leftConfig, rightConfig) => rightConfig.scopeDirectory.length - leftConfig.scopeDirectory.length);
3803
4289
  let rootTsconfigPath;
3804
4290
  if (config.tsConfigPath) rootTsconfigPath = (0, node_path.resolve)(config.rootDir, config.tsConfigPath);
3805
4291
  else {
@@ -3883,6 +4369,43 @@ const createResolver = (config, workspacePackages = [], options = {}) => {
3883
4369
  return false;
3884
4370
  }
3885
4371
  })();
4372
+ const packageDependencyCache = /* @__PURE__ */ new Map();
4373
+ const readPackageDependencies = (directory) => {
4374
+ const cached = packageDependencyCache.get(directory);
4375
+ if (cached) return cached;
4376
+ const dependencies = /* @__PURE__ */ new Set();
4377
+ const packageJsonPath = (0, node_path.join)(directory, "package.json");
4378
+ try {
4379
+ const packageJson = JSON.parse(cachedReadFileSync(packageJsonPath));
4380
+ const dependencySections = [
4381
+ packageJson.dependencies,
4382
+ packageJson.devDependencies,
4383
+ packageJson.optionalDependencies
4384
+ ];
4385
+ for (const dependencySection of dependencySections) {
4386
+ if (!dependencySection || typeof dependencySection !== "object") continue;
4387
+ for (const dependencyName of Object.keys(dependencySection)) dependencies.add(dependencyName);
4388
+ }
4389
+ } catch {}
4390
+ packageDependencyCache.set(directory, dependencies);
4391
+ return dependencies;
4392
+ };
4393
+ const findNearestPackageSrcDirectoryWithDependency = (filePath, dependencyNames) => {
4394
+ let currentDirectory = (0, node_path.dirname)(filePath);
4395
+ const stopAt = options.monorepoRoot ? (0, node_path.resolve)(options.monorepoRoot) : (0, node_path.resolve)(config.rootDir);
4396
+ while (currentDirectory.length >= stopAt.length) {
4397
+ if (cachedExistsSync((0, node_path.join)(currentDirectory, "package.json"))) {
4398
+ const dependencies = readPackageDependencies(currentDirectory);
4399
+ if (dependencyNames.some((dependencyName) => dependencies.has(dependencyName))) {
4400
+ const srcDirectory = (0, node_path.join)(currentDirectory, "src");
4401
+ return cachedExistsSync(srcDirectory) ? srcDirectory : void 0;
4402
+ }
4403
+ }
4404
+ const parentDirectory = (0, node_path.dirname)(currentDirectory);
4405
+ if (parentDirectory === currentDirectory) break;
4406
+ currentDirectory = parentDirectory;
4407
+ }
4408
+ };
3886
4409
  const extractPathsFromTsconfig = (tsconfigFile, visitedFiles) => {
3887
4410
  if (visitedFiles.has(tsconfigFile)) return void 0;
3888
4411
  visitedFiles.add(tsconfigFile);
@@ -3959,6 +4482,28 @@ const createResolver = (config, workspacePackages = [], options = {}) => {
3959
4482
  }
3960
4483
  }
3961
4484
  };
4485
+ const tryResolveFromDirectory = (directory, specifier) => {
4486
+ const candidatePath = resolvePathWithExtensionFallback((0, node_path.resolve)(directory, specifier));
4487
+ if (existsAsFile(candidatePath)) return candidatePath;
4488
+ };
4489
+ const tryResolveViaWebpackConfig = (specifier, fromFile) => {
4490
+ if (webpackResolverConfigs.length === 0) return void 0;
4491
+ if (!isBareSpecifier(specifier)) return void 0;
4492
+ for (const webpackConfig of webpackResolverConfigs) {
4493
+ if (!isInsideDirectory(fromFile, webpackConfig.scopeDirectory)) continue;
4494
+ for (const alias of webpackConfig.aliases) {
4495
+ if (alias.isExact && specifier !== alias.name) continue;
4496
+ const suffix = specifier === alias.name ? "" : specifier.startsWith(`${alias.name}/`) ? specifier.slice(alias.name.length + 1) : void 0;
4497
+ if (suffix === void 0) continue;
4498
+ const aliasCandidate = tryResolveFromDirectory(alias.targetDirectory, suffix);
4499
+ if (aliasCandidate) return aliasCandidate;
4500
+ }
4501
+ for (const moduleDirectory of webpackConfig.moduleDirectories) {
4502
+ const moduleCandidate = tryResolveFromDirectory(moduleDirectory, specifier);
4503
+ if (moduleCandidate) return moduleCandidate;
4504
+ }
4505
+ }
4506
+ };
3962
4507
  const resolveModule = (specifier, fromFile) => {
3963
4508
  const queryIndex = specifier.indexOf("?");
3964
4509
  const cleanedSpecifier = queryIndex !== -1 ? specifier.slice(0, queryIndex) : specifier;
@@ -4122,6 +4667,16 @@ const createResolver = (config, workspacePackages = [], options = {}) => {
4122
4667
  resolveResultCache.set(cacheKey, resolvedResult);
4123
4668
  return resolvedResult;
4124
4669
  }
4670
+ const webpackResolved = tryResolveViaWebpackConfig(cleanedSpecifier, fromFile);
4671
+ if (webpackResolved) {
4672
+ const resolvedResult = {
4673
+ resolvedPath: webpackResolved,
4674
+ isExternal: false,
4675
+ packageName: void 0
4676
+ };
4677
+ resolveResultCache.set(cacheKey, resolvedResult);
4678
+ return resolvedResult;
4679
+ }
4125
4680
  if (isBareSpecifier(cleanedSpecifier)) {
4126
4681
  const tsconfigFile = findTsconfigForFile(fromFile);
4127
4682
  if (tsconfigFile) {
@@ -4155,6 +4710,19 @@ const createResolver = (config, workspacePackages = [], options = {}) => {
4155
4710
  }
4156
4711
  }
4157
4712
  }
4713
+ const createReactAppSrcDirectory = findNearestPackageSrcDirectoryWithDependency(fromFile, ["react-scripts", "react-app-rewired"]);
4714
+ if (createReactAppSrcDirectory) {
4715
+ const craResolved = tryResolveFromDirectory(createReactAppSrcDirectory, cleanedSpecifier);
4716
+ if (craResolved) {
4717
+ const resolvedResult = {
4718
+ resolvedPath: craResolved,
4719
+ isExternal: false,
4720
+ packageName: void 0
4721
+ };
4722
+ resolveResultCache.set(cacheKey, resolvedResult);
4723
+ return resolvedResult;
4724
+ }
4725
+ }
4158
4726
  const resolvedResult = {
4159
4727
  resolvedPath: void 0,
4160
4728
  isExternal: true,
@@ -4618,12 +5186,16 @@ const detectDeadExports = (graph, config) => {
4618
5186
  if (!module.isReachable) continue;
4619
5187
  if (module.isDeclarationFile) continue;
4620
5188
  if (module.isEntryPoint && !config.includeEntryExports) continue;
5189
+ const defaultExportLinkedNames = /* @__PURE__ */ new Set();
5190
+ for (const exportInfo of module.exports) if (exportInfo.isDefault && exportInfo.defaultExportLocalName && usageMap.has(`${module.fileId.path}::default`)) defaultExportLinkedNames.add(exportInfo.defaultExportLocalName);
4621
5191
  for (const exportInfo of module.exports) {
4622
5192
  if (exportInfo.name === "*" && exportInfo.isNamespaceReExport) continue;
4623
5193
  if (exportInfo.isReExport && exportInfo.reExportOriginalName) continue;
4624
5194
  if (!config.reportTypes && exportInfo.isTypeOnly) continue;
4625
5195
  const usageKey = `${module.fileId.path}::${exportInfo.name}`;
4626
- if (!usageMap.has(usageKey)) unusedExports.push({
5196
+ if (usageMap.has(usageKey)) continue;
5197
+ if (!exportInfo.isDefault && defaultExportLinkedNames.has(exportInfo.name)) continue;
5198
+ unusedExports.push({
4627
5199
  path: module.fileId.path,
4628
5200
  name: exportInfo.name,
4629
5201
  line: exportInfo.line,
@@ -4739,61 +5311,134 @@ const followReExportChain = (reExporterModuleIndex, exportInfo, graph, sourceToT
4739
5311
  //#endregion
4740
5312
  //#region src/utils/package-name.ts
4741
5313
  const extractPackageName = (specifier) => {
4742
- if (specifier.startsWith(".") || specifier.startsWith("/")) return void 0;
5314
+ const normalizedSpecifier = specifier.startsWith("~") ? specifier.slice(1) : specifier;
5315
+ if (normalizedSpecifier.startsWith(".") || normalizedSpecifier.startsWith("/")) return void 0;
4743
5316
  if (specifier.startsWith("node:")) return void 0;
4744
- if (specifier.startsWith("@")) {
4745
- const parts = specifier.split("/");
5317
+ if (normalizedSpecifier.startsWith("@")) {
5318
+ const parts = normalizedSpecifier.split("/");
4746
5319
  return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : void 0;
4747
5320
  }
4748
- return specifier.split("/")[0];
5321
+ return normalizedSpecifier.split("/")[0];
4749
5322
  };
4750
5323
 
4751
5324
  //#endregion
4752
- //#region src/utils/find-monorepo-root.ts
4753
- const MONOREPO_ROOT_MARKERS = [
4754
- "pnpm-workspace.yaml",
4755
- "pnpm-workspace.yml",
4756
- "lerna.json",
4757
- "nx.json",
4758
- "turbo.json",
4759
- "rush.json"
4760
- ];
4761
- const LOCKFILE_MARKERS = [
4762
- "pnpm-lock.yaml",
4763
- "yarn.lock",
4764
- "package-lock.json",
4765
- "bun.lockb",
4766
- "bun.lock"
4767
- ];
4768
- const MAX_MONOREPO_WALK_DEPTH = 5;
4769
- const findMonorepoRoot = (rootDir) => {
4770
- let currentDirectory = (0, node_path.resolve)(rootDir);
4771
- let walkedDepth = 0;
4772
- while (walkedDepth < MAX_MONOREPO_WALK_DEPTH) {
4773
- const parentDirectory = (0, node_path.dirname)(currentDirectory);
4774
- if (parentDirectory === currentDirectory) break;
4775
- currentDirectory = parentDirectory;
4776
- walkedDepth++;
4777
- if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, ".git"))) {
4778
- for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
4779
- const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
4780
- if ((0, node_fs.existsSync)(packageJsonPath)) try {
4781
- const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
4782
- if (JSON.parse(content).workspaces) return currentDirectory;
4783
- } catch {}
4784
- for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
4785
- return;
5325
+ //#region src/utils/extract-override-target.ts
5326
+ const extractOverrideTargetPackage = (overrideValue) => {
5327
+ let normalizedValue = overrideValue.trim().replace(/^["']|["']$/g, "");
5328
+ if (normalizedValue.startsWith("npm:")) normalizedValue = normalizedValue.slice(4);
5329
+ if (normalizedValue.startsWith("@")) {
5330
+ const slashIndex = normalizedValue.indexOf("/");
5331
+ if (slashIndex === -1) return void 0;
5332
+ const scope = normalizedValue.slice(0, slashIndex);
5333
+ const remainder = normalizedValue.slice(slashIndex + 1);
5334
+ const versionSeparatorIndex = remainder.indexOf("@");
5335
+ const packageName = versionSeparatorIndex === -1 ? remainder : remainder.slice(0, versionSeparatorIndex);
5336
+ if (!packageName) return void 0;
5337
+ return `${scope}/${packageName}`;
5338
+ }
5339
+ const versionSeparatorIndex = normalizedValue.indexOf("@");
5340
+ return (versionSeparatorIndex === -1 ? normalizedValue : normalizedValue.slice(0, versionSeparatorIndex)) || void 0;
5341
+ };
5342
+
5343
+ //#endregion
5344
+ //#region src/utils/collect-override-mappings-from-record.ts
5345
+ const collectOverrideMappingsFromUnknown = (fromPackage, overrideValue, mappings) => {
5346
+ if (typeof overrideValue === "string") {
5347
+ const toPackage = extractOverrideTargetPackage(overrideValue);
5348
+ if (!toPackage) return;
5349
+ mappings.push({
5350
+ fromPackage,
5351
+ toPackage
5352
+ });
5353
+ return;
5354
+ }
5355
+ if (!overrideValue || typeof overrideValue !== "object" || Array.isArray(overrideValue)) return;
5356
+ for (const [nestedFromPackage, nestedValue] of Object.entries(overrideValue)) collectOverrideMappingsFromUnknown(nestedFromPackage, nestedValue, mappings);
5357
+ };
5358
+ const collectOverrideMappingsFromRecord = (overrideRecord) => {
5359
+ const mappings = [];
5360
+ for (const [fromPackage, overrideValue] of Object.entries(overrideRecord)) collectOverrideMappingsFromUnknown(fromPackage, overrideValue, mappings);
5361
+ return mappings;
5362
+ };
5363
+
5364
+ //#endregion
5365
+ //#region src/utils/parse-pnpm-workspace-overrides.ts
5366
+ const PNPM_WORKSPACE_FILENAMES = ["pnpm-workspace.yaml", "pnpm-workspace.yml"];
5367
+ const parseIndentedYamlMapping = (lines, startLineIndex, sectionIndent) => {
5368
+ const entries = {};
5369
+ let lineIndex = startLineIndex;
5370
+ while (lineIndex < lines.length) {
5371
+ const line = lines[lineIndex];
5372
+ const trimmedLine = line.trim();
5373
+ if (trimmedLine.length === 0 || trimmedLine.startsWith("#")) {
5374
+ lineIndex++;
5375
+ continue;
4786
5376
  }
4787
- for (const marker of MONOREPO_ROOT_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, marker))) return currentDirectory;
4788
- const packageJsonPath = (0, node_path.join)(currentDirectory, "package.json");
4789
- if ((0, node_fs.existsSync)(packageJsonPath)) try {
4790
- const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
4791
- if (JSON.parse(content).workspaces) return currentDirectory;
5377
+ const indent = line.length - line.trimStart().length;
5378
+ if (indent <= sectionIndent) break;
5379
+ const colonIndex = trimmedLine.indexOf(":");
5380
+ if (colonIndex === -1) {
5381
+ lineIndex++;
5382
+ continue;
5383
+ }
5384
+ const key = trimmedLine.slice(0, colonIndex).trim().replace(/^["']|["']$/g, "");
5385
+ const rawValue = trimmedLine.slice(colonIndex + 1).trim();
5386
+ if (!key) {
5387
+ lineIndex++;
5388
+ continue;
5389
+ }
5390
+ if (rawValue.length === 0) {
5391
+ const nestedMapping = parseIndentedYamlMapping(lines, lineIndex + 1, indent);
5392
+ entries[key] = nestedMapping.entries;
5393
+ lineIndex = nestedMapping.endLineIndex;
5394
+ continue;
5395
+ }
5396
+ entries[key] = rawValue.replace(/^["']|["']$/g, "");
5397
+ lineIndex++;
5398
+ }
5399
+ return {
5400
+ entries,
5401
+ endLineIndex: lineIndex
5402
+ };
5403
+ };
5404
+ const parsePnpmWorkspaceOverrideRecords = (yamlContent) => {
5405
+ const lines = yamlContent.split("\n");
5406
+ const overrideRecords = [];
5407
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
5408
+ if (lines[lineIndex].trim() !== "overrides:") continue;
5409
+ const sectionIndent = lines[lineIndex].length - lines[lineIndex].trimStart().length;
5410
+ const parsedMapping = parseIndentedYamlMapping(lines, lineIndex + 1, sectionIndent);
5411
+ if (Object.keys(parsedMapping.entries).length > 0) overrideRecords.push(parsedMapping.entries);
5412
+ }
5413
+ return overrideRecords;
5414
+ };
5415
+ const collectPnpmWorkspaceOverrideMappings = (rootDir) => {
5416
+ const mappings = [];
5417
+ for (const workspaceFilename of PNPM_WORKSPACE_FILENAMES) {
5418
+ const workspacePath = (0, node_path.join)(rootDir, workspaceFilename);
5419
+ if (!(0, node_fs.existsSync)(workspacePath)) continue;
5420
+ try {
5421
+ const overrideRecords = parsePnpmWorkspaceOverrideRecords((0, node_fs.readFileSync)(workspacePath, "utf-8"));
5422
+ for (const overrideRecord of overrideRecords) mappings.push(...collectOverrideMappingsFromRecord(overrideRecord));
4792
5423
  } catch {
4793
5424
  continue;
4794
5425
  }
4795
- for (const lockfile of LOCKFILE_MARKERS) if ((0, node_fs.existsSync)((0, node_path.join)(currentDirectory, lockfile))) return currentDirectory;
4796
5426
  }
5427
+ return mappings;
5428
+ };
5429
+
5430
+ //#endregion
5431
+ //#region src/utils/matches-package-import-reference.ts
5432
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5433
+ const matchesPackageImportReference = (content, packageName) => {
5434
+ const escapedPackageName = escapeRegExp(packageName);
5435
+ const subpathPattern = `(?:/[^'"]*)?`;
5436
+ return [
5437
+ new RegExp(`\\bfrom\\s+['"]${escapedPackageName}${subpathPattern}['"]`),
5438
+ new RegExp(`\\bimport\\s+(?:[^'";\\n]*?\\sfrom\\s+)?['"]${escapedPackageName}${subpathPattern}['"]`),
5439
+ new RegExp(`\\brequire\\s*\\(\\s*['"]${escapedPackageName}${subpathPattern}['"]\\s*\\)`),
5440
+ new RegExp(`\\bimport\\s*\\(\\s*['"]${escapedPackageName}${subpathPattern}['"]`)
5441
+ ].some((pattern) => pattern.test(content));
4797
5442
  };
4798
5443
 
4799
5444
  //#endregion
@@ -4845,6 +5490,8 @@ const detectStalePackages = (graph, config) => {
4845
5490
  const packageJsonConfigReferenced = collectPackageJsonConfigReferences(workspacePackageJsonPath, declaredNames);
4846
5491
  for (const packageName of packageJsonConfigReferenced) usedPackageNames.add(packageName);
4847
5492
  }
5493
+ const nxProjectReferenced = collectNxProjectJsonReferences(config.rootDir, declaredNames, binToPackage);
5494
+ for (const packageName of nxProjectReferenced) usedPackageNames.add(packageName);
4848
5495
  const configSearchRoots = monorepoRoot && monorepoRoot !== config.rootDir ? [config.rootDir, monorepoRoot] : [config.rootDir];
4849
5496
  for (const configSearchRoot of configSearchRoots) {
4850
5497
  const configReferenced = collectConfigReferencedPackages(configSearchRoot, graph, declaredNames);
@@ -4884,6 +5531,15 @@ const detectStalePackages = (graph, config) => {
4884
5531
  }
4885
5532
  const peerSatisfied = collectPeerSatisfiedPackages(nodeModulesRoot, declaredNames, usedPackageNames);
4886
5533
  for (const packageName of peerSatisfied) usedPackageNames.add(packageName);
5534
+ const staticPeerSatisfied = collectStaticPeerSatisfiedPackages(declaredNames, usedPackageNames);
5535
+ for (const packageName of staticPeerSatisfied) usedPackageNames.add(packageName);
5536
+ const implicitCompanionPackages = collectImplicitCompanionPackages(declaredNames, usedPackageNames);
5537
+ for (const packageName of implicitCompanionPackages) usedPackageNames.add(packageName);
5538
+ const overrideMappings = collectOverrideMappings(configSearchRoots, allPackageJsonPaths, monorepoRoot);
5539
+ for (const { fromPackage, toPackage } of overrideMappings) {
5540
+ if (declaredNames.has(toPackage)) usedPackageNames.add(toPackage);
5541
+ if (usedPackageNames.has(fromPackage) && declaredNames.has(toPackage)) usedPackageNames.add(toPackage);
5542
+ }
4887
5543
  const candidateUnused = /* @__PURE__ */ new Set();
4888
5544
  for (const [dependencyName] of declaredDependencies) {
4889
5545
  if (isAlwaysConsideredUsed(dependencyName)) continue;
@@ -4937,11 +5593,76 @@ const collectPeerSatisfiedPackages = (rootDir, declaredNames, confirmedUsedNames
4937
5593
  }
4938
5594
  return peerSatisfied;
4939
5595
  };
5596
+ const STATIC_PEER_DEPENDENCY_MAP = {
5597
+ "@apollo/client": ["graphql"],
5598
+ "@docusaurus/core": ["@mdx-js/react"],
5599
+ "@fortawesome/react-fontawesome": ["@fortawesome/fontawesome-svg-core"],
5600
+ "@gorhom/bottom-sheet": ["react-native-gesture-handler", "react-native-reanimated"],
5601
+ "@hookform/resolvers": ["zod"],
5602
+ "@mdx-js/loader": ["@mdx-js/react"],
5603
+ "@mui/material": ["react-transition-group", "styled-components"],
5604
+ "@stripe/react-stripe-js": ["@stripe/stripe-js"],
5605
+ "@tiptap/core": ["@tiptap/pm"],
5606
+ "@tiptap/react": ["@tiptap/pm"],
5607
+ "@trpc/server": ["zod"],
5608
+ "chart.js": [],
5609
+ "fumadocs-core": ["@mdx-js/react"],
5610
+ "fumadocs-mdx": ["@mdx-js/react"],
5611
+ "fumadocs-ui": ["@mdx-js/react"],
5612
+ "graphql-request": ["graphql"],
5613
+ nextra: ["@mdx-js/react"],
5614
+ "nextra-theme-blog": ["@mdx-js/react"],
5615
+ "nextra-theme-docs": ["@mdx-js/react"],
5616
+ "react-app-polyfill": ["core-js"],
5617
+ "react-bootstrap": ["react-transition-group"],
5618
+ "react-chartjs-2": ["chart.js"],
5619
+ "react-redux": ["redux"],
5620
+ "react-router-dom": ["react-router"],
5621
+ "redux-thunk": ["redux"],
5622
+ sanity: ["styled-components"],
5623
+ sequelize: ["pg"],
5624
+ "stylis-plugin-rtl": ["stylis"],
5625
+ urql: ["graphql"],
5626
+ "use-immer": ["immer"],
5627
+ zustand: ["immer"]
5628
+ };
5629
+ const collectStaticPeerSatisfiedPackages = (declaredNames, confirmedUsedNames) => {
5630
+ const peerSatisfied = /* @__PURE__ */ new Set();
5631
+ for (const [packageName, peerNames] of Object.entries(STATIC_PEER_DEPENDENCY_MAP)) {
5632
+ if (!confirmedUsedNames.has(packageName)) continue;
5633
+ for (const peerName of peerNames) if (declaredNames.has(peerName)) peerSatisfied.add(peerName);
5634
+ }
5635
+ return peerSatisfied;
5636
+ };
5637
+ const IMPLICIT_COMPANION_DEPENDENCY_MAP = {
5638
+ jest: ["jest-config"],
5639
+ "jest-cli": ["jest-config"],
5640
+ "vite-plus": ["@voidzero-dev/vite-plus-core"]
5641
+ };
5642
+ const collectImplicitCompanionPackages = (declaredNames, confirmedUsedNames) => {
5643
+ const companions = /* @__PURE__ */ new Set();
5644
+ for (const [packageName, companionNames] of Object.entries(IMPLICIT_COMPANION_DEPENDENCY_MAP)) {
5645
+ if (!confirmedUsedNames.has(packageName)) continue;
5646
+ for (const companionName of companionNames) if (declaredNames.has(companionName)) companions.add(companionName);
5647
+ }
5648
+ return companions;
5649
+ };
4940
5650
  const SHELL_SPLIT_PATTERN = /\s*(?:&&|\|\||[;&|])\s*/;
4941
5651
  const CLI_BINARY_TO_PACKAGE = {
5652
+ "babel-node": "@babel/node",
5653
+ "trigger.dev": "trigger.dev",
5654
+ "@formatjs/cli": "@formatjs/cli",
4942
5655
  "react-scripts": "react-scripts",
4943
5656
  "webpack-cli": "webpack-cli",
4944
5657
  "webpack-dev-server": "webpack-dev-server",
5658
+ babel: "@babel/cli",
5659
+ chokidar: "chokidar-cli",
5660
+ "replace-in-file": "replace-in-file",
5661
+ tauri: "@tauri-apps/cli",
5662
+ tinacms: "@tinacms/cli",
5663
+ "tsc-alias": "tsc-alias",
5664
+ formatjs: "@formatjs/cli",
5665
+ prompt: "prompt",
4945
5666
  vitest: "vitest",
4946
5667
  jest: "jest",
4947
5668
  prisma: "prisma",
@@ -4957,7 +5678,15 @@ const CLI_BINARY_TO_PACKAGE = {
4957
5678
  "rndebugger-open": "react-native-debugger-open",
4958
5679
  "simple-git-hooks": "simple-git-hooks",
4959
5680
  "generate-arg-types": "@webstudio-is/generate-arg-types",
4960
- email: "@react-email/preview-server"
5681
+ email: "@react-email/preview-server",
5682
+ vp: "vite-plus",
5683
+ turbo: "turbo",
5684
+ changeset: "@changesets/cli",
5685
+ tsx: "tsx"
5686
+ };
5687
+ const CLI_BINARY_FALLBACK_PACKAGES = {
5688
+ babel: ["babel-cli"],
5689
+ jest: ["jest-cli"]
4961
5690
  };
4962
5691
  const ENV_WRAPPER_BINARY_SET = new Set([
4963
5692
  "cross-env",
@@ -4990,36 +5719,43 @@ const collectScriptReferencedPackages = (packageJsonPath, declaredNames, binToPa
4990
5719
  if (!scripts || typeof scripts !== "object") return referenced;
4991
5720
  for (const scriptCommand of Object.values(scripts)) {
4992
5721
  if (typeof scriptCommand !== "string") continue;
4993
- const segments = scriptCommand.split(SHELL_SPLIT_PATTERN);
4994
- for (const segment of segments) {
4995
- const tokens = segment.trim().split(/\s+/);
4996
- if (tokens.length === 0) continue;
4997
- let binaryIndex = 0;
4998
- const firstToken = tokens[0].replace(/^.*\//, "");
4999
- if (ENV_WRAPPER_BINARY_SET.has(firstToken)) {
5000
- const envPackage = binToPackage.get(firstToken);
5001
- if (envPackage && declaredNames.has(envPackage)) referenced.add(envPackage);
5002
- binaryIndex = 1;
5003
- while (binaryIndex < tokens.length && INLINE_ENV_VAR_PATTERN.test(tokens[binaryIndex])) binaryIndex++;
5004
- if (binaryIndex >= tokens.length) continue;
5005
- }
5006
- while (binaryIndex < tokens.length && INLINE_ENV_VAR_PATTERN.test(tokens[binaryIndex])) binaryIndex++;
5007
- if (binaryIndex >= tokens.length) continue;
5008
- const binaryToken = tokens[binaryIndex].replace(/^.*\//, "");
5009
- const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
5010
- for (const candidateBinary of [binaryToken, effectiveBinary]) {
5011
- if (!candidateBinary) continue;
5012
- const mappedPackage = binToPackage.get(candidateBinary);
5013
- if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
5014
- if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
5015
- }
5016
- }
5722
+ const commandReferenced = collectCommandReferencedPackages(scriptCommand, declaredNames, binToPackage);
5723
+ for (const packageName of commandReferenced) referenced.add(packageName);
5017
5724
  }
5018
5725
  } catch {
5019
5726
  return referenced;
5020
5727
  }
5021
5728
  return referenced;
5022
5729
  };
5730
+ const collectCommandReferencedPackages = (command, declaredNames, binToPackage) => {
5731
+ const referenced = /* @__PURE__ */ new Set();
5732
+ const segments = command.split(SHELL_SPLIT_PATTERN);
5733
+ for (const segment of segments) {
5734
+ const tokens = segment.trim().split(/\s+/);
5735
+ if (tokens.length === 0) continue;
5736
+ let binaryIndex = 0;
5737
+ const firstToken = tokens[0].replace(/^.*\//, "");
5738
+ if (ENV_WRAPPER_BINARY_SET.has(firstToken)) {
5739
+ const envPackage = binToPackage.get(firstToken);
5740
+ if (envPackage && declaredNames.has(envPackage)) referenced.add(envPackage);
5741
+ binaryIndex = 1;
5742
+ while (binaryIndex < tokens.length && INLINE_ENV_VAR_PATTERN.test(tokens[binaryIndex])) binaryIndex++;
5743
+ if (binaryIndex >= tokens.length) continue;
5744
+ }
5745
+ while (binaryIndex < tokens.length && INLINE_ENV_VAR_PATTERN.test(tokens[binaryIndex])) binaryIndex++;
5746
+ if (binaryIndex >= tokens.length) continue;
5747
+ const binaryToken = tokens[binaryIndex].replace(/^.*\//, "");
5748
+ const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
5749
+ for (const candidateBinary of [binaryToken, effectiveBinary]) {
5750
+ if (!candidateBinary) continue;
5751
+ const mappedPackage = binToPackage.get(candidateBinary);
5752
+ if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
5753
+ for (const fallbackPackage of CLI_BINARY_FALLBACK_PACKAGES[candidateBinary] ?? []) if (declaredNames.has(fallbackPackage)) referenced.add(fallbackPackage);
5754
+ if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
5755
+ }
5756
+ }
5757
+ return referenced;
5758
+ };
5023
5759
  const CONFIG_FILE_GLOBS = [
5024
5760
  "postcss.config.{js,cjs,mjs,ts}",
5025
5761
  ".babelrc",
@@ -5100,6 +5836,42 @@ const PACKAGE_JSON_CONFIG_SECTIONS = [
5100
5836
  "resolutions",
5101
5837
  "overrides"
5102
5838
  ];
5839
+ const collectOverrideMappingsFromPackageJson = (packageJsonPath) => {
5840
+ const mappings = [];
5841
+ try {
5842
+ const content = (0, node_fs.readFileSync)(packageJsonPath, "utf-8");
5843
+ const packageJson = JSON.parse(content);
5844
+ const overrideSections = [
5845
+ packageJson.overrides,
5846
+ packageJson.resolutions,
5847
+ packageJson.pnpm?.overrides
5848
+ ];
5849
+ for (const overrideSection of overrideSections) {
5850
+ if (!overrideSection || typeof overrideSection !== "object") continue;
5851
+ mappings.push(...collectOverrideMappingsFromRecord(overrideSection));
5852
+ }
5853
+ } catch {
5854
+ return mappings;
5855
+ }
5856
+ return mappings;
5857
+ };
5858
+ const collectOverrideMappings = (configSearchRoots, packageJsonPaths, monorepoRoot) => {
5859
+ const mappings = [];
5860
+ const seenMappings = /* @__PURE__ */ new Set();
5861
+ const addMappings = (nextMappings) => {
5862
+ for (const mapping of nextMappings) {
5863
+ const mappingKey = `${mapping.fromPackage}->${mapping.toPackage}`;
5864
+ if (seenMappings.has(mappingKey)) continue;
5865
+ seenMappings.add(mappingKey);
5866
+ mappings.push(mapping);
5867
+ }
5868
+ };
5869
+ for (const packageJsonPath of packageJsonPaths) addMappings(collectOverrideMappingsFromPackageJson(packageJsonPath));
5870
+ const workspaceRoots = new Set(configSearchRoots);
5871
+ if (monorepoRoot) workspaceRoots.add(monorepoRoot);
5872
+ for (const workspaceRoot of workspaceRoots) addMappings(collectPnpmWorkspaceOverrideMappings(workspaceRoot));
5873
+ return mappings;
5874
+ };
5103
5875
  const collectPackageJsonConfigReferences = (packageJsonPath, declaredNames) => {
5104
5876
  const referenced = /* @__PURE__ */ new Set();
5105
5877
  try {
@@ -5116,6 +5888,39 @@ const collectPackageJsonConfigReferences = (packageJsonPath, declaredNames) => {
5116
5888
  }
5117
5889
  return referenced;
5118
5890
  };
5891
+ const collectNxProjectJsonReferences = (rootDir, declaredNames, binToPackage) => {
5892
+ const referenced = /* @__PURE__ */ new Set();
5893
+ const projectJsonPaths = fast_glob.default.sync(["project.json", "**/project.json"], {
5894
+ cwd: rootDir,
5895
+ absolute: true,
5896
+ onlyFiles: true,
5897
+ ignore: [
5898
+ "**/node_modules/**",
5899
+ "**/dist/**",
5900
+ "**/build/**"
5901
+ ],
5902
+ deep: 5
5903
+ });
5904
+ for (const projectJsonPath of projectJsonPaths) try {
5905
+ const content = (0, node_fs.readFileSync)(projectJsonPath, "utf-8");
5906
+ const projectJson = JSON.parse(content);
5907
+ const projectText = JSON.stringify(projectJson);
5908
+ for (const packageName of declaredNames) if (projectText.includes(packageName)) referenced.add(packageName);
5909
+ for (const stringValue of collectStringValues(projectJson)) {
5910
+ const commandReferenced = collectCommandReferencedPackages(stringValue, declaredNames, binToPackage);
5911
+ for (const packageName of commandReferenced) referenced.add(packageName);
5912
+ }
5913
+ } catch {
5914
+ continue;
5915
+ }
5916
+ return referenced;
5917
+ };
5918
+ const collectStringValues = (value) => {
5919
+ if (typeof value === "string") return [value];
5920
+ if (!value || typeof value !== "object") return [];
5921
+ if (Array.isArray(value)) return value.flatMap(collectStringValues);
5922
+ return Object.values(value).flatMap(collectStringValues);
5923
+ };
5119
5924
  const TSCONFIG_GLOBS = [
5120
5925
  "tsconfig.json",
5121
5926
  "tsconfig.*.json",
@@ -5192,7 +5997,7 @@ const scanSourceFilesForPackageImports = (rootDir, candidatePackages) => {
5192
5997
  if (candidatePackages.size === 0) break;
5193
5998
  try {
5194
5999
  const content = (0, node_fs.readFileSync)(filePath, "utf-8");
5195
- for (const packageName of candidatePackages) if (content.includes(`'${packageName}'`) || content.includes(`"${packageName}"`) || content.includes(`'${packageName}/`) || content.includes(`"${packageName}/`)) {
6000
+ for (const packageName of candidatePackages) if (matchesPackageImportReference(content, packageName)) {
5196
6001
  found.add(packageName);
5197
6002
  candidatePackages.delete(packageName);
5198
6003
  }