deslop-js 0.7.1 → 0.7.3

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.
@@ -1,4 +1,4 @@
1
- import { a as OUTPUT_DIRECTORIES, c as SCRIPT_ENTRY_PATTERNS, d as SOURCE_EXTENSIONS$3, f as STANDALONE_PROJECT_LOCKFILES, i as MONOREPO_ROOT_MARKERS, l as SCRIPT_EXTENSIONLESS_FILE_PATTERN, o as RESOLVER_EXTENSIONS, r as LOCKFILE_MARKERS, s as SCRIPT_CONFIG_FILE_PATTERN, t as extractReactRouterRouteModuleEntries, u as SCRIPT_FILE_PATTERN } from "./parse-xkrZfjHl.mjs";
1
+ import { a as OUTPUT_DIRECTORIES, c as SCRIPT_ENTRY_PATTERNS, d as SOURCE_EXTENSIONS$3, f as STANDALONE_PROJECT_LOCKFILES, i as MONOREPO_ROOT_MARKERS, l as SCRIPT_EXTENSIONLESS_FILE_PATTERN, o as RESOLVER_EXTENSIONS, r as LOCKFILE_MARKERS, s as SCRIPT_CONFIG_FILE_PATTERN, t as extractReactRouterRouteModuleEntries, u as SCRIPT_FILE_PATTERN } from "./parse-CDdo_Ppt.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
  import { existsSync, readFileSync, statSync } from "node:fs";
4
4
  import fg from "fast-glob";
