knip 5.44.5 → 5.45.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.
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
 
@@ -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
  };
@@ -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',
@@ -67,8 +68,6 @@ export const resolve = (_binary, args, options) => {
67
68
  }
68
69
  if ((!dir && manifestScriptNames.has(command)) || commands.includes(command))
69
70
  return [];
70
- const bin = command === 'exec' ? toBinary(binary) : toBinary(command);
71
- if (dir)
72
- Object.assign(bin, { dir });
73
- return [bin];
71
+ const opts = dir ? { cwd: dir } : {};
72
+ return fromArgs(argsFrom(args, command === 'exec' ? binary : command), opts);
74
73
  };
@@ -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),
@@ -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();
@@ -98,7 +101,7 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
98
101
  else if (isProductionEntry(input)) {
99
102
  productionEntryFilePatterns.add(isAbsolute(s) ? relative(dir, s) : s);
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) {
@@ -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)[];
@@ -1,13 +1,21 @@
1
1
  import { compact } from '../../util/array.js';
2
2
  import { toConfig, toDeferResolve } from '../../util/input.js';
3
3
  import { getPackageNameFromFilePath, getPackageNameFromModuleSpecifier } from '../../util/modules.js';
4
- import { isAbsolute, isInternal } from '../../util/path.js';
4
+ import { extname, isAbsolute, isInternal } from '../../util/path.js';
5
5
  import { getDependenciesFromConfig } from '../babel/index.js';
