knip 2.30.0 → 2.31.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
@@ -80,7 +80,9 @@ For updates or questions, come hang out in [The Knip Barn (Discord)][10], or fol
80
80
 
81
81
  ### Installation
82
82
 
83
- npm install -D knip
83
+ ```
84
+ npm install -D knip
85
+ ```
84
86
 
85
87
  Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.6.
86
88
 
@@ -445,7 +447,9 @@ In a more fine-grained manner, you can also ignore only specific issue types:
445
447
 
446
448
  When a repository is self-contained or private, you may want to include entry files when reporting unused exports:
447
449
 
448
- knip --include-entry-exports
450
+ ```
451
+ knip --include-entry-exports
452
+ ```
449
453
 
450
454
  Knip will also report unused exports in entry source files and scripts (such as those referenced in `package.json`). But
451
455
  not in entry and configuration files from plugins, such as `next.config.js` or `src/routes/+page.svelte`.
@@ -587,12 +591,16 @@ Alternatively, they can be added to the configuration (e.g. `"exclude": ["depend
587
591
 
588
592
  Use `--include` to report only specific issue types (the following example commands do the same):
589
593
 
590
- knip --include files --include dependencies
591
- knip --include files,dependencies
594
+ ```
595
+ knip --include files --include dependencies
596
+ knip --include files,dependencies
597
+ ```
592
598
 
593
599
  Use `--exclude` to ignore reports you're not interested in:
594
600
 
595
- knip --include files --exclude classMembers,enumMembers
601
+ ```
602
+ knip --include files --exclude classMembers,enumMembers
603
+ ```
596
604
 
597
605
  Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
598
606
 
@@ -660,7 +668,8 @@ Knip takes the following JSDoc/TSDoc tags into account:
660
668
 
661
669
  ## Command Line Options
662
670
 
663
- $ npx knip --help
671
+ ```
672
+ $ npx knip --help
664
673
  ✂️ Find unused files, dependencies and exports in your JavaScript and TypeScript projects
665
674
 
666
675
  Usage: knip [options]
@@ -704,6 +713,7 @@ Knip takes the following JSDoc/TSDoc tags into account:
704
713
  $ knip --debug --debug-file-filter '(specific|particular)-module'
705
714
 
706
715
  More documentation and bug reports: https://github.com/webpro/knip
716
+ ```
707
717
 
708
718
  ## Potential boost with `--no-gitignore`
709
719
 
@@ -739,15 +749,19 @@ Below some similar commands to get another idea of what Knip does in comparison:
739
749
 
740
750
  The following commands are similar:
741
751
 
742
- depcheck
743
- knip --dependencies
752
+ ```
753
+ depcheck
754
+ knip --dependencies
755
+ ```
744
756
 
745
757
  ### unimported
746
758
 
747
759
  The following commands are similar:
748
760
 
749
- unimported
750
- knip --production --dependencies --include files
761
+ ```
762
+ unimported
763
+ knip --production --dependencies --include files
764
+ ```
751
765
 
752
766
  Also see [production mode][25].
753
767
 
@@ -755,17 +769,21 @@ Also see [production mode][25].
755
769
 
756
770
  The following commands are similar:
757
771
 
758
- ts-unused-exports
759
- knip --include exports,types,nsExports,nsTypes
760
- knip --exports # Adds unused enum and class members
772
+ ```
773
+ ts-unused-exports
774
+ knip --include exports,types,nsExports,nsTypes
775
+ knip --exports # Adds unused enum and class members
776
+ ```
761
777
 
762
778
  ### ts-prune
763
779
 
764
780
  The following commands are similar:
765
781
 
766
- ts-prune
767
- knip --include exports,types
768
- knip --exports # Adds unused exports/types in namespaces and unused enum/class members
782
+ ```
783
+ ts-prune
784
+ knip --include exports,types
785
+ knip --exports # Adds unused exports/types in namespaces and unused enum/class members
786
+ ```
769
787
 
770
788
  ## Projects using Knip
771
789
 
@@ -27,6 +27,7 @@ export declare class ConfigurationChief {
27
27
  enabledWorkspaces: Workspace[];
28
28
  localWorkspaces: Set<string>;
29
29
  resolvedConfigFilePath?: string;
30
+ rawConfig?: any;
30
31
  constructor({ cwd, isProduction }: ConfigurationManagerOptions);
31
32
  init(): Promise<void>;
32
33
  getCompilers(): [SyncCompilers, AsyncCompilers];
@@ -36,6 +37,7 @@ export declare class ConfigurationChief {
36
37
  private getListedWorkspaces;
37
38
  private getIgnoredWorkspacePatterns;
38
39
  private getManifestWorkspaces;
40
+ private getConfiguredWorkspaceKeys;
39
41
  private getAdditionalWorkspaceNames;
40
42
  private getAvailableWorkspaceNames;
41
43
  private getEnabledWorkspaces;
@@ -41,9 +41,8 @@ const defaultConfig = {
41
41
  ignoreWorkspaces: [],
42
42
  syncCompilers: new Map(),
43
43
  asyncCompilers: new Map(),
44
- workspaces: {
45
- [ROOT_WORKSPACE_NAME]: getDefaultWorkspaceConfig(),
46
- },
44
+ defaultWorkspaceConfig: getDefaultWorkspaceConfig(),
45
+ rootPluginConfigs: {},
47
46
  };
48
47
  const PLUGIN_NAMES = Object.keys(plugins);
49
48
  export class ConfigurationChief {
@@ -60,6 +59,7 @@ export class ConfigurationChief {
60
59
  enabledWorkspaces = [];
61
60
  localWorkspaces = new Set();
62
61
  resolvedConfigFilePath;
62
+ rawConfig;
63
63
  constructor({ cwd, isProduction }) {
64
64
  this.cwd = cwd;
65
65
  this.isProduction = isProduction;
@@ -86,11 +86,9 @@ export class ConfigurationChief {
86
86
  if (rawConfigArg && !this.resolvedConfigFilePath && !manifest.knip) {
87
87
  throw new ConfigurationError(`Unable to find ${rawConfigArg} or package.json#knip`);
88
88
  }
