knip 5.44.5 → 5.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +3 -3
  2. package/dist/ConfigurationChief.d.ts +1 -0
  3. package/dist/ConfigurationChief.js +6 -0
  4. package/dist/IssueCollector.js +2 -1
  5. package/dist/IssueFixer.js +1 -1
  6. package/dist/WorkspaceWorker.d.ts +6 -1
  7. package/dist/WorkspaceWorker.js +49 -28
  8. package/dist/binaries/bash-parser.js +2 -1
  9. package/dist/binaries/package-manager/bun.js +7 -3
  10. package/dist/binaries/package-manager/yarn.js +5 -4
  11. package/dist/binaries/plugins.js +23 -20
  12. package/dist/cli.js +6 -4
  13. package/dist/compilers/index.d.ts +1 -0
  14. package/dist/graph/analyze.d.ts +1 -1
  15. package/dist/graph/analyze.js +2 -2
  16. package/dist/graph/build.js +9 -12
  17. package/dist/index.js +5 -4
  18. package/dist/plugins/angular/index.js +2 -2
  19. package/dist/plugins/eslint/helpers.d.ts +2 -2
  20. package/dist/plugins/eslint/helpers.js +14 -6
  21. package/dist/plugins/eslint/index.d.ts +5 -2
  22. package/dist/plugins/eslint/index.js +31 -3
  23. package/dist/plugins/eslint/types.d.ts +5 -4
  24. package/dist/plugins/index.d.ts +1 -7
  25. package/dist/plugins/playwright/index.d.ts +0 -6
  26. package/dist/plugins/playwright/index.js +1 -1
  27. package/dist/plugins/playwright-ct/index.d.ts +0 -6
  28. package/dist/plugins/playwright-ct/index.js +1 -2
  29. package/dist/plugins/storybook/index.js +2 -2
  30. package/dist/plugins/typescript/index.js +4 -2
  31. package/dist/plugins/xo/index.js +2 -2
  32. package/dist/plugins/xo/types.d.ts +2 -2
  33. package/dist/reporters/codeclimate.d.ts +3 -0
  34. package/dist/reporters/codeclimate.js +87 -0
  35. package/dist/reporters/compact.js +1 -1
  36. package/dist/reporters/disclosure.js +11 -24
  37. package/dist/reporters/index.d.ts +2 -1
  38. package/dist/reporters/index.js +2 -0
  39. package/dist/reporters/symbols.d.ts +1 -1
  40. package/dist/reporters/symbols.js +15 -32
  41. package/dist/schema/configuration.d.ts +3 -0
  42. package/dist/schema/configuration.js +1 -0
  43. package/dist/types/cli.d.ts +1 -1
  44. package/dist/types/config.d.ts +2 -1
  45. package/dist/types/issues.d.ts +4 -3
  46. package/dist/typescript/SourceFile.d.ts +0 -1
  47. package/dist/typescript/find-internal-references.js +9 -2
  48. package/dist/util/cli-arguments.d.ts +2 -1
  49. package/dist/util/cli-arguments.js +3 -1
  50. package/dist/util/get-referenced-inputs.js +2 -2
  51. package/dist/util/input.d.ts +3 -3
  52. package/dist/util/input.js +5 -5
  53. package/dist/util/issue-initializers.js +1 -1
  54. package/dist/version.d.ts +1 -1
  55. package/dist/version.js +1 -1
  56. package/package.json +6 -6
  57. package/schema.json +5 -0
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
 
10
10
  <div align="center">
11
11
 
