deslop-js 0.7.1 → 0.7.2

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) {
@@ -7415,9 +7578,9 @@ const EXCLUDED_EXTENSIONS = new Set([
7415
7578
  ".graphql",
7416
7579
  ".gql"
7417
7580
  ]);
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?$)/;
7581
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy|test-d)\.|(?:^|\/)__tests__\/)/;
7582
+ const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|public|scripts)\/(?!.*node_modules)/;
7583
+ 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
7584
  const hasExcludedExtension = (filePath) => {
7422
7585
  const lastDot = filePath.lastIndexOf(".");
7423
7586
  if (lastDot === -1) return false;
@@ -7493,6 +7656,7 @@ const detectDeadExports = (graph, config) => {
7493
7656
  if (usageMap.has(usageKey)) continue;
7494
7657
  if (module.localIdentifierReferences.includes(exportInfo.name)) continue;
7495
7658
  if (!exportInfo.isDefault && defaultExportLinkedNames.has(exportInfo.name)) continue;
7659
+ if (exportInfo.isDefault && exportInfo.defaultExportLocalName && usageMap.has(`${module.fileId.path}::${exportInfo.defaultExportLocalName}`)) continue;
7496
7660
  unusedExports.push({
7497
7661
  path: module.fileId.path,
7498
7662
  name: exportInfo.name,
@@ -7527,6 +7691,10 @@ const buildUsageMap = (graph) => {
7527
7691
  const targetModule = graph.modules[edge.target];
7528
7692
  if (!targetModule) continue;
7529
7693
  const sourceModule = graph.modules[edge.source];
7694
+ if (edge.isDynamic && edge.importedSymbols.length === 0) {
7695
+ markAllExportsUsedRecursive(targetModule, graph, sourceToTargetMap, usedExportKeys, /* @__PURE__ */ new Set());
7696
+ continue;
7697
+ }
7530
7698
  for (const symbol of edge.importedSymbols) if (symbol.isNamespace) handleNamespaceImport(sourceModule, targetModule, symbol.localName, graph, sourceToTargetMap, usedExportKeys);
7531
7699
  else {
7532
7700
  const importName = symbol.isDefault ? "default" : symbol.importedName;
@@ -7916,10 +8084,12 @@ const hasJsxFiles = (graph) => graph.modules.some((module) => {
7916
8084
  const filePath = module.fileId.path;
7917
8085
  return filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
7918
8086
  });
8087
+ const KNOWN_PEER_DEPENDENCY_NAMES = new Map([["vitest-axe", ["axe-core"]], ["jest-axe", ["axe-core"]]]);
7919
8088
  const collectPeerSatisfiedPackages = (nodeModulesSearchRoots, declaredNames, confirmedUsedNames) => {
7920
8089
  const peerSatisfied = /* @__PURE__ */ new Set();
7921
8090
  for (const installedName of declaredNames) {
7922
8091
  if (!confirmedUsedNames.has(installedName)) continue;
8092
+ for (const knownPeerName of KNOWN_PEER_DEPENDENCY_NAMES.get(installedName) ?? []) if (declaredNames.has(knownPeerName)) peerSatisfied.add(knownPeerName);
7923
8093
  const installedPackageJsonPath = findInstalledPackageJsonPath(installedName, nodeModulesSearchRoots);
7924
8094
  if (!installedPackageJsonPath) continue;
7925
8095
  try {
@@ -7942,22 +8112,41 @@ const findInstalledPackageJsonPath = (packageName, nodeModulesSearchRoots) => {
7942
8112
  };
7943
8113
  const SHELL_SPLIT_PATTERN = /\s*(?:&&|\|\||[;&|])\s*/;
7944
8114
  const INLINE_ENV_VAR_PATTERN = /^[A-Z_][A-Z0-9_]*=/;
8115
+ const KNOWN_PACKAGE_BIN_NAMES = new Map([
8116
+ ["@tauri-apps/cli", ["tauri"]],
8117
+ ["@typescript/native-preview", ["tsgo"]],
8118
+ ["playwright-chromium", ["playwright"]],
8119
+ ["playwright-firefox", ["playwright"]],
8120
+ ["playwright-webkit", ["playwright"]]
8121
+ ]);
8122
+ const staticBinNamesForPackage = (packageName) => {
8123
+ const binNames = [...KNOWN_PACKAGE_BIN_NAMES.get(packageName) ?? []];
8124
+ const unscopedName = packageName.split("/").pop();
8125
+ if (unscopedName.endsWith("-cli") && unscopedName.length > 4) binNames.push(unscopedName.slice(0, -4));
8126
+ return binNames;
8127
+ };
7945
8128
  const buildBinaryPackageIndex = (nodeModulesSearchRoots, declaredNames) => {
7946
8129
  const binToPackage = /* @__PURE__ */ new Map();
7947
8130
  const packagesProvidingBinary = /* @__PURE__ */ new Set();
8131
+ const addBinMapping = (binaryName, packageName) => {
8132
+ const mappedPackages = binToPackage.get(binaryName) ?? /* @__PURE__ */ new Set();
8133
+ mappedPackages.add(packageName);
8134
+ binToPackage.set(binaryName, mappedPackages);
8135
+ };
7948
8136
  for (const packageName of declaredNames) {
8137
+ for (const staticBinName of staticBinNamesForPackage(packageName)) addBinMapping(staticBinName, packageName);
7949
8138
  const packageBinJsonPath = findInstalledPackageJsonPath(packageName, nodeModulesSearchRoots);
7950
8139
  if (!packageBinJsonPath) continue;
7951
8140
  try {
7952
8141
  const binContent = (0, node_fs.readFileSync)(packageBinJsonPath, "utf-8");
7953
8142
  const binField = JSON.parse(binContent).bin;
7954
8143
  if (typeof binField === "string" && binField.length > 0) {
7955
- binToPackage.set(packageName.split("/").pop(), packageName);
8144
+ addBinMapping(packageName.split("/").pop(), packageName);
7956
8145
  packagesProvidingBinary.add(packageName);
7957
8146
  } else if (typeof binField === "object" && binField !== null) {
7958
8147
  const binaryNames = Object.keys(binField);
7959
8148
  if (binaryNames.length === 0) continue;
7960
- for (const binaryName of binaryNames) binToPackage.set(binaryName, packageName);
8149
+ for (const binaryName of binaryNames) addBinMapping(binaryName, packageName);
7961
8150
  packagesProvidingBinary.add(packageName);
7962
8151
  }
7963
8152
  } catch {
@@ -8002,8 +8191,7 @@ const collectCommandReferencedPackages = (command, declaredNames, binToPackage)
8002
8191
  const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
8003
8192
  for (const candidateBinary of [binaryToken, effectiveBinary]) {
8004
8193
  if (!candidateBinary) continue;
8005
- const mappedPackage = binToPackage.get(candidateBinary);
8006
- if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8194
+ for (const mappedPackage of binToPackage.get(candidateBinary) ?? []) if (declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8007
8195
  if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
8008
8196
  }
8009
8197
  }
@@ -8083,6 +8271,12 @@ const collectConfigReferencedPackages = (rootDir, graph, declaredNames, summaryC
8083
8271
  deep: 6
8084
8272
  }, summaryCache);
8085
8273
  for (const documentationPath of documentationFiles) addMatchesFromFile(documentationPath, "importReference", matchesPackageImportReference);
8274
+ const toolingSourceFiles = globPackageFiles(rootDir, ["**/{.dumi,.storybook,.docz,.styleguidist}/**/*.{ts,tsx,js,jsx,mts,mjs}"], {
8275
+ ignore: ["**/node_modules/**"],
8276
+ dot: true,
8277
+ deep: 8
8278
+ }, summaryCache);
8279
+ for (const toolingSourcePath of toolingSourceFiles) addMatchesFromFile(toolingSourcePath, "importReference", matchesPackageImportReference);
8086
8280
  return referenced;
8087
8281
  };
8088
8282
  const PACKAGE_JSON_CONFIG_SECTIONS = [
@@ -8335,14 +8529,47 @@ const isAlwaysConsideredUsed = (dependencyName) => {
8335
8529
  //#endregion
8336
8530
  //#region src/report/cycles.ts
8337
8531
  const UNDEFINED_INDEX = -1;
8532
+ const isCompileTimeErasedEdge = (edge, graph) => {
8533
+ if (edge.isReExportEdge) return false;
8534
+ if (edge.importedSymbols.length === 0) return false;
8535
+ const targetModule = graph.modules[edge.target];
8536
+ if (!targetModule) return false;
8537
+ return edge.importedSymbols.every((symbol) => {
8538
+ if (symbol.isTypeOnly) return true;
8539
+ if (symbol.isNamespace) return false;
8540
+ const exportName = symbol.isDefault ? "default" : symbol.importedName;
8541
+ const matchingExports = targetModule.exports.filter((exportInfo) => exportInfo.name === exportName);
8542
+ return matchingExports.length > 0 && matchingExports.every((exportInfo) => exportInfo.isTypeOnly);
8543
+ });
8544
+ };
8338
8545
  const buildAdjacencyList = (graph) => {
8339
8546
  const targetSets = Array.from({ length: graph.modules.length }, () => /* @__PURE__ */ new Set());
8340
8547
  for (const edge of graph.edges) {
8548
+ if (edge.isDynamic) continue;
8341
8549
  if (edge.importedSymbols.every((symbol) => symbol.isTypeOnly)) continue;
8550
+ if (isCompileTimeErasedEdge(edge, graph)) continue;
8342
8551
  if (edge.target < graph.modules.length) targetSets[edge.source].add(edge.target);
8343
8552
  }
8344
8553
  return targetSets.map((targets) => [...targets]);
8345
8554
  };
8555
+ const buildModuleInitAccessEdgeSet = (graph) => {
8556
+ const initAccessEdges = /* @__PURE__ */ new Set();
8557
+ for (const edge of graph.edges) {
8558
+ if (edge.isDynamic || edge.isReExportEdge) continue;
8559
+ const topLevelReferences = graph.modules[edge.source]?.topLevelImportReferences;
8560
+ if (!topLevelReferences || topLevelReferences.length === 0) continue;
8561
+ if (edge.importedSymbols.some((symbol) => !symbol.isTypeOnly && topLevelReferences.includes(symbol.localName))) initAccessEdges.add(`${edge.source}:${edge.target}`);
8562
+ }
8563
+ return initAccessEdges;
8564
+ };
8565
+ const cycleHasModuleInitAccess = (cycle, initAccessEdges) => {
8566
+ for (let position = 0; position < cycle.length; position++) {
8567
+ const source = cycle[position];
8568
+ const target = cycle[(position + 1) % cycle.length];
8569
+ if (initAccessEdges.has(`${source}:${target}`)) return true;
8570
+ }
8571
+ return false;
8572
+ };
8346
8573
  const findStronglyConnectedComponents = (adjacencyList) => {
8347
8574
  const nodeCount = adjacencyList.length;
8348
8575
  if (nodeCount === 0) return [];
@@ -8463,6 +8690,7 @@ const enumerateElementaryCycles = (componentNodes, adjacencyList, graph) => {
8463
8690
  };
8464
8691
  const detectCycles = (graph) => {
8465
8692
  const adjacencyList = buildAdjacencyList(graph);
8693
+ const initAccessEdges = buildModuleInitAccessEdgeSet(graph);
8466
8694
  const components = findStronglyConnectedComponents(adjacencyList);
8467
8695
  const allCycles = [];
8468
8696
  const seenKeys = /* @__PURE__ */ new Set();
@@ -8472,6 +8700,7 @@ const detectCycles = (graph) => {
8472
8700
  if (component.length > 50) continue;
8473
8701
  const elementaryCycles = enumerateElementaryCycles(component, adjacencyList, graph);
8474
8702
  for (const cycle of elementaryCycles) {
8703
+ if (!cycleHasModuleInitAccess(cycle, initAccessEdges)) continue;
8475
8704
  const key = cycle.join(",");
8476
8705
  if (!seenKeys.has(key)) {
8477
8706
  seenKeys.add(key);
@@ -12496,7 +12725,7 @@ const isRecordValue = (value) => typeof value === "object" && value !== null &&
12496
12725
  const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
12497
12726
  const isOptionalArray = (value) => value === void 0 || Array.isArray(value);
12498
12727
  const emptyStore = (scopeHash) => ({
12499
- version: 2,
12728
+ version: 3,
12500
12729
  scopeHash,
12501
12730
  fileList: null,
12502
12731
  resolutions: null,
@@ -12506,10 +12735,10 @@ const emptyStore = (scopeHash) => ({
12506
12735
  const readPersistedStore = (cachePath, scopeHash) => {
12507
12736
  try {
12508
12737
  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)) {
12738
+ if (isRecordValue(parsed) && parsed.version === 3 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12510
12739
  const persisted = parsed;
12511
12740
  return {
12512
- version: 2,
12741
+ version: 3,
12513
12742
  scopeHash,
12514
12743
  fileList: isRecordValue(persisted.fileList) ? persisted.fileList : null,
12515
12744
  resolutions: isRecordValue(persisted.resolutions) && typeof persisted.resolutions.hash === "string" && isRecordValue(persisted.resolutions.entries) ? persisted.resolutions : null,
@@ -12532,6 +12761,7 @@ const serializeParsedSource = (parsed, shouldPersistDryPatternFields) => ({
12532
12761
  memberAccesses: toPersistedArray(parsed.memberAccesses),
12533
12762
  wholeObjectUses: toPersistedArray(parsed.wholeObjectUses),
12534
12763
  localIdentifierReferences: toPersistedArray(intersectLocalReferencesWithOwnExports(parsed)),
12764
+ topLevelImportReferences: toPersistedArray(parsed.topLevelImportReferences),
12535
12765
  referencedFilenames: toPersistedArray(parsed.referencedFilenames),
12536
12766
  ...shouldPersistDryPatternFields ? {
12537
12767
  redundantTypePatterns: toPersistedArray(parsed.redundantTypePatterns),
@@ -12551,6 +12781,7 @@ const PERSISTED_SOURCE_ARRAY_FIELDS = [
12551
12781
  "memberAccesses",
12552
12782
  "wholeObjectUses",
12553
12783
  "localIdentifierReferences",
12784
+ "topLevelImportReferences",
12554
12785
  "referencedFilenames",
12555
12786
  "redundantTypePatterns",
12556
12787
  "identityWrappers",
@@ -12573,6 +12804,7 @@ const reviveParsedSource = (persisted) => {
12573
12804
  memberAccesses: source.memberAccesses ?? [],
12574
12805
  wholeObjectUses: source.wholeObjectUses ?? [],
12575
12806
  localIdentifierReferences: source.localIdentifierReferences ?? [],
12807
+ topLevelImportReferences: source.topLevelImportReferences ?? [],
12576
12808
  referencedFilenames: source.referencedFilenames ?? [],
12577
12809
  redundantTypePatterns: source.redundantTypePatterns ?? [],
12578
12810
  identityWrappers: source.identityWrappers ?? [],
@@ -12776,7 +13008,7 @@ const createSummaryCache = (cachePath, config) => {
12776
13008
  if (resolutionEntry !== void 0) compactedResolutions[resolutionKey] = resolutionEntry;
12777
13009
  }
12778
13010
  const serialized = JSON.stringify({
12779
- version: 2,
13011
+ version: 3,
12780
13012
  scopeHash,
12781
13013
  fileList: store.fileList,
12782
13014
  resolutions: {
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) {
@@ -7382,9 +7545,9 @@ const EXCLUDED_EXTENSIONS = new Set([
7382
7545
  ".graphql",
7383
7546
  ".gql"
7384
7547
  ]);
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?$)/;
7548
+ const TEST_FILE_PATTERN = /(?:\.(?:test|spec|stories|story|cy|test-d)\.|(?:^|\/)__tests__\/)/;
7549
+ const EXCLUDED_DIRECTORY_PATTERN = /(?:^|\/)(?:e2e|cypress|playwright|__fixtures__|__snapshots__|public|scripts)\/(?!.*node_modules)/;
7550
+ 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
7551
  const hasExcludedExtension = (filePath) => {
7389
7552
  const lastDot = filePath.lastIndexOf(".");
7390
7553
  if (lastDot === -1) return false;
@@ -7460,6 +7623,7 @@ const detectDeadExports = (graph, config) => {
7460
7623
  if (usageMap.has(usageKey)) continue;
7461
7624
  if (module.localIdentifierReferences.includes(exportInfo.name)) continue;
7462
7625
  if (!exportInfo.isDefault && defaultExportLinkedNames.has(exportInfo.name)) continue;
7626
+ if (exportInfo.isDefault && exportInfo.defaultExportLocalName && usageMap.has(`${module.fileId.path}::${exportInfo.defaultExportLocalName}`)) continue;
7463
7627
  unusedExports.push({
7464
7628
  path: module.fileId.path,
7465
7629
  name: exportInfo.name,
@@ -7494,6 +7658,10 @@ const buildUsageMap = (graph) => {
7494
7658
  const targetModule = graph.modules[edge.target];
7495
7659
  if (!targetModule) continue;
7496
7660
  const sourceModule = graph.modules[edge.source];
7661
+ if (edge.isDynamic && edge.importedSymbols.length === 0) {
7662
+ markAllExportsUsedRecursive(targetModule, graph, sourceToTargetMap, usedExportKeys, /* @__PURE__ */ new Set());
7663
+ continue;
7664
+ }
7497
7665
  for (const symbol of edge.importedSymbols) if (symbol.isNamespace) handleNamespaceImport(sourceModule, targetModule, symbol.localName, graph, sourceToTargetMap, usedExportKeys);
7498
7666
  else {
7499
7667
  const importName = symbol.isDefault ? "default" : symbol.importedName;
@@ -7883,10 +8051,12 @@ const hasJsxFiles = (graph) => graph.modules.some((module) => {
7883
8051
  const filePath = module.fileId.path;
7884
8052
  return filePath.endsWith(".tsx") || filePath.endsWith(".jsx");
7885
8053
  });
8054
+ const KNOWN_PEER_DEPENDENCY_NAMES = new Map([["vitest-axe", ["axe-core"]], ["jest-axe", ["axe-core"]]]);
7886
8055
  const collectPeerSatisfiedPackages = (nodeModulesSearchRoots, declaredNames, confirmedUsedNames) => {
7887
8056
  const peerSatisfied = /* @__PURE__ */ new Set();
7888
8057
  for (const installedName of declaredNames) {
7889
8058
  if (!confirmedUsedNames.has(installedName)) continue;
8059
+ for (const knownPeerName of KNOWN_PEER_DEPENDENCY_NAMES.get(installedName) ?? []) if (declaredNames.has(knownPeerName)) peerSatisfied.add(knownPeerName);
7890
8060
  const installedPackageJsonPath = findInstalledPackageJsonPath(installedName, nodeModulesSearchRoots);
7891
8061
  if (!installedPackageJsonPath) continue;
7892
8062
  try {
@@ -7909,22 +8079,41 @@ const findInstalledPackageJsonPath = (packageName, nodeModulesSearchRoots) => {
7909
8079
  };
7910
8080
  const SHELL_SPLIT_PATTERN = /\s*(?:&&|\|\||[;&|])\s*/;
7911
8081
  const INLINE_ENV_VAR_PATTERN = /^[A-Z_][A-Z0-9_]*=/;
8082
+ const KNOWN_PACKAGE_BIN_NAMES = new Map([
8083
+ ["@tauri-apps/cli", ["tauri"]],
8084
+ ["@typescript/native-preview", ["tsgo"]],
8085
+ ["playwright-chromium", ["playwright"]],
8086
+ ["playwright-firefox", ["playwright"]],
8087
+ ["playwright-webkit", ["playwright"]]
8088
+ ]);
8089
+ const staticBinNamesForPackage = (packageName) => {
8090
+ const binNames = [...KNOWN_PACKAGE_BIN_NAMES.get(packageName) ?? []];
8091
+ const unscopedName = packageName.split("/").pop();
8092
+ if (unscopedName.endsWith("-cli") && unscopedName.length > 4) binNames.push(unscopedName.slice(0, -4));
8093
+ return binNames;
8094
+ };
7912
8095
  const buildBinaryPackageIndex = (nodeModulesSearchRoots, declaredNames) => {
7913
8096
  const binToPackage = /* @__PURE__ */ new Map();
7914
8097
  const packagesProvidingBinary = /* @__PURE__ */ new Set();
8098
+ const addBinMapping = (binaryName, packageName) => {
8099
+ const mappedPackages = binToPackage.get(binaryName) ?? /* @__PURE__ */ new Set();
8100
+ mappedPackages.add(packageName);
8101
+ binToPackage.set(binaryName, mappedPackages);
8102
+ };
7915
8103
  for (const packageName of declaredNames) {
8104
+ for (const staticBinName of staticBinNamesForPackage(packageName)) addBinMapping(staticBinName, packageName);
7916
8105
  const packageBinJsonPath = findInstalledPackageJsonPath(packageName, nodeModulesSearchRoots);
7917
8106
  if (!packageBinJsonPath) continue;
7918
8107
  try {
7919
8108
  const binContent = readFileSync(packageBinJsonPath, "utf-8");
7920
8109
  const binField = JSON.parse(binContent).bin;
7921
8110
  if (typeof binField === "string" && binField.length > 0) {
7922
- binToPackage.set(packageName.split("/").pop(), packageName);
8111
+ addBinMapping(packageName.split("/").pop(), packageName);
7923
8112
  packagesProvidingBinary.add(packageName);
7924
8113
  } else if (typeof binField === "object" && binField !== null) {
7925
8114
  const binaryNames = Object.keys(binField);
7926
8115
  if (binaryNames.length === 0) continue;
7927
- for (const binaryName of binaryNames) binToPackage.set(binaryName, packageName);
8116
+ for (const binaryName of binaryNames) addBinMapping(binaryName, packageName);
7928
8117
  packagesProvidingBinary.add(packageName);
7929
8118
  }
7930
8119
  } catch {
@@ -7969,8 +8158,7 @@ const collectCommandReferencedPackages = (command, declaredNames, binToPackage)
7969
8158
  const effectiveBinary = binaryToken === "npx" || binaryToken === "pnpx" || binaryToken === "bunx" ? tokens[binaryIndex + 1]?.replace(/^.*\//, "") ?? "" : binaryToken;
7970
8159
  for (const candidateBinary of [binaryToken, effectiveBinary]) {
7971
8160
  if (!candidateBinary) continue;
7972
- const mappedPackage = binToPackage.get(candidateBinary);
7973
- if (mappedPackage && declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
8161
+ for (const mappedPackage of binToPackage.get(candidateBinary) ?? []) if (declaredNames.has(mappedPackage)) referenced.add(mappedPackage);
7974
8162
  if (declaredNames.has(candidateBinary)) referenced.add(candidateBinary);
7975
8163
  }
7976
8164
  }
@@ -8050,6 +8238,12 @@ const collectConfigReferencedPackages = (rootDir, graph, declaredNames, summaryC
8050
8238
  deep: 6
8051
8239
  }, summaryCache);
8052
8240
  for (const documentationPath of documentationFiles) addMatchesFromFile(documentationPath, "importReference", matchesPackageImportReference);
8241
+ const toolingSourceFiles = globPackageFiles(rootDir, ["**/{.dumi,.storybook,.docz,.styleguidist}/**/*.{ts,tsx,js,jsx,mts,mjs}"], {
8242
+ ignore: ["**/node_modules/**"],
8243
+ dot: true,
8244
+ deep: 8
8245
+ }, summaryCache);
8246
+ for (const toolingSourcePath of toolingSourceFiles) addMatchesFromFile(toolingSourcePath, "importReference", matchesPackageImportReference);
8053
8247
  return referenced;
8054
8248
  };
8055
8249
  const PACKAGE_JSON_CONFIG_SECTIONS = [
@@ -8302,14 +8496,47 @@ const isAlwaysConsideredUsed = (dependencyName) => {
8302
8496
  //#endregion
8303
8497
  //#region src/report/cycles.ts
8304
8498
  const UNDEFINED_INDEX = -1;
8499
+ const isCompileTimeErasedEdge = (edge, graph) => {
8500
+ if (edge.isReExportEdge) return false;
8501
+ if (edge.importedSymbols.length === 0) return false;
8502
+ const targetModule = graph.modules[edge.target];
8503
+ if (!targetModule) return false;
8504
+ return edge.importedSymbols.every((symbol) => {
8505
+ if (symbol.isTypeOnly) return true;
8506
+ if (symbol.isNamespace) return false;
8507
+ const exportName = symbol.isDefault ? "default" : symbol.importedName;
8508
+ const matchingExports = targetModule.exports.filter((exportInfo) => exportInfo.name === exportName);
8509
+ return matchingExports.length > 0 && matchingExports.every((exportInfo) => exportInfo.isTypeOnly);
8510
+ });
8511
+ };
8305
8512
  const buildAdjacencyList = (graph) => {
8306
8513
  const targetSets = Array.from({ length: graph.modules.length }, () => /* @__PURE__ */ new Set());
8307
8514
  for (const edge of graph.edges) {
8515
+ if (edge.isDynamic) continue;
8308
8516
  if (edge.importedSymbols.every((symbol) => symbol.isTypeOnly)) continue;
8517
+ if (isCompileTimeErasedEdge(edge, graph)) continue;
8309
8518
  if (edge.target < graph.modules.length) targetSets[edge.source].add(edge.target);
8310
8519
  }
8311
8520
  return targetSets.map((targets) => [...targets]);
8312
8521
  };
8522
+ const buildModuleInitAccessEdgeSet = (graph) => {
8523
+ const initAccessEdges = /* @__PURE__ */ new Set();
8524
+ for (const edge of graph.edges) {
8525
+ if (edge.isDynamic || edge.isReExportEdge) continue;
8526
+ const topLevelReferences = graph.modules[edge.source]?.topLevelImportReferences;
8527
+ if (!topLevelReferences || topLevelReferences.length === 0) continue;
8528
+ if (edge.importedSymbols.some((symbol) => !symbol.isTypeOnly && topLevelReferences.includes(symbol.localName))) initAccessEdges.add(`${edge.source}:${edge.target}`);
8529
+ }
8530
+ return initAccessEdges;
8531
+ };
8532
+ const cycleHasModuleInitAccess = (cycle, initAccessEdges) => {
8533
+ for (let position = 0; position < cycle.length; position++) {
8534
+ const source = cycle[position];
8535
+ const target = cycle[(position + 1) % cycle.length];
8536
+ if (initAccessEdges.has(`${source}:${target}`)) return true;
8537
+ }
8538
+ return false;
8539
+ };
8313
8540
  const findStronglyConnectedComponents = (adjacencyList) => {
8314
8541
  const nodeCount = adjacencyList.length;
8315
8542
  if (nodeCount === 0) return [];
@@ -8430,6 +8657,7 @@ const enumerateElementaryCycles = (componentNodes, adjacencyList, graph) => {
8430
8657
  };
8431
8658
  const detectCycles = (graph) => {
8432
8659
  const adjacencyList = buildAdjacencyList(graph);
8660
+ const initAccessEdges = buildModuleInitAccessEdgeSet(graph);
8433
8661
  const components = findStronglyConnectedComponents(adjacencyList);
8434
8662
  const allCycles = [];
8435
8663
  const seenKeys = /* @__PURE__ */ new Set();
@@ -8439,6 +8667,7 @@ const detectCycles = (graph) => {
8439
8667
  if (component.length > 50) continue;
8440
8668
  const elementaryCycles = enumerateElementaryCycles(component, adjacencyList, graph);
8441
8669
  for (const cycle of elementaryCycles) {
8670
+ if (!cycleHasModuleInitAccess(cycle, initAccessEdges)) continue;
8442
8671
  const key = cycle.join(",");
8443
8672
  if (!seenKeys.has(key)) {
8444
8673
  seenKeys.add(key);
@@ -12463,7 +12692,7 @@ const isRecordValue = (value) => typeof value === "object" && value !== null &&
12463
12692
  const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
12464
12693
  const isOptionalArray = (value) => value === void 0 || Array.isArray(value);
12465
12694
  const emptyStore = (scopeHash) => ({
12466
- version: 2,
12695
+ version: 3,
12467
12696
  scopeHash,
12468
12697
  fileList: null,
12469
12698
  resolutions: null,
@@ -12473,10 +12702,10 @@ const emptyStore = (scopeHash) => ({
12473
12702
  const readPersistedStore = (cachePath, scopeHash) => {
12474
12703
  try {
12475
12704
  const parsed = JSON.parse(readFileSync(cachePath, "utf-8"));
12476
- if (isRecordValue(parsed) && parsed.version === 2 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12705
+ if (isRecordValue(parsed) && parsed.version === 3 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
12477
12706
  const persisted = parsed;
12478
12707
  return {
12479
- version: 2,
12708
+ version: 3,
12480
12709
  scopeHash,
12481
12710
  fileList: isRecordValue(persisted.fileList) ? persisted.fileList : null,
12482
12711
  resolutions: isRecordValue(persisted.resolutions) && typeof persisted.resolutions.hash === "string" && isRecordValue(persisted.resolutions.entries) ? persisted.resolutions : null,
@@ -12499,6 +12728,7 @@ const serializeParsedSource = (parsed, shouldPersistDryPatternFields) => ({
12499
12728
  memberAccesses: toPersistedArray(parsed.memberAccesses),
12500
12729
  wholeObjectUses: toPersistedArray(parsed.wholeObjectUses),
12501
12730
  localIdentifierReferences: toPersistedArray(intersectLocalReferencesWithOwnExports(parsed)),
12731
+ topLevelImportReferences: toPersistedArray(parsed.topLevelImportReferences),
12502
12732
  referencedFilenames: toPersistedArray(parsed.referencedFilenames),
12503
12733
  ...shouldPersistDryPatternFields ? {
12504
12734
  redundantTypePatterns: toPersistedArray(parsed.redundantTypePatterns),
@@ -12518,6 +12748,7 @@ const PERSISTED_SOURCE_ARRAY_FIELDS = [
12518
12748
  "memberAccesses",
12519
12749
  "wholeObjectUses",
12520
12750
  "localIdentifierReferences",
12751
+ "topLevelImportReferences",
12521
12752
  "referencedFilenames",
12522
12753
  "redundantTypePatterns",
12523
12754
  "identityWrappers",
@@ -12540,6 +12771,7 @@ const reviveParsedSource = (persisted) => {
12540
12771
  memberAccesses: source.memberAccesses ?? [],
12541
12772
  wholeObjectUses: source.wholeObjectUses ?? [],
12542
12773
  localIdentifierReferences: source.localIdentifierReferences ?? [],
12774
+ topLevelImportReferences: source.topLevelImportReferences ?? [],
12543
12775
  referencedFilenames: source.referencedFilenames ?? [],
12544
12776
  redundantTypePatterns: source.redundantTypePatterns ?? [],
12545
12777
  identityWrappers: source.identityWrappers ?? [],
@@ -12743,7 +12975,7 @@ const createSummaryCache = (cachePath, config) => {
12743
12975
  if (resolutionEntry !== void 0) compactedResolutions[resolutionKey] = resolutionEntry;
12744
12976
  }
12745
12977
  const serialized = JSON.stringify({
12746
- version: 2,
12978
+ version: 3,
12747
12979
  scopeHash,
12748
12980
  fileList: store.fileList,
12749
12981
  resolutions: {
@@ -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.2",
4
4
  "description": "Remove AI slop from JavaScript code.",
5
5
  "keywords": [
6
6
  "dead-code",