89
- const rawLocalConfig = this.resolvedConfigFilePath ? await _load(this.resolvedConfigFilePath) : manifest.knip;
90
- if (rawLocalConfig) {
91
- const parsedConfig = ConfigurationValidator.parse(partitionCompilers(rawLocalConfig));
92
- this.config = this.normalize(parsedConfig);
93
- }
89
+ this.rawConfig = this.resolvedConfigFilePath ? await _load(this.resolvedConfigFilePath) : manifest.knip;
90
+ const parsedConfig = this.rawConfig ? ConfigurationValidator.parse(partitionCompilers(this.rawConfig)) : {};
91
+ this.config = this.normalize(parsedConfig);
94
92
  await this.setWorkspaces();
95
93
  }
96
94
  getCompilers() {
@@ -99,58 +97,25 @@ export class ConfigurationChief {
99
97
  getRules() {
100
98
  return this.config.rules;
101
99
  }
102
- normalize(rawLocalConfig) {
103
- const initialWorkspaces = rawLocalConfig.workspaces ?? {
104
- [ROOT_WORKSPACE_NAME]: {
105
- ...rawLocalConfig,
106
- },
107
- };
108
- const rules = { ...defaultRules, ...rawLocalConfig.rules };
109
- const include = rawLocalConfig.include ?? defaultConfig.include;
110
- const exclude = rawLocalConfig.exclude ?? defaultConfig.exclude;
111
- const ignore = arrayify(rawLocalConfig.ignore ?? defaultConfig.ignore);
112
- const ignoreBinaries = rawLocalConfig.ignoreBinaries ?? [];
113
- const ignoreExportsUsedInFile = rawLocalConfig.ignoreExportsUsedInFile ?? false;
114
- const ignoreDependencies = rawLocalConfig.ignoreDependencies ?? [];
115
- const ignoreWorkspaces = rawLocalConfig.ignoreWorkspaces ?? defaultConfig.ignoreWorkspaces;
116
- const { syncCompilers, asyncCompilers } = rawLocalConfig;
100
+ normalize(rawConfig) {
101
+ const rules = { ...defaultRules, ...rawConfig.rules };
102
+ const include = rawConfig.include ?? defaultConfig.include;
103
+ const exclude = rawConfig.exclude ?? defaultConfig.exclude;
104
+ const ignore = arrayify(rawConfig.ignore ?? defaultConfig.ignore);
105
+ const ignoreBinaries = rawConfig.ignoreBinaries ?? [];
106
+ const ignoreExportsUsedInFile = rawConfig.ignoreExportsUsedInFile ?? false;
107
+ const ignoreDependencies = rawConfig.ignoreDependencies ?? [];
108
+ const ignoreWorkspaces = rawConfig.ignoreWorkspaces ?? defaultConfig.ignoreWorkspaces;
109
+ const { syncCompilers, asyncCompilers } = rawConfig;
117
110
  const extensions = [...Object.keys(syncCompilers ?? {}), ...Object.keys(asyncCompilers ?? {})];
118
111
  const defaultWorkspaceConfig = getDefaultWorkspaceConfig(extensions);
119
112
  const rootPluginConfigs = {};
120
- for (const [name, pluginConfig] of Object.entries(rawLocalConfig)) {
113
+ for (const [name, pluginConfig] of Object.entries(rawConfig)) {
121
114
  const pluginName = toCamelCase(name);
122
115
  if (PLUGIN_NAMES.includes(pluginName)) {
123
116
  rootPluginConfigs[pluginName] = normalizePluginConfig(pluginConfig);
124
117
  }
125
118
  }
126
- const workspaces = Object.entries(initialWorkspaces)
127
- .filter(([workspaceName]) => !ignoreWorkspaces.includes(workspaceName))
128
- .reduce((workspaces, workspace) => {
129
- const [workspaceName, workspaceConfig] = workspace;
130
- const entry = workspaceConfig.entry ? arrayify(workspaceConfig.entry) : defaultWorkspaceConfig.entry;
131
- const project = workspaceConfig.project ? arrayify(workspaceConfig.project) : defaultWorkspaceConfig.project;
132
- const paths = workspaceConfig.paths ?? defaultWorkspaceConfig.paths;
133
- workspaces[workspaceName] = {
134
- entry,
135
- project,
136
- paths,
137
- ignore: arrayify(workspaceConfig.ignore),
138
- ignoreBinaries: arrayify(workspaceConfig.ignoreBinaries),
139
- ignoreDependencies: arrayify(workspaceConfig.ignoreDependencies),
140
- };
141
- for (const [name, pluginConfig] of Object.entries(rootPluginConfigs)) {
142
- const pluginName = toCamelCase(name);
143
- if (pluginConfig)
144
- workspaces[workspaceName][pluginName] = pluginConfig;
145
- }
146
- for (const [name, pluginConfig] of Object.entries(workspaceConfig)) {
147
- const pluginName = toCamelCase(name);
148
- if (PLUGIN_NAMES.includes(pluginName)) {
149
- workspaces[workspaceName][pluginName] = normalizePluginConfig(pluginConfig);
150
- }
151
- }
152
- return workspaces;
153
- }, {});
154
119
  return {
155
120
  rules,
156
121
  include,
@@ -162,7 +127,8 @@ export class ConfigurationChief {
162
127
  ignoreWorkspaces,
163
128
  syncCompilers: new Map(Object.entries(syncCompilers ?? {})),
164
129
  asyncCompilers: new Map(Object.entries(asyncCompilers ?? {})),
165
- workspaces,
130
+ rootPluginConfigs,
131
+ defaultWorkspaceConfig,
166
132
  };
167
133
  }
168
134
  async setWorkspaces() {
@@ -197,14 +163,21 @@ export class ConfigurationChief {
197
163
  });
198
164
  const manifestWorkspaces = new Map();
199
165
  for (const [pkgName, dir] of workspaces.entries()) {
200
- manifestWorkspaces.set(relative(this.cwd, dir), pkgName);
166
+ manifestWorkspaces.set(relative(this.cwd, dir) || ROOT_WORKSPACE_NAME, pkgName);
201
167
  }
202
168
  return manifestWorkspaces;
203
169
  }
170
+ getConfiguredWorkspaceKeys() {
171
+ const initialWorkspaces = this.rawConfig?.workspaces
172
+ ? Object.keys(this.rawConfig.workspaces)
173
+ : [ROOT_WORKSPACE_NAME];
174
+ const ignoreWorkspaces = this.rawConfig?.ignoreWorkspaces ?? defaultConfig.ignoreWorkspaces;
175
+ return initialWorkspaces.filter(workspaceName => !ignoreWorkspaces.includes(workspaceName));
176
+ }
204
177
  async getAdditionalWorkspaceNames() {
205
- const additionalWorkspaceKeys = Object.keys(this.config.workspaces);
206
- const patterns = additionalWorkspaceKeys.filter(key => key.includes('*'));
207
- const dirs = additionalWorkspaceKeys.filter(key => !key.includes('*'));
178
+ const workspaceKeys = this.getConfiguredWorkspaceKeys();
179
+ const patterns = workspaceKeys.filter(key => key.includes('*'));
180
+ const dirs = workspaceKeys.filter(key => !key.includes('*'));
208
181
  const globbedDirs = await _dirGlob({ patterns, cwd: this.cwd });
209
182
  return new Set([...dirs, ...globbedDirs].filter(name => name !== ROOT_WORKSPACE_NAME &&
210
183
  !this.manifestWorkspaces.has(name) &&
@@ -260,16 +233,39 @@ export class ConfigurationChief {
260
233
  .map(workspaceName => `!${workspaceName}`);
261
234
  }
262
235
  getConfigKeyForWorkspace(workspaceName) {
263
- return Object.keys(this.config.workspaces)
236
+ return this.getConfiguredWorkspaceKeys()
264
237
  .sort(byPathDepth)
265
238
  .reverse()
266
239
  .find(pattern => micromatch.isMatch(workspaceName, pattern));
267
240
  }
268
241
  getConfigForWorkspace(workspaceName) {
269
242
  const key = this.getConfigKeyForWorkspace(workspaceName);
270
- if (key && this.config?.workspaces?.[key])
271
- return this.config.workspaces[key];
272
- return getDefaultWorkspaceConfig();
243
+ const defaultConfig = this.config.defaultWorkspaceConfig;
244
+ const workspaces = this.rawConfig?.workspaces ?? {};
245
+ const workspaceConfig = (key
246
+ ? key === ROOT_WORKSPACE_NAME && !(ROOT_WORKSPACE_NAME in workspaces)
247
+ ? this.rawConfig
248
+ : workspaces[key]
249
+ : {}) ?? {};
250
+ const entry = workspaceConfig.entry ? arrayify(workspaceConfig.entry) : defaultConfig.entry;
251
+ const project = workspaceConfig.project ? arrayify(workspaceConfig.project) : defaultConfig.project;
252
+ const paths = workspaceConfig.paths ?? defaultConfig.paths;
253
+ const ignore = arrayify(workspaceConfig.ignore);
254
+ const ignoreBinaries = arrayify(workspaceConfig.ignoreBinaries);
255
+ const ignoreDependencies = arrayify(workspaceConfig.ignoreDependencies);
256
+ const plugins = {};
257
+ for (const [name, pluginConfig] of Object.entries(this.config.rootPluginConfigs)) {
258
+ const pluginName = toCamelCase(name);
259
+ if (typeof pluginConfig !== 'undefined')
260
+ plugins[pluginName] = pluginConfig;
261
+ }
262
+ for (const [name, pluginConfig] of Object.entries(workspaceConfig)) {
263
+ const pluginName = toCamelCase(name);
264
+ if (PLUGIN_NAMES.includes(pluginName)) {
265
+ plugins[pluginName] = normalizePluginConfig(pluginConfig);
266
+ }
267
+ }
268
+ return { entry, project, paths, ignore, ignoreBinaries, ignoreDependencies, ...plugins };
273
269
  }
274
270
  getIssueTypesToReport() {
275
271
  const cliArgs = { include, exclude, dependencies, exports };