@@ -1916,6 +1916,13 @@ const extractCiWorkflowEntries = (rootDir) => {
1916
1916
  const entries = [];
1917
1917
  const workflowsDir = join(rootDir, ".github", "workflows");
1918
1918
  if (!existsSync(workflowsDir)) return entries;
1919
+ const nestedToolPackageJsonPaths = fg.sync("**/package.json", {
1920
+ cwd: join(rootDir, ".github"),
1921
+ absolute: true,
1922
+ onlyFiles: true,
1923
+ ignore: ["**/node_modules/**"]
1924
+ });
1925
+ for (const nestedPackageJsonPath of nestedToolPackageJsonPaths) entries.push(...extractScriptEntries(dirname(nestedPackageJsonPath)));
1919
1926
  const workflowFiles = fg.sync("*.{yml,yaml}", {
1920
1927
  cwd: workflowsDir,
1921
1928
  absolute: true,
@@ -3518,8 +3525,10 @@ const discoverTestRunnerEntryPoints = (rootDir, workspacePackages) => {
3518
3525
  customPatterns = extractJestTestMatchPatterns(directory);
3519
3526
  if (customPatterns.length === 0 && monorepoRoot) customPatterns = extractJestTestMatchPatterns(monorepoRoot);
3520
3527
  }
3521
- if (customPatterns.length > 0) activatedPatterns.push(...customPatterns);
3522
- else activatedPatterns.push(...runner.entryPatterns);
3528
+ if (customPatterns.length > 0) {
3529
+ activatedPatterns.push(...customPatterns);
3530
+ if (isJestRunner) activatedPatterns.push("**/__mocks__/**/*.{ts,tsx,js,jsx,mjs,cjs}");
3531
+ } else activatedPatterns.push(...runner.entryPatterns);
3523
3532
  activatedFixturePatterns.push(...runner.fixturePatterns);
3524
3533
  activatedAlwaysUsed.push(...runner.alwaysUsed);
3525
3534
  }
package/dist/index.cjs CHANGED
@@ -1355,6 +1355,7 @@ const parseCssImports = (filePath) => {
1355
1355
  memberAccesses: [],
1356
1356
  wholeObjectUses: [],
1357
1357
  localIdentifierReferences: [],
1358
+ topLevelImportReferences: [],
1358
1359
  referencedFilenames: [],
1359
1360
  redundantTypePatterns: [],
1360
1361
  identityWrappers: [],
@@ -1383,18 +1384,142 @@ const collectLocalIdentifierReferences = (statements) => {
1383
1384
  for (const value of Object.values(record)) if (Array.isArray(value)) for (const innerValue of value) visitNode(innerValue);
1384
1385
  else if (value && typeof value === "object") visitNode(value);
1385
1386
  };
1387
+ const visitExportedDeclarationValues = (declaration) => {
1388
+ if (!declaration || typeof declaration !== "object") return;
1389
+ const record = declaration;
1390
+ if (record.type === "VariableDeclaration" && Array.isArray(record.declarations)) {
1391
+ for (const declarator of record.declarations) if (declarator && typeof declarator === "object") visitNode(declarator.init);
1392
+ return;
1393
+ }
1394
+ if (record.type === "FunctionDeclaration" || record.type === "ClassDeclaration") {
1395
+ visitNode(record.params);
1396
+ visitNode(record.superClass);
1397
+ visitNode(record.body);
1398
+ return;
1399
+ }
1400
+ if (typeof record.type === "string" && !record.type.startsWith("TS")) visitNode(declaration);
1401
+ };
1386
1402
  for (const statement of statements) {
1387
- if (statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") continue;
1403
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1404
+ if (statement.type === "ExportNamedDeclaration") {
1405
+ visitExportedDeclarationValues(statement.declaration);
1406
+ continue;
1407
+ }
1408
+ if (statement.type === "ExportDefaultDeclaration") {
1409
+ visitExportedDeclarationValues(statement.declaration);
1410
+ continue;
1411
+ }
1388
1412
  visitNode(statement);
1389
1413
  }
1390
1414
  return references;
1391
1415
  };
1416
+ const TS_VALUE_WRAPPER_NODE_TYPES = new Set([
1417
+ "TSAsExpression",
1418
+ "TSSatisfiesExpression",
1419
+ "TSNonNullExpression",
1420
+ "TSInstantiationExpression",
1421
+ "TSTypeAssertion"
1422
+ ]);
1423
+ const TS_RUNTIME_DECLARATION_NODE_TYPES = new Set([
1424
+ "TSEnumDeclaration",
1425
+ "TSModuleDeclaration",
1426
+ "TSExportAssignment"
1427
+ ]);
1428
+ const FUNCTION_NODE_TYPES = new Set([
1429
+ "FunctionDeclaration",
1430
+ "FunctionExpression",
1431
+ "ArrowFunctionExpression"
1432
+ ]);
1433
+ const collectStaticImportLocalNames = (imports) => {
1434
+ const localNames = /* @__PURE__ */ new Set();
1435
+ for (const importInfo of imports) {
1436
+ if (importInfo.isDynamic || importInfo.isTypeOnly) continue;
1437
+ for (const binding of importInfo.importedNames) {
1438
+ if (binding.isTypeOnly) continue;
1439
+ const localName = binding.alias ?? binding.name;
1440
+ if (localName && localName !== "*") localNames.add(localName);
1441
+ }
1442
+ }
1443
+ return localNames;
1444
+ };
1445
+ const collectTopLevelImportReferences = (bodyNodes, importLocalNames) => {
1446
+ const referencedNames = /* @__PURE__ */ new Set();
1447
+ if (importLocalNames.size === 0) return [];
1448
+ const visitClassBody = (classBody) => {
1449
+ const bodyElements = classBody.body ?? [];
1450
+ for (const element of bodyElements) {
1451
+ if (element.type === "StaticBlock") {
1452
+ visitValueNode(element.body);
1453
+ continue;
1454
+ }
1455
+ if (Boolean(element.computed)) visitValueNode(element.key);
1456
+ const isStatic = Boolean(element.static);
1457
+ if (element.type === "PropertyDefinition" && isStatic) visitValueNode(element.value);
1458
+ visitValueNode(element.decorators);
1459
+ }
1460
+ };
1461
+ const visitValueNode = (node) => {
1462
+ if (Array.isArray(node)) {
1463
+ for (const element of node) visitValueNode(element);
1464
+ return;
1465
+ }
1466
+ if (!isWalkableNode(node)) return;
1467
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") {
1468
+ const identifierName = node.name;
1469
+ if (identifierName && importLocalNames.has(identifierName)) referencedNames.add(identifierName);
1470
+ return;
1471
+ }
1472
+ if (node.type.startsWith("TS")) {
1473
+ if (TS_VALUE_WRAPPER_NODE_TYPES.has(node.type)) {
1474
+ visitValueNode(node.expression);
1475
+ return;
1476
+ }
1477
+ if (!TS_RUNTIME_DECLARATION_NODE_TYPES.has(node.type)) return;
1478
+ }
1479
+ if (FUNCTION_NODE_TYPES.has(node.type)) return;
1480
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
1481
+ visitValueNode(node.superClass);
1482
+ visitValueNode(node.decorators);
1483
+ const classBody = node.body;
1484
+ if (classBody) visitClassBody(classBody);
1485
+ return;
1486
+ }
1487
+ if (node.type === "CallExpression" || node.type === "NewExpression") {
1488
+ const callee = node.callee;
1489
+ if (callee && FUNCTION_NODE_TYPES.has(callee.type)) visitValueNode(callee.body);
1490
+ }
1491
+ if (node.type === "MemberExpression" || node.type === "JSXMemberExpression") {
1492
+ const memberNode = node;
1493
+ visitValueNode(memberNode.object);
1494
+ if (node.computed) visitValueNode(memberNode.property);
1495
+ return;
1496
+ }
1497
+ if (node.type === "Property") {
1498
+ const propertyNode = node;
1499
+ if (node.computed) visitValueNode(propertyNode.key);
1500
+ visitValueNode(propertyNode.value);
1501
+ return;
1502
+ }
1503
+ for (const value of Object.values(node)) if (Array.isArray(value)) for (const element of value) visitValueNode(element);
1504
+ else if (value && typeof value === "object") visitValueNode(value);
1505
+ };
1506
+ for (const statement of bodyNodes) {
1507
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1508
+ if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration") {
1509
+ visitValueNode(statement.declaration);
1510
+ continue;
1511
+ }
1512
+ visitValueNode(statement);
1513
+ }
1514
+ return [...referencedNames];
1515
+ };
1392
1516
  const createEmptyParsedSource = () => ({
1393
1517
  imports: [],
1394
1518
  exports: [],
1395
1519
  memberAccesses: [],
1396
1520
  wholeObjectUses: [],
1397
1521
  localIdentifierReferences: [],
1522
+ topLevelImportReferences: [],
1398
1523
  referencedFilenames: [],
1399
1524
  redundantTypePatterns: [],
1400
1525
  identityWrappers: [],
@@ -1606,6 +1731,7 @@ const parseSourceFile = (filePath) => {
1606
1731
  collectMemberAccesses(program.body, namespaceLocalNames, memberAccesses, wholeObjectUses);
1607
1732
  }, void 0);
1608
1733
  const localIdentifierReferences = safeWalk("collectLocalIdentifierReferences", () => collectLocalIdentifierReferences(program.body), []);
1734
+ const topLevelImportReferences = safeWalk("collectTopLevelImportReferences", () => collectTopLevelImportReferences(program.body, collectStaticImportLocalNames(imports)), []);
1609
1735
  const redundantTypePatterns = [];
1610
1736
  const identityWrappers = [];
1611
1737
  const typeDefinitionHashes = [];
@@ -1650,6 +1776,7 @@ const parseSourceFile = (filePath) => {
1650
1776
  memberAccesses,
1651
1777
  wholeObjectUses,
1652
1778
  localIdentifierReferences,
1779
+ topLevelImportReferences,
1653
1780
  referencedFilenames: extractReferencedFilenames(sourceText),
1654
1781
  redundantTypePatterns,
1655
1782
  identityWrappers,
@@ -1790,6 +1917,29 @@ const collectMemberAccesses = (bodyNodes, namespaceLocalNames, memberAccesses, w
1790
1917
  const spreadArgument = node.argument;
1791
1918
  if (spreadArgument?.type === "Identifier" && namespaceLocalNames.has(spreadArgument.name)) wholeObjectUses.push(spreadArgument.name);
1792
1919
  }
1920
+ if (node.type === "VariableDeclarator") {
1921
+ const declarator = node;
1922
+ if (declarator.init?.type === "Identifier" && namespaceLocalNames.has(declarator.init.name) && declarator.id?.type === "ObjectPattern") {
1923
+ const namespaceName = declarator.init.name;
1924
+ const patternProperties = declarator.id.properties;
1925
+ for (const property of patternProperties) {
1926
+ if (property.type === "RestElement") {
1927
+ wholeObjectUses.push(namespaceName);
1928
+ continue;
1929
+ }
1930
+ const propertyKey = property.key;
1931
+ if (Boolean(property.computed)) wholeObjectUses.push(namespaceName);
1932
+ else if (propertyKey?.type === "Identifier" && propertyKey.name) memberAccesses.push({
1933
+ objectName: namespaceName,
1934
+ memberName: propertyKey.name
1935
+ });
1936
+ else if (propertyKey?.type === "Literal" && typeof propertyKey.value === "string") memberAccesses.push({
1937
+ objectName: namespaceName,
1938
+ memberName: propertyKey.value
1939
+ });
1940
+ }
1941
+ }
1942
+ }
1793
1943
  if (node.type === "ForInStatement") {
1794
1944
  const forInRight = node.right;
1795
1945
  if (forInRight?.type === "Identifier" && namespaceLocalNames.has(forInRight.name)) wholeObjectUses.push(forInRight.name);
@@ -5123,6 +5273,13 @@ const extractCiWorkflowEntries = (rootDir) => {
5123
5273
  const entries = [];
5124
5274
  const workflowsDir = (0, node_path.join)(rootDir, ".github", "workflows");
5125
5275
  if (!(0, node_fs.existsSync)(workflowsDir)) return entries;
5276
+ const nestedToolPackageJsonPaths = fast_glob.default.sync("**/package.json", {
5277
+ cwd: (0, node_path.join)(rootDir, ".github"),
5278
+ absolute: true,
5279
+ onlyFiles: true,
5280
+ ignore: ["**/node_modules/**"]
5281
+ });
5282
+ for (const nestedPackageJsonPath of nestedToolPackageJsonPaths) entries.push(...extractScriptEntries((0, node_path.dirname)(nestedPackageJsonPath)));
5126
5283
  const workflowFiles = fast_glob.default.sync("*.{yml,yaml}", {
5127
5284
  cwd: workflowsDir,
5128
5285
  absolute: true,
@@ -6725,8 +6882,10 @@ const discoverTestRunnerEntryPoints = (rootDir, workspacePackages) => {
6725
6882
  customPatterns = extractJestTestMatchPatterns(directory);
6726
6883
  if (customPatterns.length === 0 && monorepoRoot) customPatterns = extractJestTestMatchPatterns(monorepoRoot);
6727
6884
  }
6728
- if (customPatterns.length > 0) activatedPatterns.push(...customPatterns);
6729
- else activatedPatterns.push(...runner.entryPatterns);
6885
+ if (customPatterns.length > 0) {
6886
+ activatedPatterns.push(...customPatterns);
6887
+ if (isJestRunner) activatedPatterns.push("**/__mocks__/**/*.{ts,tsx,js,jsx,mjs,cjs}");
6888
+ } else activatedPatterns.push(...runner.entryPatterns);
6730
6889
  activatedFixturePatterns.push(...runner.fixturePatterns);
6731
6890
  activatedAlwaysUsed.push(...runner.alwaysUsed);
6732
6891
  }
@@ -6957,6 +7116,7 @@ const deserializeParsedSource = (serialized) => ({
6957
7116
  memberAccesses: serialized.memberAccesses,
6958
7117
  wholeObjectUses: serialized.wholeObjectUses,
6959
7118
  localIdentifierReferences: serialized.localIdentifierReferences,
7119
+ topLevelImportReferences: serialized.topLevelImportReferences,
6960
7120
  referencedFilenames: serialized.referencedFilenames,
6961
7121
  redundantTypePatterns: serialized.redundantTypePatterns,
6962
7122
  identityWrappers: serialized.identityWrappers,
@@ -7021,6 +7181,7 @@ const parseFilesWithWorkerPool = async (files, workerCount) => {
7021
7181
  memberAccesses: [],
7022
7182
  wholeObjectUses: [],
7023
7183
  localIdentifierReferences: [],
7184
+ topLevelImportReferences: [],
7024
7185
  referencedFilenames: [],
7025
7186
  redundantTypePatterns: [],
7026
7187
  identityWrappers: [],
@@ -7095,6 +7256,7 @@ const buildDependencyGraph = (inputs) => {
7095
7256
  memberAccesses: input.parsed.memberAccesses,
7096
7257
  wholeObjectUses: input.parsed.wholeObjectUses,
7097
7258
  localIdentifierReferences: input.parsed.localIdentifierReferences,
7259
+ topLevelImportReferences: input.parsed.topLevelImportReferences,
7098
7260
  referencedFilenames: input.parsed.referencedFilenames,
7099
7261
  redundantTypePatterns: input.parsed.redundantTypePatterns,
7100
7262
  identityWrappers: input.parsed.identityWrappers,
@@ -7113,12 +7275,13 @@ const buildDependencyGraph = (inputs) => {
7113
7275
  }));
7114
7276
  const edges = [];
7115
7277
  const reverseEdges = /* @__PURE__ */ new Map();
7116
- const addEdge = (sourceIndex, targetIndex, symbols, isReExportEdge = false, reExportedNames = [], reExportMappings = []) => {
7278
+ const addEdge = (sourceIndex, targetIndex, symbols, isReExportEdge = false, reExportedNames = [], reExportMappings = [], isDynamic = false) => {
7117
7279
  edges.push({
7118
7280
  source: sourceIndex,
7119
7281
  target: targetIndex,
7120
7282
  importedSymbols: symbols,
7121
7283
  isReExportEdge,
7284
+ isDynamic,
7122
7285
  reExportedNames,
7123
7286
  reExportMappings
7124
7287
  });
@@ -7137,7 +7300,7 @@ const buildDependencyGraph = (inputs) => {
7137
7300
  const relativePath = toPosixPath(node_path.default.relative(sourceDir, filePath));
7138
7301
  if ((0, minimatch.minimatch)(relativePath.startsWith(".") ? relativePath : `./${relativePath}`, globPattern)) {
7139
7302
  const targetIndex = fileIdMap.get(filePath);
7140
- if (targetIndex !== void 0) addEdge(sourceIndex, targetIndex, []);
7303
+ if (targetIndex !== void 0) addEdge(sourceIndex, targetIndex, [], false, [], [], true);
7141
7304
  }
7142
7305
  }
7143
7306
  continue;
@@ -7152,7 +7315,7 @@ const buildDependencyGraph = (inputs) => {
7152
7315
  isTypeOnly: importedName.isTypeOnly,
7153
7316
  isNamespace: importedName.isNamespace,
7154
7317
  isDefault: importedName.isDefault
7155
- })));
7318
+ })), false, [], [], importInfo.isDynamic);
7156
7319
  }
7157
7320
  const reExportsByTarget = /* @__PURE__ */ new Map();
7158
7321
  for (const exportInfo of input.parsed.exports) {
@@ -7394,6 +7557,7 @@ const resolveReExportChains = (graph) => {
7394
7557
  const buildSourceTargetMap = (graph) => {
7395
7558
  const sourceTargets = /* @__PURE__ */ new Map();
7396
7559
  for (const edge of graph.edges) {
7560
+ if (!edge.isReExportEdge) continue;
7397
7561
  const existing = sourceTargets.get(edge.source);
7398
7562
  if (existing) {
7399
7563
  if (!existing.includes(edge.target)) existing.push(edge.target);
@@ -7415,9 +7579,9 @@ const EXCLUDED_EXTENSIONS = new Set([
7415
7579
  ".graphql",
7416
7580
  ".gql"
7417
7581
  ]);
7418
- const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy)\.|(?:^|\/)__tests__\/)/;
7419
- const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|scripts)\/(?!.*node_modules)/;
7420
- const CONFIG_FILE_PATTERN = /(?:^|\/)(?:[^/]+\.config\.[tj]sx?$|[^/]+\.setup\.[tj]sx?$|setupTests\.[tj]sx?$|jest\.setup\.[tj]sx?$|vitest\.setup\.[tj]sx?$)/;
7582
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy|test-d)\.|(?:^|\/)__tests__\/)/;
7583
+ const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|public|scripts)\/(?!.*node_modules)/;
7584
+ const CONFIG_FILE_PATTERN = /(?:^|\/)(?:[^/]+\.config\.[tj]sx?$|[^/]+\.config\.[^/]+\.[tj]sx?$|[^/]+\.setup\.[tj]sx?$|setupTests\.[tj]sx?$|jest\.setup\.[tj]sx?$|vitest\.setup\.[tj]sx?$)/;
7421
7585
  const hasExcludedExtension = (filePath) => {
7422
7586
  const lastDot = filePath.lastIndexOf(".");
7423
7587
  if (lastDot === -1) return false;
@@ -7493,6 +7657,7 @@ const detectDeadExports = (graph, config) => {
7493
7657
  if (usageMap.has(usageKey)) continue;
7494
7658
  if (module.localIdentifierReferences.includes(exportInfo.name)) continue;
7495
7659
  if (!exportInfo.isDefault && defaultExportLinkedNames.has(exportInfo.name)) continue;
7660
+ if (exportInfo.isDefault && exportInfo.defaultExportLocalName && usageMap.has(`${module.fileId.path}::${exportInfo.defaultExportLocalName}`)) continue;
7496
7661
  unusedExports.push({
7497
7662
  path: module.fileId.path,
7498
7663
  name: exportInfo.name,
@@ -7527,6 +7692,10 @@ const buildUsageMap = (graph) => {
7527
7692
  const targetModule = graph.modules[edge.target];
7528
7693
  if (!targetModule) continue;
7529
7694
  const sourceModule = graph.modules[edge.source];
7695
+ if (edge.isDynamic && edge.importedSymbols.length === 0) {
7696
+ markAllExportsUsedRecursive(targetModule, graph, sourceToTargetMap, usedExportKeys, /* @__PURE__ */ new Set());
7697
+ continue;
7698
+ }
7530
7699
  for (const symbol of edge.importedSymbols) if (symbol.isNamespace) handleNamespaceImport(sourceModule, targetModule, symbol.localName, graph, sourceToTargetMap, usedExportKeys);
7531
7700
  else {
7532
7701
  const importName = symbol.isDefault ? "default" : symbol.importedName;
@@ -7573,6 +7742,7 @@ const extractAccessedMemberNames = (memberAccesses, objectName) => {
7573
7742
  const buildSourceToTargetsMap = (graph) => {
7574
7743
  const sourceToTargets = /* @__PURE__ */ new Map();
7575
7744
  for (const edge of graph.edges) {
7745
+ if (!edge.isReExportEdge) continue;
7576
7746
  const existing = sourceToTargets.get(edge.source);
7577
7747
  if (existing) {
7578
7748
  if (!existing.includes(edge.target)) existing.push(edge.target);
@@ -7916,10 +8086,12 @@ const hasJsxFiles = (graph) => graph.modules.some((module) => {
7916
8086
  const filePath = module.fileId.path;
7917
8087
  return filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
7918
8088
  });
8089
+ const KNOWN_PEER_DEPENDENCY_NAMES = new Map([["vitest-axe", ["axe-core"]], ["jest-axe", ["axe-core"]]]);
7919
8090
  const collectPeerSatisfiedPackages = (nodeModulesSearchRoots, declaredNames, confirmedUsedNames) => {
7920
8091
  const peerSatisfied = /* @__PURE__ */ new Set();
7921
8092
  for (const installedName of declaredNames) {
7922
8093
  if (!confirmedUsedNames.has(installedName)) continue;
8094
+ for (const knownPeerName of KNOWN_PEER_DEPENDENCY_NAMES.get(installedName) ?? []) if (declaredNames.has(knownPeerName)) peerSatisfied.add(knownPeerName);
7923
8095
  const installedPackageJsonPath = findInstalledPackageJsonPath(installedName, nodeModulesSearchRoots);
7924
8096
  if (!installedPackageJsonPath) continue;
7925
8097
  try {
@@ -7942,22 +8114,41 @@ const findInstalledPackageJsonPath = (packageName, nodeModulesSearchRoots) => {
7942
8114
  };
7943
8115
  const SHELL_SPLIT_PATTERN = /\s*(?:&&|\|\||[;&|])\s*/;
7944
8116
  const INLINE_ENV_VAR_PATTERN = /^[A-Z_][A-Z0-9_]*=/;
8117
+ const KNOWN_PACKAGE_BIN_NAMES = new Map([
8118
+ ["@tauri-apps/cli", ["tauri"]],
8119
+ ["@typescript/native-preview", ["tsgo"]],
8120
+ ["playwright-chromium", ["playwright"]],
8121
+ ["playwright-firefox", ["playwright"]],
8122
+ ["playwright-webkit", ["playwright"]]
8123
+ ]);
8124
+ const staticBinNamesForPackage = (packageName) => {
8125
+ const binNames = [...KNOWN_PACKAGE_BIN_NAMES.get(packageName) ?? []];
8126
+ const unscopedName = packageName.split("/").pop();
8127
+ if (unscopedName.endsWith("-cli") && unscopedName.length > 4) binNames.push(unscopedName.slice(0, -4));
8128
+ return binNames;
8129
+ };
7945
8130
  const buildBinaryPackageIndex = (nodeModulesSearchRoots, declaredNames) => {
7946
8131
  const binToPackage = /* @__PURE__ */ new Map();
7947
8132
  const packagesProvidingBinary = /* @__PURE__ */ new Set();
8133
+ const addBinMapping = (binaryName, packageName) => {
8134
+ const mappedPackages = binToPackage.get(binaryName) ?? /* @__PURE__ */ new Set();
8135
+ mappedPackages.add(packageName);
8136
+ binToPackage.set(binaryName, mappedPackages);
8137
+ };
7948
8138
  for (const packageName of declaredNames) {
8139
+ for (const staticBinName of staticBinNamesForPackage(packageName)) addBinMapping(staticBinName, packageName);
7949
8140
  const packageBinJsonPath = findInstalledPackageJsonPath(packageName, nodeModulesSearchRoots);
7950
8141
  if (!packageBinJsonPath) continue;
7951
8142
  try {
7952
8143
  const binContent = (0, node_fs.readFileSync)(packageBinJsonPath, "utf-8");
7953
8144
  const binField = JSON.parse(binContent).bin;
7954
8145
  if (typeof binField === "string" && binField.length > 0) {
7955
- binToPackage.set(packageName.split("/").pop(), packageName);
8146
+ addBinMapping(packageName.split("/").pop(), packageName);
7956
8147
  packagesProvidingBinary.add(packageName);
7957
8148
  } else if (typeof binField === "object" && binField !== null) {
7958
8149
  const binaryNames = Object.keys(binField);
7959
8150
  if (binaryNames.length === 0) continue;
7960
- for (const binaryName of binaryNames) binToPackage.set(binaryName, packageName);
8151
+ for (const binaryName of binaryNames) addBinMapping(binaryName, packageName);
7961
8152
  packagesProvidingBinary.add(packageName);
7962
8153
  }
7963
8154
  } catch {
@@ -8002,8 +8193,7 @@ const collectCommandReferencedPackages = (command, declaredNames, binToPackage)
8002
8193
  const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
8003
8194
  for (const candidateBinary of [binaryToken, effectiveBinary]) {
8004
8195
  if (!candidateBinary) continue;
8005
- const mappedPackage = binToPackage.get(candidateBinary);
8006
- if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8196
+ for (const mappedPackage of binToPackage.get(candidateBinary) ?? []) if (declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8007
8197
  if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
8008
8198
  }
8009
8199
  }
@@ -8083,6 +8273,12 @@ const collectConfigReferencedPackages = (rootDir, graph, declaredNames, summaryC
8083
8273
  deep: 6
8084
8274
  }, summaryCache);
8085
8275
  for (const documentationPath of documentationFiles) addMatchesFromFile(documentationPath, "importReference", matchesPackageImportReference);
8276
+ const toolingSourceFiles = globPackageFiles(rootDir, ["**/{.dumi,.storybook,.docz,.styleguidist}/**/*.{ts,tsx,js,jsx,mts,mjs}"], {
8277
+ ignore: ["**/node_modules/**"],
8278
+ dot: true,
8279
+ deep: 8
8280
+ }, summaryCache);
8281
+ for (const toolingSourcePath of toolingSourceFiles) addMatchesFromFile(toolingSourcePath, "importReference", matchesPackageImportReference);
8086
8282
  return referenced;
8087
8283
  };
8088
8284
  const PACKAGE_JSON_CONFIG_SECTIONS = [
@@ -8335,14 +8531,47 @@ const isAlwaysConsideredUsed = (dependencyName) => {
8335
8531
  //#endregion
8336
8532
  //#region src/report/cycles.ts
8337
8533
  const UNDEFINED_INDEX = -1;
8534
+ const isCompileTimeErasedEdge = (edge, graph) => {
8535
+ if (edge.isReExportEdge) return false;
8536
+ if (edge.importedSymbols.length === 0) return false;
8537
+ const targetModule = graph.modules[edge.target];
8538
+ if (!targetModule) return false;
8539
+ return edge.importedSymbols.every((symbol) => {
8540
+ if (symbol.isTypeOnly) return true;
8541
+ if (symbol.isNamespace) return false;
8542
+ const exportName = symbol.isDefault ? "default" : symbol.importedName;
8543
+ const matchingExports = targetModule.exports.filter((exportInfo) => exportInfo.name === exportName);
8544
+ return matchingExports.length > 0 && matchingExports.every((exportInfo) => exportInfo.isTypeOnly);
8545
+ });
8546
+ };
8338
8547
  const buildAdjacencyList = (graph) => {
8339
8548
  const targetSets = Array.from({ length: graph.modules.length }, () => /* @__PURE__ */ new Set());
8340
8549
  for (const edge of graph.edges) {
8550
+ if (edge.isDynamic) continue;
8341
8551
  if (edge.importedSymbols.every((symbol) => symbol.isTypeOnly)) continue;
8552
+ if (isCompileTimeErasedEdge(edge, graph)) continue;
8342
8553
  if (edge.target < graph.modules.length) targetSets[edge.source].add(edge.target);
8343
8554
  }
8344
8555
  return targetSets.map((targets) => [...targets]);
8345
8556
  };
8557
+ const buildModuleInitAccessEdgeSet = (graph) => {
8558
+ const initAccessEdges = /* @__PURE__ */ new Set();
8559
+ for (const edge of graph.edges) {
8560
+ if (edge.isDynamic || edge.isReExportEdge) continue;
8561
+ const topLevelReferences = graph.modules[edge.source]?.topLevelImportReferences;
8562
+ if (!topLevelReferences || topLevelReferences.length === 0) continue;
8563
+ if (edge.importedSymbols.some((symbol) => !symbol.isTypeOnly && topLevelReferences.includes(symbol.localName))) initAccessEdges.add(`${edge.source}:${edge.target}`);
8564
+ }
8565
+ return initAccessEdges;
8566
+ };
8567
+ const cycleHasModuleInitAccess = (cycle, initAccessEdges) => {
8568
+ for (let position = 0; position < cycle.length; position++) {
8569
+ const source = cycle[position];
8570
+ const target = cycle[(position + 1) % cycle.length];
8571
+ if (initAccessEdges.has(`${source}:${target}`)) return true;
8572
+ }
8573
+ return false;
8574
+ };
8346
8575
  const findStronglyConnectedComponents = (adjacencyList) => {
8347
8576
  const nodeCount = adjacencyList.length;
8348
8577
  if (nodeCount === 0) return [];
@@ -8463,6 +8692,7 @@ const enumerateElementaryCycles = (componentNodes, adjacencyList, graph) => {
8463
8692
  };
8464
8693
  const detectCycles = (graph) => {
8465
8694
  const adjacencyList = buildAdjacencyList(graph);
8695
+ const initAccessEdges = buildModuleInitAccessEdgeSet(graph);
8466
8696
  const components = findStronglyConnectedComponents(adjacencyList);
8467
8697
  const allCycles = [];
8468
8698
  const seenKeys = /* @__PURE__ */ new Set();
@@ -8472,6 +8702,7 @@ const detectCycles = (graph) => {
8472
8702
  if (component.length > 50) continue;
8473
8703
  const elementaryCycles = enumerateElementaryCycles(component, adjacencyList, graph);
8474
8704
  for (const cycle of elementaryCycles) {
8705
+ if (!cycleHasModuleInitAccess(cycle, initAccessEdges)) continue;
8475
8706
  const key = cycle.join(",");
8476
8707
  if (!seenKeys.has(key)) {
8477
8708
  seenKeys.add(key);
@@ -12496,7 +12727,7 @@ const isRecordValue = (value) => typeof value === "object" && value !== null &&
12496
12727
  const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
12497
12728
  const isOptionalArray = (value) => value === void 0 || Array.isArray(value);
12498
12729
  const emptyStore = (scopeHash) => ({
12499
- version: 2,
12730
+ version: 3,
12500
12731
  scopeHash,
12501
12732
  fileList: null,
12502
12733
  resolutions: null,
@@ -12506,10 +12737,10 @@ const emptyStore = (scopeHash) => ({
12506
12737
  const readPersistedStore = (cachePath, scopeHash) => {
12507
12738
  try {
12508
12739
  const parsed = JSON.parse((0, node_fs.readFileSync)(cachePath, "utf-8"));
12509
- if (isRecordValue(parsed) && parsed.version === 2 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12740
+ if (isRecordValue(parsed) && parsed.version === 3 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12510
12741
  const persisted = parsed;
12511
12742
  return {
12512
- version: 2,
12743
+ version: 3,
12513
12744
  scopeHash,
12514
12745
  fileList: isRecordValue(persisted.fileList) ? persisted.fileList : null,
12515
12746
  resolutions: isRecordValue(persisted.resolutions) && typeof persisted.resolutions.hash === "string" && isRecordValue(persisted.resolutions.entries) ? persisted.resolutions : null,
@@ -12532,6 +12763,7 @@ const serializeParsedSource = (parsed, shouldPersistDryPatternFields) => ({
12532
12763
  memberAccesses: toPersistedArray(parsed.memberAccesses),
12533
12764
  wholeObjectUses: toPersistedArray(parsed.wholeObjectUses),
12534
12765
  localIdentifierReferences: toPersistedArray(intersectLocalReferencesWithOwnExports(parsed)),
12766
+ topLevelImportReferences: toPersistedArray(parsed.topLevelImportReferences),
12535
12767
  referencedFilenames: toPersistedArray(parsed.referencedFilenames),
12536
12768
  ...shouldPersistDryPatternFields ? {
12537
12769
  redundantTypePatterns: toPersistedArray(parsed.redundantTypePatterns),
@@ -12551,6 +12783,7 @@ const PERSISTED_SOURCE_ARRAY_FIELDS = [
12551
12783
  "memberAccesses",
12552
12784
  "wholeObjectUses",
12553
12785
  "localIdentifierReferences",
12786
+ "topLevelImportReferences",
12554
12787
  "referencedFilenames",
12555
12788
  "redundantTypePatterns",
12556
12789
  "identityWrappers",
@@ -12573,6 +12806,7 @@ const reviveParsedSource = (persisted) => {
12573
12806
  memberAccesses: source.memberAccesses ?? [],
12574
12807
  wholeObjectUses: source.wholeObjectUses ?? [],
12575
12808
  localIdentifierReferences: source.localIdentifierReferences ?? [],
12809
+ topLevelImportReferences: source.topLevelImportReferences ?? [],
12576
12810
  referencedFilenames: source.referencedFilenames ?? [],
12577
12811
  redundantTypePatterns: source.redundantTypePatterns ?? [],
12578
12812
  identityWrappers: source.identityWrappers ?? [],
@@ -12776,7 +13010,7 @@ const createSummaryCache = (cachePath, config) => {
12776
13010
  if (resolutionEntry !== void 0) compactedResolutions[resolutionKey] = resolutionEntry;
12777
13011
  }
12778
13012
  const serialized = JSON.stringify({
12779
- version: 2,
13013
+ version: 3,
12780
13014
  scopeHash,
12781
13015
  fileList: store.fileList,
12782
13016
  resolutions: {
@@ -13291,9 +13525,13 @@ const analyze = async (config) => {
13291
13525
  if (discoveredFilePaths.has(resolvedImport.resolvedPath)) continue;
13292
13526
  if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && (0, node_fs.existsSync)(resolvedImport.resolvedPath)) styleFilesToAdd.add(resolvedImport.resolvedPath);
13293
13527
  }
13294
- const sortedStyleFiles = [...styleFilesToAdd].sort();
13528
+ const styleFileQueue = [...styleFilesToAdd].sort();
13295
13529
  let nextFileIndex = files.length;
13296
- for (const styleFilePath of sortedStyleFiles) {
13530
+ let styleFileQueueIndex = 0;
13531
+ while (styleFileQueueIndex < styleFileQueue.length) {
13532
+ const styleFilePath = styleFileQueue[styleFileQueueIndex];
13533
+ styleFileQueueIndex++;
13534
+ if (discoveredFilePaths.has(styleFilePath)) continue;
13297
13535
  const styleSourceFile = {
13298
13536
  index: nextFileIndex,
13299
13537
  path: styleFilePath
@@ -13319,7 +13557,10 @@ const analyze = async (config) => {
13319
13557
  }
13320
13558
  resolvedStyleImportMap.set(importInfo.specifier, resolvedImport);
13321
13559
  if (resolvedImport.resolvedPath && !discoveredFilePaths.has(resolvedImport.resolvedPath)) {
13322
- if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && (0, node_fs.existsSync)(resolvedImport.resolvedPath)) styleFilesToAdd.add(resolvedImport.resolvedPath);
13560
+ if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && !styleFilesToAdd.has(resolvedImport.resolvedPath) && (0, node_fs.existsSync)(resolvedImport.resolvedPath)) {
13561
+ styleFilesToAdd.add(resolvedImport.resolvedPath);
13562
+ styleFileQueue.push(resolvedImport.resolvedPath);
13563
+ }
13323
13564
  }
13324
13565
  }
13325
13566
  graphInputs.push({
package/dist/index.mjs CHANGED
@@ -1322,6 +1322,7 @@ const parseCssImports = (filePath) => {
1322
1322
  memberAccesses: [],
1323
1323
  wholeObjectUses: [],
1324
1324
  localIdentifierReferences: [],
1325
+ topLevelImportReferences: [],
1325
1326
  referencedFilenames: [],
1326
1327
  redundantTypePatterns: [],
1327
1328
  identityWrappers: [],
@@ -1350,18 +1351,142 @@ const collectLocalIdentifierReferences = (statements) => {
1350
1351
  for (const value of Object.values(record)) if (Array.isArray(value)) for (const innerValue of value) visitNode(innerValue);
1351
1352
  else if (value && typeof value === "object") visitNode(value);
1352
1353
  };
1354
+ const visitExportedDeclarationValues = (declaration) => {
1355
+ if (!declaration || typeof declaration !== "object") return;
1356
+ const record = declaration;
1357
+ if (record.type === "VariableDeclaration" && Array.isArray(record.declarations)) {
1358
+ for (const declarator of record.declarations) if (declarator && typeof declarator === "object") visitNode(declarator.init);
1359
+ return;
1360
+ }
1361
+ if (record.type === "FunctionDeclaration" || record.type === "ClassDeclaration") {
1362
+ visitNode(record.params);
1363
+ visitNode(record.superClass);
1364
+ visitNode(record.body);
1365
+ return;
1366
+ }
1367
+ if (typeof record.type === "string" && !record.type.startsWith("TS")) visitNode(declaration);
1368
+ };
1353
1369
  for (const statement of statements) {
1354
- if (statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") continue;
1370
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1371
+ if (statement.type === "ExportNamedDeclaration") {
1372
+ visitExportedDeclarationValues(statement.declaration);
1373
+ continue;
1374
+ }
1375
+ if (statement.type === "ExportDefaultDeclaration") {
1376
+ visitExportedDeclarationValues(statement.declaration);
1377
+ continue;
1378
+ }
1355
1379
  visitNode(statement);
1356
1380
  }
1357
1381
  return references;
1358
1382
  };
1383
+ const TS_VALUE_WRAPPER_NODE_TYPES = new Set([
1384
+ "TSAsExpression",
1385
+ "TSSatisfiesExpression",
1386
+ "TSNonNullExpression",
1387
+ "TSInstantiationExpression",
1388
+ "TSTypeAssertion"
1389
+ ]);
1390
+ const TS_RUNTIME_DECLARATION_NODE_TYPES = new Set([
1391
+ "TSEnumDeclaration",
1392
+ "TSModuleDeclaration",
1393
+ "TSExportAssignment"
1394
+ ]);
1395
+ const FUNCTION_NODE_TYPES = new Set([
1396
+ "FunctionDeclaration",
1397
+ "FunctionExpression",
1398
+ "ArrowFunctionExpression"
1399
+ ]);
1400
+ const collectStaticImportLocalNames = (imports) => {
1401
+ const localNames = /* @__PURE__ */ new Set();
1402
+ for (const importInfo of imports) {
1403
+ if (importInfo.isDynamic || importInfo.isTypeOnly) continue;
1404
+ for (const binding of importInfo.importedNames) {
1405
+ if (binding.isTypeOnly) continue;
1406
+ const localName = binding.alias ?? binding.name;
1407
+ if (localName && localName !== "*") localNames.add(localName);
1408
+ }
1409
+ }
1410
+ return localNames;
1411
+ };
1412
+ const collectTopLevelImportReferences = (bodyNodes, importLocalNames) => {
1413
+ const referencedNames = /* @__PURE__ */ new Set();
1414
+ if (importLocalNames.size === 0) return [];
1415
+ const visitClassBody = (classBody) => {
1416
+ const bodyElements = classBody.body ?? [];
1417
+ for (const element of bodyElements) {
1418
+ if (element.type === "StaticBlock") {
1419
+ visitValueNode(element.body);
1420
+ continue;
1421
+ }
1422
+ if (Boolean(element.computed)) visitValueNode(element.key);
1423
+ const isStatic = Boolean(element.static);
1424
+ if (element.type === "PropertyDefinition" && isStatic) visitValueNode(element.value);
1425
+ visitValueNode(element.decorators);
1426
+ }
1427
+ };
1428
+ const visitValueNode = (node) => {
1429
+ if (Array.isArray(node)) {
1430
+ for (const element of node) visitValueNode(element);
1431
+ return;
1432
+ }
1433
+ if (!isWalkableNode(node)) return;
1434
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") {
1435
+ const identifierName = node.name;
1436
+ if (identifierName && importLocalNames.has(identifierName)) referencedNames.add(identifierName);
1437
+ return;
1438
+ }
1439
+ if (node.type.startsWith("TS")) {
1440
+ if (TS_VALUE_WRAPPER_NODE_TYPES.has(node.type)) {
1441
+ visitValueNode(node.expression);
1442
+ return;
1443
+ }
1444
+ if (!TS_RUNTIME_DECLARATION_NODE_TYPES.has(node.type)) return;
1445
+ }
1446
+ if (FUNCTION_NODE_TYPES.has(node.type)) return;
1447
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
1448
+ visitValueNode(node.superClass);
1449
+ visitValueNode(node.decorators);
1450
+ const classBody = node.body;
1451
+ if (classBody) visitClassBody(classBody);
1452
+ return;
1453
+ }
1454
+ if (node.type === "CallExpression" || node.type === "NewExpression") {
1455
+ const callee = node.callee;
1456
+ if (callee && FUNCTION_NODE_TYPES.has(callee.type)) visitValueNode(callee.body);
1457
+ }
1458
+ if (node.type === "MemberExpression" || node.type === "JSXMemberExpression") {
1459
+ const memberNode = node;
1460
+ visitValueNode(memberNode.object);
1461
+ if (node.computed) visitValueNode(memberNode.property);
1462
+ return;
1463
+ }
1464
+ if (node.type === "Property") {
1465
+ const propertyNode = node;
1466
+ if (node.computed) visitValueNode(propertyNode.key);
1467
+ visitValueNode(propertyNode.value);
1468
+ return;
1469
+ }
1470
+ for (const value of Object.values(node)) if (Array.isArray(value)) for (const element of value) visitValueNode(element);
1471
+ else if (value && typeof value === "object") visitValueNode(value);
1472
+ };
1473
+ for (const statement of bodyNodes) {
1474
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1475
+ if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration") {
1476
+ visitValueNode(statement.declaration);
1477
+ continue;
1478
+ }
1479
+ visitValueNode(statement);
1480
+ }
1481
+ return [...referencedNames];
1482
+ };
1359
1483
  const createEmptyParsedSource = () => ({
1360
1484
  imports: [],
1361
1485
  exports: [],
1362
1486
  memberAccesses: [],
1363
1487
  wholeObjectUses: [],
1364
1488
  localIdentifierReferences: [],
1489
+ topLevelImportReferences: [],
1365
1490
  referencedFilenames: [],
1366
1491
  redundantTypePatterns: [],
1367
1492
  identityWrappers: [],
@@ -1573,6 +1698,7 @@ const parseSourceFile = (filePath) => {
1573
1698
  collectMemberAccesses(program.body, namespaceLocalNames, memberAccesses, wholeObjectUses);
1574
1699
  }, void 0);
1575
1700
  const localIdentifierReferences = safeWalk("collectLocalIdentifierReferences", () => collectLocalIdentifierReferences(program.body), []);
1701
+ const topLevelImportReferences = safeWalk("collectTopLevelImportReferences", () => collectTopLevelImportReferences(program.body, collectStaticImportLocalNames(imports)), []);
1576
1702
  const redundantTypePatterns = [];
1577
1703
  const identityWrappers = [];
1578
1704
  const typeDefinitionHashes = [];
@@ -1617,6 +1743,7 @@ const parseSourceFile = (filePath) => {
1617
1743
  memberAccesses,
1618
1744
  wholeObjectUses,
1619
1745
  localIdentifierReferences,
1746
+ topLevelImportReferences,
1620
1747
  referencedFilenames: extractReferencedFilenames(sourceText),
1621
1748
  redundantTypePatterns,
1622
1749
  identityWrappers,
@@ -1757,6 +1884,29 @@ const collectMemberAccesses = (bodyNodes, namespaceLocalNames, memberAccesses, w
1757
1884
  const spreadArgument = node.argument;
1758
1885
  if (spreadArgument?.type === "Identifier" && namespaceLocalNames.has(spreadArgument.name)) wholeObjectUses.push(spreadArgument.name);
1759
1886
  }
1887
+ if (node.type === "VariableDeclarator") {
1888
+ const declarator = node;
1889
+ if (declarator.init?.type === "Identifier" && namespaceLocalNames.has(declarator.init.name) && declarator.id?.type === "ObjectPattern") {
1890
+ const namespaceName = declarator.init.name;
1891
+ const patternProperties = declarator.id.properties;
1892
+ for (const property of patternProperties) {
1893
+ if (property.type === "RestElement") {
1894
+ wholeObjectUses.push(namespaceName);
1895
+ continue;
1896
+ }
1897
+ const propertyKey = property.key;
1898
+ if (Boolean(property.computed)) wholeObjectUses.push(namespaceName);
1899
+ else if (propertyKey?.type === "Identifier" && propertyKey.name) memberAccesses.push({
1900
+ objectName: namespaceName,
1901
+ memberName: propertyKey.name
1902
+ });
1903
+ else if (propertyKey?.type === "Literal" && typeof propertyKey.value === "string") memberAccesses.push({
1904
+ objectName: namespaceName,
1905
+ memberName: propertyKey.value
1906
+ });
1907
+ }
1908
+ }
1909
+ }
1760
1910
  if (node.type === "ForInStatement") {
1761
1911
  const forInRight = node.right;
1762
1912
  if (forInRight?.type === "Identifier" && namespaceLocalNames.has(forInRight.name)) wholeObjectUses.push(forInRight.name);
@@ -5090,6 +5240,13 @@ const extractCiWorkflowEntries = (rootDir) => {
5090
5240
  const entries = [];
5091
5241
  const workflowsDir = join(rootDir, ".github", "workflows");
5092
5242
  if (!existsSync(workflowsDir)) return entries;
5243
+ const nestedToolPackageJsonPaths = fg.sync("**/package.json", {
5244
+ cwd: join(rootDir, ".github"),
5245
+ absolute: true,
5246
+ onlyFiles: true,
5247
+ ignore: ["**/node_modules/**"]
5248
+ });
5249
+ for (const nestedPackageJsonPath of nestedToolPackageJsonPaths) entries.push(...extractScriptEntries(dirname(nestedPackageJsonPath)));
5093
5250
  const workflowFiles = fg.sync("*.{yml,yaml}", {
5094
5251
  cwd: workflowsDir,
5095
5252
  absolute: true,
@@ -6692,8 +6849,10 @@ const discoverTestRunnerEntryPoints = (rootDir, workspacePackages) => {
6692
6849
  customPatterns = extractJestTestMatchPatterns(directory);
6693
6850
  if (customPatterns.length === 0 && monorepoRoot) customPatterns = extractJestTestMatchPatterns(monorepoRoot);
6694
6851
  }
6695
- if (customPatterns.length > 0) activatedPatterns.push(...customPatterns);
6696
- else activatedPatterns.push(...runner.entryPatterns);
6852
+ if (customPatterns.length > 0) {
6853
+ activatedPatterns.push(...customPatterns);
6854
+ if (isJestRunner) activatedPatterns.push("**/__mocks__/**/*.{ts,tsx,js,jsx,mjs,cjs}");
6855
+ } else activatedPatterns.push(...runner.entryPatterns);
6697
6856
  activatedFixturePatterns.push(...runner.fixturePatterns);
6698
6857
  activatedAlwaysUsed.push(...runner.alwaysUsed);
6699
6858
  }
@@ -6924,6 +7083,7 @@ const deserializeParsedSource = (serialized) => ({
6924
7083
  memberAccesses: serialized.memberAccesses,
6925
7084
  wholeObjectUses: serialized.wholeObjectUses,
6926
7085
  localIdentifierReferences: serialized.localIdentifierReferences,
7086
+ topLevelImportReferences: serialized.topLevelImportReferences,
6927
7087
  referencedFilenames: serialized.referencedFilenames,
6928
7088
  redundantTypePatterns: serialized.redundantTypePatterns,
6929
7089
  identityWrappers: serialized.identityWrappers,
@@ -6988,6 +7148,7 @@ const parseFilesWithWorkerPool = async (files, workerCount) => {
6988
7148
  memberAccesses: [],
6989
7149
  wholeObjectUses: [],
6990
7150
  localIdentifierReferences: [],
7151
+ topLevelImportReferences: [],
6991
7152
  referencedFilenames: [],
6992
7153
  redundantTypePatterns: [],
6993
7154
  identityWrappers: [],
@@ -7062,6 +7223,7 @@ const buildDependencyGraph = (inputs) => {
7062
7223
  memberAccesses: input.parsed.memberAccesses,
7063
7224
  wholeObjectUses: input.parsed.wholeObjectUses,
7064
7225
  localIdentifierReferences: input.parsed.localIdentifierReferences,
7226
+ topLevelImportReferences: input.parsed.topLevelImportReferences,
7065
7227
  referencedFilenames: input.parsed.referencedFilenames,
7066
7228
  redundantTypePatterns: input.parsed.redundantTypePatterns,
7067
7229
  identityWrappers: input.parsed.identityWrappers,
@@ -7080,12 +7242,13 @@ const buildDependencyGraph = (inputs) => {
7080
7242
  }));
7081
7243
  const edges = [];
7082
7244
  const reverseEdges = /* @__PURE__ */ new Map();
7083
- const addEdge = (sourceIndex, targetIndex, symbols, isReExportEdge = false, reExportedNames = [], reExportMappings = []) => {
7245
+ const addEdge = (sourceIndex, targetIndex, symbols, isReExportEdge = false, reExportedNames = [], reExportMappings = [], isDynamic = false) => {
7084
7246
  edges.push({
7085
7247
  source: sourceIndex,
7086
7248
  target: targetIndex,
7087
7249
  importedSymbols: symbols,
7088
7250
  isReExportEdge,
7251
+ isDynamic,
7089
7252
  reExportedNames,
7090
7253
  reExportMappings
7091
7254
  });
@@ -7104,7 +7267,7 @@ const buildDependencyGraph = (inputs) => {
7104
7267
  const relativePath = toPosixPath(path.relative(sourceDir, filePath));
7105
7268
  if (minimatch(relativePath.startsWith(".") ? relativePath : `./${relativePath}`, globPattern)) {
7106
7269
  const targetIndex = fileIdMap.get(filePath);
7107
- if (targetIndex !== void 0) addEdge(sourceIndex, targetIndex, []);
7270
+ if (targetIndex !== void 0) addEdge(sourceIndex, targetIndex, [], false, [], [], true);
7108
7271
  }
7109
7272
  }
7110
7273
  continue;
@@ -7119,7 +7282,7 @@ const buildDependencyGraph = (inputs) => {
7119
7282
  isTypeOnly: importedName.isTypeOnly,
7120
7283
  isNamespace: importedName.isNamespace,
7121
7284
  isDefault: importedName.isDefault
7122
- })));
7285
+ })), false, [], [], importInfo.isDynamic);
7123
7286
  }
7124
7287
  const reExportsByTarget = /* @__PURE__ */ new Map();
7125
7288
  for (const exportInfo of input.parsed.exports) {
@@ -7361,6 +7524,7 @@ const resolveReExportChains = (graph) => {
7361
7524
  const buildSourceTargetMap = (graph) => {
7362
7525
  const sourceTargets = /* @__PURE__ */ new Map();
7363
7526
  for (const edge of graph.edges) {
7527
+ if (!edge.isReExportEdge) continue;
7364
7528
  const existing = sourceTargets.get(edge.source);
7365
7529
  if (existing) {
7366
7530
  if (!existing.includes(edge.target)) existing.push(edge.target);
@@ -7382,9 +7546,9 @@ const EXCLUDED_EXTENSIONS = new Set([
7382
7546
  ".graphql",
7383
7547
  ".gql"
7384
7548
  ]);
7385
- const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy)\.|(?:^|\/)__tests__\/)/;
7386
- const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|scripts)\/(?!.*node_modules)/;
7387
- const CONFIG_FILE_PATTERN = /(?:^|\/)(?:[^/]+\.config\.[tj]sx?$|[^/]+\.setup\.[tj]sx?$|setupTests\.[tj]sx?$|jest\.setup\.[tj]sx?$|vitest\.setup\.[tj]sx?$)/;
7549
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy|test-d)\.|(?:^|\/)__tests__\/)/;
7550
+ const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|public|scripts)\/(?!.*node_modules)/;
7551
+ const CONFIG_FILE_PATTERN = /(?:^|\/)(?:[^/]+\.config\.[tj]sx?$|[^/]+\.config\.[^/]+\.[tj]sx?$|[^/]+\.setup\.[tj]sx?$|setupTests\.[tj]sx?$|jest\.setup\.[tj]sx?$|vitest\.setup\.[tj]sx?$)/;
7388
7552
  const hasExcludedExtension = (filePath) => {
7389
7553
  const lastDot = filePath.lastIndexOf(".");
7390
7554
  if (lastDot === -1) return false;
@@ -7460,6 +7624,7 @@ const detectDeadExports = (graph, config) => {
7460
7624
  if (usageMap.has(usageKey)) continue;
7461
7625
  if (module.localIdentifierReferences.includes(exportInfo.name)) continue;
7462
7626
  if (!exportInfo.isDefault && defaultExportLinkedNames.has(exportInfo.name)) continue;
7627
+ if (exportInfo.isDefault && exportInfo.defaultExportLocalName && usageMap.has(`${module.fileId.path}::${exportInfo.defaultExportLocalName}`)) continue;
7463
7628
  unusedExports.push({
7464
7629
  path: module.fileId.path,
7465
7630
  name: exportInfo.name,
@@ -7494,6 +7659,10 @@ const buildUsageMap = (graph) => {
7494
7659
  const targetModule = graph.modules[edge.target];
7495
7660
  if (!targetModule) continue;
7496
7661
  const sourceModule = graph.modules[edge.source];
7662
+ if (edge.isDynamic && edge.importedSymbols.length === 0) {
7663
+ markAllExportsUsedRecursive(targetModule, graph, sourceToTargetMap, usedExportKeys, /* @__PURE__ */ new Set());
7664
+ continue;
7665
+ }
7497
7666
  for (const symbol of edge.importedSymbols) if (symbol.isNamespace) handleNamespaceImport(sourceModule, targetModule, symbol.localName, graph, sourceToTargetMap, usedExportKeys);
7498
7667
  else {
7499
7668
  const importName = symbol.isDefault ? "default" : symbol.importedName;
@@ -7540,6 +7709,7 @@ const extractAccessedMemberNames = (memberAccesses, objectName) => {
7540
7709
  const buildSourceToTargetsMap = (graph) => {
7541
7710
  const sourceToTargets = /* @__PURE__ */ new Map();
7542
7711
  for (const edge of graph.edges) {
7712
+ if (!edge.isReExportEdge) continue;
7543
7713
  const existing = sourceToTargets.get(edge.source);
7544
7714
  if (existing) {
7545
7715
  if (!existing.includes(edge.target)) existing.push(edge.target);
@@ -7883,10 +8053,12 @@ const hasJsxFiles = (graph) => graph.modules.some((module) => {
7883
8053
  const filePath = module.fileId.path;
7884
8054
  return filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
7885
8055
  });
8056
+ const KNOWN_PEER_DEPENDENCY_NAMES = new Map([["vitest-axe", ["axe-core"]], ["jest-axe", ["axe-core"]]]);
7886
8057
  const collectPeerSatisfiedPackages = (nodeModulesSearchRoots, declaredNames, confirmedUsedNames) => {
7887
8058
  const peerSatisfied = /* @__PURE__ */ new Set();
7888
8059
  for (const installedName of declaredNames) {
7889
8060
  if (!confirmedUsedNames.has(installedName)) continue;
8061
+ for (const knownPeerName of KNOWN_PEER_DEPENDENCY_NAMES.get(installedName) ?? []) if (declaredNames.has(knownPeerName)) peerSatisfied.add(knownPeerName);
7890
8062
  const installedPackageJsonPath = findInstalledPackageJsonPath(installedName, nodeModulesSearchRoots);
7891
8063
  if (!installedPackageJsonPath) continue;
7892
8064
  try {
@@ -7909,22 +8081,41 @@ const findInstalledPackageJsonPath = (packageName, nodeModulesSearchRoots) => {
7909
8081
  };
7910
8082
  const SHELL_SPLIT_PATTERN = /\s*(?:&&|\|\||[;&|])\s*/;
7911
8083
  const INLINE_ENV_VAR_PATTERN = /^[A-Z_][A-Z0-9_]*=/;
8084
+ const KNOWN_PACKAGE_BIN_NAMES = new Map([
8085
+ ["@tauri-apps/cli", ["tauri"]],
8086
+ ["@typescript/native-preview", ["tsgo"]],
8087
+ ["playwright-chromium", ["playwright"]],
8088
+ ["playwright-firefox", ["playwright"]],
8089
+ ["playwright-webkit", ["playwright"]]
8090
+ ]);
8091
+ const staticBinNamesForPackage = (packageName) => {
8092
+ const binNames = [...KNOWN_PACKAGE_BIN_NAMES.get(packageName) ?? []];
8093
+ const unscopedName = packageName.split("/").pop();
8094
+ if (unscopedName.endsWith("-cli") && unscopedName.length > 4) binNames.push(unscopedName.slice(0, -4));
8095
+ return binNames;
8096
+ };
7912
8097
  const buildBinaryPackageIndex = (nodeModulesSearchRoots, declaredNames) => {
7913
8098
  const binToPackage = /* @__PURE__ */ new Map();
7914
8099
  const packagesProvidingBinary = /* @__PURE__ */ new Set();
8100
+ const addBinMapping = (binaryName, packageName) => {
8101
+ const mappedPackages = binToPackage.get(binaryName) ?? /* @__PURE__ */ new Set();
8102
+ mappedPackages.add(packageName);
8103
+ binToPackage.set(binaryName, mappedPackages);
8104
+ };
7915
8105
  for (const packageName of declaredNames) {
8106
+ for (const staticBinName of staticBinNamesForPackage(packageName)) addBinMapping(staticBinName, packageName);
7916
8107
  const packageBinJsonPath = findInstalledPackageJsonPath(packageName, nodeModulesSearchRoots);
7917
8108
  if (!packageBinJsonPath) continue;
7918
8109
  try {
7919
8110
  const binContent = readFileSync(packageBinJsonPath, "utf-8");
7920
8111
  const binField = JSON.parse(binContent).bin;
7921
8112
  if (typeof binField === "string" && binField.length > 0) {
7922
- binToPackage.set(packageName.split("/").pop(), packageName);
8113
+ addBinMapping(packageName.split("/").pop(), packageName);
7923
8114
  packagesProvidingBinary.add(packageName);
7924
8115
  } else if (typeof binField === "object" && binField !== null) {
7925
8116
  const binaryNames = Object.keys(binField);
7926
8117
  if (binaryNames.length === 0) continue;
7927
- for (const binaryName of binaryNames) binToPackage.set(binaryName, packageName);
8118
+ for (const binaryName of binaryNames) addBinMapping(binaryName, packageName);
7928
8119
  packagesProvidingBinary.add(packageName);
7929
8120
  }
7930
8121
  } catch {
@@ -7969,8 +8160,7 @@ const collectCommandReferencedPackages = (command, declaredNames, binToPackage)
7969
8160
  const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
7970
8161
  for (const candidateBinary of [binaryToken, effectiveBinary]) {
7971
8162
  if (!candidateBinary) continue;
7972
- const mappedPackage = binToPackage.get(candidateBinary);
7973
- if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8163
+ for (const mappedPackage of binToPackage.get(candidateBinary) ?? []) if (declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
7974
8164
  if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
7975
8165
  }
7976
8166
  }
@@ -8050,6 +8240,12 @@ const collectConfigReferencedPackages = (rootDir, graph, declaredNames, summaryC
8050
8240
  deep: 6
8051
8241
  }, summaryCache);
8052
8242
  for (const documentationPath of documentationFiles) addMatchesFromFile(documentationPath, "importReference", matchesPackageImportReference);
8243
+ const toolingSourceFiles = globPackageFiles(rootDir, ["**/{.dumi,.storybook,.docz,.styleguidist}/**/*.{ts,tsx,js,jsx,mts,mjs}"], {
8244
+ ignore: ["**/node_modules/**"],
8245
+ dot: true,
8246
+ deep: 8
8247
+ }, summaryCache);
8248
+ for (const toolingSourcePath of toolingSourceFiles) addMatchesFromFile(toolingSourcePath, "importReference", matchesPackageImportReference);
8053
8249
  return referenced;
8054
8250
  };
8055
8251
  const PACKAGE_JSON_CONFIG_SECTIONS = [
@@ -8302,14 +8498,47 @@ const isAlwaysConsideredUsed = (dependencyName) => {
8302
8498
  //#endregion
8303
8499
  //#region src/report/cycles.ts
8304
8500
  const UNDEFINED_INDEX = -1;
8501
+ const isCompileTimeErasedEdge = (edge, graph) => {
8502
+ if (edge.isReExportEdge) return false;
8503
+ if (edge.importedSymbols.length === 0) return false;
8504
+ const targetModule = graph.modules[edge.target];
8505
+ if (!targetModule) return false;
8506
+ return edge.importedSymbols.every((symbol) => {
8507
+ if (symbol.isTypeOnly) return true;
8508
+ if (symbol.isNamespace) return false;
8509
+ const exportName = symbol.isDefault ? "default" : symbol.importedName;
8510
+ const matchingExports = targetModule.exports.filter((exportInfo) => exportInfo.name === exportName);
8511
+ return matchingExports.length > 0 && matchingExports.every((exportInfo) => exportInfo.isTypeOnly);
8512
+ });
8513
+ };
8305
8514
  const buildAdjacencyList = (graph) => {
8306
8515
  const targetSets = Array.from({ length: graph.modules.length }, () => /* @__PURE__ */ new Set());
8307
8516
  for (const edge of graph.edges) {
8517
+ if (edge.isDynamic) continue;
8308
8518
  if (edge.importedSymbols.every((symbol) => symbol.isTypeOnly)) continue;
8519
+ if (isCompileTimeErasedEdge(edge, graph)) continue;
8309
8520
  if (edge.target < graph.modules.length) targetSets[edge.source].add(edge.target);
8310
8521
  }
8311
8522
  return targetSets.map((targets) => [...targets]);
8312
8523
  };
8524
+ const buildModuleInitAccessEdgeSet = (graph) => {
8525
+ const initAccessEdges = /* @__PURE__ */ new Set();
8526
+ for (const edge of graph.edges) {
8527
+ if (edge.isDynamic || edge.isReExportEdge) continue;
8528
+ const topLevelReferences = graph.modules[edge.source]?.topLevelImportReferences;
8529
+ if (!topLevelReferences || topLevelReferences.length === 0) continue;
8530
+ if (edge.importedSymbols.some((symbol) => !symbol.isTypeOnly && topLevelReferences.includes(symbol.localName))) initAccessEdges.add(`${edge.source}:${edge.target}`);
8531
+ }
8532
+ return initAccessEdges;
8533
+ };
8534
+ const cycleHasModuleInitAccess = (cycle, initAccessEdges) => {
8535
+ for (let position = 0; position < cycle.length; position++) {
8536
+ const source = cycle[position];
8537
+ const target = cycle[(position + 1) % cycle.length];
8538
+ if (initAccessEdges.has(`${source}:${target}`)) return true;
8539
+ }
8540
+ return false;
8541
+ };
8313
8542
  const findStronglyConnectedComponents = (adjacencyList) => {
8314
8543
  const nodeCount = adjacencyList.length;
8315
8544
  if (nodeCount === 0) return [];
@@ -8430,6 +8659,7 @@ const enumerateElementaryCycles = (componentNodes, adjacencyList, graph) => {
8430
8659
  };
8431
8660
  const detectCycles = (graph) => {
8432
8661
  const adjacencyList = buildAdjacencyList(graph);
8662
+ const initAccessEdges = buildModuleInitAccessEdgeSet(graph);
8433
8663
  const components = findStronglyConnectedComponents(adjacencyList);
8434
8664
  const allCycles = [];
8435
8665
  const seenKeys = /* @__PURE__ */ new Set();
@@ -8439,6 +8669,7 @@ const detectCycles = (graph) => {
8439
8669
  if (component.length > 50) continue;
8440
8670
  const elementaryCycles = enumerateElementaryCycles(component, adjacencyList, graph);
8441
8671
  for (const cycle of elementaryCycles) {
8672
+ if (!cycleHasModuleInitAccess(cycle, initAccessEdges)) continue;
8442
8673
  const key = cycle.join(",");
8443
8674
  if (!seenKeys.has(key)) {
8444
8675
  seenKeys.add(key);
@@ -12463,7 +12694,7 @@ const isRecordValue = (value) => typeof value === "object" && value !== null &&
12463
12694
  const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
12464
12695
  const isOptionalArray = (value) => value === void 0 || Array.isArray(value);
12465
12696
  const emptyStore = (scopeHash) => ({
12466
- version: 2,
12697
+ version: 3,
12467
12698
  scopeHash,
12468
12699
  fileList: null,
12469
12700
  resolutions: null,
@@ -12473,10 +12704,10 @@ const emptyStore = (scopeHash) => ({
12473
12704
  const readPersistedStore = (cachePath, scopeHash) => {
12474
12705
  try {
12475
12706
  const parsed = JSON.parse(readFileSync(cachePath, "utf-8"));
12476
- if (isRecordValue(parsed) && parsed.version === 2 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12707
+ if (isRecordValue(parsed) && parsed.version === 3 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12477
12708
  const persisted = parsed;
12478
12709
  return {
12479
- version: 2,
12710
+ version: 3,
12480
12711
  scopeHash,
12481
12712
  fileList: isRecordValue(persisted.fileList) ? persisted.fileList : null,
12482
12713
  resolutions: isRecordValue(persisted.resolutions) && typeof persisted.resolutions.hash === "string" && isRecordValue(persisted.resolutions.entries) ? persisted.resolutions : null,
@@ -12499,6 +12730,7 @@ const serializeParsedSource = (parsed, shouldPersistDryPatternFields) => ({
12499
12730
  memberAccesses: toPersistedArray(parsed.memberAccesses),
12500
12731
  wholeObjectUses: toPersistedArray(parsed.wholeObjectUses),
12501
12732
  localIdentifierReferences: toPersistedArray(intersectLocalReferencesWithOwnExports(parsed)),
12733
+ topLevelImportReferences: toPersistedArray(parsed.topLevelImportReferences),
12502
12734
  referencedFilenames: toPersistedArray(parsed.referencedFilenames),
12503
12735
  ...shouldPersistDryPatternFields ? {
12504
12736
  redundantTypePatterns: toPersistedArray(parsed.redundantTypePatterns),
@@ -12518,6 +12750,7 @@ const PERSISTED_SOURCE_ARRAY_FIELDS = [
12518
12750
  "memberAccesses",
12519
12751
  "wholeObjectUses",
12520
12752
  "localIdentifierReferences",
12753
+ "topLevelImportReferences",
12521
12754
  "referencedFilenames",
12522
12755
  "redundantTypePatterns",
12523
12756
  "identityWrappers",
@@ -12540,6 +12773,7 @@ const reviveParsedSource = (persisted) => {
12540
12773
  memberAccesses: source.memberAccesses ?? [],
12541
12774
  wholeObjectUses: source.wholeObjectUses ?? [],
12542
12775
  localIdentifierReferences: source.localIdentifierReferences ?? [],
12776
+ topLevelImportReferences: source.topLevelImportReferences ?? [],
12543
12777
  referencedFilenames: source.referencedFilenames ?? [],
12544
12778
  redundantTypePatterns: source.redundantTypePatterns ?? [],
12545
12779
  identityWrappers: source.identityWrappers ?? [],
@@ -12743,7 +12977,7 @@ const createSummaryCache = (cachePath, config) => {
12743
12977
  if (resolutionEntry !== void 0) compactedResolutions[resolutionKey] = resolutionEntry;
12744
12978
  }
12745
12979
  const serialized = JSON.stringify({
12746
- version: 2,
12980
+ version: 3,
12747
12981
  scopeHash,
12748
12982
  fileList: store.fileList,
12749
12983
  resolutions: {
@@ -13258,9 +13492,13 @@ const analyze = async (config) => {
13258
13492
  if (discoveredFilePaths.has(resolvedImport.resolvedPath)) continue;
13259
13493
  if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && existsSync(resolvedImport.resolvedPath)) styleFilesToAdd.add(resolvedImport.resolvedPath);
13260
13494
  }
13261
- const sortedStyleFiles = [...styleFilesToAdd].sort();
13495
+ const styleFileQueue = [...styleFilesToAdd].sort();
13262
13496
  let nextFileIndex = files.length;
13263
- for (const styleFilePath of sortedStyleFiles) {
13497
+ let styleFileQueueIndex = 0;
13498
+ while (styleFileQueueIndex < styleFileQueue.length) {
13499
+ const styleFilePath = styleFileQueue[styleFileQueueIndex];
13500
+ styleFileQueueIndex++;
13501
+ if (discoveredFilePaths.has(styleFilePath)) continue;
13264
13502
  const styleSourceFile = {
13265
13503
  index: nextFileIndex,
13266
13504
  path: styleFilePath
@@ -13286,7 +13524,10 @@ const analyze = async (config) => {
13286
13524
  }
13287
13525
  resolvedStyleImportMap.set(importInfo.specifier, resolvedImport);
13288
13526
  if (resolvedImport.resolvedPath && !discoveredFilePaths.has(resolvedImport.resolvedPath)) {
13289
- if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && existsSync(resolvedImport.resolvedPath)) styleFilesToAdd.add(resolvedImport.resolvedPath);
13527
+ if (STYLE_EXTENSIONS.some((ext) => resolvedImport.resolvedPath.endsWith(ext)) && !styleFilesToAdd.has(resolvedImport.resolvedPath) && existsSync(resolvedImport.resolvedPath)) {
13528
+ styleFilesToAdd.add(resolvedImport.resolvedPath);
13529
+ styleFileQueue.push(resolvedImport.resolvedPath);
13530
+ }
13290
13531
  }
13291
13532
  }
13292
13533
  graphInputs.push({
@@ -1365,6 +1365,7 @@ const parseCssImports = (filePath) => {
1365
1365
  memberAccesses: [],
1366
1366
  wholeObjectUses: [],
1367
1367
  localIdentifierReferences: [],
1368
+ topLevelImportReferences: [],
1368
1369
  referencedFilenames: [],
1369
1370
  redundantTypePatterns: [],
1370
1371
  identityWrappers: [],
@@ -1393,18 +1394,142 @@ const collectLocalIdentifierReferences = (statements) => {
1393
1394
  for (const value of Object.values(record)) if (Array.isArray(value)) for (const innerValue of value) visitNode(innerValue);
1394
1395
  else if (value && typeof value === "object") visitNode(value);
1395
1396
  };
1397
+ const visitExportedDeclarationValues = (declaration) => {
1398
+ if (!declaration || typeof declaration !== "object") return;
1399
+ const record = declaration;
1400
+ if (record.type === "VariableDeclaration" && Array.isArray(record.declarations)) {
1401
+ for (const declarator of record.declarations) if (declarator && typeof declarator === "object") visitNode(declarator.init);
1402
+ return;
1403
+ }
1404
+ if (record.type === "FunctionDeclaration" || record.type === "ClassDeclaration") {
1405
+ visitNode(record.params);
1406
+ visitNode(record.superClass);
1407
+ visitNode(record.body);
1408
+ return;
1409
+ }
1410
+ if (typeof record.type === "string" && !record.type.startsWith("TS")) visitNode(declaration);
1411
+ };
1396
1412
  for (const statement of statements) {
1397
- if (statement.type === "ImportDeclaration" || statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" || statement.type === "ExportAllDeclaration") continue;
1413
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1414
+ if (statement.type === "ExportNamedDeclaration") {
1415
+ visitExportedDeclarationValues(statement.declaration);
1416
+ continue;
1417
+ }
1418
+ if (statement.type === "ExportDefaultDeclaration") {
1419
+ visitExportedDeclarationValues(statement.declaration);
1420
+ continue;
1421
+ }
1398
1422
  visitNode(statement);
1399
1423
  }
1400
1424
  return references;
1401
1425
  };
1426
+ const TS_VALUE_WRAPPER_NODE_TYPES = new Set([
1427
+ "TSAsExpression",
1428
+ "TSSatisfiesExpression",
1429
+ "TSNonNullExpression",
1430
+ "TSInstantiationExpression",
1431
+ "TSTypeAssertion"
1432
+ ]);
1433
+ const TS_RUNTIME_DECLARATION_NODE_TYPES = new Set([
1434
+ "TSEnumDeclaration",
1435
+ "TSModuleDeclaration",
1436
+ "TSExportAssignment"
1437
+ ]);
1438
+ const FUNCTION_NODE_TYPES = new Set([
1439
+ "FunctionDeclaration",
1440
+ "FunctionExpression",
1441
+ "ArrowFunctionExpression"
1442
+ ]);
1443
+ const collectStaticImportLocalNames = (imports) => {
1444
+ const localNames = /* @__PURE__ */ new Set();
1445
+ for (const importInfo of imports) {
1446
+ if (importInfo.isDynamic || importInfo.isTypeOnly) continue;
1447
+ for (const binding of importInfo.importedNames) {
1448
+ if (binding.isTypeOnly) continue;
1449
+ const localName = binding.alias ?? binding.name;
1450
+ if (localName && localName !== "*") localNames.add(localName);
1451
+ }
1452
+ }
1453
+ return localNames;
1454
+ };
1455
+ const collectTopLevelImportReferences = (bodyNodes, importLocalNames) => {
1456
+ const referencedNames = /* @__PURE__ */ new Set();
1457
+ if (importLocalNames.size === 0) return [];
1458
+ const visitClassBody = (classBody) => {
1459
+ const bodyElements = classBody.body ?? [];
1460
+ for (const element of bodyElements) {
1461
+ if (element.type === "StaticBlock") {
1462
+ visitValueNode(element.body);
1463
+ continue;
1464
+ }
1465
+ if (Boolean(element.computed)) visitValueNode(element.key);
1466
+ const isStatic = Boolean(element.static);
1467
+ if (element.type === "PropertyDefinition" && isStatic) visitValueNode(element.value);
1468
+ visitValueNode(element.decorators);
1469
+ }
1470
+ };
1471
+ const visitValueNode = (node) => {
1472
+ if (Array.isArray(node)) {
1473
+ for (const element of node) visitValueNode(element);
1474
+ return;
1475
+ }
1476
+ if (!isWalkableNode(node)) return;
1477
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") {
1478
+ const identifierName = node.name;
1479
+ if (identifierName && importLocalNames.has(identifierName)) referencedNames.add(identifierName);
1480
+ return;
1481
+ }
1482
+ if (node.type.startsWith("TS")) {
1483
+ if (TS_VALUE_WRAPPER_NODE_TYPES.has(node.type)) {
1484
+ visitValueNode(node.expression);
1485
+ return;
1486
+ }
1487
+ if (!TS_RUNTIME_DECLARATION_NODE_TYPES.has(node.type)) return;
1488
+ }
1489
+ if (FUNCTION_NODE_TYPES.has(node.type)) return;
1490
+ if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
1491
+ visitValueNode(node.superClass);
1492
+ visitValueNode(node.decorators);
1493
+ const classBody = node.body;
1494
+ if (classBody) visitClassBody(classBody);
1495
+ return;
1496
+ }
1497
+ if (node.type === "CallExpression" || node.type === "NewExpression") {
1498
+ const callee = node.callee;
1499
+ if (callee && FUNCTION_NODE_TYPES.has(callee.type)) visitValueNode(callee.body);
1500
+ }
1501
+ if (node.type === "MemberExpression" || node.type === "JSXMemberExpression") {
1502
+ const memberNode = node;
1503
+ visitValueNode(memberNode.object);
1504
+ if (node.computed) visitValueNode(memberNode.property);
1505
+ return;
1506
+ }
1507
+ if (node.type === "Property") {
1508
+ const propertyNode = node;
1509
+ if (node.computed) visitValueNode(propertyNode.key);
1510
+ visitValueNode(propertyNode.value);
1511
+ return;
1512
+ }
1513
+ for (const value of Object.values(node)) if (Array.isArray(value)) for (const element of value) visitValueNode(element);
1514
+ else if (value && typeof value === "object") visitValueNode(value);
1515
+ };
1516
+ for (const statement of bodyNodes) {
1517
+ if (statement.type === "ImportDeclaration" || statement.type === "ExportAllDeclaration") continue;
1518
+ if (statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration") {
1519
+ visitValueNode(statement.declaration);
1520
+ continue;
1521
+ }
1522
+ visitValueNode(statement);
1523
+ }
1524
+ return [...referencedNames];
1525
+ };
1402
1526
  const createEmptyParsedSource = () => ({
1403
1527
  imports: [],
1404
1528
  exports: [],
1405
1529
  memberAccesses: [],
1406
1530
  wholeObjectUses: [],
1407
1531
  localIdentifierReferences: [],
1532
+ topLevelImportReferences: [],
1408
1533
  referencedFilenames: [],
1409
1534
  redundantTypePatterns: [],
1410
1535
  identityWrappers: [],
@@ -1616,6 +1741,7 @@ const parseSourceFile = (filePath) => {
1616
1741
  collectMemberAccesses(program.body, namespaceLocalNames, memberAccesses, wholeObjectUses);
1617
1742
  }, void 0);
1618
1743
  const localIdentifierReferences = safeWalk("collectLocalIdentifierReferences", () => collectLocalIdentifierReferences(program.body), []);
1744
+ const topLevelImportReferences = safeWalk("collectTopLevelImportReferences", () => collectTopLevelImportReferences(program.body, collectStaticImportLocalNames(imports)), []);
1619
1745
  const redundantTypePatterns = [];
1620
1746
  const identityWrappers = [];
1621
1747
  const typeDefinitionHashes = [];
@@ -1660,6 +1786,7 @@ const parseSourceFile = (filePath) => {
1660
1786
  memberAccesses,
1661
1787
  wholeObjectUses,
1662
1788
  localIdentifierReferences,
1789
+ topLevelImportReferences,
1663
1790
  referencedFilenames: extractReferencedFilenames(sourceText),
1664
1791
  redundantTypePatterns,
1665
1792
  identityWrappers,
@@ -1800,6 +1927,29 @@ const collectMemberAccesses = (bodyNodes, namespaceLocalNames, memberAccesses, w
1800
1927
  const spreadArgument = node.argument;
1801
1928
  if (spreadArgument?.type === "Identifier" && namespaceLocalNames.has(spreadArgument.name)) wholeObjectUses.push(spreadArgument.name);
1802
1929
  }
1930
+ if (node.type === "VariableDeclarator") {
1931
+ const declarator = node;
1932
+ if (declarator.init?.type === "Identifier" && namespaceLocalNames.has(declarator.init.name) && declarator.id?.type === "ObjectPattern") {
1933
+ const namespaceName = declarator.init.name;
1934
+ const patternProperties = declarator.id.properties;
1935
+ for (const property of patternProperties) {
1936
+ if (property.type === "RestElement") {
1937
+ wholeObjectUses.push(namespaceName);
1938
+ continue;
1939
+ }
1940
+ const propertyKey = property.key;
1941
+ if (Boolean(property.computed)) wholeObjectUses.push(namespaceName);
1942
+ else if (propertyKey?.type === "Identifier" && propertyKey.name) memberAccesses.push({
1943
+ objectName: namespaceName,
1944
+ memberName: propertyKey.name
1945
+ });
1946
+ else if (propertyKey?.type === "Literal" && typeof propertyKey.value === "string") memberAccesses.push({
1947
+ objectName: namespaceName,
1948
+ memberName: propertyKey.value
1949
+ });
1950
+ }
1951
+ }
1952
+ }
1803
1953
  if (node.type === "ForInStatement") {
1804
1954
  const forInRight = node.right;
1805
1955
  if (forInRight?.type === "Identifier" && namespaceLocalNames.has(forInRight.name)) wholeObjectUses.push(forInRight.name);
@@ -1,4 +1,4 @@
1
- import { n as parseSourceFile } from "./parse-xkrZfjHl.mjs";
1
+ import { n as parseSourceFile } from "./parse-CDdo_Ppt.mjs";
2
2
  import { parentPort } from "node:worker_threads";
3
3
 
4
4
  //#region src/collect/parse-worker.ts
@@ -16,6 +16,7 @@ port.on("message", (message) => {
16
16
  memberAccesses: parsed.memberAccesses,
17
17
  wholeObjectUses: parsed.wholeObjectUses,
18
18
  localIdentifierReferences: parsed.localIdentifierReferences,
19
+ topLevelImportReferences: parsed.topLevelImportReferences,
19
20
  referencedFilenames: parsed.referencedFilenames,
20
21
  redundantTypePatterns: parsed.redundantTypePatterns,
21
22
  identityWrappers: parsed.identityWrappers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deslop-js",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Remove AI slop from JavaScript code.",
5
5
  "keywords": [
6
6
  "dead-code",