6
- export const getDependencies = (config, options) => {
6
+ export const getInputs = (config, options) => {
7
+ const { configFileName } = options;
8
+ if (extname(configFileName) === '.json' || !/eslint\.config/.test(configFileName)) {
9
+ return getInputsDeprecated(config, options);
10
+ }
11
+ const dependencies = config.flatMap(config => config.settings ? getDependenciesFromSettings(config.settings).filter(id => id !== '@typescript-eslint/parser') : []);
12
+ return compact(dependencies).map(id => toDeferResolve(id));
13
+ };
14
+ const getInputsDeprecated = (config, options) => {
7
15
  const extendsSpecifiers = config.extends ? compact([config.extends].flat().map(resolveExtendSpecifier)) : [];
8
16
  if (extendsSpecifiers.some(specifier => specifier?.startsWith('eslint-plugin-prettier')))
9
17
  extendsSpecifiers.push('eslint-config-prettier');
10
- const extendConfigs = extendsSpecifiers.map(specifier => toConfig('eslint', specifier, options.configFilePath));
18
+ const extendConfigs = extendsSpecifiers.map(specifier => toConfig('eslint', specifier, { containingFilePath: options.configFilePath }));
11
19
  const plugins = config.plugins ? config.plugins.map(resolvePluginSpecifier) : [];
12
20
  const parser = config.parser ?? config.parserOptions?.parser;
13
21
  const babelDependencies = config.parserOptions?.babelOptions
@@ -15,9 +23,9 @@ export const getDependencies = (config, options) => {
15
23
  : [];
16
24
  const settings = config.settings ? getDependenciesFromSettings(config.settings) : [];
17
25
  const rules = getDependenciesFromRules({});
18
- const overrides = config.overrides ? [config.overrides].flat().flatMap(d => getDependencies(d, options)) : [];
19
- const x = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(toDeferResolve);
20
- return [...extendConfigs, ...x, ...babelDependencies, ...overrides];
26
+ const overrides = config.overrides ? [config.overrides].flat().flatMap(d => getInputsDeprecated(d, options)) : [];
27
+ const deferred = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(toDeferResolve);
28
+ return [...extendConfigs, ...deferred, ...babelDependencies, ...overrides];
21
29
  };
22
30
  const isQualifiedSpecifier = (specifier) => specifier === 'eslint' ||
23
31
  /\/eslint-(config|plugin)$/.test(specifier) ||
@@ -1,5 +1,8 @@
1
1
  import type { IsPluginEnabled, ResolveConfig } from '../../types/config.js';
2
- import type { ESLintConfig } from './types.js';
2
+ import type { ESLintConfigDeprecated } from './types.js';
3
+ export declare const docs: {
4
+ note: string;
5
+ };
3
6
  declare const _default: {
4
7
  title: string;
5
8
  enablers: string[];
@@ -7,6 +10,6 @@ declare const _default: {
7
10
  packageJsonPath: string;
8
11
  entry: string[];
9
12
  config: string[];
10
- resolveConfig: ResolveConfig<ESLintConfig>;
13
+ resolveConfig: ResolveConfig<ESLintConfigDeprecated>;
11
14
  };
12
15
  export default _default;
@@ -1,14 +1,42 @@
1
1
  import { hasDependency } from '../../util/plugin.js';
2
- import { getDependencies } from './helpers.js';
2
+ import { getInputs } from './helpers.js';
3
3
  const title = 'ESLint';
4
4
  const enablers = ['eslint', '@eslint/js'];
5
5
  const isEnabled = ({ dependencies, manifest, config }) => hasDependency(dependencies, enablers) ||
6
6
  'eslint' in config ||
7
7
  Boolean(manifest.name && /(^eslint-config|\/eslint-config)/.test(manifest.name));
8
8
  const packageJsonPath = 'eslintConfig';
9
- const entry = ['eslint.config.{js,cjs,mjs}'];
9
+ const entry = ['eslint.config.{js,cjs,mjs,ts,cts,mts}'];
10
10
  const config = ['.eslintrc', '.eslintrc.{js,json,cjs}', '.eslintrc.{yml,yaml}', 'package.json'];
11
- const resolveConfig = (localConfig, options) => getDependencies(localConfig, options);
11
+ const resolveConfig = (localConfig, options) => getInputs(localConfig, options);
12
+ const note = `### ESLint v9
13
+
14
+ Only regular \`import\` statements are considered by default.
15
+ The configuration object is not resolved to find dependencies for \`settings\` such as \`"eslint-import-resolver-typescript"\`.
16
+ To enable this, lift the \`entry\` to a \`config\` file like so:
17
+
18
+ \`\`\`json
19
+ {
20
+ "eslint": ["eslint.config.ts"]
21
+ }
22
+ \`\`\`
23
+
24
+ This is not enabled by default, since this exception may be thrown by a \`@rushstack/eslint-*\` package:
25
+
26
+ > \`Error: Failed to patch ESLint because the calling module was not recognized.\`
27
+
28
+ ### ESLint v8
29
+
30
+ If relying on [configuration cascading](https://eslint.org/docs/v8.x/use/configure/configuration-files#cascading-and-hierarchy),
31
+ consider using an extended glob pattern like this:
32
+
33
+ \`\`\`json
34
+ {
35
+ "eslint": ["**/.eslintrc.js"]
36
+ }
37
+ \`\`\`
38
+ `;
39
+ export const docs = { note };
12
40
  export default {
13
41
  title,
14
42
  enablers,
@@ -17,12 +17,13 @@ type BaseConfig = {
17
17
  settings?: Settings;
18
18
  rules?: Rules;
19
19
  };
20
- export type OverrideConfig = BaseConfig & {
20
+ export type ESLintConfig = BaseConfig[];
21
+ export type OverrideConfigDeprecated = BaseConfig & {
21
22
  files: string[];
22
- overrides: OverrideConfig;
23
+ overrides: OverrideConfigDeprecated;
23
24
  };
24
- export type ESLintConfig = BaseConfig & {
25
+ export type ESLintConfigDeprecated = BaseConfig & {
25
26
  env?: Record<string, boolean>;
26
- overrides?: OverrideConfig[];
27
+ overrides?: OverrideConfigDeprecated[];
27
28
  };
28
29
  export {};
@@ -133,7 +133,7 @@ export declare const Plugins: {
133
133
  packageJsonPath: string;
134
134
  entry: string[];
135
135
  config: string[];
136
- resolveConfig: import("../types/config.js").ResolveConfig<import("./eslint/types.js").ESLintConfig>;
136
+ resolveConfig: import("../types/config.js").ResolveConfig<import("./eslint/types.js").ESLintConfigDeprecated>;
137
137
  };
138
138
  expo: {
139
139
  title: string;
@@ -403,12 +403,6 @@ export declare const Plugins: {
403
403
  entry: string[];
404
404
  resolveEntryPaths: import("../types/config.js").ResolveEntryPaths<import("./playwright/types.js").PlaywrightTestConfig>;
405
405
  resolveConfig: import("../types/config.js").ResolveConfig<import("./playwright/types.js").PlaywrightTestConfig>;
406
- args: {
407
- binaries: string[];
408
- positional: boolean;
409
- args: (args: string[]) => string[];
410
- config: boolean;
411
- };
412
406
  };
413
407
  'playwright-test': {
414
408
  title: string;
@@ -3,12 +3,6 @@ import type { PlaywrightTestConfig } from './types.js';
3
3
  export declare const entry: string[];
4
4
  export declare const resolveEntryPaths: ResolveEntryPaths<PlaywrightTestConfig>;
5
5
  export declare const resolveConfig: ResolveConfig<PlaywrightTestConfig>;
6
- export declare const args: {
7
- binaries: string[];
8
- positional: boolean;
9
- args: (args: string[]) => string[];
10
- config: boolean;
11
- };
12
6
  declare const _default: {
13
7
  title: string;
14
8
  enablers: string[];
@@ -29,7 +29,7 @@ export const resolveConfig = async (config) => {
29
29
  });
30
30
  return [...reporters].map(toDeferResolve);
31
31
  };
32
- export const args = {
32
+ const args = {
33
33
  binaries: ['playwright'],
34
34
  positional: true,
35
35
  args: (args) => args.filter(arg => arg !== 'install' && arg !== 'test'),
@@ -7,11 +7,5 @@ declare const _default: {
7
7
  entry: string[];
8
8
  resolveEntryPaths: import("../../types/config.js").ResolveEntryPaths<import("../playwright/types.js").PlaywrightTestConfig>;
9
9
  resolveConfig: import("../../types/config.js").ResolveConfig<import("../playwright/types.js").PlaywrightTestConfig>;
10
- args: {
11
- binaries: string[];
12
- positional: boolean;
13
- args: (args: string[]) => string[];
14
- config: boolean;
15
- };
16
10
  };
17
11
  export default _default;
@@ -1,5 +1,5 @@
1
1
  import { hasDependency } from '../../util/plugin.js';
2
- import { args, entry, resolveConfig, resolveEntryPaths } from '../playwright/index.js';
2
+ import { entry, resolveConfig, resolveEntryPaths } from '../playwright/index.js';
3
3
  const title = 'Playwright for components';
4
4
  const enablers = [/^@playwright\/experimental-ct-/];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
@@ -12,5 +12,4 @@ export default {
12
12
  entry,
13
13
  resolveEntryPaths,
14
14
  resolveConfig,
15
- args,
16
15
  };
@@ -9,11 +9,13 @@ const config = ['tsconfig.json'];
9
9
  const resolveConfig = async (localConfig, options) => {
10
10
  const { compilerOptions } = localConfig;
11
11
  const extend = localConfig.extends
12
- ? [localConfig.extends].flat().map(specifier => toConfig('typescript', specifier, options.configFilePath))
12
+ ? [localConfig.extends]
13
+ .flat()
14
+ .map(specifier => toConfig('typescript', specifier, { containingFilePath: options.configFilePath }))
13
15
  : [];
14
16
  const references = localConfig.references
15
17
  ?.filter(reference => reference.path.endsWith('.json'))
16
- .map(reference => toConfig('typescript', reference.path, options.configFilePath)) ?? [];
18
+ .map(reference => toConfig('typescript', reference.path, { containingFilePath: options.configFilePath })) ?? [];
17
19
  if (!(compilerOptions && localConfig))
18
20
  return compact([...extend, ...references]);
19
21
  const jsx = (compilerOptions?.jsxImportSource ? [compilerOptions.jsxImportSource] : []).map(toProductionDependency);
@@ -1,12 +1,12 @@
1
1
  import { hasDependency } from '../../util/plugin.js';
2
- import { getDependencies } from '../eslint/helpers.js';
2
+ import { getInputs } from '../eslint/helpers.js';
3
3
  const title = 'xo';
4
4
  const enablers = ['xo'];
5
5
  const isEnabled = ({ dependencies, config }) => hasDependency(dependencies, enablers) || 'xo' in config;
6
6
  const config = ['package.json', '.xo-config', '.xo-config.{js,cjs,json}', 'xo.config.{js,cjs}'];
7
7
  const entry = ['.xo-config.{js,cjs}', 'xo.config.{js,cjs}'];
8
8
  const resolveConfig = async (config, options) => {
9
- const inputs = getDependencies(config, options);
9
+ const inputs = getInputs(config, options);
10
10
  return [...inputs];
11
11
  };
12
12
  export default {
@@ -1,5 +1,5 @@
1
- import type { ESLintConfig } from '../eslint/types.js';
2
- export type XOConfig = ESLintConfig & {
1
+ import type { ESLintConfigDeprecated } from '../eslint/types.js';
2
+ export type XOConfig = ESLintConfigDeprecated & {
3
3
  envs?: string[] | undefined;
4
4
  globals?: string[] | undefined;
5
5
  ignores?: string[] | undefined;
@@ -14,7 +14,7 @@ export interface GetInputsFromScriptsOptions extends BaseOptions {
14
14
  }
15
15
  export type GetInputsFromScripts<T = GetInputsFromScriptsOptions> = (npmScripts: string | string[] | Set<string>, options: T) => Input[];
16
16
  export type GetInputsFromScriptsPartial = (npmScripts: string | string[] | Set<string>, options?: Partial<GetInputsFromScriptsOptions>) => Input[];
17
- type FromArgs = (args: string[]) => Input[];
17
+ export type FromArgs = (args: string[], options?: Partial<GetInputsFromScriptsOptions>) => Input[];
18
18
  export interface BinaryResolverOptions extends GetInputsFromScriptsOptions {
19
19
  fromArgs: FromArgs;
20
20
  }
@@ -1,7 +1,7 @@
1
1
  import { IGNORED_RUNTIME_DEPENDENCIES } from '../constants.js';
2
2
  import { debugLog } from './debug.js';
3
3
  import { isDeferResolve, toDebugString } from './input.js';
4
- import { fromBinary, isBinary, isConfigPattern, isDeferResolveEntry, isDependency } from './input.js';
4
+ import { fromBinary, isBinary, isConfig, isDeferResolveEntry, isDependency } from './input.js';
5
5
  import { getPackageNameFromSpecifier } from './modules.js';
6
6
  import { dirname, isAbsolute, isInternal, join } from './path.js';
7
7
  import { _resolveSync } from './resolve.js';
@@ -75,7 +75,7 @@ export const getReferencedInputsHandler = (collector, deputy, chief, isGitIgnore
75
75
  });
76
76
  }
77
77
  else if (!isGitIgnored(filePath)) {
78
- if (!isDeferResolveEntry(input) && !isConfigPattern(input)) {
78
+ if (!isDeferResolveEntry(input) && !isConfig(input)) {
79
79
  collector.addIssue({
80
80
  type: 'unresolved',
81
81
  filePath: containingFilePath,
@@ -10,7 +10,7 @@ export interface Input {
10
10
  }
11
11
  export interface ConfigInput extends Input {
12
12
  type: 'config';
13
- containingFilePath: string;
13
+ containingFilePath?: string;
14
14
  pluginName: PluginName;
15
15
  }
16
16
  type Options = {
@@ -25,8 +25,8 @@ export declare const toEntry: (specifier: string) => Input;
25
25
  export declare const isEntry: (input: Input) => boolean;
26
26
  export declare const toProductionEntry: (specifier: string, options?: Options) => Input;
27
27
  export declare const isProductionEntry: (input: Input) => boolean;
28
- export declare const toConfig: (pluginName: PluginName, specifier: string, containingFilePath: string) => ConfigInput;
29
- export declare const isConfigPattern: (input: Input) => input is ConfigInput;
28
+ export declare const toConfig: (pluginName: PluginName, specifier: string, options?: Options) => ConfigInput;
29
+ export declare const isConfig: (input: Input) => input is ConfigInput;
30
30
  export declare const toDependency: (specifier: string, options?: Options) => Input;
31
31
  export declare const isDependency: (input: Input) => boolean;
32
32
  export declare const toProductionDependency: (specifier: string) => Input;
@@ -1,4 +1,4 @@
1
- import { toRelative } from './path.js';
1
+ import { isAbsolute, toRelative } from './path.js';
2
2
  export const fromBinary = (input) => input.specifier;
3
3
  export const toBinary = (specifier, options = {}) => ({
4
4
  type: 'binary',
@@ -15,13 +15,13 @@ export const toProductionEntry = (specifier, options = {}) => ({
15
15
  ...options,
16
16
  });
17
17
  export const isProductionEntry = (input) => input.type === 'entry' && input.production === true;
18
- export const toConfig = (pluginName, specifier, containingFilePath) => ({
18
+ export const toConfig = (pluginName, specifier, options = {}) => ({
19
19
  type: 'config',
20
20
  specifier,
21
21
  pluginName,
22
- containingFilePath,
22
+ ...options,
23
23
  });
24
- export const isConfigPattern = (input) => input.type === 'config';
24
+ export const isConfig = (input) => input.type === 'config';
25
25
  export const toDependency = (specifier, options = {}) => ({
26
26
  type: 'dependency',
27
27
  specifier,
@@ -44,4 +44,4 @@ export const toDeferResolveProductionEntry = (specifier) => ({
44
44
  export const isDeferResolveProductionEntry = (input) => input.type === 'deferResolveEntry' && input.production === true;
45
45
  export const toDeferResolveEntry = (specifier) => ({ type: 'deferResolveEntry', specifier });
46
46
  export const isDeferResolveEntry = (input) => input.type === 'deferResolveEntry';
47
- export const toDebugString = (input) => `${input.type}:${input.specifier}${input.containingFilePath ? ` (${toRelative(input.containingFilePath)})` : ''}`;
47
+ export const toDebugString = (input) => `${input.type}:${isAbsolute(input.specifier) ? toRelative(input.specifier) : input.specifier}${input.containingFilePath ? ` (${toRelative(input.containingFilePath)})` : ''}`;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.44.5";
1
+ export declare const version = "5.45.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.44.5';
1
+ export const version = '5.45.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.44.5",
3
+ "version": "5.45.0",
4
4
  "description": "Find and fix unused files, dependencies and exports in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {