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.
- package/README.md +1 -0
- package/dist/ConfigurationChief.d.ts +1 -0
- package/dist/ConfigurationChief.js +9 -0
- package/dist/IssueCollector.d.ts +1 -0
- package/dist/ProjectPrincipal.js +2 -3
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/graph/build.js +18 -6
- package/dist/graph-explorer/cache.d.ts +10 -0
- package/dist/graph-explorer/cache.js +72 -0
- package/dist/graph-explorer/constants.d.ts +7 -0
- package/dist/graph-explorer/constants.js +7 -0
- package/dist/graph-explorer/explorer.d.ts +6 -0
- package/dist/graph-explorer/explorer.js +10 -0
- package/dist/graph-explorer/operations/find-cycles.d.ts +3 -0
- package/dist/graph-explorer/operations/find-cycles.js +38 -0
- package/dist/graph-explorer/operations/get-contention.d.ts +3 -0
- package/dist/graph-explorer/operations/get-contention.js +142 -0
- package/dist/graph-explorer/operations/get-usage.d.ts +13 -0
- package/dist/graph-explorer/operations/get-usage.js +34 -0
- package/dist/graph-explorer/operations/resolve-definition.d.ts +16 -0
- package/dist/graph-explorer/operations/resolve-definition.js +38 -0
- package/dist/graph-explorer/utils.d.ts +3 -1
- package/dist/graph-explorer/utils.js +62 -1
- package/dist/graph-explorer/walk-up.d.ts +6 -0
- package/dist/graph-explorer/walk-up.js +67 -0
- package/dist/reporters/util/configuration-hints.d.ts +9 -1
- package/dist/reporters/util/configuration-hints.js +31 -28
- package/dist/run.d.ts +9 -6
- package/dist/run.js +7 -7
- package/dist/session/build-maps.d.ts +6 -0
- package/dist/session/build-maps.js +150 -0
- package/dist/session/file-descriptor.d.ts +6 -0
- package/dist/session/file-descriptor.js +43 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.js +6 -0
- package/dist/session/session.d.ts +20 -0
- package/dist/session/session.js +18 -0
- package/dist/session/types.d.ts +35 -0
- package/dist/session/types.js +1 -0
- package/dist/types/issues.d.ts +1 -1
- package/dist/types/module-graph.d.ts +3 -3
- package/dist/types/options.d.ts +2 -0
- package/dist/typescript/SourceFileManager.d.ts +3 -0
- package/dist/typescript/SourceFileManager.js +9 -0
- package/dist/typescript/create-hosts.js +1 -1
- package/dist/util/cli-arguments.d.ts +2 -1
- package/dist/util/cli-arguments.js +2 -0
- package/dist/util/create-options.d.ts +2 -0
- package/dist/util/create-options.js +2 -0
- package/dist/util/load-tsconfig.d.ts +1 -1
- package/dist/util/load-tsconfig.js +3 -4
- package/dist/util/watch.d.ts +10 -5
- package/dist/util/watch.js +7 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -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}/`);
|
package/dist/IssueCollector.d.ts
CHANGED
|
@@ -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;
|
package/dist/ProjectPrincipal.js
CHANGED
|
@@ -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.
|
|
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) {
|
package/dist/constants.d.ts
CHANGED
|
@@ -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';
|
package/dist/graph/build.js
CHANGED
|
@@ -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,
|
|
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
|
+
};
|
|
@@ -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,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,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;
|