knip 5.61.1 → 5.61.3

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 (44) hide show
  1. package/dist/DependencyDeputy.js +7 -7
  2. package/dist/{PackageJsonPeeker.d.ts → PackagePeeker.d.ts} +1 -1
  3. package/dist/{PackageJsonPeeker.js → PackagePeeker.js} +1 -1
  4. package/dist/ProjectPrincipal.d.ts +1 -1
  5. package/dist/ProjectPrincipal.js +4 -41
  6. package/dist/WorkspaceWorker.d.ts +1 -0
  7. package/dist/WorkspaceWorker.js +9 -4
  8. package/dist/binaries/bash-parser.js +1 -1
  9. package/dist/binaries/plugins.js +2 -2
  10. package/dist/graph/build.js +48 -16
  11. package/dist/plugins/_template/index.js +1 -1
  12. package/dist/plugins/babel/index.js +7 -2
  13. package/dist/plugins/biome/index.js +2 -7
  14. package/dist/plugins/cspell/index.js +1 -1
  15. package/dist/plugins/cucumber/index.js +1 -1
  16. package/dist/plugins/cypress/index.js +1 -1
  17. package/dist/plugins/eslint/helpers.js +2 -2
  18. package/dist/plugins/eslint/index.d.ts +2 -2
  19. package/dist/plugins/eslint/index.js +20 -15
  20. package/dist/plugins/gatsby/index.js +1 -1
  21. package/dist/plugins/index.d.ts +1 -1
  22. package/dist/plugins/karma/index.js +5 -3
  23. package/dist/plugins/linthtml/index.js +1 -1
  24. package/dist/plugins/metro/index.js +1 -1
  25. package/dist/plugins/node/index.js +13 -1
  26. package/dist/plugins/nyc/index.js +1 -1
  27. package/dist/plugins/playwright/index.js +1 -1
  28. package/dist/plugins/postcss/index.js +1 -1
  29. package/dist/plugins/react-cosmos/index.js +1 -1
  30. package/dist/plugins/remark/index.js +1 -1
  31. package/dist/plugins/semantic-release/index.js +1 -1
  32. package/dist/plugins/storybook/index.js +1 -1
  33. package/dist/plugins/stryker/index.js +1 -1
  34. package/dist/plugins/typedoc/index.js +1 -1
  35. package/dist/plugins/typescript/index.js +6 -1
  36. package/dist/types/config.d.ts +2 -0
  37. package/dist/types/module-graph.d.ts +2 -0
  38. package/dist/typescript/get-imports-and-exports.d.ts +2 -15
  39. package/dist/util/input.d.ts +1 -1
  40. package/dist/util/input.js +5 -1
  41. package/dist/util/module-graph.js +2 -0
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { isBuiltin } from 'node:module';
2
- import { PackageJsonPeeker } from './PackageJsonPeeker.js';
2
+ import { PackagePeeker } from './PackagePeeker.js';
3
3
  import { DT_SCOPE, IGNORED_DEPENDENCIES, IGNORED_GLOBAL_BINARIES, IGNORED_RUNTIME_DEPENDENCIES, IGNORE_DEFINITELY_TYPED, ROOT_WORKSPACE_NAME, } from './constants.js';
4
4
  import { getDependencyMetaData } from './manifest/index.js';
5
5
  import { getDefinitelyTypedFor, getPackageFromDefinitelyTyped, getPackageNameFromModuleSpecifier, isDefinitelyTyped, } from './util/modules.js';