12
- [![NPM Version](https://img.shields.io/npm/v/knip)][1]
13
- [![NPM Downloads](https://img.shields.io/npm/dm/knip)][1]
14
- [![GitHub Repo stars](https://img.shields.io/github/stars/webpro-nl/knip)][2]
12
+ [![NPM Version](https://img.shields.io/npm/v/knip?color=f56e0f)][1]
13
+ [![NPM Downloads](https://img.shields.io/npm/dm/knip?color=f56e0f)][1]
14
+ [![GitHub Repo stars](https://img.shields.io/github/stars/webpro-nl/knip?style=flat-square&color=f56e0f)][2]
15
15
 
16
16
  </div>
17
17
 
@@ -172,5 +172,6 @@ export declare class ConfigurationChief {
172
172
  getIncludedIssueTypes(cliArgs: CLIArguments): import("./types/issues.js").Report;
173
173
  findWorkspaceByFilePath(filePath: string): Workspace | undefined;
174
174
  getUnusedIgnoredWorkspaces(): string[];
175
+ getTags(): import("./types/cli.js").Tags;
175
176
  }
176
177
  export {};
@@ -18,6 +18,7 @@ import { getKeysByValue } from './util/object.js';
18
18
  import { join, relative } from './util/path.js';
19
19
  import { normalizePluginConfig } from './util/plugin.js';
20
20
  import { toRegexOrString } from './util/regex.js';
21
+ import { splitTags } from './util/tag.js';
21
22
  import { unwrapFunction } from './util/unwrap-function.js';
22
23
  import { byPathDepth } from './util/workspace.js';
23
24
  const { config: rawConfigArg } = parsedArgValues;
@@ -43,6 +44,7 @@ const defaultConfig = {
43
44
  syncCompilers: new Map(),
44
45
  asyncCompilers: new Map(),
45
46
  rootPluginConfigs: {},
47
+ tags: [],
46
48
  };
47
49
  export class ConfigurationChief {
48
50
  cwd;
@@ -150,6 +152,7 @@ export class ConfigurationChief {
150
152
  syncCompilers: new Map(Object.entries(syncCompilers ?? {})),
151
153
  asyncCompilers: new Map(Object.entries(asyncCompilers ?? {})),
152
154
  rootPluginConfigs,
155
+ tags: rawConfig.tags ?? [],
153
156
  };
154
157
  }
155
158
  async setWorkspaces() {
@@ -371,4 +374,7 @@ export class ConfigurationChief {
371
374
  return !isDirectory(dir) || isFile(join(dir, 'package.json'));
372
375
  });
373
376
  }
377
+ getTags() {
378
+ return splitTags(this.config.tags);
379
+ }
374
380
  }
@@ -40,7 +40,8 @@ export class IssueCollector {
40
40
  if (this.isMatch(filePath))
41
41
  continue;
42
42
  this.issues.files.add(filePath);
43
- this.issues._files.add({ type: 'files', filePath, symbol: relative(filePath), severity: this.rules.files });
43
+ const symbol = relative(filePath);
44
+ this.issues._files[symbol] = [{ type: 'files', filePath, symbol, severity: this.rules.files }];
44
45
  this.counters.files++;
45
46
  this.counters.processed++;
46
47
  }
@@ -57,7 +57,7 @@ export class IssueFixer {
57
57
  async removeUnusedFiles(issues) {
58
58
  if (!this.isFixFiles)
59
59
  return;
60
- for (const issue of issues._files) {
60
+ for (const issue of Object.values(issues._files).flatMap(Object.values)) {
61
61
  await rm(issue.filePath);
62
62
  issue.isFixed = true;
63
63
  }
@@ -1,4 +1,5 @@
1
1
  import { CacheConsultant } from './CacheConsultant.js';
2
+ import type { Workspace } from './ConfigurationChief.js';
2
3
  import type { PluginName } from './types/PluginNames.js';
3
4
  import type { Configuration, WorkspaceConfiguration } from './types/config.js';
4
5
  import type { PackageJson } from './types/package-json.js';
@@ -12,6 +13,7 @@ type WorkspaceManagerOptions = {
12
13
  manifest: PackageJson;
13
14
  dependencies: DependencySet;
14
15
  getReferencedInternalFilePath: (input: Input) => string | undefined;
16
+ findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;
15
17
  rootIgnore: Configuration['ignore'];
16
18
  negatedWorkspacePatterns: string[];
17
19
  ignoredWorkspacePatterns: string[];
@@ -21,6 +23,7 @@ type WorkspaceManagerOptions = {
21
23
  isCache: boolean;
22
24
  cacheLocation: string;
23
25
  allConfigFilePaths: Set<string>;
26
+ allConfigFilesMap: Map<string, Map<PluginName, Set<string>>>;
24
27
  };
25
28
  type CacheItem = {
26
29
  resolveEntryPaths?: Input[];
@@ -34,6 +37,7 @@ export declare class WorkspaceWorker {
34
37
  manifest: PackageJson;
35
38
  dependencies: DependencySet;
36
39
  getReferencedInternalFilePath: (input: Input) => string | undefined;
40
+ findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;
37
41
  isProduction: boolean;
38
42
  isStrict: boolean;
39
43
  rootIgnore: Configuration['ignore'];
@@ -44,7 +48,8 @@ export declare class WorkspaceWorker {
44
48
  enabledPluginsInAncestors: string[];
45
49
  cache: CacheConsultant<CacheItem>;
46
50
  allConfigFilePaths: Set<string>;
47
- constructor({ name, dir, cwd, config, manifest, dependencies, isProduction, isStrict, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, isCache, cacheLocation, allConfigFilePaths, }: WorkspaceManagerOptions);
51
+ allConfigFilesMap: Map<string, Map<PluginName, Set<string>>>;
52
+ constructor({ name, dir, cwd, config, manifest, dependencies, isProduction, isStrict, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, isCache, cacheLocation, allConfigFilePaths, allConfigFilesMap, }: WorkspaceManagerOptions);
48
53
  init(): Promise<void>;
49
54
  private determineEnabledPlugins;
50
55
  private getConfigForPlugin;
@@ -1,13 +1,14 @@
1
1
  import { CacheConsultant } from './CacheConsultant.js';
2
2
  import { _getInputsFromScripts } from './binaries/index.js';
3
+ import { ROOT_WORKSPACE_NAME } from './constants.js';
3
4
  import { getFilteredScripts } from './manifest/helpers.js';
4
5
  import { PluginEntries, Plugins } from './plugins.js';
5
6
  import { compact } from './util/array.js';
6
7
  import { debugLogArray, debugLogObject } from './util/debug.js';
7
8
  import { _glob, hasNoProductionSuffix, hasProductionSuffix, negate, prependDirToPattern } from './util/glob.js';
8
- import { isConfigPattern, toDebugString, toEntry } from './util/input.js';
9
+ import { isConfig, toConfig, toDebugString, toEntry } from './util/input.js';
9
10
  import { getKeysByValue } from './util/object.js';
10
- import { basename, dirname, extname, join } from './util/path.js';
11
+ import { basename, dirname, join } from './util/path.js';
11
12
  import { getFinalEntryPaths, loadConfigForPlugin } from './util/plugin.js';
12
13
  const nullConfig = { config: null, entry: null, project: null };
13
14
  const initEnabledPluginsMap = () => Object.keys(Plugins).reduce((enabled, pluginName) => ({ ...enabled, [pluginName]: false }), {});
@@ -19,6 +20,7 @@ export class WorkspaceWorker {
19
20
  manifest;
20
21
  dependencies;
21
22
  getReferencedInternalFilePath;
23
+ findWorkspaceByFilePath;
22
24
  isProduction;
23
25
  isStrict;
24
26
  rootIgnore;
@@ -29,7 +31,8 @@ export class WorkspaceWorker {
29
31
  enabledPluginsInAncestors;
30
32
  cache;
31
33
  allConfigFilePaths;
32
- constructor({ name, dir, cwd, config, manifest, dependencies, isProduction, isStrict, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, isCache, cacheLocation, allConfigFilePaths, }) {
34
+ allConfigFilesMap;
35
+ constructor({ name, dir, cwd, config, manifest, dependencies, isProduction, isStrict, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, isCache, cacheLocation, allConfigFilePaths, allConfigFilesMap, }) {
33
36
  this.name = name;
34
37
  this.dir = dir;
35
38
  this.cwd = cwd;
@@ -43,7 +46,9 @@ export class WorkspaceWorker {
43
46
  this.ignoredWorkspacePatterns = ignoredWorkspacePatterns;
44
47
  this.enabledPluginsInAncestors = enabledPluginsInAncestors;
45
48
  this.allConfigFilePaths = allConfigFilePaths;
49
+ this.allConfigFilesMap = allConfigFilesMap;
46
50
  this.getReferencedInternalFilePath = getReferencedInternalFilePath;
51
+ this.findWorkspaceByFilePath = findWorkspaceByFilePath;
47
52
  this.cache = new CacheConsultant({ name: `plugins-${name}`, isEnabled: isCache, cacheLocation });
48
53
  }
49
54
  async init() {
@@ -173,21 +178,25 @@ export class WorkspaceWorker {
173
178
  const hasProductionInput = (input) => productionInputsFromManifest.find(d => d.specifier === input.specifier && d.type === input.type);
174
179
  const createGetInputsFromScripts = (containingFilePath) => (scripts, options) => _getInputsFromScripts(scripts, { ...baseOptions, ...options, containingFilePath });
175
180
  const inputs = [];
176
- const configFiles = new Map();
177
181
  const remainingPlugins = new Set(this.enabledPlugins);
178
182
  const addInput = (input, containingFilePath = input.containingFilePath) => inputs.push({ ...input, containingFilePath });
179
- const handleConfigInput = (pluginName, dependency) => {
180
- const configFilePath = this.getReferencedInternalFilePath(dependency);
183
+ const handleConfigInput = (pluginName, input) => {
184
+ const configFilePath = this.getReferencedInternalFilePath(input);
181
185
  if (configFilePath) {
182
- if (!configFiles.has(pluginName))
183
- configFiles.set(pluginName, new Set());
184
- configFiles.get(pluginName)?.add(configFilePath);
185
- if (extname(dependency.specifier) !== '.json')
186
- addInput(toEntry(dependency.specifier), dependency.containingFilePath);
186
+ const workspace = this.findWorkspaceByFilePath(configFilePath);
187
+ if (workspace) {
188
+ const name = this.name === ROOT_WORKSPACE_NAME ? workspace.name : this.name;
189
+ const files = this.allConfigFilesMap;
190
+ if (!files.has(name))
191
+ files.set(name, new Map());
192
+ if (!files.get(name)?.has(pluginName))
193
+ files.get(name)?.set(pluginName, new Set());
194
+ files.get(name)?.get(pluginName)?.add(configFilePath);
195
+ }
187
196
  }
188
197
  };
189
198
  for (const input of [...inputsFromManifest, ...productionInputsFromManifest]) {
190
- if (isConfigPattern(input)) {
199
+ if (isConfig(input)) {
191
200
  handleConfigInput(input.pluginName, { ...input, containingFilePath });
192
201
  }
193
202
  else {
@@ -208,9 +217,13 @@ export class WorkspaceWorker {
208
217
  const label = 'config file';
209
218
  const configFilePaths = await _glob({ patterns, cwd: rootCwd, dir: cwd, gitignore: false, label });
210
219
  const remainingConfigFilePaths = configFilePaths.filter(filePath => !this.allConfigFilePaths.has(filePath));
211
- for (const f of remainingConfigFilePaths)
212
- if (basename(f) !== 'package.json')
213
- this.allConfigFilePaths.add(f);
220
+ for (const filePath of remainingConfigFilePaths) {
221
+ if (basename(filePath) !== 'package.json') {
222
+ this.allConfigFilePaths.add(filePath);
223
+ addInput(toEntry(filePath));
224
+ addInput(toConfig(pluginName, filePath));
225
+ }
226
+ }
214
227
  const options = {
215
228
  ...baseScriptOptions,
216
229
  config,
@@ -252,10 +265,12 @@ export class WorkspaceWorker {
252
265
  if (hasResolveConfig) {
253
266
  const inputs = (await plugin.resolveConfig?.(config, opts)) ?? [];
254
267
  for (const input of inputs) {
255
- if (isConfigPattern(input)) {
268
+ if (isConfig(input)) {
256
269
  handleConfigInput(input.pluginName, { ...input, containingFilePath: configFilePath });
257
270
  }
258
- addInput(input, configFilePath);
271
+ else {
272
+ addInput(input, configFilePath);
273
+ }
259
274
  }
260
275
  data.resolveConfig = inputs;
261
276
  }
@@ -276,22 +291,28 @@ export class WorkspaceWorker {
276
291
  };
277
292
  const enabledPluginTitles = this.enabledPlugins.map(name => Plugins[name].title);
278
293
  debugLogObject(this.name, 'Enabled plugins', enabledPluginTitles);
294
+ const configFiles = this.allConfigFilesMap.get(name);
279
295
  for (const pluginName of this.enabledPlugins) {
280
- const patterns = [...this.getConfigurationFilePatterns(pluginName), ...(configFiles.get(pluginName) ?? [])];
281
- configFiles.delete(pluginName);
296
+ const patterns = [...this.getConfigurationFilePatterns(pluginName), ...(configFiles?.get(pluginName) ?? [])];
297
+ configFiles?.delete(pluginName);
282
298
  await runPlugin(pluginName, compact(patterns));
283
299
  remainingPlugins.delete(pluginName);
284
300
  }
285
- do {
286
- for (const [pluginName, dependencies] of configFiles.entries()) {
287
- configFiles.delete(pluginName);
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));
301
+ {
302
+ const configFiles = this.allConfigFilesMap.get(name);
303
+ if (configFiles) {
304
+ do {
305
+ for (const [pluginName, dependencies] of configFiles.entries()) {
306
+ configFiles.delete(pluginName);
307
+ if (this.enabledPlugins.includes(pluginName))
308
+ await runPlugin(pluginName, Array.from(dependencies));
309
+ else
310
+ for (const id of dependencies)
311
+ addInput(toEntry(id));
312
+ }
313
+ } while (remainingPlugins.size > 0 && configFiles.size > 0);
293
314
  }
294
- } while (remainingPlugins.size > 0 && configFiles.size > 0);
315
+ }
295
316
  debugLogArray(name, 'Plugin dependencies', () => compact(inputs.map(toDebugString)));
296
317
  return inputs;
297
318
  }
@@ -15,9 +15,10 @@ const isAssignment = (node) => 'type' in node && node.type === 'AssignmentWord';
15
15
  export const getDependenciesFromScript = (script, options) => {
16
16
  if (!script)
17
17
  return [];
18
- const fromArgs = (args) => {
18
+ const fromArgs = (args, opts) => {
19
19
  return getDependenciesFromScript(args.filter(arg => arg !== '--').join(' '), {
20
20
  ...options,
21
+ ...opts,
21
22
  knownBinsOnly: false,
22
23
  });
23
24
  };
@@ -5,7 +5,7 @@ import { isAbsolute, join } from '../../util/path.js';
5
5
  import { resolveX } from './bunx.js';
6
6
  const commands = ['add', 'create', 'init', 'install', 'link', 'pm', 'remove', 'run', 'test', 'update', 'upgrade', 'x'];
7
7
  export const resolve = (_binary, args, options) => {
8
- const parsed = parseArgs(args);
8
+ const parsed = parseArgs(args, { string: ['cwd'] });
9
9
  const [command, script] = parsed._;
10
10
  if (command === 'x') {
11
11
  const argsForX = args.filter(arg => arg !== 'x');
@@ -14,11 +14,15 @@ export const resolve = (_binary, args, options) => {
14
14
  const { manifestScriptNames, cwd, fromArgs } = options;
15
15
  if (command === 'run' && manifestScriptNames.has(script))
16
16
  return [];
17
- if (manifestScriptNames.has(command) || commands.includes(command))
17
+ if (manifestScriptNames.has(command))
18
+ return [];
19
+ if (command !== 'run' && commands.includes(command))
18
20
  return [];
19
21
  const filePath = command === 'run' ? script : command;
20
22
  const absFilePath = isAbsolute(filePath) ? filePath : join(cwd, filePath);
21
23
  if (isFile(absFilePath))
22
24
  return [toEntry(absFilePath)];
23
- return fromArgs(args);
25
+ const dir = parsed.cwd ? join(cwd, parsed.cwd) : undefined;
26
+ const opts = dir ? { cwd: dir } : {};
27
+ return command === 'run' ? [] : fromArgs(args, opts);
24
28
  };
@@ -2,6 +2,7 @@ import parseArgs from 'minimist';
2
2
  import { isBinary, isDependency, toBinary, toDependency } from '../../util/input.js';
3
3
  import { stripVersionFromSpecifier } from '../../util/modules.js';
4
4
  import { join } from '../../util/path.js';
5
+ import { argsFrom } from '../util.js';
5
6
  const commands = [
6
7
  'add',
7
8
  'bin',
@@ -51,6 +52,8 @@ export const resolve = (_binary, args, options) => {
51
52
  const parsed = parseArgs(args, { boolean: ['top-level'], string: ['cwd'] });
52
53
  const dir = parsed['top-level'] ? rootCwd : parsed.cwd ? join(cwd, parsed.cwd) : undefined;
53
54
  const [command, binary] = parsed._;
55
+ if (!command && !binary)
56
+ return [];
54
57
  if (command === 'run') {
55
58
  if (manifestScriptNames.has(binary))
56
59
  return [];
@@ -67,8 +70,6 @@ export const resolve = (_binary, args, options) => {
67
70
  }
68
71
  if ((!dir && manifestScriptNames.has(command)) || commands.includes(command))
69
72
  return [];
70
- const bin = command === 'exec' ? toBinary(binary) : toBinary(command);
71
- if (dir)
72
- Object.assign(bin, { dir });
73
- return [bin];
73
+ const opts = dir ? { cwd: dir } : {};
74
+ return fromArgs(argsFrom(args, command === 'exec' ? binary : command), opts);
74
75
  };
@@ -3,6 +3,7 @@ import { pluginArgsMap } from '../plugins.js';
3
3
  import { compact } from '../util/array.js';
4
4
  import { toBinary, toConfig, toDeferResolve, toDeferResolveEntry, toEntry } from '../util/input.js';
5
5
  import { extractBinary } from '../util/modules.js';
6
+ import { dirname } from '../util/path.js';
6
7
  import { resolve as fallbackResolve } from './fallback.js';
7
8
  const isGlobLikeMatch = /(^!|[*+\\(|{^$])/;
8
9
  const isGlobLike = (value) => isGlobLikeMatch.test(value);
@@ -12,23 +13,25 @@ export const resolve = (binary, _args, options) => {
12
13
  const [pluginName, pluginArgs] = pluginArgsMap.get(binary) ?? [];
13
14
  if (!pluginArgs)
14
15
  return fallbackResolve(binary, _args, options);
15
- const opts = pluginArgs;
16
- const args = typeof opts.args === 'function' ? opts.args(_args) : _args;
16
+ const inputOpts = {};
17
+ if (options.cwd && dirname(containingFilePath) !== options.cwd)
18
+ Object.assign(inputOpts, { dir: options.cwd });
19
+ const args = typeof pluginArgs.args === 'function' ? pluginArgs.args(_args) : _args;
17
20
  const parsed = parseArgs(args, {
18
21
  string: [
19
- ...(opts.nodeImportArgs ? ['import'] : []),
20
- ...(opts.config === true ? ['config'] : []),
21
- ...(opts.string ?? []),
22
+ ...(pluginArgs.nodeImportArgs ? ['import'] : []),
23
+ ...(pluginArgs.config === true ? ['config'] : []),
24
+ ...(pluginArgs.string ?? []),
22
25
  ],
23
- boolean: ['quiet', 'verbose', 'watch', ...(opts.boolean ?? [])],
26
+ boolean: ['quiet', 'verbose', 'watch', ...(pluginArgs.boolean ?? [])],
24
27
  alias: {
25
- ...(opts.nodeImportArgs ? nodeLoadersArgs : {}),
26
- ...(opts.config === true ? { config: ['c'] } : {}),
27
- ...opts.alias,
28
+ ...(pluginArgs.nodeImportArgs ? nodeLoadersArgs : {}),
29
+ ...(pluginArgs.config === true ? { config: ['c'] } : {}),
30
+ ...pluginArgs.alias,
28
31
  },
29
32
  });
30
33
  const positionals = [];
31
- if (opts.positional && parsed._[0]) {
34
+ if (pluginArgs.positional && parsed._[0]) {
32
35
  const id = parsed._[0];
33
36
  if (isGlobLike(id))
34
37
  positionals.push(toEntry(id));
@@ -40,23 +43,23 @@ export const resolve = (binary, _args, options) => {
40
43
  }
41
44
  }
42
45
  const mapToParsedKey = (id) => parsed[id];
43
- const resolved = compact(opts.resolve ? opts.resolve.flatMap(mapToParsedKey) : []);
44
- const resolvedImports = opts.nodeImportArgs && parsed.import ? [parsed.import].flat() : [];
45
- const resolvedFromArgs = typeof opts.fromArgs === 'function'
46
- ? fromArgs(opts.fromArgs(parsed, args))
47
- : Array.isArray(opts.fromArgs)
48
- ? fromArgs(opts.fromArgs.flatMap(mapToParsedKey))
46
+ const resolved = compact(pluginArgs.resolve ? pluginArgs.resolve.flatMap(mapToParsedKey) : []);
47
+ const resolvedImports = pluginArgs.nodeImportArgs && parsed.import ? [parsed.import].flat() : [];
48
+ const resolvedFromArgs = typeof pluginArgs.fromArgs === 'function'
49
+ ? fromArgs(pluginArgs.fromArgs(parsed, args))
50
+ : Array.isArray(pluginArgs.fromArgs)
51
+ ? fromArgs(pluginArgs.fromArgs.flatMap(mapToParsedKey))
49
52
  : [];
50
- const config = opts.config === true ? ['config'] : opts.config || [];
53
+ const config = pluginArgs.config === true ? ['config'] : pluginArgs.config || [];
51
54
  const mapToConfigPattern = (value) => {
52
55
  if (typeof value === 'string')
53
- return parsed[value] && pluginName ? [toConfig(pluginName, parsed[value], containingFilePath)] : [];
56
+ return parsed[value] && pluginName ? [toConfig(pluginName, parsed[value], inputOpts)] : [];
54
57
  const [id, fn] = value;
55
- return parsed[id] && pluginName ? [toConfig(pluginName, fn(parsed[id]), containingFilePath)] : [];
58
+ return parsed[id] && pluginName ? [toConfig(pluginName, fn(parsed[id]), inputOpts)] : [];
56
59
  };
57
60
  const configFilePaths = config.flatMap(mapToConfigPattern);
58
61
  return [
59
- toBinary(binary),
62
+ toBinary(binary, inputOpts),
60
63
  ...positionals,
61
64
  ...resolved.map(toDeferResolve),
62
65
  ...resolvedImports.map(toDeferResolve),
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import { splitTags } from './util/tag.js';
10
10
  import { isTrace } from './util/trace.js';
11
11
  import { version } from './version.js';
12
12
  const defaultCacheLocation = join(cwd, 'node_modules', '.cache', 'knip');
13
- const { 'allow-remove-files': isRemoveFiles = false, cache: isCache = false, 'cache-location': cacheLocation = defaultCacheLocation, debug: isDebug = false, dependencies: isDependenciesShorthand = false, exclude: excludedIssueTypes = [], 'experimental-tags': experimentalTags = [], exports: isExportsShorthand = false, files: isFilesShorthand = false, fix: isFix = false, 'fix-type': fixTypes = [], help: isHelp, include: includedIssueTypes = [], 'include-entry-exports': isIncludeEntryExports = false, 'include-libs': isIncludeLibs = false, 'isolate-workspaces': isIsolateWorkspaces = false, 'max-issues': maxIssues = '0', 'no-config-hints': isHideConfigHints = false, 'no-exit-code': noExitCode = false, 'no-gitignore': isNoGitIgnore = false, 'no-progress': isNoProgress = isDebug || isTrace, preprocessor = [], 'preprocessor-options': preprocessorOptions = '', production: isProduction = false, reporter = ['symbols'], 'reporter-options': reporterOptions = '', strict: isStrict = false, tags = [], tsConfig, version: isVersion, watch: isWatch = false, workspace: rawWorkspaceArg, } = parsedArgValues;
13
+ const { 'allow-remove-files': isRemoveFiles = false, cache: isCache = false, 'cache-location': cacheLocation = defaultCacheLocation, debug: isDebug = false, dependencies: isDependenciesShorthand = false, exclude: excludedIssueTypes = [], 'experimental-tags': experimentalTags = [], exports: isExportsShorthand = false, files: isFilesShorthand = false, fix: isFix = false, 'fix-type': fixTypes = [], help: isHelp, include: includedIssueTypes = [], 'include-entry-exports': isIncludeEntryExports = false, 'include-libs': isIncludeLibs = false, 'isolate-workspaces': isIsolateWorkspaces = false, 'max-issues': maxIssues = '0', 'no-config-hints': isDisableConfigHints = false, 'no-exit-code': noExitCode = false, 'no-gitignore': isNoGitIgnore = false, 'no-progress': isNoProgress = isDebug || isTrace, preprocessor = [], 'preprocessor-options': preprocessorOptions = '', production: isProduction = false, reporter = ['symbols'], 'reporter-options': reporterOptions = '', strict: isStrict = false, tags = [], 'treat-config-hints-as-errors': isTreatConfigHintsAsErrors = false, tsConfig, version: isVersion, watch: isWatch = false, workspace: rawWorkspaceArg, } = parsedArgValues;
14
14
  if (isHelp) {
15
15
  console.log(helpText);
16
16
  process.exit(0);
@@ -36,7 +36,7 @@ const run = async () => {
36
36
  isExportsShorthand,
37
37
  isFilesShorthand,
38
38
  isFix: isFix || fixTypes.length > 0,
39
- isHideConfigHints,
39
+ isDisableConfigHints,
40
40
  isIncludeEntryExports,
41
41
  isIncludeLibs,
42
42
  isIsolateWorkspaces,
@@ -57,7 +57,8 @@ const run = async () => {
57
57
  counters,
58
58
  tagHints,
59
59
  configurationHints,
60
- noConfigHints: isHideConfigHints,
60
+ isDisableConfigHints,
61
+ isTreatConfigHintsAsErrors,
61
62
  cwd,
62
63
  isProduction,
63
64
  isShowProgress,
@@ -83,7 +84,8 @@ const run = async () => {
83
84
  if (isIsolateWorkspaces && report.classMembers) {
84
85
  logWarning('WARNING', 'Class members are not tracked when using the --isolate-workspaces flag');
85
86
  }
86
- if (!noExitCode && totalErrorCount > Number(maxIssues)) {
87
+ if ((!noExitCode && totalErrorCount > Number(maxIssues)) ||
88
+ (isTreatConfigHintsAsErrors && configurationHints.size > 0)) {
87
89
  process.exit(1);
88
90
  }
89
91
  }
@@ -5,6 +5,7 @@ export declare const partitionCompilers: (rawLocalConfig: RawConfiguration) => {
5
5
  syncCompilers: Record<string, SyncCompilerFn>;
6
6
  asyncCompilers: Record<string, AsyncCompilerFn>;
7
7
  exclude?: ("dependencies" | "exports" | "files" | "devDependencies" | "optionalPeerDependencies" | "unlisted" | "binaries" | "unresolved" | "types" | "nsExports" | "nsTypes" | "duplicates" | "enumMembers" | "classMembers")[] | undefined;
8
+ tags?: string[] | undefined;
8
9
  include?: ("dependencies" | "exports" | "files" | "devDependencies" | "optionalPeerDependencies" | "unlisted" | "binaries" | "unresolved" | "types" | "nsExports" | "nsTypes" | "duplicates" | "enumMembers" | "classMembers")[] | undefined;
9
10
  node?: string | boolean | string[] | {
10
11
  config?: string | string[] | undefined;
@@ -17,7 +17,7 @@ interface AnalyzeOptions {
17
17
  fixer: IssueFixer;
18
18
  graph: ModuleGraph;
19
19
  isFix: boolean;
20
- isHideConfigHints: boolean;
20
+ isDisableConfigHints: boolean;
21
21
  isIncludeLibs: boolean;
22
22
  isProduction: boolean;
23
23
  report: Report;
@@ -5,13 +5,13 @@ import { findMatch } from '../util/regex.js';
5
5
  import { getShouldIgnoreHandler, getShouldIgnoreTagHandler } from '../util/tag.js';
6
6
  import { createAndPrintTrace, printTrace } from '../util/trace.js';
7
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;
8
+ const { analyzedFiles, chief, collector, deputy, entryPaths, factory, fixer, graph, isFix, isDisableConfigHints, isIncludeLibs, isProduction, report, streamer, tags, unreferencedFiles, workspace, } = options;
9
9
  const isReportDependencies = report.dependencies || report.unlisted || report.unresolved;
10
10
  const isReportValues = report.exports || report.nsExports || report.classMembers;
11
11
  const isReportTypes = report.types || report.nsTypes || report.enumMembers;
12
12
  const isReportClassMembers = report.classMembers;
13
13
  const isSkipLibs = !(isIncludeLibs || isReportClassMembers);
14
- const isShowConfigHints = !workspace && !isProduction && !isHideConfigHints;
14
+ const isShowConfigHints = !workspace && !isProduction && !isDisableConfigHints;
15
15
  const shouldIgnore = getShouldIgnoreHandler(isProduction);
16
16
  const shouldIgnoreTags = getShouldIgnoreTagHandler(tags);
17
17
  const isIdentifierReferenced = getIsIdentifierReferencedHandler(graph, entryPaths);
@@ -4,7 +4,7 @@ import { getCompilerExtensions, getIncludedCompilers } from '../compilers/index.
4
4
  import { debugLog, debugLogArray } from '../util/debug.js';
5
5
  import { getReferencedInputsHandler } from '../util/get-referenced-inputs.js';
6
6
  import { _glob, negate } from '../util/glob.js';
7
- import { isConfigPattern, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isProductionEntry, toProductionEntry, } from '../util/input.js';
7
+ import { isConfig, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isProductionEntry, toProductionEntry, } from '../util/input.js';
8
8
  import { getOrCreateFileNode, updateImportMap } from '../util/module-graph.js';
9
9
  import { getEntryPathsFromManifest } from '../util/package-json.js';
10
10
  import { dirname, isAbsolute, join, relative } from '../util/path.js';
@@ -13,6 +13,7 @@ import { augmentWorkspace, getToSourcePathHandler } from '../util/to-source-path
13
13
  import { loadTSConfig } from '../util/tsconfig-loader.js';
14
14
  export async function build({ cacheLocation, chief, collector, cwd, deputy, factory, gitignore, isCache, isFixExports, isFixTypes, isGitIgnored, isIsolateWorkspaces, isProduction, isSkipLibs, isStrict, isWatch, report, streamer, tags, tsConfigFile, workspaces, }) {
15
15
  const allConfigFilePaths = new Set();
16
+ const allConfigFilesMap = new Map();
16
17
  const enabledPluginsStore = new Map();
17
18
  const toSourceFilePath = getToSourcePathHandler(chief);
18
19
  const getReferencedInternalFilePath = getReferencedInputsHandler(collector, deputy, chief, isGitIgnored);
@@ -47,6 +48,7 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
47
48
  manifest,
48
49
  dependencies,
49
50
  getReferencedInternalFilePath: (input) => getReferencedInternalFilePath(input, workspace),
51
+ findWorkspaceByFilePath: chief.findWorkspaceByFilePath.bind(chief),
50
52
  isProduction,
51
53
  isStrict,
52
54
  rootIgnore: chief.config.ignore,
@@ -56,6 +58,7 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
56
58
  isCache,
57
59
  cacheLocation,
58
60
  allConfigFilePaths,
61
+ allConfigFilesMap,
59
62
  });
60
63
  await worker.init();
61
64
  const inputs = new Set();
@@ -91,14 +94,14 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
91
94
  const entryFilePatterns = new Set();
92
95
  const productionEntryFilePatterns = new Set();
93
96
  for (const input of inputs) {
94
- const s = input.specifier;
97
+ const specifier = input.specifier;
95
98
  if (isEntry(input)) {
96
- entryFilePatterns.add(isAbsolute(s) ? relative(dir, s) : s);
99
+ entryFilePatterns.add(isAbsolute(specifier) ? relative(dir, specifier) : specifier);
97
100
  }
98
101
  else if (isProductionEntry(input)) {
99
- productionEntryFilePatterns.add(isAbsolute(s) ? relative(dir, s) : s);
102
+ productionEntryFilePatterns.add(isAbsolute(specifier) ? relative(dir, specifier) : specifier);
100
103
  }
101
- else if (!isConfigPattern(input)) {
104
+ else if (!isConfig(input)) {
102
105
  const ws = (input.containingFilePath && chief.findWorkspaceByFilePath(input.containingFilePath)) || workspace;
103
106
  const resolvedFilePath = getReferencedInternalFilePath(input, ws);
104
107
  if (resolvedFilePath) {
@@ -214,13 +217,7 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
214
217
  const dependencies = deputy.getDependencies(workspace.name);
215
218
  const manifestScriptNames = new Set(Object.keys(chief.getManifestForWorkspace(workspace.name)?.scripts ?? {}));
216
219
  const dir = dirname(filePath);
217
- const options = {
218
- cwd: dir,
219
- rootCwd: cwd,
220
- containingFilePath: filePath,
221
- dependencies,
222
- manifestScriptNames,
223
- };
220
+ const options = { cwd: dir, rootCwd: cwd, containingFilePath: filePath, dependencies, manifestScriptNames };
224
221
  const inputs = _getInputsFromScripts(scripts, options);
225
222
  for (const input of inputs) {
226
223
  input.containingFilePath ??= filePath;
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ import { debugLogObject } from './util/debug.js';
11
11
  import { getGitIgnoredHandler } from './util/glob-core.js';
12
12
  import { getWatchHandler } from './util/watch.js';
13
13
  export const main = async (unresolvedConfiguration) => {
14
- const { cacheLocation, cwd, excludedIssueTypes, fixTypes, gitignore, includedIssueTypes, isCache, isDebug, isDependenciesShorthand, isExportsShorthand, isFilesShorthand, isFix, isHideConfigHints, isIncludeEntryExports, isIncludeLibs, isIsolateWorkspaces, isProduction, isRemoveFiles, isShowProgress, isStrict, isWatch, tags, tsConfigFile, workspace, } = unresolvedConfiguration;
14
+ const { cacheLocation, cwd, excludedIssueTypes, fixTypes, gitignore, includedIssueTypes, isCache, isDebug, isDependenciesShorthand, isExportsShorthand, isFilesShorthand, isFix, isDisableConfigHints, isIncludeEntryExports, isIncludeLibs, isIsolateWorkspaces, isProduction, isRemoveFiles, isShowProgress, isStrict, isWatch, tags, tsConfigFile, workspace, } = unresolvedConfiguration;
15
15
  debugLogObject('*', 'Unresolved configuration (from CLI arguments)', unresolvedConfiguration);
16
16
  const chief = new ConfigurationChief({ cwd, isProduction, isStrict, isIncludeEntryExports, workspace });
17
17
  const deputy = new DependencyDeputy({ isProduction, isStrict });
@@ -29,6 +29,7 @@ export const main = async (unresolvedConfiguration) => {
29
29
  });
30
30
  const rules = chief.getRules();
31
31
  const filters = chief.getFilters();
32
+ const finalTags = tags[0].length > 0 || tags[1].length > 0 ? tags : chief.getTags();
32
33
  const fixer = new IssueFixer({ isEnabled: isFix, cwd, fixTypes, isRemoveFiles });
33
34
  debugLogObject('*', 'Included issue types', report);
34
35
  const isReportClassMembers = report.classMembers;
@@ -57,7 +58,7 @@ export const main = async (unresolvedConfiguration) => {
57
58
  isWatch,
58
59
  report,
59
60
  streamer,
60
- tags,
61
+ tags: finalTags,
61
62
  tsConfigFile,
62
63
  workspaces,
63
64
  });
@@ -71,12 +72,12 @@ export const main = async (unresolvedConfiguration) => {
71
72
  fixer,
72
73
  graph,
73
74
  isFix,
74
- isHideConfigHints,
75
+ isDisableConfigHints,
75
76
  isIncludeLibs,
76
77
  isProduction,
77
78
  report,
78
79
  streamer,
79
- tags,
80
+ tags: finalTags,
80
81
  unreferencedFiles,
81
82
  workspace,
82
83
  });
@@ -22,7 +22,7 @@ const resolveConfig = async (config, options) => {
22
22
  inputs.add(toDependency(packageName));
23
23
  if (opts) {
24
24
  if ('tsConfig' in opts && typeof opts.tsConfig === 'string') {
25
- inputs.add(toConfig('typescript', opts.tsConfig, configFilePath));
25
+ inputs.add(toConfig('typescript', opts.tsConfig, { containingFilePath: configFilePath }));
26
26
  }
27
27
  }
28
28
  const defaultEntriesByOption = opts ? entriesByOption(opts) : new Map();
@@ -73,7 +73,7 @@ const resolveConfig = async (config, options) => {
73
73
  karma.inputsFromFrameworks(['jasmine']).forEach(inputs.add, inputs);
74
74
  }
75
75
  if (karmaConfig && !karma.configFiles.includes(karmaConfig)) {
76
- inputs.add(toConfig('karma', karmaConfig, options.configFilePath));
76
+ inputs.add(toConfig('karma', karmaConfig, { containingFilePath: options.configFilePath }));
77
77
  }
78
78
  }
79
79
  }
@@ -1,4 +1,4 @@
1
1
  import type { PluginOptions } from '../../types/config.js';
2
2
  import { type ConfigInput, type Input } from '../../util/input.js';
3
- import type { ESLintConfig, OverrideConfig } from './types.js';
4
- export declare const getDependencies: (config: ESLintConfig | OverrideConfig, options: PluginOptions) => (Input | ConfigInput)[];
3
+ import type { ESLintConfig, ESLintConfigDeprecated, OverrideConfigDeprecated } from './types.js';
4
+ export declare const getInputs: (config: ESLintConfigDeprecated | OverrideConfigDeprecated | ESLintConfig, options: PluginOptions) => (Input | ConfigInput)[];