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
|
@@ -1,9 +1,61 @@
|
|
|
1
|
+
import { getCachedExportedIdentifiers, setCachedExportedIdentifiers } from './cache.js';
|
|
2
|
+
import { forEachAliasReExport, forEachNamespaceReExport, forEachPassThroughReExport, getStarReExportSources, } from './visitors.js';
|
|
3
|
+
export const getExportedIdentifiers = (graph, filePath, visited = new Set()) => {
|
|
4
|
+
if (visited.has(filePath))
|
|
5
|
+
return new Map();
|
|
6
|
+
visited.add(filePath);
|
|
7
|
+
const cached = getCachedExportedIdentifiers(graph, filePath);
|
|
8
|
+
if (cached)
|
|
9
|
+
return cached;
|
|
10
|
+
const node = graph.get(filePath);
|
|
11
|
+
if (!node)
|
|
12
|
+
return new Map();
|
|
13
|
+
const identifiers = new Map();
|
|
14
|
+
const addIdentifier = (identifier, isDuplicate = false) => {
|
|
15
|
+
if (identifiers.has(identifier)) {
|
|
16
|
+
identifiers.set(identifier, true);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
identifiers.set(identifier, isDuplicate);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
for (const identifier of node.exports.keys()) {
|
|
23
|
+
if (identifier === 'default')
|
|
24
|
+
continue;
|
|
25
|
+
addIdentifier(identifier);
|
|
26
|
+
}
|
|
27
|
+
if (node.imports?.internal) {
|
|
28
|
+
for (const [importedPath, importDetails] of node.imports.internal) {
|
|
29
|
+
forEachPassThroughReExport(importDetails, (id, _sources) => {
|
|
30
|
+
if (id !== 'default')
|
|
31
|
+
addIdentifier(id);
|
|
32
|
+
});
|
|
33
|
+
forEachAliasReExport(importDetails, (_id, alias, _sources) => {
|
|
34
|
+
addIdentifier(alias);
|
|
35
|
+
});
|
|
36
|
+
forEachNamespaceReExport(importDetails, (namespace, _sources) => {
|
|
37
|
+
addIdentifier(namespace, true);
|
|
38
|
+
});
|
|
39
|
+
const starSources = getStarReExportSources(importDetails);
|
|
40
|
+
if (starSources) {
|
|
41
|
+
const nestedIdentifiers = getExportedIdentifiers(graph, importedPath, new Set(visited));
|
|
42
|
+
for (const [nestedId, isNestedDuplicate] of nestedIdentifiers) {
|
|
43
|
+
if (nestedId !== 'default')
|
|
44
|
+
addIdentifier(nestedId, isNestedDuplicate);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
setCachedExportedIdentifiers(graph, filePath, identifiers);
|
|
50
|
+
return identifiers;
|
|
51
|
+
};
|
|
1
52
|
export const hasStrictlyEnumReferences = (importsForExport, identifier) => {
|
|
2
53
|
if (!importsForExport || !importsForExport.refs.has(identifier))
|
|
3
54
|
return false;
|
|
4
|
-
for (const ref of importsForExport.refs)
|
|
55
|
+
for (const ref of importsForExport.refs) {
|
|
5
56
|
if (ref.startsWith(`${identifier}.`))
|
|
6
57
|
return false;
|
|
58
|
+
}
|
|
7
59
|
return true;
|
|
8
60
|
};
|
|
9
61
|
export const getIssueType = (hasOnlyNsReference, isType) => {
|
|
@@ -11,3 +63,12 @@ export const getIssueType = (hasOnlyNsReference, isType) => {
|
|
|
11
63
|
return isType ? 'nsTypes' : 'nsExports';
|
|
12
64
|
return isType ? 'types' : 'exports';
|
|
13
65
|
};
|
|
66
|
+
export const findImportRef = (graph, importingFile, importedFile, identifier) => {
|
|
67
|
+
const node = graph.get(importingFile);
|
|
68
|
+
if (!node)
|
|
69
|
+
return undefined;
|
|
70
|
+
for (const _import of node.imports.imports) {
|
|
71
|
+
if (_import.filePath === importedFile && _import.identifier === identifier)
|
|
72
|
+
return _import;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModuleGraph } from '../types/module-graph.js';
|
|
2
|
+
import { RE_EXPORT_KIND } from './constants.js';
|
|
3
|
+
type ReExportKind = (typeof RE_EXPORT_KIND)[keyof typeof RE_EXPORT_KIND];
|
|
4
|
+
type Visitor = (filePath: string, identifier: string, via: ReExportKind) => 'continue' | 'stop' | undefined;
|
|
5
|
+
export declare const walkUp: (graph: ModuleGraph, filePath: string, identifier: string, visitor: Visitor, visited?: Set<string>) => boolean;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { RE_EXPORT_KIND, STOP } from './constants.js';
|
|
2
|
+
import { forEachAliasReExport, getNamespaceReExportSources, getPassThroughReExportSources, getStarReExportSources, } from './visitors.js';
|
|
3
|
+
export const walkUp = (graph, filePath, identifier, visitor, visited = new Set()) => {
|
|
4
|
+
const key = `${filePath}:${identifier}`;
|
|
5
|
+
if (visited.has(key))
|
|
6
|
+
return false;
|
|
7
|
+
visited.add(key);
|
|
8
|
+
const node = graph.get(filePath);
|
|
9
|
+
if (!node)
|
|
10
|
+
return false;
|
|
11
|
+
const nodeExport = node.exports.get(identifier);
|
|
12
|
+
if (nodeExport && !nodeExport.isReExport) {
|
|
13
|
+
return visitor(filePath, identifier, RE_EXPORT_KIND.SELF) === STOP;
|
|
14
|
+
}
|
|
15
|
+
const starResolvers = [];
|
|
16
|
+
if (node.imports?.internal) {
|
|
17
|
+
for (const [importedFrom, importDetails] of node.imports.internal) {
|
|
18
|
+
let done = false;
|
|
19
|
+
if (!done) {
|
|
20
|
+
if (getPassThroughReExportSources(importDetails, identifier)) {
|
|
21
|
+
if (visitor(importedFrom, identifier, RE_EXPORT_KIND.PASSTHROUGH) === STOP) {
|
|
22
|
+
done = true;
|
|
23
|
+
}
|
|
24
|
+
else if (walkUp(graph, importedFrom, identifier, visitor, visited)) {
|
|
25
|
+
done = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (!done) {
|
|
30
|
+
forEachAliasReExport(importDetails, (id, alias) => {
|
|
31
|
+
if (alias !== identifier)
|
|
32
|
+
return;
|
|
33
|
+
if (visitor(importedFrom, id, RE_EXPORT_KIND.ALIAS) === STOP) {
|
|
34
|
+
done = true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (walkUp(graph, importedFrom, id, visitor, visited)) {
|
|
38
|
+
done = true;
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (!done) {
|
|
44
|
+
if (getNamespaceReExportSources(importDetails, identifier)) {
|
|
45
|
+
if (visitor(importedFrom, identifier, RE_EXPORT_KIND.NAMESPACE) === STOP) {
|
|
46
|
+
done = true;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!done) {
|
|
51
|
+
if (getStarReExportSources(importDetails)) {
|
|
52
|
+
starResolvers.push(() => {
|
|
53
|
+
if (visitor(importedFrom, identifier, RE_EXPORT_KIND.STAR) === STOP)
|
|
54
|
+
return true;
|
|
55
|
+
return walkUp(graph, importedFrom, identifier, visitor, visited);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (done)
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const resolveStar of starResolvers)
|
|
64
|
+
if (resolveStar())
|
|
65
|
+
return true;
|
|
66
|
+
return false;
|
|
67
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Results } from '../../run.js';
|
|
2
|
+
import type { ConfigurationHint, ConfigurationHintType, ReporterOptions } from '../../types/issues.js';
|
|
2
3
|
interface PrintHintOptions {
|
|
3
4
|
type: ConfigurationHintType;
|
|
4
5
|
identifier: string | RegExp;
|
|
@@ -11,4 +12,11 @@ declare const hintPrinters: Map<ConfigurationHintType, {
|
|
|
11
12
|
print: (options: PrintHintOptions) => string;
|
|
12
13
|
}>;
|
|
13
14
|
export { hintPrinters };
|
|
15
|
+
export interface ProcessedHint extends ConfigurationHint {
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const finalizeConfigurationHints: (results: Results, options: {
|
|
19
|
+
cwd: string;
|
|
20
|
+
configFilePath?: string;
|
|
21
|
+
}) => ProcessedHint[];
|
|
14
22
|
export declare const printConfigurationHints: ({ cwd, counters, issues, tagHints, configurationHints, isTreatConfigHintsAsErrors, includedWorkspaceDirs, configFilePath, }: ReporterOptions) => void;
|
|
@@ -59,51 +59,54 @@ const hintTypesOrder = [
|
|
|
59
59
|
['entry-empty', 'project-empty', 'entry-redundant', 'project-redundant'],
|
|
60
60
|
['package-entry'],
|
|
61
61
|
];
|
|
62
|
-
export const
|
|
63
|
-
if (counters.files > 20) {
|
|
64
|
-
const workspaces = includedWorkspaceDirs
|
|
62
|
+
export const finalizeConfigurationHints = (results, options) => {
|
|
63
|
+
if (results.counters.files > 20) {
|
|
64
|
+
const workspaces = results.includedWorkspaceDirs
|
|
65
65
|
.sort(byPathDepth)
|
|
66
66
|
.reverse()
|
|
67
67
|
.map(dir => ({ dir, size: 0 }));
|
|
68
|
-
for (const filePath of issues.files) {
|
|
68
|
+
for (const filePath of results.issues.files) {
|
|
69
69
|
const workspace = workspaces.find(ws => filePath.startsWith(ws.dir));
|
|
70
70
|
if (workspace)
|
|
71
71
|
workspace.size++;
|
|
72
72
|
}
|
|
73
73
|
if (workspaces.length === 1) {
|
|
74
|
-
configurationHints.add({ type: 'top-level-unconfigured', identifier: '.', size: workspaces[0].size });
|
|
74
|
+
results.configurationHints.add({ type: 'top-level-unconfigured', identifier: '.', size: workspaces[0].size });
|
|
75
75
|
}
|
|
76
76
|
else {
|
|
77
77
|
const topWorkspaces = workspaces.sort((a, b) => b.size - a.size).filter(ws => ws.size > 1);
|
|
78
78
|
for (const { dir, size } of topWorkspaces) {
|
|
79
|
-
const identifier = toRelative(dir, cwd) || '.';
|
|
80
|
-
configurationHints.add({ type: 'workspace-unconfigured', workspaceName: identifier, identifier, size });
|
|
79
|
+
const identifier = toRelative(dir, options.cwd) || '.';
|
|
80
|
+
results.configurationHints.add({ type: 'workspace-unconfigured', workspaceName: identifier, identifier, size });
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
|
|
84
|
+
const hintsByType = new Map();
|
|
85
|
+
for (const hint of results.configurationHints) {
|
|
86
|
+
const hints = hintsByType.get(hint.type) ?? [];
|
|
87
|
+
hintsByType.set(hint.type, [...hints, hint]);
|
|
88
|
+
}
|
|
89
|
+
return hintTypesOrder.flatMap(hintTypes => hintTypes.flatMap(hintType => {
|
|
90
|
+
const hints = hintsByType.get(hintType) ?? [];
|
|
91
|
+
const topHints = hints.length > 10 ? Array.from(hints).slice(0, 10) : hints;
|
|
92
|
+
const row = topHints.map(hint => {
|
|
93
|
+
hint.filePath = relative(options.cwd, hint.filePath ?? options.configFilePath ?? '');
|
|
94
|
+
const hintPrinter = hintPrinters.get(hint.type);
|
|
95
|
+
const message = hintPrinter ? hintPrinter.print({ ...hint, configFilePath: options.configFilePath }) : '';
|
|
96
|
+
return { ...hint, message };
|
|
97
|
+
});
|
|
98
|
+
if (hints.length !== topHints.length) {
|
|
99
|
+
const more = hints.length - topHints.length;
|
|
100
|
+
row.push({ type: hintType, identifier: `...${more} more similar hints`, filePath: '', message: '' });
|
|
101
|
+
}
|
|
102
|
+
return row;
|
|
103
|
+
}));
|
|
104
|
+
};
|
|
105
|
+
export const printConfigurationHints = ({ cwd, counters, issues, tagHints, configurationHints, isTreatConfigHintsAsErrors, includedWorkspaceDirs, configFilePath, }) => {
|
|
106
|
+
const rows = finalizeConfigurationHints({ issues, counters, configurationHints, tagHints, includedWorkspaceDirs }, { cwd, configFilePath });
|
|
107
|
+
if (rows.length > 0) {
|
|
85
108
|
const getTitle = isTreatConfigHintsAsErrors ? getColoredTitle : getDimmedTitle;
|
|
86
109
|
console.log(getTitle('Configuration hints', configurationHints.size));
|
|
87
|
-
const hintsByType = new Map();
|
|
88
|
-
for (const hint of configurationHints) {
|
|
89
|
-
const hints = hintsByType.get(hint.type) ?? [];
|
|
90
|
-
hintsByType.set(hint.type, [...hints, hint]);
|
|
91
|
-
}
|
|
92
|
-
const rows = hintTypesOrder.flatMap(hintTypes => hintTypes.flatMap(hintType => {
|
|
93
|
-
const hints = hintsByType.get(hintType) ?? [];
|
|
94
|
-
const topHints = hints.length > 10 ? Array.from(hints).slice(0, 10) : hints;
|
|
95
|
-
const row = topHints.map(hint => {
|
|
96
|
-
hint.filePath = relative(cwd, hint.filePath ?? configFilePath ?? '');
|
|
97
|
-
const hintPrinter = hintPrinters.get(hint.type);
|
|
98
|
-
const message = hintPrinter ? hintPrinter.print({ ...hint, configFilePath }) : '';
|
|
99
|
-
return { ...hint, message };
|
|
100
|
-
});
|
|
101
|
-
if (hints.length !== topHints.length) {
|
|
102
|
-
const identifier = dim(`...${hints.length - topHints.length} more similar hints`);
|
|
103
|
-
row.push({ type: hintType, identifier, filePath: '', message: '' });
|
|
104
|
-
}
|
|
105
|
-
return row;
|
|
106
|
-
}));
|
|
107
110
|
console.warn(getTableForHints(rows).toString());
|
|
108
111
|
}
|
|
109
112
|
if (tagHints.size > 0) {
|
package/dist/run.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ConsoleStreamer } from './ConsoleStreamer.js';
|
|
2
2
|
import type { MainOptions } from './util/create-options.js';
|
|
3
|
+
export type Results = Awaited<ReturnType<typeof run>>['results'];
|
|
3
4
|
export declare const run: (options: MainOptions) => Promise<{
|
|
4
5
|
results: {
|
|
5
6
|
issues: import("./types/issues.js").Issues;
|
|
@@ -8,18 +9,20 @@ export declare const run: (options: MainOptions) => Promise<{
|
|
|
8
9
|
configurationHints: Set<import("./types/issues.js").ConfigurationHint>;
|
|
9
10
|
includedWorkspaceDirs: string[];
|
|
10
11
|
};
|
|
11
|
-
|
|
12
|
+
session: {
|
|
12
13
|
listener: import("fs").WatchListener<string | Buffer<ArrayBufferLike>>;
|
|
13
|
-
handleFileChanges: (changes: {
|
|
14
|
-
type: "added" | "deleted" | "modified";
|
|
15
|
-
filePath: string;
|
|
16
|
-
}[]) => Promise<{
|
|
14
|
+
handleFileChanges: (changes: import("./util/watch.js").WatchChange[]) => Promise<{
|
|
17
15
|
duration: number;
|
|
18
16
|
mem: number;
|
|
19
17
|
}>;
|
|
20
18
|
getEntryPaths: () => Set<string>;
|
|
21
19
|
getGraph: () => import("./types/module-graph.js").ModuleGraph;
|
|
22
|
-
getIssues: () =>
|
|
20
|
+
getIssues: () => {
|
|
21
|
+
issues: import("./types/issues.js").Issues;
|
|
22
|
+
counters: import("./types/issues.js").Counters;
|
|
23
|
+
tagHints: Set<import("./types/issues.js").TagHint>;
|
|
24
|
+
configurationHints: Set<import("./types/issues.js").ConfigurationHint>;
|
|
25
|
+
};
|
|
23
26
|
} | undefined;
|
|
24
27
|
streamer: ConsoleStreamer;
|
|
25
28
|
}>;
|
package/dist/run.js
CHANGED
|
@@ -12,7 +12,7 @@ import { PrincipalFactory } from './PrincipalFactory.js';
|
|
|
12
12
|
import watchReporter from './reporters/watch.js';
|
|
13
13
|
import { debugLogArray, debugLogObject } from './util/debug.js';
|
|
14
14
|
import { getGitIgnoredHandler } from './util/glob-core.js';
|
|
15
|
-
import {
|
|
15
|
+
import { getSessionHandler } from './util/watch.js';
|
|
16
16
|
export const run = async (options) => {
|
|
17
17
|
debugLogObject('*', 'Unresolved configuration', options);
|
|
18
18
|
debugLogObject('*', 'Included issue types', options.includedIssueTypes);
|
|
@@ -54,15 +54,15 @@ export const run = async (options) => {
|
|
|
54
54
|
unreferencedFiles,
|
|
55
55
|
options,
|
|
56
56
|
});
|
|
57
|
-
let
|
|
58
|
-
if (options.isWatch) {
|
|
57
|
+
let session;
|
|
58
|
+
if (options.isWatch || options.isSession) {
|
|
59
59
|
const isIgnored = (filePath) => (!!options.cacheLocation && filePath.startsWith(options.cacheLocation)) ||
|
|
60
60
|
filePath.includes('/.git/') ||
|
|
61
61
|
isGitIgnored(filePath);
|
|
62
62
|
const onFileChange = options.isWatch
|
|
63
63
|
? ({ issues, duration }) => watchReporter(options, { issues, streamer, size: analyzedFiles.size, duration })
|
|
64
64
|
: undefined;
|
|
65
|
-
|
|
65
|
+
session = await getSessionHandler(options, {
|
|
66
66
|
analyzedFiles,
|
|
67
67
|
analyzeSourceFile,
|
|
68
68
|
chief,
|
|
@@ -76,10 +76,10 @@ export const run = async (options) => {
|
|
|
76
76
|
entryPaths,
|
|
77
77
|
});
|
|
78
78
|
if (options.isWatch)
|
|
79
|
-
watch('.', { recursive: true },
|
|
79
|
+
watch('.', { recursive: true }, session.listener);
|
|
80
80
|
}
|
|
81
81
|
const { issues, counters, tagHints, configurationHints } = collector.getIssues();
|
|
82
|
-
if (options.isFix) {
|
|
82
|
+
if (options.isFix && !options.isSession) {
|
|
83
83
|
const touchedFiles = await fixer.fixIssues(issues);
|
|
84
84
|
if (options.isFormat) {
|
|
85
85
|
const report = await formatly(Array.from(touchedFiles));
|
|
@@ -101,7 +101,7 @@ export const run = async (options) => {
|
|
|
101
101
|
configurationHints,
|
|
102
102
|
includedWorkspaceDirs: chief.includedWorkspaces.map(w => w.dir),
|
|
103
103
|
},
|
|
104
|
-
|
|
104
|
+
session,
|
|
105
105
|
streamer,
|
|
106
106
|
};
|
|
107
107
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GraphExplorer } from '../graph-explorer/explorer.js';
|
|
2
|
+
import type { FileNode, ModuleGraph } from '../types/module-graph.js';
|
|
3
|
+
import type { Export, ImportLookup, InternalImport } from './types.js';
|
|
4
|
+
export declare const buildImportLookup: (fileNode: FileNode) => ImportLookup;
|
|
5
|
+
export declare const buildExportsMap: (fileNode: FileNode, filePath: string, graph: ModuleGraph, entryPaths: Set<string>, explorer: GraphExplorer, importLookup: ImportLookup) => Map<string, Export>;
|
|
6
|
+
export declare const buildInternalImports: (fileNode: FileNode, explorer: GraphExplorer, importLookup: ImportLookup) => InternalImport[];
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { IMPORT_STAR } from '../constants.js';
|
|
2
|
+
import { getExportedIdentifiers } from '../graph-explorer/utils.js';
|
|
3
|
+
import { forEachAliasReExport, forEachNamespaceReExport, forEachPassThroughReExport, getStarReExportSources, } from '../graph-explorer/visitors.js';
|
|
4
|
+
const FALLBACK_LOCATION = { identifier: undefined, pos: 0, line: 0, col: 0 };
|
|
5
|
+
export const buildImportLookup = (fileNode) => {
|
|
6
|
+
const imports = new Map();
|
|
7
|
+
for (const _import of fileNode.imports.imports) {
|
|
8
|
+
if (!_import.filePath || !_import.identifier)
|
|
9
|
+
continue;
|
|
10
|
+
let fileMap = imports.get(_import.filePath);
|
|
11
|
+
if (!fileMap) {
|
|
12
|
+
fileMap = new Map();
|
|
13
|
+
imports.set(_import.filePath, fileMap);
|
|
14
|
+
}
|
|
15
|
+
const list = fileMap.get(_import.identifier) ?? [];
|
|
16
|
+
list.push(_import);
|
|
17
|
+
fileMap.set(_import.identifier, list);
|
|
18
|
+
}
|
|
19
|
+
return imports;
|
|
20
|
+
};
|
|
21
|
+
const excludeReExports = (location) => location.via !== 'reExport' &&
|
|
22
|
+
location.via !== 'reExportAs' &&
|
|
23
|
+
location.via !== 'reExportNS' &&
|
|
24
|
+
location.via !== 'reExportStar';
|
|
25
|
+
export const buildExportsMap = (fileNode, filePath, graph, entryPaths, explorer, importLookup) => {
|
|
26
|
+
const exportsMap = new Map();
|
|
27
|
+
const getImport = (importedFilePath, id) => importLookup.get(importedFilePath)?.get(id)?.[0] ?? FALLBACK_LOCATION;
|
|
28
|
+
const addExport = (identifier, sourceFilePath, pos, line, col, childExports) => {
|
|
29
|
+
if (exportsMap.has(identifier))
|
|
30
|
+
return;
|
|
31
|
+
const usage = explorer.getUsage(sourceFilePath, identifier);
|
|
32
|
+
const usageEntryPaths = new Set();
|
|
33
|
+
for (const location of usage.locations)
|
|
34
|
+
if (location.isEntry)
|
|
35
|
+
usageEntryPaths.add(location.filePath);
|
|
36
|
+
if (entryPaths.has(sourceFilePath))
|
|
37
|
+
usageEntryPaths.add(sourceFilePath);
|
|
38
|
+
exportsMap.set(identifier, {
|
|
39
|
+
filePath,
|
|
40
|
+
identifier,
|
|
41
|
+
pos,
|
|
42
|
+
line,
|
|
43
|
+
col,
|
|
44
|
+
importLocations: usage.locations
|
|
45
|
+
.filter(excludeReExports)
|
|
46
|
+
.sort((a, b) => a.filePath.localeCompare(b.filePath) || a.line - b.line || a.col - b.col),
|
|
47
|
+
entryPaths: usageEntryPaths,
|
|
48
|
+
exports: childExports,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
for (const _export of fileNode.exports.values()) {
|
|
52
|
+
addExport(_export.identifier, filePath, _export.pos, _export.line, _export.col, undefined);
|
|
53
|
+
}
|
|
54
|
+
for (const [importedFilePath, importMaps] of fileNode.imports.internal) {
|
|
55
|
+
forEachPassThroughReExport(importMaps, (id, _sources) => {
|
|
56
|
+
if (exportsMap.has(id))
|
|
57
|
+
return;
|
|
58
|
+
const directImport = getImport(importedFilePath, id);
|
|
59
|
+
addExport(id, filePath, directImport.pos, directImport.line, directImport.col, undefined);
|
|
60
|
+
});
|
|
61
|
+
forEachAliasReExport(importMaps, (id, alias, _sources) => {
|
|
62
|
+
if (exportsMap.has(alias))
|
|
63
|
+
return;
|
|
64
|
+
const aliasImport = getImport(importedFilePath, id);
|
|
65
|
+
addExport(alias, filePath, aliasImport.pos, aliasImport.line, aliasImport.col, undefined);
|
|
66
|
+
});
|
|
67
|
+
forEachNamespaceReExport(importMaps, (namespace, _sources) => {
|
|
68
|
+
if (exportsMap.has(namespace))
|
|
69
|
+
return;
|
|
70
|
+
const namespaceImport = getImport(importedFilePath, namespace) ?? getImport(importedFilePath, IMPORT_STAR);
|
|
71
|
+
const childExports = [];
|
|
72
|
+
const exportedIdentifiers = getExportedIdentifiers(graph, importedFilePath);
|
|
73
|
+
for (const identifier of exportedIdentifiers.keys()) {
|
|
74
|
+
const usage = explorer.getUsage(importedFilePath, identifier);
|
|
75
|
+
const usageEntryPaths = new Set();
|
|
76
|
+
for (const location of usage.locations)
|
|
77
|
+
if (location.isEntry)
|
|
78
|
+
usageEntryPaths.add(location.filePath);
|
|
79
|
+
if (entryPaths.has(importedFilePath))
|
|
80
|
+
usageEntryPaths.add(importedFilePath);
|
|
81
|
+
childExports.push({
|
|
82
|
+
filePath,
|
|
83
|
+
identifier,
|
|
84
|
+
pos: namespaceImport.pos,
|
|
85
|
+
line: namespaceImport.line,
|
|
86
|
+
col: namespaceImport.col,
|
|
87
|
+
importLocations: usage.locations,
|
|
88
|
+
entryPaths: usageEntryPaths,
|
|
89
|
+
exports: undefined,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
addExport(namespace, filePath, namespaceImport.pos, namespaceImport.line, namespaceImport.col, childExports);
|
|
93
|
+
});
|
|
94
|
+
const starSources = getStarReExportSources(importMaps);
|
|
95
|
+
if (starSources) {
|
|
96
|
+
const starImport = getImport(importedFilePath, IMPORT_STAR);
|
|
97
|
+
const exportedIdentifiers = getExportedIdentifiers(graph, importedFilePath);
|
|
98
|
+
for (const [nestedIdentifier] of exportedIdentifiers) {
|
|
99
|
+
if (nestedIdentifier === 'default')
|
|
100
|
+
continue;
|
|
101
|
+
addExport(nestedIdentifier, importedFilePath, starImport.pos, starImport.line, starImport.col, undefined);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return exportsMap;
|
|
106
|
+
};
|
|
107
|
+
export const buildInternalImports = (fileNode, explorer, importLookup) => {
|
|
108
|
+
const getImport = (importedFilePath, id) => importLookup.get(importedFilePath)?.get(id)?.[0] ?? FALLBACK_LOCATION;
|
|
109
|
+
const internalImports = [];
|
|
110
|
+
const addInternalImport = (importedFilePath, identifier, alias = identifier, importLine, importCol) => {
|
|
111
|
+
const resolution = explorer.resolveDefinition(importedFilePath, identifier);
|
|
112
|
+
const location = resolution?.type === 'symbol' && resolution.exportNode
|
|
113
|
+
? {
|
|
114
|
+
filePath: resolution.filePath,
|
|
115
|
+
pos: resolution.exportNode.pos,
|
|
116
|
+
line: resolution.exportNode.line,
|
|
117
|
+
col: resolution.exportNode.col,
|
|
118
|
+
}
|
|
119
|
+
: { filePath: importedFilePath, pos: 0, line: 0, col: 0 };
|
|
120
|
+
internalImports.push({
|
|
121
|
+
identifier: alias,
|
|
122
|
+
filePath: location.filePath,
|
|
123
|
+
pos: location.pos,
|
|
124
|
+
line: location.line,
|
|
125
|
+
col: location.col,
|
|
126
|
+
importLine,
|
|
127
|
+
importCol,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
for (const [importedFilePath, importMaps] of fileNode.imports.internal) {
|
|
131
|
+
for (const identifier of importMaps.imported.keys()) {
|
|
132
|
+
const _import = getImport(importedFilePath, identifier);
|
|
133
|
+
addInternalImport(importedFilePath, identifier, _import.identifier, _import.line, _import.col);
|
|
134
|
+
}
|
|
135
|
+
for (const [identifier, aliasMap] of importMaps.importedAs) {
|
|
136
|
+
for (const [alias] of aliasMap) {
|
|
137
|
+
const _import = getImport(importedFilePath, identifier);
|
|
138
|
+
addInternalImport(importedFilePath, identifier, alias, _import.line, _import.col);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (importMaps.importedNs.size > 0) {
|
|
142
|
+
const _import = getImport(importedFilePath, IMPORT_STAR);
|
|
143
|
+
for (const namespace of importMaps.importedNs.keys()) {
|
|
144
|
+
addInternalImport(importedFilePath, namespace, namespace, _import.line, _import.col);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
internalImports.sort((a, b) => a.importLine - b.importLine || a.importCol - b.importCol);
|
|
149
|
+
return internalImports;
|
|
150
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModuleGraph } from '../types/module-graph.js';
|
|
2
|
+
import type { File } from './types.js';
|
|
3
|
+
export interface FileDescriptorOptions {
|
|
4
|
+
isShowContention?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const buildFileDescriptor: (filePath: string, cwd: string, graph: ModuleGraph, entryPaths: Set<string>, options?: FileDescriptorOptions) => File | null;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createGraphExplorer } from '../graph-explorer/explorer.js';
|
|
2
|
+
import { toAbsolute } from '../util/path.js';
|
|
3
|
+
import { buildExportsMap, buildImportLookup, buildInternalImports } from './build-maps.js';
|
|
4
|
+
export const buildFileDescriptor = (filePath, cwd, graph, entryPaths, options = {}) => {
|
|
5
|
+
const absolutePath = toAbsolute(filePath, cwd);
|
|
6
|
+
const node = graph.get(absolutePath);
|
|
7
|
+
if (!node)
|
|
8
|
+
return null;
|
|
9
|
+
const explorer = createGraphExplorer(graph, entryPaths);
|
|
10
|
+
const metrics = { imports: 0, exports: 0, cycles: 0, contention: 0 };
|
|
11
|
+
let t0 = performance.now();
|
|
12
|
+
const importLookup = buildImportLookup(node);
|
|
13
|
+
const internalImports = buildInternalImports(node, explorer, importLookup);
|
|
14
|
+
metrics.imports = performance.now() - t0;
|
|
15
|
+
t0 = performance.now();
|
|
16
|
+
const exportsMap = buildExportsMap(node, absolutePath, graph, entryPaths, explorer, importLookup);
|
|
17
|
+
const exports = Array.from(exportsMap.values()).sort((a, b) => (a.line ?? 0) - (b.line ?? 0) || (a.col ?? 0) - (b.col ?? 0));
|
|
18
|
+
metrics.exports = performance.now() - t0;
|
|
19
|
+
t0 = performance.now();
|
|
20
|
+
const cycles = explorer.findCycles(absolutePath);
|
|
21
|
+
metrics.cycles = performance.now() - t0;
|
|
22
|
+
t0 = performance.now();
|
|
23
|
+
const contention = Object.create(null);
|
|
24
|
+
if (options.isShowContention !== false) {
|
|
25
|
+
const contentionMap = explorer.getContention(absolutePath);
|
|
26
|
+
for (const identifier of exportsMap.keys()) {
|
|
27
|
+
const details = contentionMap.get(identifier);
|
|
28
|
+
if (!details)
|
|
29
|
+
continue;
|
|
30
|
+
if (details.branching.length === 0 && details.conflict.length === 0)
|
|
31
|
+
continue;
|
|
32
|
+
contention[identifier] = details;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
metrics.contention = performance.now() - t0;
|
|
36
|
+
return {
|
|
37
|
+
internalImports,
|
|
38
|
+
exports,
|
|
39
|
+
cycles,
|
|
40
|
+
contention,
|
|
41
|
+
metrics,
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { IMPORT_STAR, KNIP_CONFIG_LOCATIONS, SIDE_EFFECTS } from '../constants.js';
|
|
2
|
+
export { finalizeConfigurationHints } from '../reporters/util/configuration-hints.js';
|
|
3
|
+
export { getIssuePrefix } from '../reporters/util/util.js';
|
|
4
|
+
export type { Results } from '../run.js';
|
|
5
|
+
export type { Issue, Issues, IssueType, Rules } from '../types/issues.js';
|
|
6
|
+
export type { PackageJson } from '../types/package-json.js';
|
|
7
|
+
export { createOptions, type MainOptions } from '../util/create-options.js';
|
|
8
|
+
export { buildFileDescriptor, type FileDescriptorOptions } from './file-descriptor.js';
|
|
9
|
+
export { createSession, type Session } from './session.js';
|
|
10
|
+
export type { ContentionDetails, Export, File, SourceLocation } from './types.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { IMPORT_STAR, KNIP_CONFIG_LOCATIONS, SIDE_EFFECTS } from '../constants.js';
|
|
2
|
+
export { finalizeConfigurationHints } from '../reporters/util/configuration-hints.js';
|
|
3
|
+
export { getIssuePrefix } from '../reporters/util/util.js';
|
|
4
|
+
export { createOptions } from '../util/create-options.js';
|
|
5
|
+
export { buildFileDescriptor } from './file-descriptor.js';
|
|
6
|
+
export { createSession } from './session.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CollectorIssues } from '../IssueCollector.js';
|
|
2
|
+
import { type ProcessedHint } from '../reporters/util/configuration-hints.js';
|
|
3
|
+
import { type Results } from '../run.js';
|
|
4
|
+
import type { MainOptions } from '../util/create-options.js';
|
|
5
|
+
import type { WatchChange } from '../util/watch.js';
|
|
6
|
+
import { type FileDescriptorOptions } from './file-descriptor.js';
|
|
7
|
+
import type { File } from './types.js';
|
|
8
|
+
type WatchUpdate = {
|
|
9
|
+
duration: number;
|
|
10
|
+
mem: number;
|
|
11
|
+
};
|
|
12
|
+
export interface Session {
|
|
13
|
+
handleFileChanges(changes: WatchChange[]): Promise<WatchUpdate>;
|
|
14
|
+
getIssues(): CollectorIssues;
|
|
15
|
+
getResults(): Results;
|
|
16
|
+
getConfigurationHints(): ProcessedHint[];
|
|
17
|
+
describeFile(filePath: string, options?: FileDescriptorOptions): File | null;
|
|
18
|
+
}
|
|
19
|
+
export declare const createSession: (options: MainOptions) => Promise<Session>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { finalizeConfigurationHints } from '../reporters/util/configuration-hints.js';
|
|
2
|
+
import { run } from '../run.js';
|
|
3
|
+
import { buildFileDescriptor } from './file-descriptor.js';
|
|
4
|
+
export const createSession = async (options) => {
|
|
5
|
+
const { session, results } = await run(options);
|
|
6
|
+
if (!session)
|
|
7
|
+
throw new Error('Unable to initialize watch session');
|
|
8
|
+
return createSessionAdapter(session, results, options);
|
|
9
|
+
};
|
|
10
|
+
const createSessionAdapter = (session, results, options) => {
|
|
11
|
+
return {
|
|
12
|
+
handleFileChanges: session.handleFileChanges,
|
|
13
|
+
getIssues: session.getIssues,
|
|
14
|
+
getResults: () => results,
|
|
15
|
+
getConfigurationHints: () => finalizeConfigurationHints(results, options),
|
|
16
|
+
describeFile: (filePath, opts) => buildFileDescriptor(filePath, options.cwd, session.getGraph(), session.getEntryPaths(), opts),
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RE_EXPORT_KIND } from '../graph-explorer/constants.js';
|
|
2
|
+
import type { Import, Position } from '../types/module-graph.js';
|
|
3
|
+
export interface SourceLocation extends Position {
|
|
4
|
+
filePath: string;
|
|
5
|
+
identifier: string;
|
|
6
|
+
}
|
|
7
|
+
export interface InternalImport extends SourceLocation {
|
|
8
|
+
importLine: number;
|
|
9
|
+
importCol: number;
|
|
10
|
+
}
|
|
11
|
+
export interface Export extends SourceLocation {
|
|
12
|
+
importLocations: SourceLocation[];
|
|
13
|
+
entryPaths: Set<string>;
|
|
14
|
+
exports: Export[] | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface ContentionDetails {
|
|
17
|
+
branching: string[];
|
|
18
|
+
conflict: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface FileMetrics {
|
|
21
|
+
imports: number;
|
|
22
|
+
exports: number;
|
|
23
|
+
cycles: number;
|
|
24
|
+
contention: number;
|
|
25
|
+
}
|
|
26
|
+
export interface File {
|
|
27
|
+
exports: Export[];
|
|
28
|
+
internalImports: InternalImport[];
|
|
29
|
+
cycles: Cycle[];
|
|
30
|
+
contention: Record<string, ContentionDetails>;
|
|
31
|
+
metrics: FileMetrics;
|
|
32
|
+
}
|
|
33
|
+
export type ImportLookup = Map<string, Map<string, Import[]>>;
|
|
34
|
+
export type Cycle = string[];
|
|
35
|
+
export type ReExportKind = (typeof RE_EXPORT_KIND)[keyof typeof RE_EXPORT_KIND];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/issues.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export type ReporterOptions = {
|
|
|
63
63
|
options: string;
|
|
64
64
|
preprocessorOptions: string;
|
|
65
65
|
includedWorkspaceDirs: string[];
|
|
66
|
-
configFilePath
|
|
66
|
+
configFilePath: string | undefined;
|
|
67
67
|
maxShowIssues?: number;
|
|
68
68
|
};
|
|
69
69
|
export type Reporter = (options: ReporterOptions) => void;
|