@@ -176,12 +176,12 @@ export class DependencyDeputy {
176
176
  for (const [workspace, { manifestPath: filePath, manifestStr }] of this._manifests.entries()) {
177
177
  const referencedDependencies = this.referencedDependencies.get(workspace);
178
178
  const hasTypesIncluded = this.getHasTypesIncluded(workspace);
179
- const peeker = new PackageJsonPeeker(manifestStr);
180
- const peerDepRecs = {};
179
+ const peeker = new PackagePeeker(manifestStr);
180
+ const peerDepCount = {};
181
181
  const isReferencedDependency = (dependency, isPeerDep) => {
182
182
  if (referencedDependencies?.has(dependency))
183
183
  return true;
184
- if (isPeerDep && peerDepRecs[dependency])
184
+ if (isPeerDep && peerDepCount[dependency])
185
185
  return false;
186
186
  const [scope, typedDependency] = dependency.split('/');
187
187
  if (scope === DT_SCOPE) {
@@ -202,10 +202,10 @@ export class DependencyDeputy {
202
202
  }
203
203
  const hostDependencies = this.getHostDependenciesFor(workspace, dependency);
204
204
  for (const { name } of hostDependencies) {
205
- if (!peerDepRecs[name])
206
- peerDepRecs[name] = 1;
205
+ if (!peerDepCount[name])
206
+ peerDepCount[name] = 1;
207
207
  else
208
- peerDepRecs[name]++;
208
+ peerDepCount[name]++;
209
209
  }
210
210
  return hostDependencies.some(hostDependency => (isPeerDep === false || !hostDependency.isPeerOptional) && isReferencedDependency(hostDependency.name, true));
211
211
  };
@@ -1,4 +1,4 @@
1
- export declare class PackageJsonPeeker {
1
+ export declare class PackagePeeker {
2
2
  private manifestStr;
3
3
  private lines;
4
4
  private sections;
@@ -1,4 +1,4 @@
1
- export class PackageJsonPeeker {
1
+ export class PackagePeeker {
2
2
  manifestStr;
3
3
  lines = [];
4
4
  sections = {};
@@ -50,7 +50,7 @@ export declare class ProjectPrincipal {
50
50
  getUsedResolvedFiles(): string[];
51
51
  private getProgramSourceFiles;
52
52
  getUnreferencedFiles(): string[];
53
- analyzeSourceFile(filePath: string, options: Omit<GetImportsAndExportsOptions, 'skipExports'>, isGitIgnored: (filePath: string) => boolean, isInternalWorkspace: (packageName: string) => boolean, getPrincipalByFilePath: (filePath: string) => undefined | ProjectPrincipal): FileNode;
53
+ analyzeSourceFile(filePath: string, options: Omit<GetImportsAndExportsOptions, 'skipExports'>): FileNode;
54
54
  invalidateFile(filePath: string): void;
55
55
  findUnusedMembers(filePath: string, members: ExportMember[]): ExportMember[];
56
56
  hasExternalReferences(filePath: string, exportedItem: Export): boolean;
@@ -1,14 +1,13 @@
1
1
  import ts from 'typescript';
2
2
  import { CacheConsultant } from './CacheConsultant.js';
3
3
  import { getCompilerExtensions } from './compilers/index.js';
4
- import { ANONYMOUS, DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS, PUBLIC_TAG } from './constants.js';
4
+ import { ANONYMOUS, DEFAULT_EXTENSIONS, PUBLIC_TAG } from './constants.js';
5
5
  import { SourceFileManager } from './typescript/SourceFileManager.js';
6
6
  import { createHosts } from './typescript/create-hosts.js';
7
7
  import { _getImportsAndExports } from './typescript/get-imports-and-exports.js';
8
8
  import { timerify } from './util/Performance.js';
9
9
  import { compact } from './util/array.js';
10
- import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from './util/modules.js';
11
- import { dirname, extname, isInNodeModules, join, toAbsolute } from './util/path.js';
10
+ import { extname, isInNodeModules, toAbsolute } from './util/path.js';
12
11
  const baseCompilerOptions = {
13
12
  allowJs: true,
14
13
  allowSyntheticDefaultImports: true,
@@ -156,7 +155,7 @@ export class ProjectPrincipal {
156
155
  const sourceFiles = this.getProgramSourceFiles();
157
156
  return Array.from(this.projectPaths).filter(filePath => !sourceFiles.has(filePath));
158
157
  }
159
- analyzeSourceFile(filePath, options, isGitIgnored, isInternalWorkspace, getPrincipalByFilePath) {
158
+ analyzeSourceFile(filePath, options) {
160
159
  const fd = this.cache.getFileDescriptor(filePath);
161
160
  if (!fd.changed && fd.meta?.data)
162
161
  return fd.meta.data;
@@ -168,43 +167,7 @@ export class ProjectPrincipal {
168
167
  throw new Error(`Unable to find ${filePath}`);
169
168
  const skipExports = this.skipExportsAnalysis.has(filePath);
170
169
  const resolve = (specifier) => this.backend.resolveModuleNames([specifier], sourceFile.fileName)[0];
171
- const { imports, ...rest } = _getImportsAndExports(sourceFile, resolve, typeChecker, { ...options, skipExports });
172
- const { internal, resolved, specifiers, unresolved, external } = imports;
173
- const unresolvedImports = new Set();
174
- for (const [specifier, specifierFilePath] of specifiers) {
175
- const packageName = getPackageNameFromModuleSpecifier(specifier);
176
- if (packageName && isInternalWorkspace(packageName)) {
177
- external.add(packageName);
178
- const principal = getPrincipalByFilePath(specifierFilePath);
179
- if (principal && !isGitIgnored(specifierFilePath))
180
- principal.addNonEntryPath(specifierFilePath);
181
- }
182
- }
183
- for (const filePath of resolved) {
184
- const isIgnored = isGitIgnored(filePath);
185
- if (!isIgnored)
186
- this.addEntryPath(filePath, { skipExportsAnalysis: true });
187
- }
188
- for (const unresolvedImport of unresolved) {
189
- const { specifier } = unresolvedImport;
190
- if (specifier.startsWith('http'))
191
- continue;
192
- const sanitizedSpecifier = sanitizeSpecifier(specifier);
193
- if (isStartsLikePackageName(sanitizedSpecifier)) {
194
- external.add(sanitizedSpecifier);
195
- }
196
- else {
197
- const isIgnored = isGitIgnored(join(dirname(filePath), sanitizedSpecifier));
198
- if (!isIgnored) {
199
- const ext = extname(sanitizedSpecifier);
200
- const hasIgnoredExtension = FOREIGN_FILE_EXTENSIONS.has(ext);
201
- if (!ext || (ext !== '.json' && !hasIgnoredExtension)) {
202
- unresolvedImports.add(unresolvedImport);
203
- }
204
- }
205
- }
206
- }
207
- return { imports: { internal, unresolved: unresolvedImports, external }, ...rest };
170
+ return _getImportsAndExports(sourceFile, resolve, typeChecker, { ...options, skipExports });
208
171
  }
209
172
  invalidateFile(filePath) {
210
173
  this.backend.fileManager.snapshotCache.delete(filePath);
@@ -29,6 +29,7 @@ type WorkspaceManagerOptions = {
29
29
  type CacheItem = {
30
30
  resolveConfig?: Input[];
31
31
  resolveFromAST?: Input[];
32
+ configFile?: Input;
32
33
  };
33
34
  export declare class WorkspaceWorker {
34
35
  name: string;
@@ -262,6 +262,8 @@ export class WorkspaceWorker {
262
262
  if (data.resolveFromAST)
263
263
  for (const id of data.resolveFromAST)
264
264
  addInput(id, configFilePath);
265
+ if (data.configFile)
266
+ addInput(data.configFile);
265
267
  continue;
266
268
  }
267
269
  const resolveOpts = {
@@ -276,7 +278,8 @@ export class WorkspaceWorker {
276
278
  if (plugin.resolveConfig && !seen.get(key)?.has(configFilePath)) {
277
279
  if (typeof plugin.setup === 'function')
278
280
  await plugin.setup(resolveOpts);
279
- const localConfig = await loadConfigForPlugin(configFilePath, plugin, resolveOpts, pluginName);
281
+ const isLoad = typeof plugin.isLoadConfig === 'function' ? plugin.isLoadConfig(resolveOpts, this.dependencies) : true;
282
+ const localConfig = isLoad && (await loadConfigForPlugin(configFilePath, plugin, resolveOpts, pluginName));
280
283
  if (localConfig) {
281
284
  const inputs = await plugin.resolveConfig(localConfig, resolveOpts);
282
285
  for (const input of inputs)
@@ -300,15 +303,17 @@ export class WorkspaceWorker {
300
303
  cache.resolveFromAST = inputs;
301
304
  }
302
305
  }
303
- if (basename(configFilePath) !== 'package.json') {
306
+ if (!isManifest) {
304
307
  addInput(toEntry(configFilePath));
305
308
  addInput(toConfig(pluginName, configFilePath));
309
+ cache.configFile = toEntry(configFilePath);
310
+ if (fd?.changed && fd.meta && !seen.get(key)?.has(configFilePath)) {
311
+ fd.meta.data = cache;
312
+ }
306
313
  if (!seen.has(key))
307
314
  seen.set(key, new Set());
308
315
  seen.get(key)?.add(configFilePath);
309
316
  }
310
- if (!isManifest && fd?.changed && fd.meta)
311
- fd.meta.data = cache;
312
317
  }
313
318
  if (plugin.resolve) {
314
319
  const dependencies = (await plugin.resolve(options)) ?? [];
@@ -48,7 +48,7 @@ export const getDependenciesFromScript = (script, options) => {
48
48
  .map(arg => parseNodeArgs(arg.split(' ')))
49
49
  .filter(args => args.require)
50
50
  .flatMap(arg => arg.require)
51
- .map(toDeferResolve) ?? [];
51
+ .map(id => toDeferResolve(id)) ?? [];
52
52
  if (binary in PackageManagerResolvers) {
53
53
  const resolver = PackageManagerResolvers[binary];
54
54
  return resolver(binary, args, { ...options, fromArgs });
@@ -61,8 +61,8 @@ export const resolve = (binary, _args, options) => {
61
61
  return [
62
62
  toBinary(binary, inputOpts),
63
63
  ...positionals,
64
- ...resolved.map(toDeferResolve),
65
- ...resolvedImports.map(toDeferResolve),
64
+ ...resolved.map(id => toDeferResolve(id)),
65
+ ...resolvedImports.map(id => toDeferResolve(id)),
66
66
  ...resolvedFromArgs,
67
67
  ...configFilePaths,
68
68
  ];
@@ -1,15 +1,16 @@
1
1
  import { WorkspaceWorker } from '../WorkspaceWorker.js';
2
2
  import { _getInputsFromScripts } from '../binaries/index.js';
3
3
  import { getCompilerExtensions, getIncludedCompilers } from '../compilers/index.js';
4
- import { DEFAULT_EXTENSIONS } from '../constants.js';
4
+ import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from '../constants.js';
5
5
  import { perfObserver } from '../util/Performance.js';
6
6
  import { debugLog, debugLogArray } from '../util/debug.js';
7
7
  import { getReferencedInputsHandler } from '../util/get-referenced-inputs.js';
8
8
  import { _glob, negate } from '../util/glob.js';
9
9
  import { isAlias, isConfig, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isIgnore, isProductionEntry, isProject, toProductionEntry, } from '../util/input.js';
10
10
  import { getOrCreateFileNode, updateImportMap } from '../util/module-graph.js';
11
+ import { getPackageNameFromModuleSpecifier, isStartsLikePackageName, sanitizeSpecifier } from '../util/modules.js';
11
12
  import { getEntryPathsFromManifest } from '../util/package-json.js';
12
- import { dirname, isAbsolute, join, relative, toRelative } from '../util/path.js';
13
+ import { dirname, extname, isAbsolute, join, relative, toRelative } from '../util/path.js';
13
14
  import { augmentWorkspace, getToSourcePathHandler, getToSourcePathsHandler } from '../util/to-source-path.js';
14
15
  import { loadTSConfig } from '../util/tsconfig-loader.js';
15
16
  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, }) {
@@ -250,37 +251,68 @@ export async function build({ cacheLocation, chief, collector, cwd, deputy, fact
250
251
  analyzedFiles.add(filePath);
251
252
  const workspace = chief.findWorkspaceByFilePath(filePath);
252
253
  if (workspace) {
253
- const { imports, exports, duplicates, scripts, traceRefs } = principal.analyzeSourceFile(filePath, {
254
+ const file = principal.analyzeSourceFile(filePath, {
254
255
  skipTypeOnly: isStrict,
255
256
  isFixExports,
256
257
  isFixTypes,
257
258
  ignoreExportsUsedInFile: chief.config.ignoreExportsUsedInFile,
258
259
  isReportClassMembers,
259
260
  tags,
260
- }, isGitIgnored, isInternalWorkspace, getPrincipalByFilePath);
261
- const node = getOrCreateFileNode(graph, filePath);
262
- node.imports = imports;
263
- node.exports = exports;
264
- node.duplicates = duplicates;
265
- node.scripts = scripts;
266
- node.traceRefs = traceRefs;
267
- updateImportMap(node, imports.internal, graph);
268
- node.internalImportCache = imports.internal;
269
- graph.set(filePath, node);
270
- if (scripts && scripts.size > 0) {
261
+ });
262
+ const _unresolved = new Set();
263
+ for (const unresolvedImport of file.imports.unresolved) {
264
+ const { specifier } = unresolvedImport;
265
+ if (specifier.startsWith('http'))
266
+ continue;
267
+ const sanitizedSpecifier = sanitizeSpecifier(specifier);
268
+ if (isStartsLikePackageName(sanitizedSpecifier)) {
269
+ file.imports.external.add(sanitizedSpecifier);
270
+ }
271
+ else {
272
+ const isIgnored = isGitIgnored(join(dirname(filePath), sanitizedSpecifier));
273
+ if (!isIgnored) {
274
+ const ext = extname(sanitizedSpecifier);
275
+ const hasIgnoredExtension = FOREIGN_FILE_EXTENSIONS.has(ext);
276
+ if (!ext || (ext !== '.json' && !hasIgnoredExtension))
277
+ _unresolved.add(unresolvedImport);
278
+ }
279
+ }
280
+ }
281
+ for (const filePath of file.imports.resolved) {
282
+ const isIgnored = isGitIgnored(filePath);
283
+ if (!isIgnored)
284
+ principal.addEntryPath(filePath, { skipExportsAnalysis: true });
285
+ }
286
+ for (const [specifier, specifierFilePath] of file.imports.specifiers) {
287
+ const packageName = getPackageNameFromModuleSpecifier(specifier);
288
+ if (packageName && isInternalWorkspace(packageName)) {
289
+ file.imports.external.add(packageName);
290
+ const principal = getPrincipalByFilePath(specifierFilePath);
291
+ if (principal && !isGitIgnored(specifierFilePath)) {
292
+ principal.addNonEntryPath(specifierFilePath);
293
+ }
294
+ }
295
+ }
296
+ if (file.scripts && file.scripts.size > 0) {
271
297
  const dependencies = deputy.getDependencies(workspace.name);
272
298
  const manifestScriptNames = new Set(Object.keys(chief.getManifestForWorkspace(workspace.name)?.scripts ?? {}));
273
299
  const dir = dirname(filePath);
274
300
  const options = { cwd: dir, rootCwd: cwd, containingFilePath: filePath, dependencies, manifestScriptNames };
275
- const inputs = _getInputsFromScripts(scripts, options);
301
+ const inputs = _getInputsFromScripts(file.scripts, options);
276
302
  for (const input of inputs) {
277
303
  input.containingFilePath ??= filePath;
278
304
  input.dir ??= dir;
279
305
  const specifierFilePath = getReferencedInternalFilePath(input, workspace);
280
306
  if (specifierFilePath)
281
- analyzeSourceFile(specifierFilePath, principal);
307
+ principal.addEntryPath(specifierFilePath, { skipExportsAnalysis: true });
282
308
  }
283
309
  }
310
+ const node = getOrCreateFileNode(graph, filePath);
311
+ file.imports.unresolved = _unresolved;
312
+ Object.assign(node, file);
313
+ updateImportMap(node, file.imports.internal, graph);
314
+ node.internalImportCache = file.imports.internal;
315
+ graph.set(filePath, node);
284
316
  }
285
317
  };
286
318
  for (let i = 0; i < principals.length; ++i) {
@@ -8,7 +8,7 @@ const entry = [];
8
8
  const production = [];
9
9
  const resolveConfig = async (config) => {
10
10
  const inputs = config?.plugins ?? [];
11
- return [...inputs].map(toDeferResolve);
11
+ return [...inputs].map(id => toDeferResolve(id));
12
12
  };
13
13
  export default {
14
14
  title,
@@ -5,14 +5,19 @@ import { api, resolveName } from './helpers.js';
5
5
  const title = 'Babel';
6
6
  const enablers = [/^@babel\//];
7
7
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
8
- const config = ['babel.config.{json,js,cjs,mjs,cts}', '.babelrc.{json,js,cjs,mjs,cts}', '.babelrc', 'package.json'];
8
+ const config = ['babel.config.{json,js,cjs,mjs,cts,ts}', '.babelrc.{json,js,cjs,mjs,cts}', '.babelrc', 'package.json'];
9
9
  const getName = (value) => [Array.isArray(value) ? value[0] : value].filter(name => typeof name === 'string');
10
10
  export const getDependenciesFromConfig = (config) => {
11
11
  const presets = config.presets?.flatMap(getName).map(name => resolveName(name, 'preset')) ?? [];
12
12
  const plugins = config.plugins?.flatMap(getName).map(name => resolveName(name, 'plugin')) ?? [];
13
13
  const nested = config.env ? Object.values(config.env).flatMap(getDependenciesFromConfig) : [];
14
14
  const overrides = config.overrides ? [config.overrides].flat().flatMap(getDependenciesFromConfig) : [];
15
- return compact([...presets.map(toDeferResolve), ...plugins.map(toDeferResolve), ...nested, ...overrides]);
15
+ return compact([
16
+ ...presets.map(id => toDeferResolve(id)),
17
+ ...plugins.map(id => toDeferResolve(id)),
18
+ ...nested,
19
+ ...overrides,
20
+ ]);
16
21
  };
17
22
  const resolveConfig = async (config) => {
18
23
  if (typeof config === 'function')
@@ -1,16 +1,11 @@
1
1
  import { toConfig } from '../../util/input.js';
2
2
  import { hasDependency } from '../../util/plugin.js';
3
3
  const title = 'Biome';
4
- const enablers = ['@biomejs/biome', 'biome'];
4
+ const enablers = ['@biomejs/biome'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
6
  const config = ['biome.json', 'biome.jsonc'];
7
7
  const resolveExtends = (extendsArray, options) => {
8
- return extendsArray.map(specifier => {
9
- if (specifier.endsWith('.json') || specifier.endsWith('.jsonc')) {
10
- return toConfig('biome', specifier);
11
- }
12
- return toConfig('biome', specifier, { containingFilePath: options.configFilePath });
13
- });
8
+ return extendsArray.map(specifier => toConfig('biome', specifier, { containingFilePath: options.configFilePath }));
14
9
  };
15
10
  const resolveConfig = (config, options) => {
16
11
  return [...resolveExtends(config.extends || [], options)];
@@ -10,7 +10,7 @@ const config = [
10
10
  'c{s,S}pell.json',
11
11
  ];
12
12
  const resolveConfig = config => {
13
- return [config?.import ?? []].flat().map(toDeferResolve);
13
+ return [config?.import ?? []].flat().map(id => toDeferResolve(id));
14
14
  };
15
15
  export default {
16
16
  title,
@@ -9,7 +9,7 @@ const resolveConfig = config => {
9
9
  const imports = (config?.import ? config.import : entry).map(id => toEntry(id));
10
10
  const formatters = config?.format ? config.format : [];
11
11
  const requires = config?.require ? config.require : [];
12
- return imports.concat([...formatters, ...requires].map(toDeferResolve));
12
+ return imports.concat([...formatters, ...requires].map(id => toDeferResolve(id)));
13
13
  };
14
14
  export default {
15
15
  title,
@@ -18,7 +18,7 @@ const resolveConfig = async (localConfig, options) => {
18
18
  const supportFiles = [localConfig.e2e?.supportFile || [], localConfig.component?.supportFile || []].flat();
19
19
  const inputs = await resolveDependencies(localConfig, options);
20
20
  return [
21
- ...inputs.map(toDeferResolve),
21
+ ...inputs.map(id => toDeferResolve(id)),
22
22
  ...(specPatterns.length > 0 ? specPatterns : TEST_FILE_PATTERNS).map(id => toEntry(id)),
23
23
  ...(supportFiles.length > 0 ? supportFiles : SUPPORT_FILE_PATTERNS).map(id => toEntry(id)),
24
24
  ];
@@ -9,7 +9,7 @@ export const getInputs = (config, options) => {
9
9
  return getInputsDeprecated(config, options);
10
10
  }
11
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));
12
+ return compact(dependencies).map(id => toDeferResolve(id, { optional: true }));
13
13
  };
14
14
  const getInputsDeprecated = (config, options) => {
15
15
  const extendsSpecifiers = config.extends ? compact([config.extends].flat().map(resolveExtendSpecifier)) : [];
@@ -24,7 +24,7 @@ const getInputsDeprecated = (config, options) => {
24
24
  const settings = config.settings ? getDependenciesFromSettings(config.settings) : [];
25
25
  const rules = getDependenciesFromRules({});
26
26
  const overrides = config.overrides ? [config.overrides].flat().flatMap(d => getInputsDeprecated(d, options)) : [];
27
- const deferred = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(toDeferResolve);
27
+ const deferred = compact([...extendsSpecifiers, ...plugins, parser, ...settings, ...rules]).map(id => toDeferResolve(id));
28
28
  return [...extendConfigs, ...deferred, ...babelDependencies, ...overrides];
29
29
  };
30
30
  const isQualifiedSpecifier = (specifier) => specifier === 'eslint' ||
@@ -1,4 +1,4 @@
1
- import type { IsPluginEnabled, ResolveConfig } from '../../types/config.js';
1
+ import type { IsLoadConfig, IsPluginEnabled, ResolveConfig } from '../../types/config.js';
2
2
  import type { ESLintConfigDeprecated } from './types.js';
3
3
  export declare const docs: {
4
4
  note: string;
@@ -8,8 +8,8 @@ declare const _default: {
8
8
  enablers: string[];
9
9
  isEnabled: IsPluginEnabled;
10
10
  packageJsonPath: string;
11
- entry: string[];
12
11
  config: string[];
12
+ isLoadConfig: IsLoadConfig;
13
13
  resolveConfig: ResolveConfig<ESLintConfigDeprecated>;
14
14
  };
15
15
  export default _default;
@@ -5,24 +5,29 @@ const enablers = ['eslint', '@eslint/js'];
5
5
  const isEnabled = ({ dependencies, manifest }) => hasDependency(dependencies, enablers) ||
6
6
  Boolean(manifest.name && /(^eslint-config|\/eslint-config)/.test(manifest.name));
7
7
  const packageJsonPath = 'eslintConfig';
8
- const entry = ['eslint.config.{js,cjs,mjs,ts,cts,mts}'];
9
- const config = ['.eslintrc', '.eslintrc.{js,json,cjs}', '.eslintrc.{yml,yaml}', 'package.json'];
8
+ const config = [
9
+ 'eslint.config.{js,cjs,mjs,ts,cts,mts}',
10
+ '.eslintrc',
11
+ '.eslintrc.{js,json,cjs}',
12
+ '.eslintrc.{yml,yaml}',
13
+ 'package.json',
14
+ ];
15
+ const isLoadConfig = ({ manifest }, dependencies) => {
16
+ const version = manifest.devDependencies?.['eslint'] || manifest.dependencies?.['eslint'];
17
+ if (version) {
18
+ const major = version.match(/\d+/);
19
+ if (major && Number.parseInt(major[0], 10) === 9 && dependencies.has('eslint-config-next')) {
20
+ return false;
21
+ }
22
+ }
23
+ return true;
24
+ };
10
25
  const resolveConfig = (localConfig, options) => getInputs(localConfig, options);
11
26
  const note = `### ESLint v9
12
27
 
13
- Only regular \`import\` statements are considered by default.
14
- The configuration object is not resolved to find dependencies for \`settings\` such as \`"eslint-import-resolver-typescript"\`.
15
- To enable this, lift the \`entry\` to a \`config\` file like so:
16
-
17
- \`\`\`json
18
- {
19
- "eslint": ["eslint.config.ts"]
20
- }
21
- \`\`\`
22
-
23
- This is not enabled by default, since this exception may be thrown by a \`@rushstack/eslint-*\` package:
28
+ The ESLint plugin config resolver is disabled when using \`eslint-config-next\` (\`next lint\`).
24
29
 
25
- > \`Error: Failed to patch ESLint because the calling module was not recognized.\`
30
+ Root cause: [microsoft/rushstack#4965](https://github.com/microsoft/rushstack/issues/4965)/[#5049](https://github.com/microsoft/rushstack/issues/5049)
26
31
 
27
32
  ### ESLint v8
28
33
 
@@ -41,7 +46,7 @@ export default {
41
46
  enablers,
42
47
  isEnabled,
43
48
  packageJsonPath,
44
- entry,
45
49
  config,
50
+ isLoadConfig,
46
51
  resolveConfig,
47
52
  };
@@ -17,7 +17,7 @@ const resolveConfig = async (localConfig, options) => {
17
17
  if (/gatsby-config/.test(configFileName)) {
18
18
  return localConfig.plugins
19
19
  .map(plugin => (typeof plugin === 'string' ? plugin : plugin.resolve))
20
- .map(toDeferResolve);
20
+ .map(id => toDeferResolve(id));
21
21
  }
22
22
  if (/gatsby-node/.test(configFileName)) {
23
23
  const plugins = new Set();
@@ -179,8 +179,8 @@ export declare const Plugins: {
179
179
  enablers: string[];
180
180
  isEnabled: import("../types/config.js").IsPluginEnabled;
181
181
  packageJsonPath: string;
182
- entry: string[];
183
182
  config: string[];
183
+ isLoadConfig: import("../types/config.js").IsLoadConfig;
184
184
  resolveConfig: import("../types/config.js").ResolveConfig<import("./eslint/types.js").ESLintConfigDeprecated>;
185
185
  };
186
186
  expo: {
@@ -1,5 +1,5 @@
1
1
  import { toEntry } from '../../util/input.js';
2
- import { join } from '../../util/path.js';
2
+ import { isAbsolute, join } from '../../util/path.js';
3
3
  import { hasDependency } from '../../util/plugin.js';
4
4
  import { configFiles, inputsFromFrameworks, inputsFromPlugins, loadConfig } from './helpers.js';
5
5
  const title = 'Karma';
@@ -19,12 +19,14 @@ const resolveConfig = async (localConfig, options) => {
19
19
  if (config.files) {
20
20
  for (const fileOrPatternObj of config.files) {
21
21
  const fileOrPattern = typeof fileOrPatternObj === 'string' ? fileOrPatternObj : fileOrPatternObj.pattern;
22
- inputs.add(toEntry(join(options.configFileDir, basePath, fileOrPattern)));
22
+ const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);
23
+ inputs.add(toEntry(absPath));
23
24
  }
24
25
  }
25
26
  if (config.exclude) {
26
27
  for (const fileOrPattern of config.exclude) {
27
- inputs.add(toEntry(`!${join(options.configFileDir, basePath, fileOrPattern)}`));
28
+ const absPath = isAbsolute(fileOrPattern) ? fileOrPattern : join(options.configFileDir, basePath, fileOrPattern);
29
+ inputs.add(toEntry(`!${absPath}`));
28
30
  }
29
31
  }
30
32
  return Array.from(inputs);
@@ -9,7 +9,7 @@ const config = ['package.json', ...toCosmiconfig('linthtml')];
9
9
  const resolveConfig = config => {
10
10
  const extensions = config.extends ?? [];
11
11
  const plugins = config.plugins ?? [];
12
- return [extensions, plugins].flat().map(toDeferResolve);
12
+ return [extensions, plugins].flat().map(id => toDeferResolve(id));
13
13
  };
14
14
  export default {
15
15
  title,
@@ -35,7 +35,7 @@ const resolveConfig = async (config) => {
35
35
  inputs.push(transformer.minifierPath);
36
36
  if (transformer?.babelTransformerPath)
37
37
  inputs.push(transformer.babelTransformerPath);
38
- return Array.from(i).concat([...inputs].map(toDeferResolve));
38
+ return Array.from(i).concat([...inputs].map(id => toDeferResolve(id)));
39
39
  };
40
40
  const note = `False positives for platform-specific unused files?
41
41
  Override the default \`entry\` patterns to match platforms and extensions.`;
@@ -21,7 +21,19 @@ const args = {
21
21
  positional: true,
22
22
  nodeImportArgs: true,
23
23
  resolve: ['test-reporter'],
24
- boolean: ['deprecation', 'experimental-strip-types', 'harmony', 'test-only', 'test', 'warnings', 'watch'],
24
+ boolean: [
25
+ 'deprecation',
26
+ 'experimental-strip-types',
27
+ 'experimental-transform-types',
28
+ 'harmony',
29
+ 'inspect-brk',
30
+ 'inspect-wait',
31
+ 'inspect',
32
+ 'test-only',
33
+ 'test',
34
+ 'warnings',
35
+ 'watch',
36
+ ],
25
37
  args: (args) => args.filter(arg => !/--test-reporter[= ](spec|tap|dot|junit|lcov)/.test(arg)),
26
38
  };
27
39
  export default {
@@ -7,7 +7,7 @@ const config = ['.nycrc', '.nycrc.{json,yml,yaml}', 'nyc.config.js', 'package.js
7
7
  const resolveConfig = config => {
8
8
  const extend = config?.extends ?? [];
9
9
  const requires = config?.require ?? [];
10
- return [extend, requires].flat().map(toDeferResolve);
10
+ return [extend, requires].flat().map(id => toDeferResolve(id));
11
11
  };
12
12
  export default {
13
13
  title,
@@ -24,7 +24,7 @@ export const resolveConfig = async (localConfig, options) => {
24
24
  });
25
25
  return projects
26
26
  .flatMap(config => toEntryPatterns(config.testMatch ?? entry, cwd, configFileDir, config, localConfig))
27
- .concat(reporters.map(toDeferResolve));
27
+ .concat(reporters.map(id => toDeferResolve(id)));
28
28
  };
29
29
  const args = {
30
30
  binaries: ['playwright'],
@@ -19,7 +19,7 @@ const resolveConfig = config => {
19
19
  return [];
20
20
  })
21
21
  : [];
22
- const inputs = plugins.map(toDeferResolve);
22
+ const inputs = plugins.map(id => toDeferResolve(id));
23
23
  return ['tailwindcss', '@tailwindcss/postcss'].some(tailwindPlugin => plugins.includes(tailwindPlugin))
24
24
  ? [...inputs, toDependency('postcss')]
25
25
  : inputs;
@@ -18,7 +18,7 @@ const resolveConfig = async (localConfig) => {
18
18
  ];
19
19
  return [...entries, ...decoratorEntry]
20
20
  .map(id => toEntry(id))
21
- .concat((localConfig?.plugins ?? []).map(toDeferResolve));
21
+ .concat((localConfig?.plugins ?? []).map(id => toDeferResolve(id)));
22
22
  };
23
23
  export default {
24
24
  title,
@@ -15,7 +15,7 @@ const resolveConfig = config => {
15
15
  return [];
16
16
  })
17
17
  .map(plugin => (plugin.startsWith('remark-') ? plugin : `remark-${plugin}`)) ?? [];
18
- return plugins.map(toDeferResolve);
18
+ return plugins.map(id => toDeferResolve(id));
19
19
  };
20
20
  export default {
21
21
  title,
@@ -15,7 +15,7 @@ const excludePackages = [
15
15
  ];
16
16
  const resolveConfig = config => {
17
17
  const plugins = (config?.plugins ?? []).map(plugin => (Array.isArray(plugin) ? plugin[0] : plugin));
18
- return plugins.filter(plugin => !excludePackages.includes(plugin)).map(toDeferResolve);
18
+ return plugins.filter(plugin => !excludePackages.includes(plugin)).map(id => toDeferResolve(id));
19
19
  };
20
20
  export default {
21
21
  title,
@@ -33,7 +33,7 @@ const resolveConfig = async (localConfig, options) => {
33
33
  const frameworks = framework ? [framework] : [];
34
34
  return [
35
35
  ...patterns.map(id => toEntry(id)),
36
- ...addons.map(toDeferResolve),
36
+ ...addons.map(id => toDeferResolve(id)),
37
37
  ...builderPackages.map(id => toDependency(id)),
38
38
  ...frameworks.map(id => toDependency(id)),
39
39
  ];
@@ -10,7 +10,7 @@ const resolveConfig = localConfig => {
10
10
  ? localConfig.checkers.map(checker => `@stryker-mutator/${checker}-checker`)
11
11
  : [];
12
12
  const plugins = localConfig.plugins ?? [];
13
- return [...runners, ...checkers, ...plugins].map(toDeferResolve);
13
+ return [...runners, ...checkers, ...plugins].map(id => toDeferResolve(id));
14
14
  };
15
15
  export default {
16
16
  title,
@@ -16,7 +16,7 @@ const resolveConfig = config => {
16
16
  const cfg = 'typedocOptions' in config ? config.typedocOptions : config;
17
17
  const plugins = cfg?.plugin ?? [];
18
18
  const themes = cfg?.theme ?? [];
19
- return [...plugins, ...themes].map(toDeferResolve);
19
+ return [...plugins, ...themes].map(id => toDeferResolve(id));
20
20
  };
21
21
  const args = {
22
22
  resolve: ['plugin', 'theme'],
@@ -24,7 +24,12 @@ const resolveConfig = async (localConfig, options) => {
24
24
  ? compilerOptions.plugins.map(plugin => (typeof plugin === 'object' && 'name' in plugin ? plugin.name : ''))
25
25
  : [];
26
26
  const importHelpers = compilerOptions?.importHelpers ? ['tslib'] : [];
27
- return compact([...extend, ...references, ...[...types, ...plugins, ...importHelpers].map(toDeferResolve), ...jsx]);
27
+ return compact([
28
+ ...extend,
29
+ ...references,
30
+ ...[...types, ...plugins, ...importHelpers].map(id => toDeferResolve(id)),
31
+ ...jsx,
32
+ ]);
28
33
  };
29
34
  const args = {
30
35
  binaries: ['tsc'],
@@ -92,6 +92,7 @@ export interface PluginOptions extends BaseOptions {
92
92
  }
93
93
  type PluginSetup = (options: PluginOptions) => Promise<void> | void;
94
94
  type PluginTeardown = (options: PluginOptions) => Promise<void> | void;
95
+ export type IsLoadConfig = (options: PluginOptions, dependencies: Set<string>) => boolean;
95
96
  export type ResolveConfig<T = any> = (config: T, options: PluginOptions) => Promise<Input[]> | Input[];
96
97
  export type Resolve = (options: PluginOptions) => Promise<Input[]> | Input[];
97
98
  export type GetSourceFile = (filePath: string) => ts.SourceFile | undefined;
@@ -113,6 +114,7 @@ export interface Plugin {
113
114
  project?: string[];
114
115
  setup?: PluginSetup;
115
116
  teardown?: PluginTeardown;
117
+ isLoadConfig?: IsLoadConfig;
116
118
  resolveConfig?: ResolveConfig;
117
119
  resolve?: Resolve;
118
120
  resolveFromAST?: ResolveFromAST;
@@ -55,6 +55,8 @@ export type FileNode = {
55
55
  internal: ImportMap;
56
56
  external: Set<string>;
57
57
  unresolved: Set<UnresolvedImport>;
58
+ resolved: Set<FilePath>;
59
+ specifiers: Set<[string, FilePath]>;
58
60
  };
59
61
  exports: ExportMap;
60
62
  duplicates: Iterable<Array<IssueSymbol>>;
@@ -1,18 +1,5 @@
1
1
  import ts from 'typescript';
2
2
  import type { GetImportsAndExportsOptions } from '../types/config.js';
3
- import type { IssueSymbol } from '../types/issues.js';
4
- import type { ExportMap, ImportMap, UnresolvedImport } from '../types/module-graph.js';
3
+ import type { FileNode } from '../types/module-graph.js';
5
4
  import type { BoundSourceFile } from './SourceFile.js';
6
- export declare const _getImportsAndExports: (sourceFile: BoundSourceFile, resolveModule: (specifier: string) => ts.ResolvedModuleFull | undefined, typeChecker: ts.TypeChecker, options: GetImportsAndExportsOptions) => {
7
- imports: {
8
- internal: ImportMap;
9
- external: Set<string>;
10
- resolved: Set<string>;
11
- specifiers: Set<[string, string]>;
12
- unresolved: Set<UnresolvedImport>;
13
- };
14
- exports: ExportMap;
15
- duplicates: IssueSymbol[][];
16
- scripts: Set<string>;
17
- traceRefs: Set<string>;
18
- };
5
+ export declare const _getImportsAndExports: (sourceFile: BoundSourceFile, resolveModule: (specifier: string) => ts.ResolvedModuleFull | undefined, typeChecker: ts.TypeChecker, options: GetImportsAndExportsOptions) => FileNode;
@@ -44,7 +44,7 @@ export declare const isConfig: (input: Input) => input is ConfigInput;
44
44
  export declare const toDependency: (specifier: string, options?: Options) => Input;
45
45
  export declare const isDependency: (input: Input) => boolean;
46
46
  export declare const toProductionDependency: (specifier: string) => Input;
47
- export declare const toDeferResolve: (specifier: string) => Input;
47
+ export declare const toDeferResolve: (specifier: string, options?: Options) => Input;
48
48
  export declare const isDeferResolve: (input: Input) => boolean;
49
49
  export declare const toDeferResolveProductionEntry: (specifier: string, options?: Options) => Input;
50
50
  export declare const isDeferResolveProductionEntry: (input: Input) => boolean;
@@ -35,7 +35,11 @@ export const toProductionDependency = (specifier) => ({
35
35
  specifier,
36
36
  production: true,
37
37
  });
38
- export const toDeferResolve = (specifier) => ({ type: 'deferResolve', specifier });
38
+ export const toDeferResolve = (specifier, options = {}) => ({
39
+ type: 'deferResolve',
40
+ specifier,
41
+ ...options,
42
+ });
39
43
  export const isDeferResolve = (input) => input.type === 'deferResolve';
40
44
  export const toDeferResolveProductionEntry = (specifier, options = {}) => ({
41
45
  type: 'deferResolveEntry',
@@ -35,6 +35,8 @@ const createFileNode = () => ({
35
35
  internal: new Map(),
36
36
  external: new Set(),
37
37
  unresolved: new Set(),
38
+ resolved: new Set(),
39
+ specifiers: new Set(),
38
40
  },
39
41
  exports: new Map(),
40
42
  duplicates: new Set(),
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.61.1";
1
+ export declare const version = "5.61.3";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.61.1';
1
+ export const version = '5.61.3';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.61.1",
3
+ "version": "5.61.3",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "homepage": "https://knip.dev",
6
6
  "repository": {