knip 5.44.0 → 5.44.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/ConfigurationChief.js +3 -3
  2. package/dist/DependencyDeputy.d.ts +1 -1
  3. package/dist/DependencyDeputy.js +4 -4
  4. package/dist/IssueCollector.js +1 -1
  5. package/dist/ProjectPrincipal.d.ts +2 -2
  6. package/dist/WorkspaceWorker.js +5 -1
  7. package/dist/graph/analyze.d.ts +30 -0
  8. package/dist/graph/analyze.js +232 -0
  9. package/dist/graph/build.d.ts +40 -0
  10. package/dist/graph/build.js +268 -0
  11. package/dist/index.js +46 -484
  12. package/dist/plugins/react-router/index.js +1 -1
  13. package/dist/plugins/stylelint/index.js +1 -1
  14. package/dist/plugins/stylelint/types.d.ts +1 -1
  15. package/dist/{ConfigurationValidator.d.ts → schema/configuration.d.ts} +1 -1
  16. package/dist/{ConfigurationValidator.js → schema/configuration.js} +2 -2
  17. package/dist/types/config.d.ts +2 -2
  18. package/dist/types/{dependency-graph.d.ts → module-graph.d.ts} +1 -1
  19. package/dist/typescript/find-internal-references.d.ts +1 -1
  20. package/dist/typescript/get-imports-and-exports.d.ts +1 -1
  21. package/dist/typescript/get-imports-and-exports.js +1 -1
  22. package/dist/util/has-strictly-ns-references.d.ts +2 -2
  23. package/dist/util/is-identifier-referenced.d.ts +2 -2
  24. package/dist/util/{dependency-graph.d.ts → module-graph.d.ts} +3 -3
  25. package/dist/util/trace.d.ts +2 -2
  26. package/dist/util/watch.d.ts +4 -4
  27. package/dist/util/watch.js +3 -4
  28. package/dist/version.d.ts +1 -1
  29. package/dist/version.js +1 -1
  30. package/package.json +1 -1
  31. /package/dist/types/{dependency-graph.js → module-graph.js} +0 -0
  32. /package/dist/{issues/initializers.d.ts → util/issue-initializers.d.ts} +0 -0
  33. /package/dist/{issues/initializers.js → util/issue-initializers.js} +0 -0
  34. /package/dist/util/{dependency-graph.js → module-graph.js} +0 -0
@@ -1,8 +1,7 @@
1
1
  import picomatch from 'picomatch';
2
- import { ConfigurationValidator } from './ConfigurationValidator.js';
3
2
  import { partitionCompilers } from './compilers/index.js';
4
3
  import { DEFAULT_EXTENSIONS, KNIP_CONFIG_LOCATIONS, ROOT_WORKSPACE_NAME } from './constants.js';
5
- import { defaultRules } from './issues/initializers.js';
4
+ import { knipConfigurationSchema } from './schema/configuration.js';
6
5
  import { pluginNames } from './types/PluginNames.js';
7
6
  import { arrayify, compact } from './util/array.js';
8
7
  import parsedArgValues from './util/cli-arguments.js';
@@ -11,6 +10,7 @@ import { ConfigurationError } from './util/errors.js';
11
10
  import { findFile, isDirectory, isFile, loadJSON } from './util/fs.js';
12
11
  import { getIncludedIssueTypes } from './util/get-included-issue-types.js';
13
12
  import { _dirGlob } from './util/glob.js';
13
+ import { defaultRules } from './util/issue-initializers.js';
14
14
  import { _load } from './util/loader.js';
15
15
  import mapWorkspaces from './util/map-workspaces.js';
16
16
  import { getKeysByValue } from './util/object.js';
@@ -96,7 +96,7 @@ export class ConfigurationChief {
96
96
  this.rawConfig = this.resolvedConfigFilePath
97
97
  ? await this.loadResolvedConfigurationFile(this.resolvedConfigFilePath)
98
98
  : manifest.knip;
99
- const parsedConfig = this.rawConfig ? ConfigurationValidator.parse(partitionCompilers(this.rawConfig)) : {};
99
+ const parsedConfig = this.rawConfig ? knipConfigurationSchema.parse(partitionCompilers(this.rawConfig)) : {};
100
100
  this.config = this.normalize(parsedConfig);
101
101
  await this.setWorkspaces();
102
102
  }
@@ -49,7 +49,7 @@ export declare class DependencyDeputy {
49
49
  setInstalledBinaries(workspaceName: string, installedBinaries: Map<string, Set<string>>): void;
50
50
  getInstalledBinaries(workspaceName: string): InstalledBinaries | undefined;
51
51
  setHasTypesIncluded(workspaceName: string, hasTypesIncluded: Set<string>): void;
52
- getHasTypesIncluded(workspaceName: string): InstalledBinaries | undefined;
52
+ getHasTypesIncluded(workspaceName: string): Set<string> | undefined;
53
53
  addReferencedDependency(workspaceName: string, packageName: string): void;
54
54
  addReferencedBinary(workspaceName: string, binaryName: string): void;
55
55
  setHostDependencies(workspaceName: string, hostDependencies: HostDependencies): void;
@@ -93,7 +93,7 @@ export class DependencyDeputy {
93
93
  this.hasTypesIncluded.set(workspaceName, hasTypesIncluded);
94
94
  }
95
95
  getHasTypesIncluded(workspaceName) {
96
- return this.installedBinaries.get(workspaceName);
96
+ return this.hasTypesIncluded.get(workspaceName);
97
97
  }
98
98
  addReferencedDependency(workspaceName, packageName) {
99
99
  if (!this.referencedDependencies.has(workspaceName)) {
@@ -133,7 +133,7 @@ export class DependencyDeputy {
133
133
  if (closestWorkspaceName || closestWorkspaceNameForTypes) {
134
134
  if (closestWorkspaceName)
135
135
  this.addReferencedDependency(closestWorkspaceName, packageName);
136
- if (closestWorkspaceNameForTypes)
136
+ if (closestWorkspaceNameForTypes && !this.hasTypesIncluded.get(closestWorkspaceNameForTypes)?.has(packageName))
137
137
  this.addReferencedDependency(closestWorkspaceNameForTypes, typesPackageName);
138
138
  return true;
139
139
  }
@@ -185,7 +185,7 @@ export class DependencyDeputy {
185
185
  const typedPackageName = getPackageFromDefinitelyTyped(typedDependency);
186
186
  if (IGNORE_DEFINITELY_TYPED.has(typedPackageName))
187
187
  return true;
188
- if (hasTypesIncluded?.has(typedDependency))
188
+ if (hasTypesIncluded?.has(typedPackageName))
189
189
  return false;
190
190
  const hostDependencies = [
191
191
  ...this.getHostDependenciesFor(workspace, dependency),
@@ -193,7 +193,7 @@ export class DependencyDeputy {
193
193
  ];
194
194
  if (hostDependencies.length)
195
195
  return !!hostDependencies.find(host => isReferencedDependency(host.name, true));
196
- if (!referencedDependencies)
196
+ if (!referencedDependencies?.has(dependency))
197
197
  return false;
198
198
  return referencedDependencies.has(typedPackageName);
199
199
  }
@@ -1,6 +1,6 @@
1
1
  import picomatch from 'picomatch';
2
- import { initCounters, initIssues } from './issues/initializers.js';
3
2
  import { timerify } from './util/Performance.js';
3
+ import { initCounters, initIssues } from './util/issue-initializers.js';
4
4
  import { relative } from './util/path.js';
5
5
  const hasConfigurationHint = (hints, hint) => Array.from(hints).some(item => item.identifier === hint.identifier && item.type === hint.type && item.workspaceName === hint.workspaceName);
6
6
  const isMatch = timerify(picomatch.isMatch, 'isMatch');
@@ -2,7 +2,7 @@ import ts from 'typescript';
2
2
  import { CacheConsultant } from './CacheConsultant.js';
3
3
  import type { AsyncCompilers, SyncCompilers } from './compilers/types.js';
4
4
  import type { GetImportsAndExportsOptions } from './types/config.js';
5
- import type { DependencyGraph, Export, ExportMember, FileNode } from './types/dependency-graph.js';
5
+ import type { Export, ExportMember, FileNode, ModuleGraph } from './types/module-graph.js';
6
6
  import type { PrincipalOptions } from './types/project.js';
7
7
  import type { SourceFileManager } from './typescript/SourceFileManager.js';
8
8
  import type { ResolveModuleNames } from './typescript/resolve-module-names.js';
@@ -54,5 +54,5 @@ export declare class ProjectPrincipal {
54
54
  invalidateFile(filePath: string): void;
55
55
  findUnusedMembers(filePath: string, members: ExportMember[]): ExportMember[];
56
56
  hasExternalReferences(filePath: string, exportedItem: Export): boolean;
57
- reconcileCache(graph: DependencyGraph): void;
57
+ reconcileCache(graph: ModuleGraph): void;
58
58
  }
@@ -285,7 +285,11 @@ export class WorkspaceWorker {
285
285
  do {
286
286
  for (const [pluginName, dependencies] of configFiles.entries()) {
287
287
  configFiles.delete(pluginName);
288
- await runPlugin(pluginName, Array.from(dependencies));
288
+ if (this.enabledPlugins.includes(pluginName))
289
+ await runPlugin(pluginName, Array.from(dependencies));
290
+ else
291
+ for (const id of dependencies)
292
+ addInput(toEntry(id));
289
293
  }
290
294
  } while (remainingPlugins.size > 0 && configFiles.size > 0);
291
295
  debugLogArray(name, 'Plugin dependencies', () => compact(inputs.map(toDebugString)));
@@ -0,0 +1,30 @@
1
+ import type { ConfigurationChief } from '../ConfigurationChief.js';
2
+ import type { ConsoleStreamer } from '../ConsoleStreamer.js';
3
+ import type { DependencyDeputy } from '../DependencyDeputy.js';
4
+ import type { IssueCollector } from '../IssueCollector.js';
5
+ import type { IssueFixer } from '../IssueFixer.js';
6
+ import type { PrincipalFactory } from '../PrincipalFactory.js';
7
+ import type { Tags } from '../types/cli.js';
8
+ import type { Report } from '../types/issues.js';
9
+ import type { ModuleGraph } from '../types/module-graph.js';
10
+ interface AnalyzeOptions {
11
+ analyzedFiles: Set<string>;
12
+ chief: ConfigurationChief;
13
+ collector: IssueCollector;
14
+ deputy: DependencyDeputy;
15
+ entryPaths: Set<string>;
16
+ factory: PrincipalFactory;
17
+ fixer: IssueFixer;
18
+ graph: ModuleGraph;
19
+ isFix: boolean;
20
+ isHideConfigHints: boolean;
21
+ isIncludeLibs: boolean;
22
+ isProduction: boolean;
23
+ report: Report;
24
+ streamer: ConsoleStreamer;
25
+ tags: Tags;
26
+ unreferencedFiles: Set<string>;
27
+ workspace?: string;
28
+ }
29
+ export declare const analyze: (options: AnalyzeOptions) => Promise<() => Promise<void>>;
30
+ export {};
@@ -0,0 +1,232 @@
1
+ import { getType, hasStrictlyEnumReferences, hasStrictlyNsReferences } from '../util/has-strictly-ns-references.js';
2
+ import { getIsIdentifierReferencedHandler } from '../util/is-identifier-referenced.js';
3
+ import { getPackageNameFromModuleSpecifier } from '../util/modules.js';
4
+ import { findMatch } from '../util/regex.js';
5
+ import { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from '../util/tag.js';
6
+ import { createAndPrintTrace, printTrace } from '../util/trace.js';
7
+ export const analyze = async (options) => {
8
+ const { analyzedFiles, chief, collector, deputy, entryPaths, factory, fixer, graph, isFix, isHideConfigHints, isIncludeLibs, isProduction, report, streamer, tags, unreferencedFiles, workspace, } = options;
9
+ const isReportDependencies = report.dependencies || report.unlisted || report.unresolved;
10
+ const isReportValues = report.exports || report.nsExports || report.classMembers;
11
+ const isReportTypes = report.types || report.nsTypes || report.enumMembers;
12
+ const isReportClassMembers = report.classMembers;
13
+ const isSkipLibs = !(isIncludeLibs || isReportClassMembers);
14
+ const isShowConfigHints = !workspace && !isProduction && !isHideConfigHints;
15
+ const shouldIgnore = getShouldIgnoreHandler(isProduction);
16
+ const shouldIgnoreTags = getShouldIgnoreTagHandler(tags);
17
+ const isIdentifierReferenced = getIsIdentifierReferencedHandler(graph, entryPaths);
18
+ const ignoreExportsUsedInFile = chief.config.ignoreExportsUsedInFile;
19
+ const isExportedItemReferenced = (exportedItem) => exportedItem.refs[1] ||
20
+ (exportedItem.refs[0] > 0 &&
21
+ (typeof ignoreExportsUsedInFile === 'object'
22
+ ? exportedItem.type !== 'unknown' && !!ignoreExportsUsedInFile[exportedItem.type]
23
+ : ignoreExportsUsedInFile));
24
+ const analyzeGraph = async () => {
25
+ if (isReportValues || isReportTypes) {
26
+ streamer.cast('Connecting the dots...');
27
+ for (const [filePath, file] of graph.entries()) {
28
+ const exportItems = file.exports;
29
+ if (!exportItems || exportItems.size === 0)
30
+ continue;
31
+ const workspace = chief.findWorkspaceByFilePath(filePath);
32
+ if (workspace) {
33
+ const { isIncludeEntryExports } = workspace.config;
34
+ const principal = factory.getPrincipalByPackageName(workspace.pkgName);
35
+ const isEntry = entryPaths.has(filePath);
36
+ if (!isIncludeEntryExports && isEntry) {
37
+ createAndPrintTrace(filePath, { isEntry });
38
+ continue;
39
+ }
40
+ const importsForExport = file.imported;
41
+ for (const [identifier, exportedItem] of exportItems.entries()) {
42
+ if (!isFix && exportedItem.isReExport)
43
+ continue;
44
+ if (shouldIgnore(exportedItem.jsDocTags))
45
+ continue;
46
+ const isIgnored = shouldIgnoreTags(exportedItem.jsDocTags);
47
+ if (importsForExport) {
48
+ const { isReferenced, reExportingEntryFile, traceNode } = isIdentifierReferenced(filePath, identifier, isIncludeEntryExports);
49
+ if ((isReferenced || exportedItem.refs[1]) && isIgnored) {
50
+ for (const tagName of exportedItem.jsDocTags) {
51
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
52
+ collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
53
+ }
54
+ }
55
+ }
56
+ if (isIgnored)
57
+ continue;
58
+ if (reExportingEntryFile) {
59
+ if (!isIncludeEntryExports) {
60
+ createAndPrintTrace(filePath, { identifier, isEntry, hasRef: isReferenced });
61
+ continue;
62
+ }
63
+ const reExportedItem = graph.get(reExportingEntryFile)?.exports.get(identifier);
64
+ if (reExportedItem && shouldIgnore(reExportedItem.jsDocTags))
65
+ continue;
66
+ }
67
+ if (traceNode)
68
+ printTrace(traceNode, filePath, identifier);
69
+ if (isReferenced) {
70
+ if (report.enumMembers && exportedItem.type === 'enum') {
71
+ if (!report.nsTypes && importsForExport.refs.has(identifier))
72
+ continue;
73
+ if (hasStrictlyEnumReferences(importsForExport, identifier))
74
+ continue;
75
+ for (const member of exportedItem.members) {
76
+ if (findMatch(workspace.ignoreMembers, member.identifier))
77
+ continue;
78
+ if (shouldIgnore(member.jsDocTags))
79
+ continue;
80
+ if (member.refs[0] === 0) {
81
+ const id = `${identifier}.${member.identifier}`;
82
+ const { isReferenced } = isIdentifierReferenced(filePath, id, true);
83
+ const isIgnored = shouldIgnoreTags(member.jsDocTags);
84
+ if (!isReferenced) {
85
+ if (isIgnored)
86
+ continue;
87
+ const isIssueAdded = collector.addIssue({
88
+ type: 'enumMembers',
89
+ filePath,
90
+ workspace: workspace.name,
91
+ symbol: member.identifier,
92
+ parentSymbol: identifier,
93
+ pos: member.pos,
94
+ line: member.line,
95
+ col: member.col,
96
+ });
97
+ if (isFix && isIssueAdded && member.fix)
98
+ fixer.addUnusedTypeNode(filePath, [member.fix]);
99
+ }
100
+ else if (isIgnored) {
101
+ for (const tagName of exportedItem.jsDocTags) {
102
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
103
+ collector.addTagHint({ type: 'tag', filePath, identifier: id, tagName });
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ if (principal && isReportClassMembers && exportedItem.type === 'class') {
111
+ const members = exportedItem.members.filter(member => !(findMatch(workspace.ignoreMembers, member.identifier) || shouldIgnore(member.jsDocTags)));
112
+ for (const member of principal.findUnusedMembers(filePath, members)) {
113
+ if (shouldIgnoreTags(member.jsDocTags)) {
114
+ const identifier = `${exportedItem.identifier}.${member.identifier}`;
115
+ for (const tagName of exportedItem.jsDocTags) {
116
+ if (tags[1].includes(tagName.replace(/^\@/, ''))) {
117
+ collector.addTagHint({ type: 'tag', filePath, identifier, tagName });
118
+ }
119
+ }
120
+ continue;
121
+ }
122
+ const isIssueAdded = collector.addIssue({
123
+ type: 'classMembers',
124
+ filePath,
125
+ workspace: workspace.name,
126
+ symbol: member.identifier,
127
+ parentSymbol: exportedItem.identifier,
128
+ pos: member.pos,
129
+ line: member.line,
130
+ col: member.col,
131
+ });
132
+ if (isFix && isIssueAdded && member.fix)
133
+ fixer.addUnusedTypeNode(filePath, [member.fix]);
134
+ }
135
+ }
136
+ continue;
137
+ }
138
+ }
139
+ const [hasStrictlyNsRefs, namespace] = hasStrictlyNsReferences(graph, importsForExport, identifier);
140
+ const isType = ['enum', 'type', 'interface'].includes(exportedItem.type);
141
+ if (hasStrictlyNsRefs && ((!report.nsTypes && isType) || !(report.nsExports || isType)))
142
+ continue;
143
+ if (!isExportedItemReferenced(exportedItem)) {
144
+ if (isIgnored)
145
+ continue;
146
+ if (!isSkipLibs && principal?.hasExternalReferences(filePath, exportedItem))
147
+ continue;
148
+ const type = getType(hasStrictlyNsRefs, isType);
149
+ const isIssueAdded = collector.addIssue({
150
+ type,
151
+ filePath,
152
+ workspace: workspace.name,
153
+ symbol: identifier,
154
+ symbolType: exportedItem.type,
155
+ parentSymbol: namespace,
156
+ pos: exportedItem.pos,
157
+ line: exportedItem.line,
158
+ col: exportedItem.col,
159
+ });
160
+ if (isFix && isIssueAdded) {
161
+ if (isType)
162
+ fixer.addUnusedTypeNode(filePath, exportedItem.fixes);
163
+ else
164
+ fixer.addUnusedExportNode(filePath, exportedItem.fixes);
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ for (const [filePath, file] of graph.entries()) {
172
+ const ws = chief.findWorkspaceByFilePath(filePath);
173
+ if (ws) {
174
+ if (file.duplicates) {
175
+ for (const symbols of file.duplicates) {
176
+ if (symbols.length > 1) {
177
+ const symbol = symbols.map(s => s.symbol).join('|');
178
+ collector.addIssue({ type: 'duplicates', filePath, workspace: ws.name, symbol, symbols });
179
+ }
180
+ }
181
+ }
182
+ if (file.imports?.external) {
183
+ for (const specifier of file.imports.external) {
184
+ const packageName = getPackageNameFromModuleSpecifier(specifier);
185
+ const isHandled = packageName && deputy.maybeAddReferencedExternalDependency(ws, packageName);
186
+ if (!isHandled)
187
+ collector.addIssue({
188
+ type: 'unlisted',
189
+ filePath,
190
+ workspace: ws.name,
191
+ symbol: packageName ?? specifier,
192
+ specifier,
193
+ });
194
+ }
195
+ }
196
+ if (file.imports?.unresolved) {
197
+ for (const unresolvedImport of file.imports.unresolved) {
198
+ const { specifier, pos, line, col } = unresolvedImport;
199
+ collector.addIssue({ type: 'unresolved', filePath, workspace: ws.name, symbol: specifier, pos, line, col });
200
+ }
201
+ }
202
+ }
203
+ }
204
+ const unusedFiles = [...unreferencedFiles].filter(filePath => !analyzedFiles.has(filePath));
205
+ collector.addFilesIssues(unusedFiles);
206
+ collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });
207
+ if (isReportDependencies) {
208
+ const { dependencyIssues, devDependencyIssues, optionalPeerDependencyIssues } = deputy.settleDependencyIssues();
209
+ for (const issue of dependencyIssues)
210
+ collector.addIssue(issue);
211
+ if (!isProduction)
212
+ for (const issue of devDependencyIssues)
213
+ collector.addIssue(issue);
214
+ for (const issue of optionalPeerDependencyIssues)
215
+ collector.addIssue(issue);
216
+ deputy.removeIgnoredIssues(collector.getIssues());
217
+ if (isShowConfigHints) {
218
+ const configurationHints = deputy.getConfigurationHints();
219
+ for (const hint of configurationHints)
220
+ collector.addConfigurationHint(hint);
221
+ }
222
+ }
223
+ if (isShowConfigHints) {
224
+ const unusedIgnoredWorkspaces = chief.getUnusedIgnoredWorkspaces();
225
+ for (const identifier of unusedIgnoredWorkspaces) {
226
+ collector.addConfigurationHint({ type: 'ignoreWorkspaces', identifier });
227
+ }
228
+ }
229
+ };
230
+ await analyzeGraph();
231
+ return analyzeGraph;
232
+ };
@@ -0,0 +1,40 @@
1
+ import type { ConfigurationChief, Workspace } from '../ConfigurationChief.js';
2
+ import type { ConsoleStreamer } from '../ConsoleStreamer.js';
3
+ import type { DependencyDeputy } from '../DependencyDeputy.js';
4
+ import type { IssueCollector } from '../IssueCollector.js';
5
+ import type { PrincipalFactory } from '../PrincipalFactory.js';
6
+ import type { ProjectPrincipal } from '../ProjectPrincipal.js';
7
+ import type { Tags } from '../types/cli.js';
8
+ import type { Report } from '../types/issues.js';
9
+ import type { ModuleGraph } from '../types/module-graph.js';
10
+ interface BuildOptions {
11
+ cacheLocation: string;
12
+ chief: ConfigurationChief;
13
+ collector: IssueCollector;
14
+ cwd: string;
15
+ deputy: DependencyDeputy;
16
+ factory: PrincipalFactory;
17
+ gitignore: boolean;
18
+ isCache: boolean;
19
+ isFixExports: boolean;
20
+ isFixTypes: boolean;
21
+ isGitIgnored: (path: string) => boolean;
22
+ isIsolateWorkspaces: boolean;
23
+ isProduction: boolean;
24
+ isSkipLibs: boolean;
25
+ isStrict: boolean;
26
+ isWatch: boolean;
27
+ report: Report;
28
+ streamer: ConsoleStreamer;
29
+ tags: Tags;
30
+ tsConfigFile?: string;
31
+ workspaces: Workspace[];
32
+ }
33
+ export declare function build({ cacheLocation, chief, collector, cwd, deputy, factory, gitignore, isCache, isFixExports, isFixTypes, isGitIgnored, isIsolateWorkspaces, isProduction, isSkipLibs, isStrict, isWatch, report, streamer, tags, tsConfigFile, workspaces, }: BuildOptions): Promise<{
34
+ graph: ModuleGraph;
35
+ entryPaths: Set<string>;
36
+ analyzedFiles: Set<string>;
37
+ unreferencedFiles: Set<string>;
38
+ analyzeSourceFile: (filePath: string, principal: ProjectPrincipal) => void;
39
+ }>;
40
+ export {};