knip 5.74.0 → 5.75.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +1 -0
  2. package/dist/ConfigurationChief.d.ts +1 -0
  3. package/dist/ConfigurationChief.js +9 -0
  4. package/dist/IssueCollector.d.ts +1 -0
  5. package/dist/ProjectPrincipal.js +2 -3
  6. package/dist/constants.d.ts +1 -0
  7. package/dist/constants.js +1 -0
  8. package/dist/graph/build.js +18 -6
  9. package/dist/graph-explorer/cache.d.ts +10 -0
  10. package/dist/graph-explorer/cache.js +72 -0
  11. package/dist/graph-explorer/constants.d.ts +7 -0
  12. package/dist/graph-explorer/constants.js +7 -0
  13. package/dist/graph-explorer/explorer.d.ts +6 -0
  14. package/dist/graph-explorer/explorer.js +10 -0
  15. package/dist/graph-explorer/operations/find-cycles.d.ts +3 -0
  16. package/dist/graph-explorer/operations/find-cycles.js +38 -0
  17. package/dist/graph-explorer/operations/get-contention.d.ts +3 -0
  18. package/dist/graph-explorer/operations/get-contention.js +142 -0
  19. package/dist/graph-explorer/operations/get-usage.d.ts +13 -0
  20. package/dist/graph-explorer/operations/get-usage.js +34 -0
  21. package/dist/graph-explorer/operations/resolve-definition.d.ts +16 -0
  22. package/dist/graph-explorer/operations/resolve-definition.js +38 -0
  23. package/dist/graph-explorer/utils.d.ts +3 -1
  24. package/dist/graph-explorer/utils.js +62 -1
  25. package/dist/graph-explorer/walk-up.d.ts +6 -0
  26. package/dist/graph-explorer/walk-up.js +67 -0
  27. package/dist/reporters/util/configuration-hints.d.ts +9 -1
  28. package/dist/reporters/util/configuration-hints.js +31 -28
  29. package/dist/run.d.ts +9 -6
  30. package/dist/run.js +7 -7
  31. package/dist/session/build-maps.d.ts +6 -0
  32. package/dist/session/build-maps.js +150 -0
  33. package/dist/session/file-descriptor.d.ts +6 -0
  34. package/dist/session/file-descriptor.js +43 -0
  35. package/dist/session/index.d.ts +10 -0
  36. package/dist/session/index.js +6 -0
  37. package/dist/session/session.d.ts +20 -0
  38. package/dist/session/session.js +18 -0
  39. package/dist/session/types.d.ts +35 -0
  40. package/dist/session/types.js +1 -0
  41. package/dist/types/issues.d.ts +1 -1
  42. package/dist/types/module-graph.d.ts +3 -3
  43. package/dist/types/options.d.ts +2 -0
  44. package/dist/typescript/SourceFileManager.d.ts +3 -0
  45. package/dist/typescript/SourceFileManager.js +9 -0
  46. package/dist/typescript/create-hosts.js +1 -1
  47. package/dist/util/cli-arguments.d.ts +2 -1
  48. package/dist/util/cli-arguments.js +2 -0
  49. package/dist/util/create-options.d.ts +2 -0
  50. package/dist/util/create-options.js +2 -0
  51. package/dist/util/load-tsconfig.d.ts +1 -1
  52. package/dist/util/load-tsconfig.js +3 -4
  53. package/dist/util/watch.d.ts +10 -5
  54. package/dist/util/watch.js +7 -5
  55. package/dist/version.d.ts +1 -1
  56. package/dist/version.js +1 -1
  57. package/package.json +11 -2
package/README.md CHANGED
@@ -21,6 +21,7 @@ performance, less maintenance and easier refactorings.
21
21
  - GitHub repo: [webpro-nl/knip][4]
22
22
  - npm package: [knip][1]
23
23
  - [Contributing Guide][7]
24
+ - Follow [@webpro.nl on Bluesky][3] for updates
24
25
  - [Sponsor Knip!][8]
25
26
 
26
27
  ## Contributors
