knip 5.64.2 → 5.65.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.
@@ -626,6 +626,7 @@ export declare class ConfigurationChief {
626
626
  project?: string | string[] | undefined;
627
627
  paths?: Record<string, string[]> | undefined;
628
628
  ignore?: string | string[] | undefined;
629
+ ignoreFiles?: string | string[] | undefined;
629
630
  ignoreBinaries?: (string | RegExp)[] | undefined;
630
631
  ignoreDependencies?: (string | RegExp)[] | undefined;
631
632
  ignoreMembers?: (string | RegExp)[] | undefined;
@@ -756,6 +757,7 @@ export declare class ConfigurationChief {
756
757
  project: string[];
757
758
  paths: Record<string, string[]>;
758
759
  ignore: string[];
760
+ ignoreFiles: string[];
759
761
  isIncludeEntryExports: boolean;
760
762
  };
761
763
  findWorkspaceByFilePath(filePath: string): Workspace | undefined;
@@ -31,6 +31,7 @@ const getDefaultWorkspaceConfig = (extensions = []) => {
31
31
  const isPluginName = (name) => pluginNames.includes(name);
32
32
  const defaultConfig = {
33
33
  ignore: [],
34
+ ignoreFiles: [],
34
35
  ignoreBinaries: [],
35
36
  ignoreDependencies: [],
36
37
  ignoreMembers: [],
@@ -91,6 +92,7 @@ export class ConfigurationChief {
91
92
  }
92
93
  normalize(rawConfig) {
93
94
  const ignore = arrayify(rawConfig.ignore ?? defaultConfig.ignore);
95
+ const ignoreFiles = arrayify(rawConfig.ignoreFiles ?? defaultConfig.ignoreFiles);
94
96
  const ignoreBinaries = rawConfig.ignoreBinaries ?? [];
95
97
  const ignoreDependencies = rawConfig.ignoreDependencies ?? [];
96
98
  const ignoreMembers = rawConfig.ignoreMembers ?? [];
@@ -107,6 +109,7 @@ export class ConfigurationChief {
107
109
  }
108
110
  return {
109
111
  ignore,
112
+ ignoreFiles,
110
113
  ignoreBinaries,
111
114
  ignoreDependencies,
112
115
  ignoreMembers,
@@ -309,6 +312,7 @@ export class ConfigurationChief {
309
312
  const project = workspaceConfig.project ? arrayify(workspaceConfig.project) : baseConfig.project;
310
313
  const paths = workspaceConfig.paths ?? {};
311
314
  const ignore = arrayify(workspaceConfig.ignore);
315
+ const ignoreFiles = arrayify(workspaceConfig.ignoreFiles);
312
316
  const isIncludeEntryExports = workspaceConfig.includeEntryExports ?? this.config.isIncludeEntryExports;
313
317
  const plugins = {};
314
318
  for (const [pluginName, pluginConfig] of Object.entries(this.config.rootPluginConfigs)) {
@@ -320,7 +324,7 @@ export class ConfigurationChief {
320
324
  plugins[pluginName] = normalizePluginConfig(pluginConfig);
321
325
  }
322
326
  }
323
- return { entry, project, paths, ignore, isIncludeEntryExports, ...plugins };
327
+ return { entry, project, paths, ignore, ignoreFiles, isIncludeEntryExports, ...plugins };
324
328
  }
325
329
  findWorkspaceByFilePath(filePath) {
326
330
  const workspaceDir = this.availableWorkspaceDirs.find(workspaceDir => filePath.startsWith(`${workspaceDir}/`));
@@ -10,9 +10,12 @@ export declare class IssueCollector {
10
10
  private configurationHints;
11
11
  private tagHints;
12
12
  private ignorePatterns;
13
+ private ignoreFilesPatterns;
13
14
  private isMatch;
15
+ private isFileMatch;
14
16
  constructor(options: MainOptions);
15
17
  addIgnorePatterns(patterns: string[]): void;
18
+ addIgnoreFilesPatterns(patterns: string[]): void;
16
19
  addFileCounts({ processed, unused }: {
17
20
  processed: number;
18
21
  unused: number;
@@ -13,18 +13,27 @@ export class IssueCollector {
13
13
  configurationHints = new Map();
14
14
  tagHints = new Set();
15
15
  ignorePatterns = new Set();
16
+ ignoreFilesPatterns = new Set();
16
17
  isMatch;
18
+ isFileMatch;
17
19
  constructor(options) {
18
20
  this.cwd = options.cwd;
19
21
  this.rules = options.rules;
20
22
  this.filter = options.workspace ? join(options.cwd, options.workspace) : undefined;
21
23
  this.isMatch = () => false;
24
+ this.isFileMatch = () => false;
22
25
  }
23
26
  addIgnorePatterns(patterns) {
24
27
  for (const pattern of patterns)
25
28
  this.ignorePatterns.add(pattern);
26
- const p = [...this.ignorePatterns];
27
- this.isMatch = (filePath) => isMatch(filePath, p, { dot: true });
29
+ const _patterns = Array.from(this.ignorePatterns);
30
+ this.isMatch = (filePath) => isMatch(filePath, _patterns, { dot: true });
31
+ }
32
+ addIgnoreFilesPatterns(patterns) {
33
+ for (const pattern of patterns)
34
+ this.ignoreFilesPatterns.add(pattern);
35
+ const _patterns = Array.from(this.ignoreFilesPatterns);
36
+ this.isFileMatch = (filePath) => isMatch(filePath, _patterns, { dot: true });
28
37
  }
29
38
  addFileCounts({ processed, unused }) {
30
39
  this.counters.processed += processed;
@@ -38,6 +47,8 @@ export class IssueCollector {
38
47
  continue;
39
48
  if (this.isMatch(filePath))
40
49
  continue;
50
+ if (this.isFileMatch(filePath))
51
+ continue;
41
52
  this.issues.files.add(filePath);
42
53
  const symbol = relative(this.cwd, filePath);
43
54
  this.issues._files[symbol] = [{ type: 'files', filePath, symbol, severity: this.rules.files }];
@@ -1,6 +1,6 @@
1
1
  import { CacheConsultant } from './CacheConsultant.js';
2
2
  import { type Workspace } from './ConfigurationChief.js';
3
- import type { Configuration, GetReferencedInternalFilePath, GetSourceFile, WorkspaceConfiguration } from './types/config.js';
3
+ import type { GetReferencedInternalFilePath, GetSourceFile, WorkspaceConfiguration } from './types/config.js';
4
4
  import type { ConfigurationHint } from './types/issues.js';
5
5
  import type { PluginName } from './types/PluginNames.js';
6
6
  import type { PackageJson } from './types/package-json.js';
@@ -16,7 +16,6 @@ type WorkspaceManagerOptions = {
16
16
  getReferencedInternalFilePath: GetReferencedInternalFilePath;
17
17
  findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;
18
18
  getSourceFile: GetSourceFile;
19
- rootIgnore: Configuration['ignore'];
20
19
  negatedWorkspacePatterns: string[];
21
20
  ignoredWorkspacePatterns: string[];
22
21
  enabledPluginsInAncestors: string[];
@@ -37,7 +36,6 @@ export declare class WorkspaceWorker {
37
36
  getReferencedInternalFilePath: GetReferencedInternalFilePath;
38
37
  findWorkspaceByFilePath: (filePath: string) => Workspace | undefined;
39
38
  getSourceFile: GetSourceFile;
40
- rootIgnore: Configuration['ignore'];
41
39
  negatedWorkspacePatterns: string[];
42
40
  ignoredWorkspacePatterns: string[];
43
41
  options: MainOptions;
@@ -46,7 +44,7 @@ export declare class WorkspaceWorker {
46
44
  enabledPluginsInAncestors: string[];
47
45
  cache: CacheConsultant<CacheItem>;
48
46
  configFilesMap: Map<string, Map<PluginName, Set<string>>>;
49
- constructor({ name, dir, config, manifest, dependencies, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, getSourceFile, configFilesMap, options, }: WorkspaceManagerOptions);
47
+ constructor({ name, dir, config, manifest, dependencies, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, getSourceFile, configFilesMap, options, }: WorkspaceManagerOptions);
50
48
  init(): Promise<void>;
51
49
  private determineEnabledPlugins;
52
50
  private getConfigForPlugin;
@@ -58,7 +56,6 @@ export declare class WorkspaceWorker {
58
56
  getProductionEntryFilePatterns(negatedTestFilePatterns: string[]): string[];
59
57
  getProductionProjectFilePatterns(negatedTestFilePatterns: string[]): string[];
60
58
  private getConfigurationFilePatterns;
61
- getIgnorePatterns(): string[];
62
59
  runPlugins(): Promise<Input[]>;
63
60
  getConfigurationHints(type: 'entry' | 'project', patterns: string[], filePaths: string[], includedPaths: Set<string>): Set<ConfigurationHint>;
64
61
  onDispose(): void;
@@ -7,7 +7,7 @@ import { getFilteredScripts } from './manifest/helpers.js';
7
7
  import { PluginEntries, Plugins } from './plugins.js';
8
8
  import { compact } from './util/array.js';
9
9
  import { debugLogArray, debugLogObject } from './util/debug.js';
10
- import { _glob, hasNoProductionSuffix, hasProductionSuffix, negate, prependDirToPattern } from './util/glob.js';
10
+ import { _glob, hasNoProductionSuffix, hasProductionSuffix, negate } from './util/glob.js';
11
11
  import { isConfig, toConfig, toDebugString, toEntry, toProductionEntry, } from './util/input.js';
12
12
  import { getKeysByValue } from './util/object.js';
13
13
  import { timerify } from './util/Performance.js';
@@ -25,7 +25,6 @@ export class WorkspaceWorker {
25
25
  getReferencedInternalFilePath;
26
26
  findWorkspaceByFilePath;
27
27
  getSourceFile;
28
- rootIgnore;
29
28
  negatedWorkspacePatterns = [];
30
29
  ignoredWorkspacePatterns = [];
31
30
  options;
@@ -34,13 +33,12 @@ export class WorkspaceWorker {
34
33
  enabledPluginsInAncestors;
35
34
  cache;
36
35
  configFilesMap;
37
- constructor({ name, dir, config, manifest, dependencies, rootIgnore, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, getSourceFile, configFilesMap, options, }) {
36
+ constructor({ name, dir, config, manifest, dependencies, negatedWorkspacePatterns, ignoredWorkspacePatterns, enabledPluginsInAncestors, getReferencedInternalFilePath, findWorkspaceByFilePath, getSourceFile, configFilesMap, options, }) {
38
37
  this.name = name;
39
38
  this.dir = dir;
40
39
  this.config = config;
41
40
  this.manifest = manifest;
42
41
  this.dependencies = dependencies;
43
- this.rootIgnore = rootIgnore;
44
42
  this.negatedWorkspacePatterns = negatedWorkspacePatterns;
45
43
  this.ignoredWorkspacePatterns = ignoredWorkspacePatterns;
46
44
  this.enabledPluginsInAncestors = enabledPluginsInAncestors;
@@ -159,9 +157,6 @@ export class WorkspaceWorker {
159
157
  const pluginConfig = this.getConfigForPlugin(pluginName);
160
158
  return pluginConfig.config ?? plugin.config ?? [];
161
159
  }
162
- getIgnorePatterns() {
163
- return [...this.rootIgnore, ...this.config.ignore.map(pattern => prependDirToPattern(this.name, pattern))];
164
- }
165
160
  async runPlugins() {
166
161
  const wsName = this.name;
167
162
  const cwd = this.dir;
@@ -1149,6 +1149,7 @@ export declare const partitionCompilers: (rawLocalConfig: RawConfiguration) => {
1149
1149
  project?: string | string[] | undefined;
1150
1150
  paths?: Record<string, string[]> | undefined;
1151
1151
  ignore?: string | string[] | undefined;
1152
+ ignoreFiles?: string | string[] | undefined;
1152
1153
  ignoreBinaries?: (string | RegExp)[] | undefined;
1153
1154
  ignoreDependencies?: (string | RegExp)[] | undefined;
1154
1155
  ignoreMembers?: (string | RegExp)[] | undefined;
@@ -1163,6 +1164,7 @@ export declare const partitionCompilers: (rawLocalConfig: RawConfiguration) => {
1163
1164
  project?: string | string[] | undefined;
1164
1165
  paths?: Record<string, string[]> | undefined;
1165
1166
  ignore?: string | string[] | undefined;
1167
+ ignoreFiles?: string | string[] | undefined;
1166
1168
  ignoreBinaries?: (string | RegExp)[] | undefined;
1167
1169
  ignoreDependencies?: (string | RegExp)[] | undefined;
1168
1170
  ignoreMembers?: (string | RegExp)[] | undefined;
@@ -3,7 +3,7 @@ import { getCompilerExtensions, getIncludedCompilers } from '../compilers/index.
3
3
  import { DEFAULT_EXTENSIONS, FOREIGN_FILE_EXTENSIONS } from '../constants.js';
4
4
  import { debugLog, debugLogArray } from '../util/debug.js';
5
5
  import { getReferencedInputsHandler } from '../util/get-referenced-inputs.js';
6
- import { _glob, _syncGlob, negate } from '../util/glob.js';
6
+ import { _glob, _syncGlob, negate, prependDirToPattern } from '../util/glob.js';
7
7
  import { isAlias, isConfig, isDeferResolveEntry, isDeferResolveProductionEntry, isEntry, isIgnore, isProductionEntry, isProject, toProductionEntry, } from '../util/input.js';
8
8
  import { loadTSConfig } from '../util/load-tsconfig.js';
9
9
  import { getOrCreateFileNode, updateImportMap } from '../util/module-graph.js';
@@ -34,6 +34,8 @@ export async function build({ chief, collector, deputy, factory, isGitIgnored, s
34
34
  ...chief.getIgnores(name),
35
35
  });
36
36
  }
37
+ collector.addIgnorePatterns(chief.config.ignore.map(p => join(options.cwd, p)));
38
+ collector.addIgnoreFilesPatterns(chief.config.ignoreFiles.map(p => join(options.cwd, p)));
37
39
  for (const workspace of workspaces) {
38
40
  const { name, dir, ancestors, pkgName, manifestPath: filePath } = workspace;
39
41
  streamer.cast('Analyzing workspace', name);
@@ -57,7 +59,6 @@ export async function build({ chief, collector, deputy, factory, isGitIgnored, s
57
59
  dependencies,
58
60
  getReferencedInternalFilePath: (input) => getReferencedInternalFilePath(input, workspace),
59
61
  findWorkspaceByFilePath: chief.findWorkspaceByFilePath.bind(chief),
60
- rootIgnore: chief.config.ignore,
61
62
  negatedWorkspacePatterns: chief.getNegatedWorkspacePatterns(name),
62
63
  ignoredWorkspacePatterns: chief.getIgnoredWorkspacesFor(name),
63
64
  enabledPluginsInAncestors: ancestors.flatMap(ancestor => enabledPluginsStore.get(ancestor) ?? []),
@@ -72,9 +73,9 @@ export async function build({ chief, collector, deputy, factory, isGitIgnored, s
72
73
  for (const id of definitionPaths)
73
74
  inputs.add(toProductionEntry(id, { containingFilePath: tsConfigFilePath }));
74
75
  }
75
- const ignore = worker.getIgnorePatterns();
76
76
  const sharedGlobOptions = { cwd: options.cwd, dir, gitignore: options.gitignore };
77
- collector.addIgnorePatterns(ignore.map(pattern => join(options.cwd, pattern)));
77
+ collector.addIgnorePatterns(config.ignore.map(p => join(options.cwd, prependDirToPattern(name, p))));
78
+ collector.addIgnoreFilesPatterns(config.ignoreFiles.map(p => join(options.cwd, prependDirToPattern(name, p))));
78
79
  const entrySpecifiersFromManifest = getEntrySpecifiersFromManifest(manifest);
79
80
  for (const filePath of await toSourceFilePaths(entrySpecifiersFromManifest, dir, extensionGlobStr)) {
80
81
  inputs.add(toProductionEntry(filePath));
@@ -10,16 +10,24 @@ const config = [
10
10
  ...toLilconfig('lint-staged'),
11
11
  ...toLilconfig('lintstaged'),
12
12
  ];
13
+ const resolveEntry = async (value) => {
14
+ if (Array.isArray(value))
15
+ return (await Promise.all(value.map(resolveEntry))).flat();
16
+ if (typeof value === 'function')
17
+ return [await value([])].flat().filter(item => typeof item === 'string');
18
+ return typeof value === 'string' ? [value] : [];
19
+ };
13
20
  const resolveConfig = async (config, options) => {
14
21
  if (options.isProduction)
15
22
  return [];
16
- if (typeof config === 'function')
17
- config = config();
18
- if (!config)
23
+ const cfg = typeof config === 'function' ? await config([]) : config;
24
+ if (!cfg)
19
25
  return [];
20
26
  const inputs = new Set();
21
- for (const entry of Object.values(config).flat()) {
22
- const scripts = [typeof entry === 'function' ? await entry([]) : entry].flat();
27
+ for (const [key, entry] of Object.entries(cfg)) {
28
+ if (key.startsWith('_'))
29
+ continue;
30
+ const scripts = await resolveEntry(entry);
23
31
  for (const id of options.getInputsFromScripts(scripts))
24
32
  inputs.add(id);
25
33
  }
@@ -1,5 +1,10 @@
1
- type Script = string | string[];
2
- type Entry = Script | ((filenames: string[]) => Script | Promise<Script>);
3
- type Config = Record<string, Entry>;
4
- export type LintStagedConfig = Config | (() => Config);
1
+ type SyncGenerateTask = (stagedFileNames: readonly string[]) => string | string[];
2
+ type AsyncGenerateTask = (stagedFileNames: readonly string[]) => Promise<string | string[]>;
3
+ type GenerateTask = SyncGenerateTask | AsyncGenerateTask;
4
+ type TaskFunction = {
5
+ title: string;
6
+ task: (stagedFileNames: readonly string[]) => void | Promise<void>;
7
+ };
8
+ export type Entry = string | TaskFunction | GenerateTask | (string | GenerateTask)[];
9
+ export type LintStagedConfig = Record<string, Entry> | GenerateTask;
5
10
  export {};
@@ -37,7 +37,7 @@ const args = {
37
37
  alias: { project: ['p'] },
38
38
  config: [['project', (p) => (p.endsWith('.json') ? p : join(p, 'tsconfig.json'))]],
39
39
  };
40
- const note = '[Whats up with that configurable tsconfig.json location?](/reference/faq#whats-up-with-that-configurable-tsconfigjson-location)';
40
+ const note = "[What's up with that configurable tsconfig.json location?](/reference/faq#whats-up-with-that-configurable-tsconfigjson-location)";
41
41
  export const docs = { note };
42
42
  export default {
43
43
  title,
@@ -97,6 +97,8 @@ export const findWebpackDependenciesFromConfig = async (config, options) => {
97
97
  entries.push(entry['filename']);
98
98
  }
99
99
  }
100
+ if (entries.length === 0 && opts.context)
101
+ entries.push('./src/index');
100
102
  for (const entry of entries) {
101
103
  if (isInternal(entry)) {
102
104
  const dir = opts.context ? opts.context : cwd;
@@ -1147,6 +1147,7 @@ export declare const knipConfigurationSchema: z.ZodMiniObject<{
1147
1147
  project: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1148
1148
  paths: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>>>;
1149
1149
  ignore: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1150
+ ignoreFiles: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1150
1151
  ignoreBinaries: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
1151
1152
  ignoreDependencies: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
1152
1153
  ignoreMembers: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
@@ -1165,6 +1166,7 @@ export declare const knipConfigurationSchema: z.ZodMiniObject<{
1165
1166
  project: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1166
1167
  paths: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>>>;
1167
1168
  ignore: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1169
+ ignoreFiles: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>>;
1168
1170
  ignoreBinaries: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
1169
1171
  ignoreDependencies: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
1170
1172
  ignoreMembers: z.ZodMiniOptional<z.ZodMiniArray<z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniCustom<RegExp, RegExp>]>>>;
@@ -39,6 +39,7 @@ const rootConfigurationSchema = z.object({
39
39
  project: z.optional(globSchema),
40
40
  paths: z.optional(pathsSchema),
41
41
  ignore: z.optional(globSchema),
42
+ ignoreFiles: z.optional(globSchema),
42
43
  ignoreBinaries: z.optional(stringOrRegexSchema),
43
44
  ignoreDependencies: z.optional(stringOrRegexSchema),
44
45
  ignoreMembers: z.optional(stringOrRegexSchema),
@@ -61,6 +62,7 @@ const baseWorkspaceConfigurationSchema = z.object({
61
62
  project: z.optional(globSchema),
62
63
  paths: z.optional(pathsSchema),
63
64
  ignore: z.optional(globSchema),
65
+ ignoreFiles: z.optional(globSchema),
64
66
  ignoreBinaries: z.optional(stringOrRegexSchema),
65
67
  ignoreDependencies: z.optional(stringOrRegexSchema),
66
68
  ignoreMembers: z.optional(stringOrRegexSchema),
@@ -34,6 +34,7 @@ export type GetImportsAndExportsOptions = {
34
34
  };
35
35
  export interface Configuration {
36
36
  ignore: NormalizedGlob;
37
+ ignoreFiles: NormalizedGlob;
37
38
  ignoreBinaries: IgnorePatterns;
38
39
  ignoreDependencies: IgnorePatterns;
39
40
  ignoreExportsUsedInFile: IgnoreExportsUsedInFile;
@@ -56,6 +57,7 @@ interface BaseWorkspaceConfiguration {
56
57
  project: NormalizedGlob;
57
58
  paths: Record<string, string[]>;
58
59
  ignore: NormalizedGlob;
60
+ ignoreFiles: NormalizedGlob;
59
61
  isIncludeEntryExports: boolean;
60
62
  }
61
63
  type PluginConfiguration = EnsuredPluginConfiguration | boolean;
@@ -34,7 +34,7 @@ export declare const getLineAndCharacterOfPosition: (node: ts.Node, pos: number)
34
34
  };
35
35
  export declare const getAccessMembers: (typeChecker: ts.TypeChecker, node: ts.Identifier) => string[];
36
36
  export declare const isDestructuring: (node: ts.Node) => boolean;
37
- export declare const getDestructuredIds: (name: ts.ObjectBindingPattern) => string[];
37
+ export declare const getDestructuredNames: (name: ts.ObjectBindingPattern) => [string[], boolean];
38
38
  export declare const isConsiderReferencedNS: (node: ts.Identifier) => boolean;
39
39
  export declare const isObjectEnumerationCallExpressionArgument: (node: ts.Identifier) => boolean;
40
40
  export declare const isInForIteration: (node: ts.Node) => boolean;
@@ -169,7 +169,18 @@ export const isDestructuring = (node) => node.parent &&
169
169
  ts.isVariableDeclaration(node.parent) &&
170
170
  ts.isVariableDeclarationList(node.parent.parent) &&
171
171
  ts.isObjectBindingPattern(node.parent.name);
172
- export const getDestructuredIds = (name) => name.elements.map(element => element.name.getText());
172
+ export const getDestructuredNames = (name) => {
173
+ const members = [];
174
+ let hasSpread = false;
175
+ for (const element of name.elements) {
176
+ if (element.dotDotDotToken) {
177
+ hasSpread = true;
178
+ break;
179
+ }
180
+ members.push(element.name.getText());
181
+ }
182
+ return [members, hasSpread];
183
+ };
173
184
  export const isConsiderReferencedNS = (node) => ts.isPropertyAssignment(node.parent) ||
174
185
  ts.isShorthandPropertyAssignment(node.parent) ||
175
186
  (ts.isCallExpression(node.parent) && node.parent.arguments.includes(node)) ||
@@ -6,7 +6,7 @@ import { getPackageNameFromFilePath, isStartsLikePackageName, sanitizeSpecifier
6
6
  import { timerify } from '../util/Performance.js';
7
7
  import { isInNodeModules } from '../util/path.js';
8
8
  import { shouldIgnore } from '../util/tag.js';
9
- import { getAccessMembers, getDestructuredIds, getJSDocTags, getLineAndCharacterOfPosition, getTypeRef, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, isInForIteration, isObjectEnumerationCallExpressionArgument, isReferencedInExport, } from './ast-helpers.js';
9
+ import { getAccessMembers, getDestructuredNames, getJSDocTags, getLineAndCharacterOfPosition, getTypeRef, isAccessExpression, isConsiderReferencedNS, isDestructuring, isImportSpecifier, isInForIteration, isObjectEnumerationCallExpressionArgument, isReferencedInExport, } from './ast-helpers.js';
10
10
  import { findInternalReferences, isType } from './find-internal-references.js';
11
11
  import getDynamicImportVisitors from './visitors/dynamic-imports/index.js';
12
12
  import getExportVisitors from './visitors/exports/index.js';
@@ -259,9 +259,14 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
259
259
  if (ts.isPropertyAccessExpression(node.parent)) {
260
260
  const ns = String(symbol.escapedName);
261
261
  const key = String(node.parent.name.escapedText);
262
- const members = getDestructuredIds(node.parent.parent.name).map(n => `${key}.${n}`);
263
- addNsMemberRefs(imports, ns, key);
264
- addNsMemberRefs(imports, ns, members);
262
+ const [members, hasSpread] = getDestructuredNames(node.parent.parent.name);
263
+ if (hasSpread)
264
+ imports.refs.add(id);
265
+ else {
266
+ const ids = members.map(id => `${key}.${id}`);
267
+ addNsMemberRefs(imports, ns, key);
268
+ addNsMemberRefs(imports, ns, ids);
269
+ }
265
270
  }
266
271
  }
267
272
  else {
@@ -270,8 +275,11 @@ const getImportsAndExports = (sourceFile, resolveModule, typeChecker, options, i
270
275
  }
271
276
  }
272
277
  else if (isDestructuring(node)) {
273
- const members = getDestructuredIds(node.parent.name);
274
- addNsMemberRefs(imports, id, members);
278
+ const [members, hasSpread] = getDestructuredNames(node.parent.name);
279
+ if (hasSpread)
280
+ imports.refs.add(id);
281
+ else
282
+ addNsMemberRefs(imports, id, members);
275
283
  }
276
284
  else {
277
285
  const typeRef = getTypeRef(node);
@@ -1183,6 +1183,7 @@ export declare const createOptions: (options: CreateOptions) => Promise<{
1183
1183
  project?: string | string[] | undefined;
1184
1184
  paths?: Record<string, string[]> | undefined;
1185
1185
  ignore?: string | string[] | undefined;
1186
+ ignoreFiles?: string | string[] | undefined;
1186
1187
  ignoreBinaries?: (string | RegExp)[] | undefined;
1187
1188
  ignoreDependencies?: (string | RegExp)[] | undefined;
1188
1189
  ignoreMembers?: (string | RegExp)[] | undefined;
@@ -1197,6 +1198,7 @@ export declare const createOptions: (options: CreateOptions) => Promise<{
1197
1198
  project?: string | string[] | undefined;
1198
1199
  paths?: Record<string, string[]> | undefined;
1199
1200
  ignore?: string | string[] | undefined;
1201
+ ignoreFiles?: string | string[] | undefined;
1200
1202
  ignoreBinaries?: (string | RegExp)[] | undefined;
1201
1203
  ignoreDependencies?: (string | RegExp)[] | undefined;
1202
1204
  ignoreMembers?: (string | RegExp)[] | undefined;
@@ -97,7 +97,7 @@ export const createOptions = async (options) => {
97
97
  isShowProgress: parsedCLIArgs['no-progress'] === false && process.stdout.isTTY && typeof process.stdout.cursorTo === 'function',
98
98
  isSkipLibs: !(isIncludeLibs || includedIssueTypes.classMembers),
99
99
  isStrict,
100
- isTrace: parsedCLIArgs.trace ?? false,
100
+ isTrace: Boolean(parsedCLIArgs.trace ?? parsedCLIArgs['trace-file'] ?? parsedCLIArgs['trace-export']),
101
101
  isTreatConfigHintsAsErrors: parsedCLIArgs['treat-config-hints-as-errors'] ?? parsedConfig.treatConfigHintsAsErrors ?? false,
102
102
  isWatch: parsedCLIArgs.watch ?? rest.isWatch ?? false,
103
103
  parsedConfig,
package/dist/util/fs.js CHANGED
@@ -48,7 +48,7 @@ export const loadTOML = async (filePath) => {
48
48
  };
49
49
  export const parseJSON = async (filePath, contents) => {
50
50
  try {
51
- return JSON.parse(stripJsonComments(contents, { trailingCommas: true }));
51
+ return JSON.parse(stripJsonComments(contents, { trailingCommas: true, whitespace: false }));
52
52
  }
53
53
  catch (error) {
54
54
  throw new LoaderError(`Error parsing ${filePath}`, { cause: error });
@@ -154,14 +154,15 @@ export async function glob(_patterns, options) {
154
154
  }
155
155
  if (willCache)
156
156
  cachedGlobIgnores.set(options.dir, compact(_ignore));
157
- const ignorePatterns = (cachedIgnores || _ignore).concat(negatedPatterns);
157
+ const ignorePatterns = (cachedIgnores || _ignore).concat(negatedPatterns.map(pattern => pattern.slice(1)));
158
158
  const { dir, label, ...fgOptions } = { ...options, ignore: ignorePatterns };
159
159
  const paths = await fg.glob(patterns, fgOptions);
160
- const name = relative(options.cwd, dir) || ROOT_WORKSPACE_NAME;
161
- debugLogObject(name, label ? `Finding ${label}` : 'Finding paths', () => ({
160
+ debugLogObject(relative(options.cwd, dir) || ROOT_WORKSPACE_NAME, label ? `Finding ${label}` : 'Finding paths', () => ({
162
161
  patterns,
163
162
  ...fgOptions,
164
- ignore: hasCache ? `// using cache from ${name}` : ignorePatterns,
163
+ ignore: hasCache && ignorePatterns.length === (cachedIgnores || _ignore).length
164
+ ? `// using cache from previous glob cwd: ${fgOptions.cwd}`
165
+ : ignorePatterns,
165
166
  paths,
166
167
  }));
167
168
  return paths;
@@ -21,7 +21,7 @@ const load = async (filePath) => {
21
21
  if (ext === '' && isInternal(filePath)) {
22
22
  return await loadFile(filePath);
23
23
  }
24
- if (ext === '.json' || ext === '.jsonc') {
24
+ if (ext === '.json' || ext === '.jsonc' || ext === '.json5') {
25
25
  return await loadJSON(filePath);
26
26
  }
27
27
  if (typeof Bun !== 'undefined') {
@@ -33,7 +33,7 @@ const renderTrace = (node, level = 0, levels = new Set()) => {
33
33
  export const printTrace = (node, filePath, options, identifier) => {
34
34
  if (!options.isTrace)
35
35
  return;
36
- if (options.traceExport && identifier && identifier !== options.traceExport)
36
+ if (options.traceExport && identifier !== options.traceExport)
37
37
  return;
38
38
  if (options.traceFile && filePath !== toAbsolute(options.traceFile, options.cwd))
39
39
  return;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "5.64.2";
1
+ export declare const version = "5.65.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '5.64.2';
1
+ export const version = '5.65.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "5.64.2",
3
+ "version": "5.65.0",
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": {
package/schema.json CHANGED
@@ -22,27 +22,32 @@
22
22
  ],
23
23
  "properties": {
24
24
  "ignoreBinaries": {
25
- "title": "Binaries to ignore (regex allowed)",
25
+ "title": "Binaries to exclude from the report (regex allowed)",
26
26
  "examples": ["rm", "docker-compose", "curl"],
27
27
  "$ref": "#/definitions/list"
28
28
  },
29
29
  "ignoreDependencies": {
30
- "title": "Dependencies from package.json to ignore (regex allowed)",
30
+ "title": "Dependencies to exclude from the report (regex allowed)",
31
+ "examples": ["husky", "lint-staged"],
32
+ "$ref": "#/definitions/list"
33
+ },
34
+ "ignoreFiles": {
35
+ "title": "Unused files to exclude from the report",
31
36
  "examples": ["husky", "lint-staged"],
32
37
  "$ref": "#/definitions/list"
33
38
  },
34
39
  "ignoreMembers": {
35
- "title": "Class and enum members to ignore (regex allowed)",
40
+ "title": "Class and enum members to exclude from the report (regex allowed)",
36
41
  "examples": ["render", "on.*"],
37
42
  "$ref": "#/definitions/list"
38
43
  },
39
44
  "ignoreUnresolved": {
40
- "title": "Unresolved imports to ignore (regex allowed)",
45
+ "title": "Unresolved imports to exclude from the report (regex allowed)",
41
46
  "examples": ["#/virtual"],
42
47
  "$ref": "#/definitions/list"
43
48
  },
44
49
  "ignoreWorkspaces": {
45
- "title": "Workspaces to ignore",
50
+ "title": "Workspaces to exclude from the report",
46
51
  "examples": ["packages/ignore-me"],
47
52
  "$ref": "#/definitions/list"
48
53
  },
@@ -240,23 +245,29 @@
240
245
  "$ref": "#/definitions/paths"
241
246
  },
242
247
  "ignore": {
243
- "title": "Files to ignore in the analysis.",
244
- "example": ["**/fixtures", "mocks"],
248
+ "title": "Files to exclude from the report (any issue type)",
249
+ "example": ["**/fixtures/**", "mocks/**"],
250
+ "default": [],
251
+ "$ref": "#/definitions/globPatterns"
252
+ },
253
+ "ignoreFiles": {
254
+ "title": "Unused files to exclude the report",
255
+ "example": ["**/fixtures/**", "mocks/**"],
245
256
  "default": [],
246
257
  "$ref": "#/definitions/globPatterns"
247
258
  },
248
259
  "ignoreBinaries": {
249
- "title": "Binaries to ignore (regex allowed)",
260
+ "title": "Binaries to exclude from the report (regex allowed)",
250
261
  "examples": ["rm", "docker-compose", "curl"],
251
262
  "$ref": "#/definitions/list"
252
263
  },
253
264
  "ignoreDependencies": {
254
- "title": "Dependencies from package.json to ignore (regex allowed)",
265
+ "title": "Dependencies to exclude from the report (regex allowed)",
255
266
  "examples": ["husky", "lint-staged"],
256
267
  "$ref": "#/definitions/list"
257
268
  },
258
269
  "ignoreUnresolved": {
259
- "title": "Unresolved imports to ignore (regex allowed)",
270
+ "title": "Unresolved imports to exclude from the report (regex allowed)",
260
271
  "examples": ["#/virtual"],
261
272
  "$ref": "#/definitions/list"
262
273
  },