knip 5.70.2 → 5.72.0

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.
Files changed (86) hide show
  1. package/dist/ConfigurationChief.d.ts +6 -0
  2. package/dist/ConsoleStreamer.js +1 -1
  3. package/dist/DependencyDeputy.js +2 -2
  4. package/dist/ProjectPrincipal.js +1 -1
  5. package/dist/WorkspaceWorker.js +1 -1
  6. package/dist/binaries/bash-parser.js +14 -1
  7. package/dist/compilers/index.d.ts +10 -0
  8. package/dist/compilers/index.js +4 -1
  9. package/dist/compilers/scss.d.ts +6 -0
  10. package/dist/compilers/scss.js +14 -0
  11. package/dist/constants.js +1 -0
  12. package/dist/graph/analyze.js +38 -22
  13. package/dist/graph-explorer/constants.d.ts +2 -0
  14. package/dist/graph-explorer/constants.js +2 -0
  15. package/dist/graph-explorer/explorer.d.ts +11 -0
  16. package/dist/graph-explorer/explorer.js +10 -0
  17. package/dist/graph-explorer/operations/build-exports-tree.d.ts +15 -0
  18. package/dist/graph-explorer/operations/build-exports-tree.js +96 -0
  19. package/dist/graph-explorer/operations/has-strictly-ns-references.d.ts +2 -0
  20. package/dist/graph-explorer/operations/has-strictly-ns-references.js +98 -0
  21. package/dist/graph-explorer/operations/is-referenced.d.ts +4 -0
  22. package/dist/graph-explorer/operations/is-referenced.js +89 -0
  23. package/dist/graph-explorer/utils.d.ts +3 -0
  24. package/dist/graph-explorer/utils.js +13 -0
  25. package/dist/graph-explorer/visitors.d.ts +12 -0
  26. package/dist/graph-explorer/visitors.js +30 -0
  27. package/dist/graph-explorer/walk-down.d.ts +5 -0
  28. package/dist/graph-explorer/walk-down.js +116 -0
  29. package/dist/index.js +2 -98
  30. package/dist/plugins/angular/index.js +3 -3
  31. package/dist/plugins/index.d.ts +1 -0
  32. package/dist/plugins/index.js +2 -0
  33. package/dist/plugins/next/index.js +4 -3
  34. package/dist/plugins/next/resolveFromAST.d.ts +1 -0
  35. package/dist/plugins/next/resolveFromAST.js +42 -1
  36. package/dist/plugins/rsbuild/index.js +8 -0
  37. package/dist/plugins/rsbuild/types.d.ts +1 -0
  38. package/dist/plugins/storybook/index.js +12 -4
  39. package/dist/plugins/storybook/types.d.ts +5 -0
  40. package/dist/plugins/svgo/index.js +1 -1
  41. package/dist/plugins/svgr/index.d.ts +3 -0
  42. package/dist/plugins/svgr/index.js +24 -0
  43. package/dist/plugins/svgr/types.d.ts +3 -0
  44. package/dist/plugins/svgr/types.js +1 -0
  45. package/dist/plugins/vite/helpers.js +1 -1
  46. package/dist/plugins/vite/index.js +4 -0
  47. package/dist/plugins/vitest/index.js +1 -1
  48. package/dist/reporters/codeclimate.js +3 -7
  49. package/dist/reporters/util/util.d.ts +2 -1
  50. package/dist/reporters/util/util.js +1 -0
  51. package/dist/reporters/watch.js +1 -1
  52. package/dist/run.d.ts +25 -0
  53. package/dist/run.js +107 -0
  54. package/dist/schema/configuration.d.ts +10 -0
  55. package/dist/schema/plugins.d.ts +5 -0
  56. package/dist/schema/plugins.js +1 -0
  57. package/dist/types/PluginNames.d.ts +2 -2
  58. package/dist/types/PluginNames.js +1 -0
  59. package/dist/types/issues.d.ts +4 -4
  60. package/dist/types/module-graph.d.ts +11 -13
  61. package/dist/typescript/ast-helpers.d.ts +5 -1
  62. package/dist/typescript/ast-helpers.js +47 -2
  63. package/dist/typescript/get-imports-and-exports.js +78 -12
  64. package/dist/typescript/resolve-module-names.js +5 -4
  65. package/dist/typescript/visitors/dynamic-imports/importCall.js +5 -19
  66. package/dist/util/Performance.js +4 -2
  67. package/dist/util/create-options.d.ts +10 -0
  68. package/dist/util/create-options.js +8 -4
  69. package/dist/util/file-entry-cache.js +1 -1
  70. package/dist/util/graph-sequencer.js +1 -1
  71. package/dist/util/module-graph.js +7 -8
  72. package/dist/util/resolve.d.ts +3 -2
  73. package/dist/util/resolve.js +25 -2
  74. package/dist/util/trace.d.ts +2 -20
  75. package/dist/util/trace.js +41 -66
  76. package/dist/util/watch.d.ts +19 -3
  77. package/dist/util/watch.js +28 -17
  78. package/dist/version.d.ts +1 -1
  79. package/dist/version.js +1 -1
  80. package/package.json +7 -7
  81. package/schema.json +4 -0
  82. package/vendor/bash-parser/index.d.ts +6 -1
  83. package/dist/util/has-strictly-ns-references.d.ts +0 -4
  84. package/dist/util/has-strictly-ns-references.js +0 -103
  85. package/dist/util/is-identifier-referenced.d.ts +0 -9
  86. package/dist/util/is-identifier-referenced.js +0 -145