@@ -49,6 +49,7 @@ export declare class ConfigurationChief {
49
49
  getManifestForWorkspace(name: string): import("./types/package-json.js").PackageJson | undefined;
50
50
  private getDescendentWorkspaces;
51
51
  getIgnoredWorkspacesFor(name: string): string[];
52
+ createIgnoredWorkspaceMatcher(name: string, dir: string): (filePath: string) => boolean;
52
53
  getNegatedWorkspacePatterns(name: string): string[];
53
54
  private getConfigKeyForWorkspace;
54
55
  getWorkspaceConfig(workspaceName: string): {
@@ -270,6 +270,15 @@ export class ConfigurationChief {
270
270
  .filter(workspaceName => workspaceName !== name)
271
271
  .filter(workspaceName => name === ROOT_WORKSPACE_NAME || workspaceName.startsWith(name));
272
272
  }
273
+ createIgnoredWorkspaceMatcher(name, dir) {
274
+ const ignoredWorkspaces = this.getIgnoredWorkspacesFor(name);
275
+ if (ignoredWorkspaces.length === 0)
276
+ return () => false;
277
+ return (filePath) => {
278
+ const relativePath = filePath.startsWith(dir) ? filePath.slice(dir.length + 1) : filePath;
279
+ return picomatch.isMatch(relativePath, ignoredWorkspaces);
280
+ };
281
+ }
273
282
  getNegatedWorkspacePatterns(name) {
274
283
  const descendentWorkspaces = this.getDescendentWorkspaces(name);
275
284
  const matchName = new RegExp(`^${name}/`);
@@ -1,6 +1,7 @@
1
1
  import type { IgnoreIssues } from './types/config.js';
2
2
  import type { ConfigurationHint, Issue, TagHint } from './types/issues.js';
3
3
  import type { MainOptions } from './util/create-options.js';
4
+ export type CollectorIssues = ReturnType<IssueCollector['getIssues']>;
4
5
  export declare class IssueCollector {
5
6
  private cwd;
6
7
  private rules;
@@ -54,7 +54,7 @@ export class ProjectPrincipal {
54
54
  this.syncCompilers = syncCompilers;
55
55
  this.asyncCompilers = asyncCompilers;
56
56
  this.cwd = options.cwd;
57
- this.isWatch = options.isWatch;
57
+ this.isWatch = options.isWatch || options.isSession;
58
58
  this.cache = new CacheConsultant(pkgName || ANONYMOUS, options);
59
59
  this.toSourceFilePath = toSourceFilePath;
60
60
  this.backend = {
@@ -168,8 +168,7 @@ export class ProjectPrincipal {
168
168
  return _getImportsAndExports(sourceFile, resolve, typeChecker, options, ignoreExportsUsedInFile, skipExports);
169
169
  }
170
170
  invalidateFile(filePath) {
171
- this.backend.fileManager.snapshotCache.delete(filePath);
172
- this.backend.fileManager.sourceFileCache.delete(filePath);
171
+ this.backend.fileManager.invalidate(filePath);
173
172
  }
174
173
  findUnusedMembers(filePath, members) {
175
174
  if (!this.findReferences || !this.getImplementationAtPosition) {
@@ -3,6 +3,7 @@ export declare const IMPORT_STAR = "*";
3
3
  export declare const ANONYMOUS = "__anonymous";
4
4
  export declare const KNIP_CONFIG_LOCATIONS: string[];
5
5
  export declare const DEFAULT_EXTENSIONS: string[];
6
+ export declare const IS_DTS: RegExp;
6
7
  export declare const GLOBAL_IGNORE_PATTERNS: string[];
7
8
  export declare const PUBLIC_TAG = "@public";
8
9
  export declare const INTERNAL_TAG = "@internal";
package/dist/constants.js CHANGED
@@ -12,6 +12,7 @@ export const KNIP_CONFIG_LOCATIONS = [
12
12
  'knip.config.js',
13
13
  ];
14
14
  export const DEFAULT_EXTENSIONS = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.mts', '.cts'];
15
+ export const IS_DTS = /\.d\.(c|m)?ts$/;
15
16
  export const GLOBAL_IGNORE_PATTERNS = ['**/node_modules/**', '.yarn'];
16
17
  export const PUBLIC_TAG = '@public';
17
18
  export const INTERNAL_TAG = '@internal';
@@ -1,6 +1,7 @@
1
1
  import { _getInputsFromScripts } from '../binaries/index.js';
2
2
  import { getCompilerExtensions, getIncludedCompilers } from '../compilers/index.js';
3
- import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from '../constants.js';
3
+ import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, IS_DTS } from '../constants.js';
4
+ import { partition } from '../util/array.js';
4
5
  import { debugLog, debugLogArray } from '../util/debug.js';
5
6
  import { getReferencedInputsHandler } from '../util/get-referenced-inputs.js';
6
7
  import { _glob, _syncGlob, negate, prependDirToPattern } from '../util/glob.js';
@@ -50,7 +51,8 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
50
51
  const extensionGlobStr = `.{${[...DEFAULT_EXTENSIONS, ...extensions].map(ext => ext.slice(1)).join(',')}}`;
51
52
  const config = chief.getConfigForWorkspace(name, extensions);
52
53
  const tsConfigFilePath = join(dir, options.tsConfigFile ?? 'tsconfig.json');
53
- const { isFile, compilerOptions, definitionPaths } = await loadTSConfig(tsConfigFilePath);
54
+ const { isFile, compilerOptions, fileNames } = await loadTSConfig(tsConfigFilePath);
55
+ const [definitionPaths, tscSourcePaths] = partition(fileNames, filePath => IS_DTS.test(filePath));
54
56
  if (isFile)
55
57
  augmentWorkspace(workspace, dir, compilerOptions);
56
58
  const worker = new WorkspaceWorker({
@@ -204,7 +206,17 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
204
206
  principal.addEntryPaths(pluginWorkspaceEntryPaths, { skipExportsAnalysis: true });
205
207
  }
206
208
  }
207
- {
209
+ if (options.isUseTscFiles) {
210
+ const isIgnoredWorkspace = chief.createIgnoredWorkspaceMatcher(name, dir);
211
+ debugLogArray(name, 'Using tsconfig files as project files', tscSourcePaths);
212
+ for (const filePath of tscSourcePaths) {
213
+ if (!isGitIgnored(filePath) && !isIgnoredWorkspace(filePath)) {
214
+ principal.addProgramPath(filePath);
215
+ principal.addProjectPath(filePath);
216
+ }
217
+ }
218
+ }
219
+ else {
208
220
  const patterns = options.isProduction
209
221
  ? worker.getProductionProjectFilePatterns(negatedEntryPatterns)
210
222
  : worker.getProjectFilePatterns([
@@ -246,7 +258,7 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
246
258
  tags: options.tags,
247
259
  };
248
260
  const analyzeSourceFile = (filePath, principal) => {
249
- if (!options.isWatch && analyzedFiles.has(filePath))
261
+ if (!options.isWatch && !options.isSession && analyzedFiles.has(filePath))
250
262
  return;
251
263
  analyzedFiles.add(filePath);
252
264
  const workspace = chief.findWorkspaceByFilePath(filePath);
@@ -355,13 +367,13 @@ export async function build({ chief, collector, counselor, deputy, factory, isGi
355
367
  for (const filePath of principal.entryPaths)
356
368
  entryPaths.add(filePath);
357
369
  principal.reconcileCache(graph);
358
- if (options.isIsolateWorkspaces || (options.isSkipLibs && !options.isWatch)) {
370
+ if (options.isIsolateWorkspaces || (options.isSkipLibs && !options.isWatch && !options.isSession)) {
359
371
  factory.deletePrincipal(principal, options.cwd);
360
372
  principals[i] = undefined;
361
373
  }
362
374
  perfObserver.addMemoryMark(factory.getPrincipalCount());
363
375
  }
364
- if (!options.isWatch && options.isSkipLibs && !options.isIsolateWorkspaces) {
376
+ if (!options.isWatch && !options.isSession && options.isSkipLibs && !options.isIsolateWorkspaces) {
365
377
  for (const principal of principals) {
366
378
  if (principal)
367
379
  factory.deletePrincipal(principal, options.cwd);
@@ -0,0 +1,10 @@
1
+ import type { ModuleGraph } from '../types/module-graph.js';
2
+ import type { UsageResult } from './operations/get-usage.js';
3
+ import type { DefinitionResult } from './operations/resolve-definition.js';
4
+ export declare const invalidateCache: (graph: ModuleGraph) => void;
5
+ export declare const getCachedDefinition: (graph: ModuleGraph, filePath: string, identifier: string) => DefinitionResult | null | undefined;
6
+ export declare const setCachedDefinition: (graph: ModuleGraph, filePath: string, identifier: string, result: DefinitionResult | null) => void;
7
+ export declare const getCachedUsage: (graph: ModuleGraph, filePath: string, identifier: string) => UsageResult | undefined;
8
+ export declare const setCachedUsage: (graph: ModuleGraph, filePath: string, identifier: string, result: UsageResult) => void;
9
+ export declare const getCachedExportedIdentifiers: (graph: ModuleGraph, filePath: string) => Map<string, boolean> | undefined;
10
+ export declare const setCachedExportedIdentifiers: (graph: ModuleGraph, filePath: string, result: Map<string, boolean>) => void;
@@ -0,0 +1,72 @@
1
+ const caches = new WeakMap();
2
+ const createEmptyCache = () => ({
3
+ definitions: new Map(),
4
+ usage: new Map(),
5
+ importLookup: new Map(),
6
+ exportedIdentifiers: new Map(),
7
+ generation: 0,
8
+ });
9
+ const getCache = (graph) => {
10
+ let cache = caches.get(graph);
11
+ if (!cache) {
12
+ cache = createEmptyCache();
13
+ caches.set(graph, cache);
14
+ }
15
+ return cache;
16
+ };
17
+ export const invalidateCache = (graph) => {
18
+ const cache = caches.get(graph);
19
+ if (cache) {
20
+ cache.definitions.clear();
21
+ cache.usage.clear();
22
+ cache.importLookup.clear();
23
+ cache.exportedIdentifiers.clear();
24
+ cache.generation++;
25
+ }
26
+ };
27
+ export const getCachedDefinition = (graph, filePath, identifier) => {
28
+ const cache = caches.get(graph);
29
+ if (!cache)
30
+ return undefined;
31
+ const fileCache = cache.definitions.get(filePath);
32
+ if (!fileCache)
33
+ return undefined;
34
+ return fileCache.has(identifier) ? fileCache.get(identifier) : undefined;
35
+ };
36
+ export const setCachedDefinition = (graph, filePath, identifier, result) => {
37
+ const cache = getCache(graph);
38
+ let fileCache = cache.definitions.get(filePath);
39
+ if (!fileCache) {
40
+ fileCache = new Map();
41
+ cache.definitions.set(filePath, fileCache);
42
+ }
43
+ fileCache.set(identifier, result);
44
+ };
45
+ export const getCachedUsage = (graph, filePath, identifier) => {
46
+ const cache = caches.get(graph);
47
+ if (!cache)
48
+ return undefined;
49
+ const fileCache = cache.usage.get(filePath);
50
+ if (!fileCache)
51
+ return undefined;
52
+ return fileCache.get(identifier);
53
+ };
54
+ export const setCachedUsage = (graph, filePath, identifier, result) => {
55
+ const cache = getCache(graph);
56
+ let fileCache = cache.usage.get(filePath);
57
+ if (!fileCache) {
58
+ fileCache = new Map();
59
+ cache.usage.set(filePath, fileCache);
60
+ }
61
+ fileCache.set(identifier, result);
62
+ };
63
+ export const getCachedExportedIdentifiers = (graph, filePath) => {
64
+ const cache = caches.get(graph);
65
+ if (!cache)
66
+ return undefined;
67
+ return cache.exportedIdentifiers.get(filePath);
68
+ };
69
+ export const setCachedExportedIdentifiers = (graph, filePath, result) => {
70
+ const cache = getCache(graph);
71
+ cache.exportedIdentifiers.set(filePath, result);
72
+ };
@@ -1,2 +1,9 @@
1
1
  export declare const CONTINUE = "continue";
2
2
  export declare const STOP = "stop";
3
+ export declare const RE_EXPORT_KIND: {
4
+ readonly ALIAS: "alias";
5
+ readonly NAMESPACE: "namespace";
6
+ readonly PASSTHROUGH: "passthrough";
7
+ readonly SELF: "self";
8
+ readonly STAR: "star";
9
+ };
@@ -1,2 +1,9 @@
1
1
  export const CONTINUE = 'continue';
2
2
  export const STOP = 'stop';
3
+ export const RE_EXPORT_KIND = {
4
+ ALIAS: 'alias',
5
+ NAMESPACE: 'namespace',
6
+ PASSTHROUGH: 'passthrough',
7
+ SELF: 'self',
8
+ STAR: 'star',
9
+ };
@@ -8,4 +8,10 @@ export declare const createGraphExplorer: (graph: ModuleGraph, entryPaths: Set<s
8
8
  filePath?: string;
9
9
  identifier?: string;
10
10
  }) => import("./operations/build-exports-tree.js").TreeNode[];
11
+ resolveDefinition: (filePath: string, identifier: string) => import("./operations/resolve-definition.js").DefinitionResult | null;
12
+ getUsage: (filePath: string, identifier: string) => import("./operations/get-usage.js").UsageResult;
13
+ findCycles: (filePath: string, maxDepth?: number) => import("../session/types.js").Cycle[];
14
+ getContention: (filePath: string) => Map<string, import("../session/types.js").ContentionDetails>;
15
+ invalidateCache: () => void;
11
16
  };
17
+ export type GraphExplorer = ReturnType<typeof createGraphExplorer>;
@@ -1,10 +1,20 @@
1
+ import { invalidateCache as invalidateCacheInternal } from './cache.js';
1
2
  import { buildExportsTree } from './operations/build-exports-tree.js';
3
+ import { findCycles } from './operations/find-cycles.js';
4
+ import { getContention } from './operations/get-contention.js';
5
+ import { getUsage } from './operations/get-usage.js';
2
6
  import { hasStrictlyNsReferences } from './operations/has-strictly-ns-references.js';
3
7
  import { isReferenced } from './operations/is-referenced.js';
8
+ import { resolveDefinition } from './operations/resolve-definition.js';
4
9
  export const createGraphExplorer = (graph, entryPaths) => {
5
10
  return {
6
11
  isReferenced: (filePath, identifier, options) => isReferenced(graph, entryPaths, filePath, identifier, options),
7
12
  hasStrictlyNsReferences: (filePath, identifier) => hasStrictlyNsReferences(graph, graph.get(filePath)?.imported, identifier),
8
13
  buildExportsTree: (options) => buildExportsTree(graph, entryPaths, options),
14
+ resolveDefinition: (filePath, identifier) => resolveDefinition(graph, filePath, identifier),
15
+ getUsage: (filePath, identifier) => getUsage(graph, entryPaths, filePath, identifier),
16
+ findCycles: (filePath, maxDepth) => findCycles(graph, filePath, maxDepth),
17
+ getContention: (filePath) => getContention(graph, filePath),
18
+ invalidateCache: () => invalidateCacheInternal(graph),
9
19
  };
10
20
  };
@@ -0,0 +1,3 @@
1
+ import type { Cycle } from '../../session/types.js';
2
+ import type { ModuleGraph } from '../../types/module-graph.js';
3
+ export declare const findCycles: (graph: ModuleGraph, filePath: string, maxDepth?: number) => Cycle[];
@@ -0,0 +1,38 @@
1
+ export const findCycles = (graph, filePath, maxDepth = 16) => {
2
+ const cycles = [];
3
+ const visited = new Set();
4
+ const pathSet = new Set([filePath]);
5
+ const path = [filePath];
6
+ const visit = (currentPath) => {
7
+ if (path.length > maxDepth)
8
+ return;
9
+ const node = graph.get(currentPath);
10
+ if (!node?.imports?.internal)
11
+ return;
12
+ const nonTypeOnlyImports = new Set();
13
+ for (const _import of node.imports.imports) {
14
+ if (_import.filePath && !_import.isTypeOnly)
15
+ nonTypeOnlyImports.add(_import.filePath);
16
+ }
17
+ for (const [importedPath] of node.imports.internal) {
18
+ if (!nonTypeOnlyImports.has(importedPath))
19
+ continue;
20
+ if (importedPath === filePath) {
21
+ cycles.push([...path, importedPath]);
22
+ continue;
23
+ }
24
+ if (visited.has(importedPath))
25
+ continue;
26
+ if (!pathSet.has(importedPath)) {
27
+ path.push(importedPath);
28
+ pathSet.add(importedPath);
29
+ visit(importedPath);
30
+ pathSet.delete(importedPath);
31
+ path.pop();
32
+ }
33
+ }
34
+ visited.add(currentPath);
35
+ };
36
+ visit(filePath);
37
+ return cycles;
38
+ };
@@ -0,0 +1,3 @@
1
+ import type { ContentionDetails } from '../../session/types.js';
2
+ import type { ModuleGraph } from '../../types/module-graph.js';
3
+ export declare const getContention: (graph: ModuleGraph, filePath: string) => Map<string, ContentionDetails>;
@@ -0,0 +1,142 @@
1
+ import { IMPORT_STAR } from '../../constants.js';
2
+ import { getExportedIdentifiers } from '../utils.js';
3
+ import { forEachAliasReExport, forEachPassThroughReExport, getStarReExportSources } from '../visitors.js';
4
+ export const getContention = (graph, filePath) => {
5
+ const node = graph.get(filePath);
6
+ if (!node)
7
+ return new Map();
8
+ const exportedIdentifiers = getExportedIdentifiers(graph, filePath);
9
+ const result = new Map();
10
+ for (const identifier of exportedIdentifiers.keys()) {
11
+ if (identifier === 'default')
12
+ continue;
13
+ const details = getContentionForIdentifier(graph, filePath, identifier);
14
+ if (details && (details.branching.length > 0 || details.conflict.length > 0)) {
15
+ result.set(identifier, details);
16
+ }
17
+ }
18
+ return result;
19
+ };
20
+ const getContentionForIdentifier = (graph, startFilePath, identifier) => {
21
+ const network = buildReExportNetwork(graph, startFilePath, identifier);
22
+ if (network.files.size <= 1)
23
+ return null;
24
+ const branchingFiles = [];
25
+ for (const file of network.files) {
26
+ const sourceCount = network.reExportsFrom.get(file)?.size ?? 0;
27
+ if (sourceCount > 1) {
28
+ branchingFiles.push(file);
29
+ }
30
+ }
31
+ const hasConflict = network.definitions.size > 1;
32
+ if (branchingFiles.length === 0 && !hasConflict)
33
+ return null;
34
+ return {
35
+ branching: branchingFiles.sort(),
36
+ conflict: hasConflict ? Array.from(network.definitions).sort() : [],
37
+ };
38
+ };
39
+ const buildReExportNetwork = (graph, startFilePath, identifier) => {
40
+ const network = {
41
+ files: new Set(),
42
+ definitions: new Set(),
43
+ reExportsFrom: new Map(),
44
+ };
45
+ const upVisited = new Set();
46
+ const downVisited = new Set();
47
+ walkUp(graph, network, startFilePath, identifier, upVisited);
48
+ walkDown(graph, network, startFilePath, identifier, downVisited);
49
+ for (const file of network.files) {
50
+ if (!upVisited.has(file)) {
51
+ walkUp(graph, network, file, identifier, upVisited);
52
+ }
53
+ }
54
+ for (const definitionFile of network.definitions) {
55
+ if (!downVisited.has(definitionFile))
56
+ walkDown(graph, network, definitionFile, identifier, downVisited);
57
+ }
58
+ return network;
59
+ };
60
+ const walkUp = (graph, network, filePath, identifier, visited) => {
61
+ if (visited.has(filePath))
62
+ return;
63
+ visited.add(filePath);
64
+ const node = graph.get(filePath);
65
+ if (!node)
66
+ return;
67
+ const exportedIds = getExportedIdentifiers(graph, filePath);
68
+ if (!exportedIds.has(identifier))
69
+ return;
70
+ network.files.add(filePath);
71
+ const exp = node.exports.get(identifier);
72
+ if (exp && !exp.isReExport) {
73
+ network.definitions.add(filePath);
74
+ }
75
+ for (const [sourcePath, importMaps] of node.imports.internal) {
76
+ forEachPassThroughReExport(importMaps, (id, _sources) => {
77
+ if (id !== identifier)
78
+ return;
79
+ addEdge(network, sourcePath, filePath);
80
+ walkUp(graph, network, sourcePath, identifier, visited);
81
+ });
82
+ forEachAliasReExport(importMaps, (sourceId, alias, _sources) => {
83
+ if (alias !== identifier)
84
+ return;
85
+ addEdge(network, sourcePath, filePath);
86
+ walkUp(graph, network, sourcePath, sourceId, visited);
87
+ });
88
+ const starSources = getStarReExportSources(importMaps);
89
+ if (starSources) {
90
+ const sourceExports = getExportedIdentifiers(graph, sourcePath);
91
+ if (sourceExports.has(identifier)) {
92
+ addEdge(network, sourcePath, filePath);
93
+ walkUp(graph, network, sourcePath, identifier, visited);
94
+ }
95
+ }
96
+ }
97
+ };
98
+ const walkDown = (graph, network, filePath, identifier, visited) => {
99
+ if (visited.has(filePath))
100
+ return;
101
+ visited.add(filePath);
102
+ const node = graph.get(filePath);
103
+ if (!node?.imported)
104
+ return;
105
+ const processConsumer = (consumerPath) => {
106
+ network.files.add(consumerPath);
107
+ addEdge(network, filePath, consumerPath);
108
+ const consumerExport = graph.get(consumerPath)?.exports.get(identifier);
109
+ if (consumerExport && !consumerExport.isReExport)
110
+ network.definitions.add(consumerPath);
111
+ walkDown(graph, network, consumerPath, identifier, visited);
112
+ };
113
+ const directConsumers = node.imported.reExported.get(identifier);
114
+ if (directConsumers) {
115
+ for (const consumerPath of directConsumers)
116
+ processConsumer(consumerPath);
117
+ }
118
+ for (const [sourceId, aliasMap] of node.imported.reExportedAs) {
119
+ if (sourceId === identifier) {
120
+ for (const [_alias, consumers] of aliasMap) {
121
+ for (const consumerPath of consumers)
122
+ processConsumer(consumerPath);
123
+ }
124
+ }
125
+ }
126
+ const starConsumers = node.imported.reExported.get(IMPORT_STAR);
127
+ if (starConsumers) {
128
+ for (const consumerPath of starConsumers) {
129
+ const consumerExports = getExportedIdentifiers(graph, consumerPath);
130
+ if (consumerExports.has(identifier))
131
+ processConsumer(consumerPath);
132
+ }
133
+ }
134
+ };
135
+ const addEdge = (network, source, consumer) => {
136
+ let sources = network.reExportsFrom.get(consumer);
137
+ if (!sources) {
138
+ sources = new Set();
139
+ network.reExportsFrom.set(consumer, sources);
140
+ }
141
+ sources.add(source);
142
+ };
@@ -0,0 +1,13 @@
1
+ import type { Identifier, ModuleGraph, Position } from '../../types/module-graph.js';
2
+ import { type Via } from '../walk-down.js';
3
+ export interface UsageLocation extends Position {
4
+ filePath: string;
5
+ identifier: string;
6
+ isEntry: boolean;
7
+ via: Via;
8
+ }
9
+ export interface UsageResult {
10
+ locations: UsageLocation[];
11
+ reExportingEntryFile: string | undefined;
12
+ }
13
+ export declare const getUsage: (graph: ModuleGraph, entryPaths: Set<string>, filePath: string, identifier: Identifier) => UsageResult;
@@ -0,0 +1,34 @@
1
+ import { IMPORT_STAR } from '../../constants.js';
2
+ import { getCachedUsage, setCachedUsage } from '../cache.js';
3
+ import { CONTINUE } from '../constants.js';
4
+ import { findImportRef } from '../utils.js';
5
+ import { walkDown } from '../walk-down.js';
6
+ export const getUsage = (graph, entryPaths, filePath, identifier) => {
7
+ const cached = getCachedUsage(graph, filePath, identifier);
8
+ if (cached)
9
+ return cached;
10
+ const locations = [];
11
+ let reExportingEntryFile;
12
+ if (entryPaths.has(filePath)) {
13
+ reExportingEntryFile = filePath;
14
+ }
15
+ walkDown(graph, filePath, identifier, (sourceFile, sourceId, importingFile, id, isEntry, via) => {
16
+ const lookupId = via === 'importNS' ? IMPORT_STAR : sourceId;
17
+ const importRef = findImportRef(graph, importingFile, sourceFile, lookupId);
18
+ locations.push({
19
+ filePath: importingFile,
20
+ identifier: id,
21
+ pos: importRef?.pos ?? 0,
22
+ line: importRef?.line ?? 0,
23
+ col: importRef?.col ?? 0,
24
+ isEntry,
25
+ via,
26
+ });
27
+ if (isEntry && !reExportingEntryFile)
28
+ reExportingEntryFile = importingFile;
29
+ return CONTINUE;
30
+ }, entryPaths);
31
+ const result = { locations, reExportingEntryFile };
32
+ setCachedUsage(graph, filePath, identifier, result);
33
+ return result;
34
+ };
@@ -0,0 +1,16 @@
1
+ import type { ReExportKind } from '../../session/types.js';
2
+ import type { Export, Identifier, ModuleGraph } from '../../types/module-graph.js';
3
+ interface TraversalStep {
4
+ filePath: string;
5
+ identifier: string;
6
+ via: ReExportKind;
7
+ }
8
+ export interface DefinitionResult {
9
+ type: 'symbol' | 'namespace';
10
+ filePath: string;
11
+ identifier: string;
12
+ exportNode: Export | undefined;
13
+ chain: TraversalStep[];
14
+ }
15
+ export declare const resolveDefinition: (graph: ModuleGraph, filePath: string, identifier: Identifier) => DefinitionResult | null;
16
+ export {};
@@ -0,0 +1,38 @@
1
+ import { getCachedDefinition, setCachedDefinition } from '../cache.js';
2
+ import { CONTINUE, STOP } from '../constants.js';
3
+ import { walkUp } from '../walk-up.js';
4
+ export const resolveDefinition = (graph, filePath, identifier) => {
5
+ const cached = getCachedDefinition(graph, filePath, identifier);
6
+ if (cached !== undefined)
7
+ return cached;
8
+ const chain = [];
9
+ let result = null;
10
+ walkUp(graph, filePath, identifier, (resolvedPath, resolvedId, via) => {
11
+ chain.push({ filePath: resolvedPath, identifier: resolvedId, via });
12
+ if (via === 'self') {
13
+ const node = graph.get(resolvedPath);
14
+ const exportNode = node?.exports.get(resolvedId);
15
+ result = {
16
+ type: 'symbol',
17
+ filePath: resolvedPath,
18
+ identifier: resolvedId,
19
+ exportNode,
20
+ chain,
21
+ };
22
+ return STOP;
23
+ }
24
+ if (via === 'namespace') {
25
+ result = {
26
+ type: 'namespace',
27
+ filePath: resolvedPath,
28
+ identifier: resolvedId,
29
+ exportNode: undefined,
30
+ chain,
31
+ };
32
+ return STOP;
33
+ }
34
+ return CONTINUE;
35
+ });
36
+ setCachedDefinition(graph, filePath, identifier, result);
37
+ return result;
38
+ };
@@ -1,3 +1,5 @@
1
- import type { ImportMaps } from '../types/module-graph.js';
1
+ import type { Import, ImportMaps, ModuleGraph } from '../types/module-graph.js';
2
+ export declare const getExportedIdentifiers: (graph: ModuleGraph, filePath: string, visited?: Set<string>) => Map<string, boolean>;
2
3
  export declare const hasStrictlyEnumReferences: (importsForExport: ImportMaps | undefined, identifier: string) => boolean;
3
4
  export declare const getIssueType: (hasOnlyNsReference: boolean, isType: boolean) => "exports" | "nsExports" | "types" | "nsTypes";
5
+ export declare const findImportRef: (graph: ModuleGraph, importingFile: string, importedFile: string, identifier: string) => Import | undefined;