@@ -527,6 +527,11 @@ export declare class ConfigurationChief {
527
527
  entry?: string | string[] | undefined;
528
528
  project?: string | string[] | undefined;
529
529
  } | undefined;
530
+ svgr?: string | boolean | string[] | {
531
+ config?: string | string[] | undefined;
532
+ entry?: string | string[] | undefined;
533
+ project?: string | string[] | undefined;
534
+ } | undefined;
530
535
  syncpack?: string | boolean | string[] | {
531
536
  config?: string | string[] | undefined;
532
537
  entry?: string | string[] | undefined;
@@ -761,6 +766,7 @@ export declare class ConfigurationChief {
761
766
  stylelint?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
762
767
  svelte?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
763
768
  svgo?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
769
+ svgr?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
764
770
  syncpack?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
765
771
  tailwind?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
766
772
  taskfile?: (boolean | import("./types/config.js").EnsuredPluginConfiguration) | undefined;
@@ -3,7 +3,7 @@ export class ConsoleStreamer {
3
3
  isWatch = false;
4
4
  lines = 0;
5
5
  constructor(options) {
6
- this.isEnabled = options.isShowProgress && !options.isDebug;
6
+ this.isEnabled = options.isShowProgress;
7
7
  this.isWatch = options.isWatch;
8
8
  }
9
9
  clearLines(count) {
@@ -174,7 +174,7 @@ export class DependencyDeputy {
174
174
  const dependencyIssues = [];
175
175
  const devDependencyIssues = [];
176
176
  const optionalPeerDependencyIssues = [];
177
- for (const [workspace, { manifestPath: filePath, manifestStr }] of this._manifests.entries()) {
177
+ for (const [workspace, { manifestPath: filePath, manifestStr }] of this._manifests) {
178
178
  const referencedDependencies = this.referencedDependencies.get(workspace);
179
179
  const hasTypesIncluded = this.getHasTypesIncluded(workspace);
180
180
  const peeker = new PackagePeeker(manifestStr);
@@ -342,7 +342,7 @@ export class DependencyDeputy {
342
342
  }
343
343
  getConfigurationHints() {
344
344
  const configurationHints = new Set();
345
- for (const [workspaceName, manifest] of this._manifests.entries()) {
345
+ for (const [workspaceName, manifest] of this._manifests) {
346
346
  for (const identifier of manifest.unusedIgnoreDependencies) {
347
347
  configurationHints.add({ workspaceName, identifier, type: 'ignoreDependencies' });
348
348
  }
@@ -217,7 +217,7 @@ export class ProjectPrincipal {
217
217
  return externalRefs.length > 0;
218
218
  }
219
219
  reconcileCache(graph) {
220
- for (const [filePath, file] of graph.entries()) {
220
+ for (const [filePath, file] of graph) {
221
221
  const fd = this.cache.getFileDescriptor(filePath);
222
222
  if (!fd?.meta)
223
223
  continue;
@@ -319,7 +319,7 @@ export class WorkspaceWorker {
319
319
  const configFiles = this.configFilesMap.get(wsName);
320
320
  if (configFiles) {
321
321
  do {
322
- for (const [pluginName, dependencies] of configFiles.entries()) {
322
+ for (const [pluginName, dependencies] of configFiles) {
323
323
  configFiles.delete(pluginName);
324
324
  if (this.enabledPlugins.includes(pluginName)) {
325
325
  for (const input of await runPlugin(pluginName, Array.from(dependencies)))
@@ -25,6 +25,12 @@ export const getDependenciesFromScript = (script, options) => {
25
25
  knownBinsOnly: false,
26
26
  });
27
27
  };
28
+ const definedFunctions = new Set();
29
+ const collectFunctionNames = (nodes) => {
30
+ for (const node of nodes)
31
+ if (node.type === 'Function')
32
+ definedFunctions.add(node.name.text);
33
+ };
28
34
  const getDependenciesFromNodes = (nodes) => nodes.flatMap(node => {
29
35
  switch (node.type) {
30
36
  case 'Command': {
@@ -41,6 +47,8 @@ export const getDependenciesFromScript = (script, options) => {
41
47
  return [];
42
48
  if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
43
49
  return [];
50
+ if (definedFunctions.has(binary))
51
+ return [];
44
52
  const args = node.suffix?.map(arg => arg.text) ?? [];
45
53
  if (['!', 'test'].includes(binary))
46
54
  return fromArgs(args);
@@ -82,13 +90,18 @@ export const getDependenciesFromScript = (script, options) => {
82
90
  return getDependenciesFromNodes(node.commands);
83
91
  case 'Function':
84
92
  return getDependenciesFromNodes(node.body.commands);
93
+ case 'Subshell':
94
+ return getDependenciesFromNodes(node.list.commands);
85
95
  default:
86
96
  return [];
87
97
  }
88
98
  });
89
99
  try {
90
100
  const parsed = parse(script);
91
- return parsed?.commands ? getDependenciesFromNodes(parsed.commands) : [];
101
+ if (!parsed?.commands)
102
+ return [];
103
+ collectFunctionNames(parsed.commands);
104
+ return getDependenciesFromNodes(parsed.commands);
92
105
  }
93
106
  catch (error) {
94
107
  const msg = `Warning: failed to parse and ignoring script in ${relative(options.cwd, options.containingFilePath)} (${truncate(script, 30)})`;
@@ -479,6 +479,11 @@ export declare const partitionCompilers: (rawLocalConfig: RawConfiguration) => {
479
479
  entry?: string | string[] | undefined;
480
480
  project?: string | string[] | undefined;
481
481
  } | undefined;
482
+ svgr?: string | boolean | string[] | {
483
+ config?: string | string[] | undefined;
484
+ entry?: string | string[] | undefined;
485
+ project?: string | string[] | undefined;
486
+ } | undefined;
482
487
  syncpack?: string | boolean | string[] | {
483
488
  config?: string | string[] | undefined;
484
489
  entry?: string | string[] | undefined;
@@ -1075,6 +1080,11 @@ export declare const partitionCompilers: (rawLocalConfig: RawConfiguration) => {
1075
1080
  entry?: string | string[] | undefined;
1076
1081
  project?: string | string[] | undefined;
1077
1082
  } | undefined;
1083
+ svgr?: string | boolean | string[] | {
1084
+ config?: string | string[] | undefined;
1085
+ entry?: string | string[] | undefined;
1086
+ project?: string | string[] | undefined;
1087
+ } | undefined;
1078
1088
  syncpack?: string | boolean | string[] | {
1079
1089
  config?: string | string[] | undefined;
1080
1090
  entry?: string | string[] | undefined;
@@ -2,6 +2,7 @@ import Astro from './astro.js';
2
2
  import AstroMDX from './astro-mdx.js';
3
3
  import MDX from './mdx.js';
4
4
  import Prisma from './prisma.js';
5
+ import SCSS from './scss.js';
5
6
  import Svelte from './svelte.js';
6
7
  import CSS from './tailwind.js';
7
8
  import Vue from './vue.js';
@@ -33,12 +34,14 @@ const compilers = new Map([
33
34
  ['.css', CSS],
34
35
  ['.mdx', MDX],
35
36
  ['.prisma', Prisma],
37
+ ['.sass', SCSS],
38
+ ['.scss', SCSS],
36
39
  ['.svelte', Svelte],
37
40
  ['.vue', Vue],
38
41
  ]);
39
42
  export const getIncludedCompilers = (syncCompilers, asyncCompilers, dependencies) => {
40
43
  const hasDependency = (packageName) => dependencies.has(packageName);
41
- for (const [extension, { condition, compiler }] of compilers.entries()) {
44
+ for (const [extension, { condition, compiler }] of compilers) {
42
45
  if (extension === '.mdx' && AstroMDX.condition(hasDependency)) {
43
46
  syncCompilers.set(extension, AstroMDX.compiler);
44
47
  }
@@ -0,0 +1,6 @@
1
+ import type { HasDependency } from './types.js';
2
+ declare const _default: {
3
+ condition: (hasDependency: HasDependency) => boolean;
4
+ compiler: (text: string) => string;
5
+ };
6
+ export default _default;
@@ -0,0 +1,14 @@
1
+ const condition = (hasDependency) => hasDependency('sass') || hasDependency('sass-embedded') || hasDependency('node-sass');
2
+ const importMatcher = /@(?:use|import|forward)\s+['"](pkg:)?([^'"]+)['"]/g;
3
+ const toRelative = (specifier) => (specifier.startsWith('.') ? specifier : `./${specifier}`);
4
+ const compiler = (text) => {
5
+ const imports = [];
6
+ let match;
7
+ let index = 0;
8
+ while ((match = importMatcher.exec(text))) {
9
+ if (match[2])
10
+ imports.push(`import _$${index++} from '${match[1] ? match[2] : toRelative(match[2])}';`);
11
+ }
12
+ return imports.join('\n');
13
+ };
14
+ export default { condition, compiler };
package/dist/constants.js CHANGED
@@ -89,6 +89,7 @@ export const IGNORED_GLOBAL_BINARIES = new Set([
89
89
  'rmdir',
90
90
  'rsync',
91
91
  'scp',
92
+ 'sed',
92
93
  'seq',
93
94
  'set',
94
95
  'sh',
@@ -1,23 +1,24 @@
1
- import { getType, hasStrictlyEnumReferences, hasStrictlyNsReferences } from '../util/has-strictly-ns-references.js';
2
- import { getIsIdentifierReferencedHandler } from '../util/is-identifier-referenced.js';
1
+ import { createGraphExplorer } from '../graph-explorer/explorer.js';
2
+ import { getIssueType, hasStrictlyEnumReferences } from '../graph-explorer/utils.js';
3
3
  import { getPackageNameFromModuleSpecifier } from '../util/modules.js';
4
+ import { toRelative } from '../util/path.js';
4
5
  import { findMatch } from '../util/regex.js';
5
6
  import { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from '../util/tag.js';
6
- import { createAndPrintTrace, printTrace } from '../util/trace.js';
7
+ import { formatTrace } from '../util/trace.js';
7
8
  export const analyze = async ({ analyzedFiles, counselor, chief, collector, deputy, entryPaths, factory, graph, streamer, unreferencedFiles, options, }) => {
8
9
  const shouldIgnore = getShouldIgnoreHandler(options.isProduction);
9
10
  const shouldIgnoreTags = getShouldIgnoreTagHandler(options.tags);
10
- const isIdentifierReferenced = getIsIdentifierReferencedHandler(graph, entryPaths, options.isTrace);
11
+ const explorer = createGraphExplorer(graph, entryPaths);
11
12
  const ignoreExportsUsedInFile = chief.config.ignoreExportsUsedInFile;
12
- const isExportedItemReferenced = (exportedItem) => exportedItem.refs[1] ||
13
- (exportedItem.refs[0] > 0 &&
13
+ const isExportReferencedInFile = (exportedItem) => exportedItem.self[1] ||
14
+ (exportedItem.self[0] > 0 &&
14
15
  (typeof ignoreExportsUsedInFile === 'object'
15
16
  ? exportedItem.type !== 'unknown' && !!ignoreExportsUsedInFile[exportedItem.type]
16
17
  : ignoreExportsUsedInFile));
17
18
  const analyzeGraph = async () => {
18
19
  if (options.isReportValues || options.isReportTypes) {
19
20
  streamer.cast('Connecting the dots');
20
- for (const [filePath, file] of graph.entries()) {
21
+ for (const [filePath, file] of graph) {
21
22
  const exportItems = file.exports;
22
23
  if (!exportItems || exportItems.size === 0)
23
24
  continue;
@@ -27,17 +28,18 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
27
28
  const principal = factory.getPrincipalByPackageName(workspace.pkgName);
28
29
  const isEntry = entryPaths.has(filePath);
29
30
  if (!isIncludeEntryExports && isEntry) {
30
- createAndPrintTrace(filePath, options, { isEntry });
31
31
  continue;
32
32
  }
33
33
  const importsForExport = file.imported;
34
- for (const [identifier, exportedItem] of exportItems.entries()) {
34
+ for (const [identifier, exportedItem] of exportItems) {
35
35
  if (shouldIgnore(exportedItem.jsDocTags))
36
36
  continue;
37
37
  const isIgnored = shouldIgnoreTags(exportedItem.jsDocTags);
38
38
  if (importsForExport) {
39
- const { isReferenced, reExportingEntryFile, traceNode } = isIdentifierReferenced(filePath, identifier, isIncludeEntryExports);
40
- if ((isReferenced || exportedItem.refs[1]) && isIgnored) {
39
+ const [isReferenced, reExportingEntryFile] = explorer.isReferenced(filePath, identifier, {
40
+ includeEntryExports: isIncludeEntryExports,
41
+ });
42
+ if ((isReferenced || exportedItem.self[1]) && isIgnored) {
41
43
  for (const tagName of exportedItem.jsDocTags) {
42
44
  if (options.tags[1].includes(tagName.replace(/^@/, ''))) {
43
45
  collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
@@ -46,17 +48,14 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
46
48
  }
47
49
  if (isIgnored)
48
50
  continue;
49
- if (reExportingEntryFile) {
51
+ if (reExportingEntryFile && !isReferenced) {
50
52
  if (!isIncludeEntryExports) {
51
- createAndPrintTrace(filePath, options, { identifier, isEntry, hasRef: isReferenced });
52
53
  continue;
53
54
  }
54
55
  const reExportedItem = graph.get(reExportingEntryFile)?.exports.get(identifier);
55
56
  if (reExportedItem && shouldIgnore(reExportedItem.jsDocTags))
56
57
  continue;
57
58
  }
58
- if (traceNode)
59
- printTrace(traceNode, filePath, options, identifier);
60
59
  if (isReferenced) {
61
60
  if (options.includedIssueTypes.enumMembers && exportedItem.type === 'enum') {
62
61
  if (!options.includedIssueTypes.nsTypes && importsForExport.refs.has(identifier))
@@ -68,11 +67,13 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
68
67
  continue;
69
68
  if (shouldIgnore(member.jsDocTags))
70
69
  continue;
71
- if (member.refs[0] === 0) {
70
+ if (member.self[0] === 0) {
72
71
  const id = `${identifier}.${member.identifier}`;
73
- const { isReferenced } = isIdentifierReferenced(filePath, id, true);
72
+ const [isMemberReferenced] = explorer.isReferenced(filePath, id, {
73
+ includeEntryExports: true,
74
+ });
74
75
  const isIgnored = shouldIgnoreTags(member.jsDocTags);
75
- if (!isReferenced) {
76
+ if (!isMemberReferenced) {
76
77
  if (isIgnored)
77
78
  continue;
78
79
  collector.addIssue({
@@ -125,17 +126,17 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
125
126
  continue;
126
127
  }
127
128
  }
128
- const [hasStrictlyNsRefs, namespace] = hasStrictlyNsReferences(graph, importsForExport, identifier);
129
+ const [hasStrictlyNsRefs, namespace] = explorer.hasStrictlyNsReferences(filePath, identifier);
129
130
  const isType = ['enum', 'type', 'interface'].includes(exportedItem.type);
130
131
  if (hasStrictlyNsRefs &&
131
132
  ((!options.includedIssueTypes.nsTypes && isType) || !(options.includedIssueTypes.nsExports || isType)))
132
133
  continue;
133
- if (!isExportedItemReferenced(exportedItem)) {
134
+ if (!isExportReferencedInFile(exportedItem)) {
134
135
  if (isIgnored)
135
136
  continue;
136
137
  if (!options.isSkipLibs && principal?.hasExternalReferences(filePath, exportedItem))
137
138
  continue;
138
- const type = getType(hasStrictlyNsRefs, isType);
139
+ const type = getIssueType(hasStrictlyNsRefs, isType);
139
140
  collector.addIssue({
140
141
  type,
141
142
  filePath,
@@ -153,7 +154,7 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
153
154
  }
154
155
  }
155
156
  }
156
- for (const [filePath, file] of graph.entries()) {
157
+ for (const [filePath, file] of graph) {
157
158
  const ws = chief.findWorkspaceByFilePath(filePath);
158
159
  if (ws) {
159
160
  if (file.duplicates && options.includedIssueTypes.duplicates) {
@@ -227,5 +228,20 @@ export const analyze = async ({ analyzedFiles, counselor, chief, collector, depu
227
228
  collector.addConfigurationHint(hint);
228
229
  };
229
230
  await analyzeGraph();
231
+ if (options.isTrace) {
232
+ const nodes = explorer.buildExportsTree({ filePath: options.traceFile, identifier: options.traceExport });
233
+ nodes.sort((a, b) => a.filePath.localeCompare(b.filePath) || a.identifier.localeCompare(b.identifier));
234
+ const toRel = (path) => toRelative(path, options.cwd);
235
+ const isReferenced = (node) => {
236
+ if (explorer.isReferenced(node.filePath, node.identifier, { includeEntryExports: false })[0])
237
+ return true;
238
+ if (explorer.hasStrictlyNsReferences(node.filePath, node.identifier)[0])
239
+ return true;
240
+ const exportItem = graph.get(node.filePath)?.exports.get(node.identifier);
241
+ return exportItem ? isExportReferencedInFile(exportItem) : false;
242
+ };
243
+ for (const node of nodes)
244
+ console.log(formatTrace(node, toRel, isReferenced(node)));
245
+ }
230
246
  return analyzeGraph;
231
247
  };
@@ -0,0 +1,2 @@
1
+ export declare const CONTINUE = "continue";
2
+ export declare const STOP = "stop";
@@ -0,0 +1,2 @@
1
+ export const CONTINUE = 'continue';
2
+ export const STOP = 'stop';
@@ -0,0 +1,11 @@
1
+ import type { ModuleGraph } from '../types/module-graph.js';
2
+ export declare const createGraphExplorer: (graph: ModuleGraph, entryPaths: Set<string>) => {
3
+ isReferenced: (filePath: string, identifier: string, options: {
4
+ includeEntryExports: boolean;
5
+ }) => [boolean, string | undefined];
6
+ hasStrictlyNsReferences: (filePath: string, identifier: string) => [boolean, (string | undefined)?];
7
+ buildExportsTree: (options: {
8
+ filePath?: string;
9
+ identifier?: string;
10
+ }) => import("./operations/build-exports-tree.js").TreeNode[];
11
+ };
@@ -0,0 +1,10 @@
1
+ import { buildExportsTree } from './operations/build-exports-tree.js';
2
+ import { hasStrictlyNsReferences } from './operations/has-strictly-ns-references.js';
3
+ import { isReferenced } from './operations/is-referenced.js';
4
+ export const createGraphExplorer = (graph, entryPaths) => {
5
+ return {
6
+ isReferenced: (filePath, identifier, options) => isReferenced(graph, entryPaths, filePath, identifier, options),
7
+ hasStrictlyNsReferences: (filePath, identifier) => hasStrictlyNsReferences(graph, graph.get(filePath)?.imported, identifier),
8
+ buildExportsTree: (options) => buildExportsTree(graph, entryPaths, options),
9
+ };
10
+ };
@@ -0,0 +1,15 @@
1
+ import type { Identifier, ModuleGraph } from '../../types/module-graph.js';
2
+ import type { Via } from '../walk-down.js';
3
+ export interface TreeNode {
4
+ filePath: string;
5
+ identifier: string;
6
+ originalId: string | undefined;
7
+ via: Via | undefined;
8
+ refs: string[];
9
+ isEntry: boolean;
10
+ children: TreeNode[];
11
+ }
12
+ export declare const buildExportsTree: (graph: ModuleGraph, entryPaths: Set<string>, options: {
13
+ filePath?: string;
14
+ identifier?: Identifier;
15
+ }) => TreeNode[];
@@ -0,0 +1,96 @@
1
+ import { CONTINUE } from '../constants.js';
2
+ import { walkDown } from '../walk-down.js';
3
+ export const buildExportsTree = (graph, entryPaths, options) => {
4
+ const traces = [];
5
+ const processFile = (filePath, file) => {
6
+ for (const exportId of options.identifier ? [options.identifier] : file.exports.keys()) {
7
+ if (!options.identifier || file.exports.has(exportId)) {
8
+ const trace = buildExportTree(graph, entryPaths, filePath, exportId);
9
+ if (trace)
10
+ traces.push(trace);
11
+ }
12
+ }
13
+ };
14
+ if (options.filePath) {
15
+ const file = graph.get(options.filePath);
16
+ if (file)
17
+ processFile(options.filePath, file);
18
+ }
19
+ else {
20
+ for (const [filePath, file] of graph)
21
+ processFile(filePath, file);
22
+ }
23
+ return traces;
24
+ };
25
+ const buildExportTree = (graph, entryPaths, filePath, identifier) => {
26
+ const file = graph.get(filePath);
27
+ const rootNode = {
28
+ filePath,
29
+ identifier,
30
+ refs: filterRefs(file?.imported?.refs, identifier),
31
+ isEntry: entryPaths.has(filePath),
32
+ children: [],
33
+ originalId: undefined,
34
+ via: undefined,
35
+ };
36
+ const nodeMap = new Map();
37
+ nodeMap.set(`${filePath}:${identifier}`, rootNode);
38
+ walkDown(graph, filePath, identifier, (sourceFile, sourceId, importingFile, id, isEntry, via) => {
39
+ const importMaps = graph.get(importingFile)?.imports.internal.get(sourceFile);
40
+ const importRefs = importMaps?.refs;
41
+ const ns = id.split('.')[0];
42
+ if (via === 'importNS' && !hasRelevantRef(importRefs, id) && !isNsReExported(importMaps, ns))
43
+ return CONTINUE;
44
+ const key = `${importingFile}:${id}`;
45
+ const isRenamed = via.endsWith('As') && sourceId !== ns;
46
+ const refs = filterRefs(importRefs, id);
47
+ const childNode = nodeMap.get(key) ?? {
48
+ filePath: importingFile,
49
+ identifier: id,
50
+ originalId: isRenamed ? sourceId : undefined,
51
+ via,
52
+ refs,
53
+ isEntry,
54
+ children: [],
55
+ };
56
+ nodeMap.set(key, childNode);
57
+ let parentNode = nodeMap.get(`${sourceFile}:${sourceId}`);
58
+ if (!parentNode) {
59
+ for (const [k, v] of nodeMap) {
60
+ if (k.startsWith(`${sourceFile}:${sourceId}.`) || k === `${sourceFile}:${sourceId}`) {
61
+ parentNode = v;
62
+ break;
63
+ }
64
+ }
65
+ }
66
+ (parentNode ?? rootNode).children.push(childNode);
67
+ return CONTINUE;
68
+ }, entryPaths);
69
+ pruneReExportStarOnlyBranches(rootNode);
70
+ return rootNode;
71
+ };
72
+ const filterRefs = (refs, id) => {
73
+ if (!refs)
74
+ return [];
75
+ return Array.from(refs).filter(ref => id === ref || id.startsWith(`${ref}.`) || ref.startsWith(`${id}.`));
76
+ };
77
+ const hasRelevantRef = (refs, id) => {
78
+ if (!refs || refs.size === 0)
79
+ return false;
80
+ return Array.from(refs).some(ref => ref === id || ref.startsWith(`${id}.`));
81
+ };
82
+ const isNsReExported = (importMaps, ns) => {
83
+ if (!importMaps)
84
+ return false;
85
+ return importMaps.reExportedAs.has(ns) || importMaps.reExportedNs.has(ns);
86
+ };
87
+ const hasNonReExportStar = (node) => {
88
+ if (node.via && node.via !== 'reExportStar')
89
+ return true;
90
+ return node.children.some(child => hasNonReExportStar(child));
91
+ };
92
+ const pruneReExportStarOnlyBranches = (node) => {
93
+ node.children = node.children.filter(child => hasNonReExportStar(child));
94
+ for (const child of node.children)
95
+ pruneReExportStarOnlyBranches(child);
96
+ };
@@ -0,0 +1,2 @@
1
+ import type { ImportMaps, ModuleGraph } from '../../types/module-graph.js';
2
+ export declare const hasStrictlyNsReferences: (graph: ModuleGraph, importsForExport: ImportMaps | undefined, identifier: string) => [boolean, string?];
@@ -0,0 +1,98 @@
1
+ import { forEachAliasReExport, forEachNamespaceReExport, forEachPassThroughReExport, getStarReExportSources, } from '../visitors.js';
2
+ export const hasStrictlyNsReferences = (graph, importsForExport, identifier) => {
3
+ if (!importsForExport)
4
+ return [false];
5
+ let foundNamespace;
6
+ const aliasByIdentifier = new Map();
7
+ const namespaceReExports = new Map();
8
+ const namespaceEdges = [];
9
+ const directById = new Map();
10
+ forEachPassThroughReExport(importsForExport, (id, sources) => {
11
+ directById.set(id, sources);
12
+ });
13
+ forEachAliasReExport(importsForExport, (id, alias, sources) => {
14
+ let arr = aliasByIdentifier.get(id);
15
+ if (!arr) {
16
+ arr = [];
17
+ aliasByIdentifier.set(id, arr);
18
+ }
19
+ arr.push({ alias, sources });
20
+ });
21
+ forEachNamespaceReExport(importsForExport, (namespace, sources) => {
22
+ namespaceEdges.push({ namespace, sources });
23
+ let arr = namespaceReExports.get(namespace);
24
+ if (!arr) {
25
+ arr = [];
26
+ namespaceReExports.set(namespace, arr);
27
+ }
28
+ arr.push(sources);
29
+ });
30
+ const starSources = getStarReExportSources(importsForExport);
31
+ const followReExports = (sources, nextId, propagateNamespace = true) => {
32
+ for (const filePath of sources) {
33
+ const file = graph.get(filePath);
34
+ if (!file?.imported)
35
+ continue;
36
+ const result = hasStrictlyNsReferences(graph, file.imported, nextId);
37
+ if (result[0] === false)
38
+ return result;
39
+ if (propagateNamespace && result[1])
40
+ foundNamespace = result[1];
41
+ }
42
+ return undefined;
43
+ };
44
+ for (const ns of importsForExport.importedNs.keys()) {
45
+ const hasNsRef = importsForExport.refs.has(ns);
46
+ if (!hasNsRef)
47
+ return [false, ns];
48
+ for (const ref of importsForExport.refs) {
49
+ if (ref.startsWith(`${ns}.`))
50
+ return [false, ns];
51
+ }
52
+ const nsReExports = namespaceReExports.get(ns);
53
+ if (nsReExports) {
54
+ for (const sources of nsReExports) {
55
+ const result = followReExports(sources, identifier, false);
56
+ if (result)
57
+ return result;
58
+ }
59
+ }
60
+ const nsAliases = aliasByIdentifier.get(ns);
61
+ if (nsAliases) {
62
+ for (const { sources } of nsAliases) {
63
+ const result = followReExports(sources, identifier, false);
64
+ if (result)
65
+ return result;
66
+ }
67
+ }
68
+ foundNamespace = ns;
69
+ }
70
+ const directSources = directById.get(identifier);
71
+ if (directSources) {
72
+ const result = followReExports(directSources, identifier, true);
73
+ if (result)
74
+ return result;
75
+ }
76
+ if (starSources) {
77
+ const result = followReExports(starSources, identifier, true);
78
+ if (result)
79
+ return result;
80
+ }
81
+ const [id, ...rest] = identifier.split('.');
82
+ const aliasEntries = aliasByIdentifier.get(id);
83
+ if (aliasEntries) {
84
+ for (const { alias, sources } of aliasEntries) {
85
+ const result = followReExports(sources, [alias, ...rest].join('.'), true);
86
+ if (result)
87
+ return result;
88
+ }
89
+ }
90
+ for (const { namespace: ns, sources } of namespaceEdges) {
91
+ const result = followReExports(sources, `${ns}.${identifier}`, true);
92
+ if (result)
93
+ return result;
94
+ }
95
+ if (foundNamespace)
96
+ return [true, foundNamespace];
97
+ return [false];
98
+ };
@@ -0,0 +1,4 @@
1
+ import type { Identifier, ModuleGraph } from '../../types/module-graph.js';
2
+ export declare const isReferenced: (graph: ModuleGraph, entryPaths: Set<string>, filePath: string, id: Identifier, options: {
3
+ includeEntryExports: boolean;
4
+ }) => [boolean, string | undefined];