knip 2.3.2 → 2.6.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 +139 -133
- package/dist/ConfigurationChief.d.ts +15 -0
- package/dist/ConfigurationChief.js +10 -1
- package/dist/ConfigurationValidator.d.ts +6 -3
- package/dist/ConfigurationValidator.js +16 -0
- package/dist/IssueCollector.d.ts +12 -7
- package/dist/IssueCollector.js +7 -3
- package/dist/PrincipalFactory.d.ts +1 -3
- package/dist/PrincipalFactory.js +6 -6
- package/dist/ProjectPrincipal.d.ts +4 -5
- package/dist/ProjectPrincipal.js +2 -5
- package/dist/WorkspaceWorker.js +4 -3
- package/dist/binaries/resolvers/fallback.js +4 -2
- package/dist/binaries/resolvers/yarn.js +1 -0
- package/dist/cli.js +2 -2
- package/dist/constants.js +2 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +47 -42
- package/dist/issues/initializers.d.ts +2 -1
- package/dist/issues/initializers.js +1 -0
- package/dist/plugins/eslint/helpers.js +3 -3
- package/dist/plugins/eslint/types.d.ts +1 -1
- package/dist/plugins/jest/index.js +2 -3
- package/dist/reporters/codeowners.js +2 -2
- package/dist/reporters/compact.js +1 -1
- package/dist/reporters/symbols.js +7 -5
- package/dist/reporters/util.d.ts +4 -1
- package/dist/reporters/util.js +6 -4
- package/dist/types/config.d.ts +2 -0
- package/dist/types/issues.d.ts +3 -0
- package/dist/typescript/visitors/scripts/execa.d.ts +3 -0
- package/dist/typescript/visitors/scripts/execa.js +16 -0
- package/dist/typescript/visitors/scripts/index.js +2 -1
- package/dist/util/compilers.d.ts +2 -1
- package/dist/util/object.d.ts +1 -0
- package/dist/util/object.js +8 -0
- package/dist/util/path.d.ts +5 -4
- package/dist/util/path.js +6 -4
- package/dist/util/tsconfig-loader.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +10 -6
- package/schema.json +20 -0
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# ✂️ Knip
|
|
2
2
|
|
|
3
3
|
Knip finds **unused files, dependencies and exports** in your JavaScript and TypeScript projects. Less code and
|
|
4
|
-
dependencies
|
|
4
|
+
dependencies lead to improved performance, less maintenance and easier refactorings.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
7
|
export const myVar = true;
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
This is where ESLint stops: it handles files in isolation, so it does not know whether `myVar` is used somewhere else.
|
|
11
|
-
This is where Knip starts: it lints the project as a whole
|
|
11
|
+
This is where Knip starts: it lints the project as a whole and finds unused exports, files and dependencies.
|
|
12
12
|
|
|
13
|
-
It's only human to forget
|
|
13
|
+
It's only human to forget to remove things that you no longer use. But how do you find out? Where do you start finding
|
|
14
14
|
things that can be removed?
|
|
15
15
|
|
|
16
16
|
The dots don't connect themselves. This is where Knip comes in:
|
|
@@ -53,18 +53,18 @@ Well, almost, this is the full list of default extensions: `js`, `mjs`, `cjs`, `
|
|
|
53
53
|
|
|
54
54
|
### Entry Files
|
|
55
55
|
|
|
56
|
-
Knip looks for entry files at
|
|
56
|
+
Knip looks for entry files at those default locations, but also in other places:
|
|
57
57
|
|
|
58
58
|
- The `main`, `bin` and `exports` fields of `package.json`.
|
|
59
|
-
- [Plugins][2] such as for Next.js, Remix, Gatsby or Svelte
|
|
60
|
-
- The `scripts` in package.json may
|
|
59
|
+
- [Plugins][2] such as for Next.js, Remix, Gatsby or Svelte add entry files.
|
|
60
|
+
- The `scripts` in package.json or other scripts may provide entry files.
|
|
61
61
|
- Knip does this for each [workspace][1] it finds.
|
|
62
62
|
|
|
63
63
|
In other words, Knip looks in many places and you may not need much configuration. In a perfectly boring world where
|
|
64
64
|
everything is according to defaults you don't even need a `knip.json` file.
|
|
65
65
|
|
|
66
66
|
Larger projects tend to have more things customized, and therefore probably get more out of Knip with a configuration
|
|
67
|
-
file. Let's say you are using `.ts` files
|
|
67
|
+
file. Let's say you are using `.ts` files exclusively and have all source files only in the `src` directory:
|
|
68
68
|
|
|
69
69
|
```json
|
|
70
70
|
{
|
|
@@ -171,31 +171,63 @@ This example shows more output related to unused and unlisted dependencies:
|
|
|
171
171
|
|
|
172
172
|
The report contains the following types of issues:
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
174
|
+
| Title | Key | Description |
|
|
175
|
+
| :------------- | :---------------------------------- | :---------------------------------------------------- |
|
|
176
|
+
| `files` | Unused files | unable to find references to this file |
|
|
177
|
+
| `dependencies` | Unused dependencies | unable to find references to this dependency |
|
|
178
|
+
| `dependencies` | Unused devDependencies | unable to find references to this dependency |
|
|
179
|
+
| `unlisted` | Unlisted dependencies | used dependencies not listed in package.json _(1)_ |
|
|
180
|
+
| `unresolved` | Unresolved imports | unable to resolve this (import) specifier |
|
|
181
|
+
| `exports` | Unused exports | unable to find references to this export |
|
|
182
|
+
| `nsExports` | Unused exports in namespaces | unable to find direct references to this export _(2)_ |
|
|
183
|
+
| `types` | Unused exported types | unable to find references to this exported type |
|
|
184
|
+
| `nsTypes` | Unused exported types in namespaces | unable to find direct references to this export _(2)_ |
|
|
185
|
+
| `enumMembers` | Unused exported enum members | unable to find references to this enum member |
|
|
186
|
+
| `classMembers` | Unused exported class members | unable to find references to this class member |
|
|
187
|
+
| `duplicates` | Duplicate exports | the same thing is exported more than once |
|
|
186
188
|
|
|
187
189
|
When an issue type has zero issues, it is not shown.
|
|
188
190
|
|
|
189
|
-
|
|
190
|
-
name, but not always (e.g. `tsc` of `typescript` or `webpack` from `webpack-cli`).
|
|
191
|
+
Getting too many reported issues and false positives? Read more about [handling issues][8].
|
|
191
192
|
|
|
192
|
-
_(
|
|
193
|
+
_(1)_ If an unlisted dependency is prefixed with `bin:` it means the dependency containing that binary is unlisted. This
|
|
194
|
+
often equals the package name, but not always (e.g. `tsc` of `typescript` or `webpack` from `webpack-cli`).
|
|
195
|
+
|
|
196
|
+
_(2)_ The variable or type is not referenced directly and has become a member of a namespace. Knip can't find a
|
|
193
197
|
reference to it, so you can _probably_ remove it.
|
|
194
198
|
|
|
195
|
-
|
|
199
|
+
## Output
|
|
200
|
+
|
|
201
|
+
### Rules
|
|
202
|
+
|
|
203
|
+
Use `rules` in the configuration to customize the issue types that count towards the total error count, or to exclude
|
|
204
|
+
them altogether.
|
|
205
|
+
|
|
206
|
+
- `error` (default): printed, adds to total error count (similar to the `--include` filter)
|
|
207
|
+
- `warn`: printed in faded/grey color, does not add to error count (i.e. the exit code)
|
|
208
|
+
- `off`: not printed, does not add to error count (similar to the `--exclude` filter)
|
|
209
|
+
|
|
210
|
+
Example:
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
{
|
|
214
|
+
"rules": {
|
|
215
|
+
"files": "warn",
|
|
216
|
+
"classMembers": "off",
|
|
217
|
+
"duplicates": "off"
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
See [reading the report][9] for the list of issue types.
|
|
223
|
+
|
|
224
|
+
The rules are modeled after the ESLint `rules` configuration, and could be extended in the future. For instance, to
|
|
225
|
+
apply filters or configurations only to a specific issue type.
|
|
196
226
|
|
|
197
|
-
|
|
198
|
-
|
|
227
|
+
### Filters
|
|
228
|
+
|
|
229
|
+
You can `--include` or `--exclude` any of the reported issue types to slice & dice the report to your needs.
|
|
230
|
+
Alternatively, they can be added to the configuration (e.g. `"exclude": ["dependencies"]`).
|
|
199
231
|
|
|
200
232
|
Use `--include` to report only specific issue types (the following example commands do the same):
|
|
201
233
|
|
|
@@ -208,8 +240,16 @@ Use `--exclude` to ignore reports you're not interested in:
|
|
|
208
240
|
|
|
209
241
|
Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
|
|
210
242
|
|
|
211
|
-
|
|
212
|
-
|
|
243
|
+
See [reading the report][9] for the list of issue types.
|
|
244
|
+
|
|
245
|
+
### When to use rules or filters
|
|
246
|
+
|
|
247
|
+
Filters are meant to be used as command-line flags, rules allow for more fine-grained configuration.
|
|
248
|
+
|
|
249
|
+
- Rules are more fine-grained since they also have "warn".
|
|
250
|
+
- Rules could be extended in the future.
|
|
251
|
+
- Filters can be set in configuration and from CLI, rules only in configuration.
|
|
252
|
+
- Filters have two groups (`--dependencies` and `--types`), rules don't have any grouping.
|
|
213
253
|
|
|
214
254
|
## Ignore
|
|
215
255
|
|
|
@@ -226,11 +266,11 @@ There are a few ways to tell Knip to ignore certain packages, binaries, dependen
|
|
|
226
266
|
|
|
227
267
|
These can also be configured per workspace (except for `ignoreWorkspaces`).
|
|
228
268
|
|
|
229
|
-
##
|
|
269
|
+
## What's next?
|
|
230
270
|
|
|
231
271
|
This is the fun part! Knip, knip, knip ✂️
|
|
232
272
|
|
|
233
|
-
As always, make sure to
|
|
273
|
+
As always, make sure to back up files or use Git before deleting files or making changes. Run tests to verify results.
|
|
234
274
|
|
|
235
275
|
- Unused files can be removed.
|
|
236
276
|
- Unused dependencies can be removed from `package.json`.
|
|
@@ -242,9 +282,14 @@ As always, make sure to backup files or use Git before deleting files or making
|
|
|
242
282
|
|
|
243
283
|
🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to remove things!
|
|
244
284
|
|
|
245
|
-
|
|
285
|
+
Getting too many reported issues and false positives? Read more about [handling issues][8].
|
|
286
|
+
|
|
287
|
+
## Workspaces
|
|
246
288
|
|
|
247
|
-
Workspaces
|
|
289
|
+
Workspaces are handled out-of-the-box by Knip. Every workspace is part of the analysis.
|
|
290
|
+
|
|
291
|
+
Workspaces are sometimes also referred to as package-based monorepos, or as packages in a monorepo. Knip uses the term
|
|
292
|
+
workspace exclusively to indicate a directory that has a `package.json`.
|
|
248
293
|
|
|
249
294
|
Here's an example `knip.json` configuration with some custom `entry` and `project` patterns:
|
|
250
295
|
|
|
@@ -267,12 +312,9 @@ Here's an example `knip.json` configuration with some custom `entry` and `projec
|
|
|
267
312
|
```
|
|
268
313
|
|
|
269
314
|
It might be useful to run Knip first with no or little configuration to see where it needs custom `entry` and/or
|
|
270
|
-
`project` files.
|
|
271
|
-
|
|
272
|
-
Workspaces are sometimes also referred to as packages in a monorepo. Knip uses the term workspaces exclusively to
|
|
273
|
-
indicate the directories that have a `package.json`.
|
|
315
|
+
`project` files. Each workspace has the same [default configuration][10].
|
|
274
316
|
|
|
275
|
-
|
|
317
|
+
The root workspace is named `"."` under `workspaces` (like in the example).
|
|
276
318
|
|
|
277
319
|
Knip supports workspaces as defined in three possible locations:
|
|
278
320
|
|
|
@@ -283,15 +325,14 @@ Knip supports workspaces as defined in three possible locations:
|
|
|
283
325
|
Extra "workspaces" not configured as a workspace in the root `package.json` can be configured as well, Knip is happy to
|
|
284
326
|
analyze unused dependencies and exports from any directory with a `package.json`.
|
|
285
327
|
|
|
328
|
+
The `ignore`, `ignoreBinaries` and `ignoreDependencies` options are available inside workspace configurations.
|
|
329
|
+
|
|
286
330
|
Here's some example output when running Knip in a workspace:
|
|
287
331
|
|
|
288
332
|
<img src="./assets/screenshot-workspaces.png" alt="example output in workspaces" width="578">
|
|
289
333
|
|
|
290
334
|
Use `--debug` to get more verbose output.
|
|
291
335
|
|
|
292
|
-
Use `ignoreBinaries` and `ignoreDependencies` at the root of `knip.json` for global effect, or inside any workspace
|
|
293
|
-
config for local effect.
|
|
294
|
-
|
|
295
336
|
## Plugins
|
|
296
337
|
|
|
297
338
|
Plugins tell Knip where to look for configuration and entry files, and if necessary have a custom dependency finder.
|
|
@@ -353,8 +394,8 @@ Knip contains a growing list of plugins:
|
|
|
353
394
|
- [Webpack][plugin-webpack]
|
|
354
395
|
|
|
355
396
|
Plugins are automatically activated. Each plugin is automatically enabled based on simple heuristics. Most of them check
|
|
356
|
-
whether one of a few dependencies
|
|
357
|
-
|
|
397
|
+
whether one of a few dependencies is listed in `package.json`. Once enabled, they add a set of `config` files for
|
|
398
|
+
themselves and/or `entry` files for Knip to analyze.
|
|
358
399
|
|
|
359
400
|
- `config` files are given to the plugin's dependency finder
|
|
360
401
|
- `entry` files are given to Knip to include with the analysis of the rest of the source code
|
|
@@ -374,10 +415,10 @@ and can be analyzed like any other source file. That's why these configuration f
|
|
|
374
415
|
|
|
375
416
|
### Override plugin configuration
|
|
376
417
|
|
|
377
|
-
Usually no custom configuration is required for plugins, but if your project uses custom file locations then Knip
|
|
378
|
-
to override any defaults. Let's take Cypress for example. By default it uses `cypress.config.js`, but your
|
|
379
|
-
`config/cypress.js`. Also, the default pattern for test files is `cypress/e2e/**/*.cy.js`, but your project
|
|
380
|
-
`e2e-tests/*.spec.ts`. Here's how to configure this:
|
|
418
|
+
Usually, no custom configuration is required for plugins, but if your project uses custom file locations then Knip
|
|
419
|
+
allows you to override any defaults. Let's take Cypress for example. By default it uses `cypress.config.js`, but your
|
|
420
|
+
project uses `config/cypress.js`. Also, the default pattern for test files is `cypress/e2e/**/*.cy.js`, but your project
|
|
421
|
+
has them at `e2e-tests/*.spec.ts`. Here's how to configure this:
|
|
381
422
|
|
|
382
423
|
```json
|
|
383
424
|
{
|
|
@@ -389,9 +430,9 @@ to override any defaults. Let's take Cypress for example. By default it uses `cy
|
|
|
389
430
|
|
|
390
431
|
### Multi-project repositories
|
|
391
432
|
|
|
392
|
-
Some repositories have a single `package.json`, but consist of multiple projects with
|
|
393
|
-
|
|
394
|
-
Cypress configuration and test files. In that case, we could configure the Cypress plugin like this:
|
|
433
|
+
Some repositories have a single `package.json`, but consist of multiple projects with configuration files across the
|
|
434
|
+
repository (such as the [Nx "intregrated repo" style][11]). Let's assume some of these projects are apps and have their
|
|
435
|
+
own Cypress configuration and test files. In that case, we could configure the Cypress plugin like this:
|
|
395
436
|
|
|
396
437
|
```json
|
|
397
438
|
{
|
|
@@ -407,18 +448,18 @@ In case a plugin causes issues, it can be disabled by using `false` as its value
|
|
|
407
448
|
|
|
408
449
|
### Create a new plugin
|
|
409
450
|
|
|
410
|
-
Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][
|
|
451
|
+
Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][12]. This
|
|
411
452
|
guide also contains more details if you want to learn more about plugins and why they are useful.
|
|
412
453
|
|
|
413
454
|
## Compilers
|
|
414
455
|
|
|
415
|
-
Knip v2 introduces compilers
|
|
416
|
-
|
|
456
|
+
Knip v2 introduces compilers that allow to include files that are not JavaScript or TypeScript in the process of finding
|
|
457
|
+
unused or missing dependencies. For instance, `.mdx`, `.vue` and `.svelte` files come to mind.
|
|
417
458
|
|
|
418
|
-
Currently this is only supported by using `knip.js` or `knip.ts`. Provide a `compilers` object in the configuration
|
|
459
|
+
Currently, this is only supported by using `knip.js` or `knip.ts`. Provide a `compilers` object in the configuration
|
|
419
460
|
where each key represents the extension and the value is a function that takes the contents of these files as input and
|
|
420
461
|
returns JavaScript or TypeScript as output. Here is an example that compiles `.mdx` files to JavaScript so these files
|
|
421
|
-
and their imports and exports become part of the
|
|
462
|
+
and their imports and exports become part of the analysis:
|
|
422
463
|
|
|
423
464
|
```js
|
|
424
465
|
import { compileSync } from 'mdx-js/mdx';
|
|
@@ -430,13 +471,13 @@ export default {
|
|
|
430
471
|
};
|
|
431
472
|
```
|
|
432
473
|
|
|
433
|
-
Read [Compilers][
|
|
474
|
+
Read [Compilers][13] for more details and examples.
|
|
434
475
|
|
|
435
476
|
## Production Mode
|
|
436
477
|
|
|
437
478
|
The default mode for Knip is holistic and targets all project code, including configuration files and tests. Test files
|
|
438
|
-
usually import production files. This prevents the production files or
|
|
439
|
-
sometimes both of them can be removed. This is why Knip has a "production mode".
|
|
479
|
+
usually import production files. This prevents the production files or their exports from being reported as unused,
|
|
480
|
+
while sometimes both of them can be removed. This is why Knip has a "production mode".
|
|
440
481
|
|
|
441
482
|
To tell Knip what is production code, add an exclamation mark behind each `pattern!` that is meant for production and
|
|
442
483
|
use the `--production` flag. Here's an example:
|
|
@@ -451,7 +492,7 @@ use the `--production` flag. Here's an example:
|
|
|
451
492
|
Here's what's included in production mode analysis:
|
|
452
493
|
|
|
453
494
|
- Only `entry` and `project` patterns suffixed with `!`.
|
|
454
|
-
- Only production `entry` file patterns exported
|
|
495
|
+
- Only production `entry` file patterns exported by plugins (such as Next.js and Gatsby).
|
|
455
496
|
- Only the `start` and `postinstall` scripts (e.g. not the `test` or other npm scripts in `package.json`).
|
|
456
497
|
- Only unused `exports`, `nsExports` and `classMembers` are reported (not `types`, `nsTypes`, `enumMembers`).
|
|
457
498
|
|
|
@@ -488,7 +529,7 @@ aliases. They can be configured manually:
|
|
|
488
529
|
|
|
489
530
|
Each workspace can also have its own `paths` configured. Note that Knip `paths` follow the TypeScript semantics:
|
|
490
531
|
|
|
491
|
-
- Path values
|
|
532
|
+
- Path values are an array of relative paths.
|
|
492
533
|
- Paths without an `*` are exact matches.
|
|
493
534
|
|
|
494
535
|
## Reporters
|
|
@@ -504,14 +545,14 @@ Knip provides the following built-in reporters:
|
|
|
504
545
|
|
|
505
546
|
When the provided built-in reporters are not sufficient, a custom reporter can be implemented.
|
|
506
547
|
|
|
507
|
-
Pass `--reporter ./my-reporter` from the command
|
|
548
|
+
Pass `--reporter ./my-reporter` from the command line. The data can then be used to write issues to `stdout`, a JSON or
|
|
508
549
|
CSV file, or sent to a service.
|
|
509
550
|
|
|
510
|
-
Find more details and ideas in [custom reporters][
|
|
551
|
+
Find more details and ideas in [custom reporters][14].
|
|
511
552
|
|
|
512
553
|
## Public exports
|
|
513
554
|
|
|
514
|
-
Sometimes a file that's not an entry file has one or more exports that are public
|
|
555
|
+
Sometimes a file that's not an entry file has one or more exports that are public and should not be reported as unused.
|
|
515
556
|
Such variables and types can be marked with the JSDoc `@public` tag:
|
|
516
557
|
|
|
517
558
|
```js
|
|
@@ -529,54 +570,10 @@ export const split = function () {};
|
|
|
529
570
|
|
|
530
571
|
Knip does not report public exports and types as unused.
|
|
531
572
|
|
|
532
|
-
##
|
|
533
|
-
|
|
534
|
-
### Really, another unused file/dependency/export finder?
|
|
535
|
-
|
|
536
|
-
There are already some great packages available if you want to find unused dependencies OR unused exports.
|
|
537
|
-
|
|
538
|
-
I love the Unix philosophy ("do one thing well"). But in this case I believe it's efficient to handle multiple concerns
|
|
539
|
-
in a single tool. When building a dependency graph of the project, an abstract syntax tree for each file, and traversing
|
|
540
|
-
all of this, why not collect the various issues in one go?
|
|
541
|
-
|
|
542
|
-
### How do I handle too many output/false positives?
|
|
543
|
-
|
|
544
|
-
#### Too many unused files
|
|
545
|
-
|
|
546
|
-
When the list of unused files is too long, this means the gap between the set of `entry` and the set of `project` files
|
|
547
|
-
needs tweaking. The gap can be narrowed down by increasing the `entry` files or reducing the `project` files, for
|
|
548
|
-
instance by ignoring specific directories that are not related to the source code imported by the `entry` files.
|
|
549
|
-
|
|
550
|
-
#### Too many unused dependencies
|
|
551
|
-
|
|
552
|
-
Dependencies that are only imported in unused files are also marked as unused. So a long list of unused files would be
|
|
553
|
-
good to remedy first.
|
|
573
|
+
## Handling Issues
|
|
554
574
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
to match the custom location(s).
|
|
558
|
-
|
|
559
|
-
When the dependencies don't have a Knip plugin yet, please file an issue or [create a new plugin][14].
|
|
560
|
-
|
|
561
|
-
#### Too many unused exports
|
|
562
|
-
|
|
563
|
-
Unused exports of `entry` files are not reported. When exports of other files are marked as unused, because they are
|
|
564
|
-
meant to be used by consumers of the library, there are a few options:
|
|
565
|
-
|
|
566
|
-
1. Add the containing file to the `entry` array in the configuration.
|
|
567
|
-
2. Re-export from an existing entry file.
|
|
568
|
-
3. Mark the exported value or type [using the JSDoc `@public` tag][15].
|
|
569
|
-
|
|
570
|
-
### How to start using Knip in CI while having too many issues to sort out?
|
|
571
|
-
|
|
572
|
-
Eventually this type of QA only really works when it's tied to an automated workflow. But with too many issues to
|
|
573
|
-
resolve this might not be feasible right away, especially in existing larger codebase. Here are a few options that may
|
|
574
|
-
help in the meantime:
|
|
575
|
-
|
|
576
|
-
- Use `--no-exit-code` for exit code 0 in CI.
|
|
577
|
-
- Use `--include` (or `--exclude`) to report only the issue types that have little or no errors.
|
|
578
|
-
- Use separate Knip commands to analyze e.g. only `--dependencies` or `--exports`.
|
|
579
|
-
- Use [ignore patterns][9] to filter out the most problematic areas.
|
|
575
|
+
How to handle a long list of reported issues? Seeing too many false positives? Read more about [handling issues][15]
|
|
576
|
+
describing potential causes for false positives, and how to handle them.
|
|
580
577
|
|
|
581
578
|
## Comparison
|
|
582
579
|
|
|
@@ -641,53 +638,62 @@ The following commands are similar:
|
|
|
641
638
|
Many thanks to some of the early adopters of Knip:
|
|
642
639
|
|
|
643
640
|
- [Block Protocol][21]
|
|
644
|
-
- [
|
|
645
|
-
- [
|
|
646
|
-
- [
|
|
647
|
-
- [
|
|
648
|
-
- [
|
|
649
|
-
- [
|
|
641
|
+
- [Cursor][22]
|
|
642
|
+
- [DeepmergeTS][23]
|
|
643
|
+
- [eslint-plugin-functional][24]
|
|
644
|
+
- [freeCodeCamp.org][25]
|
|
645
|
+
- [is-immutable-type][26]
|
|
646
|
+
- [release-it][27]
|
|
647
|
+
- [Template TypeScript Node Package][28]
|
|
650
648
|
|
|
651
649
|
## Knip?!
|
|
652
650
|
|
|
653
651
|
Knip is Dutch for a "cut". A Dutch expression is "to be ge**knip**t for something", which means to be perfectly suited
|
|
654
|
-
for the job. I'm motivated to make
|
|
652
|
+
for the job. I'm motivated to make Knip perfectly suited for the job of cutting projects to perfection! ✂️
|
|
653
|
+
|
|
654
|
+
## Really, another unused file/dependency/export finder?
|
|
655
|
+
|
|
656
|
+
As listed above, there are already some great packages available if you want to find unused dependencies OR unused
|
|
657
|
+
exports. I love the Unix philosophy ("do one thing well"). But looking at what Knip does, I believe it's efficient to
|
|
658
|
+
handle multiple concerns in a single tool. When building a dependency graph of the project, an abstract syntax tree for
|
|
659
|
+
each file, and traversing all of this, why not collect the various issues in one go?
|
|
655
660
|
|
|
656
661
|
## Contributors
|
|
657
662
|
|
|
658
663
|
Special thanks to the wonderful people who have contributed to this project:
|
|
659
664
|
|
|
660
|
-
[![Contributors][
|
|
665
|
+
[![Contributors][30]][29]
|
|
661
666
|
|
|
662
|
-
[1]: #workspaces
|
|
667
|
+
[1]: #workspaces
|
|
663
668
|
[2]: #plugins
|
|
664
669
|
[3]: #compilers
|
|
665
670
|
[4]: #reporters
|
|
666
671
|
[5]: #custom-reporters
|
|
667
672
|
[6]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
|
|
668
673
|
[7]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
|
|
669
|
-
[8]: #
|
|
670
|
-
[9]: #
|
|
671
|
-
[10]:
|
|
672
|
-
[11]:
|
|
673
|
-
[12]: ./docs/
|
|
674
|
-
[13]: ./docs/
|
|
675
|
-
[14]:
|
|
676
|
-
[15]:
|
|
674
|
+
[8]: #handling-issues
|
|
675
|
+
[9]: #reading-the-report
|
|
676
|
+
[10]: #configuration
|
|
677
|
+
[11]: https://nx.dev/concepts/integrated-vs-package-based
|
|
678
|
+
[12]: ./docs/writing-a-plugin.md
|
|
679
|
+
[13]: ./docs/compilers.md
|
|
680
|
+
[14]: ./docs/custom-reporters.md
|
|
681
|
+
[15]: ./docs/handling-issues.md
|
|
677
682
|
[16]: https://github.com/depcheck/depcheck
|
|
678
683
|
[17]: https://github.com/smeijer/unimported
|
|
679
684
|
[18]: https://github.com/pzavolinsky/ts-unused-exports
|
|
680
685
|
[19]: https://github.com/nadeesha/ts-prune
|
|
681
686
|
[20]: #production-mode
|
|
682
687
|
[21]: https://github.com/blockprotocol/blockprotocol
|
|
683
|
-
[22]: https://github.com/
|
|
684
|
-
[23]: https://github.com/
|
|
685
|
-
[24]: https://github.com/
|
|
686
|
-
[25]: https://github.com/
|
|
687
|
-
[26]: https://github.com/
|
|
688
|
-
[27]: https://github.com/
|
|
689
|
-
[28]: https://github.com/
|
|
690
|
-
[29]: https://
|
|
688
|
+
[22]: https://github.com/getcursor/cursor
|
|
689
|
+
[23]: https://github.com/RebeccaStevens/deepmerge-ts
|
|
690
|
+
[24]: https://github.com/eslint-functional/eslint-plugin-functional
|
|
691
|
+
[25]: https://github.com/freeCodeCamp/freeCodeCamp
|
|
692
|
+
[26]: https://github.com/RebeccaStevens/is-immutable-type
|
|
693
|
+
[27]: https://github.com/release-it/release-it
|
|
694
|
+
[28]: https://github.com/JoshuaKGoldberg/template-typescript-node-package
|
|
695
|
+
[29]: https://github.com/webpro/knip/graphs/contributors
|
|
696
|
+
[30]: https://contrib.rocks/image?repo=webpro/knip
|
|
691
697
|
[plugin-ava]: ./src/plugins/ava
|
|
692
698
|
[plugin-babel]: ./src/plugins/babel
|
|
693
699
|
[plugin-capacitor]: ./src/plugins/capacitor
|
|
@@ -29,7 +29,22 @@ export declare class ConfigurationChief {
|
|
|
29
29
|
constructor({ cwd, isProduction }: ConfigurationManagerOptions);
|
|
30
30
|
init(): Promise<void>;
|
|
31
31
|
getCompilers(): [SyncCompilers, AsyncCompilers];
|
|
32
|
+
getRules(): import("./types/issues.js").Rules;
|
|
32
33
|
normalize(rawLocalConfig: RawConfiguration): {
|
|
34
|
+
rules: {
|
|
35
|
+
files: import("./types/issues.js").IssueSeverity;
|
|
36
|
+
dependencies: import("./types/issues.js").IssueSeverity;
|
|
37
|
+
devDependencies: import("./types/issues.js").IssueSeverity;
|
|
38
|
+
unlisted: import("./types/issues.js").IssueSeverity;
|
|
39
|
+
unresolved: import("./types/issues.js").IssueSeverity;
|
|
40
|
+
exports: import("./types/issues.js").IssueSeverity;
|
|
41
|
+
types: import("./types/issues.js").IssueSeverity;
|
|
42
|
+
nsExports: import("./types/issues.js").IssueSeverity;
|
|
43
|
+
nsTypes: import("./types/issues.js").IssueSeverity;
|
|
44
|
+
duplicates: import("./types/issues.js").IssueSeverity;
|
|
45
|
+
enumMembers: import("./types/issues.js").IssueSeverity;
|
|
46
|
+
classMembers: import("./types/issues.js").IssueSeverity;
|
|
47
|
+
};
|
|
33
48
|
include: string[];
|
|
34
49
|
exclude: string[];
|
|
35
50
|
ignore: string[];
|
|
@@ -2,6 +2,7 @@ import mapWorkspaces from '@npmcli/map-workspaces';
|
|
|
2
2
|
import micromatch from 'micromatch';
|
|
3
3
|
import { ConfigurationValidator } from './ConfigurationValidator.js';
|
|
4
4
|
import { ROOT_WORKSPACE_NAME, DEFAULT_EXTENSIONS, KNIP_CONFIG_LOCATIONS } from './constants.js';
|
|
5
|
+
import { defaultRules } from './issues/initializers.js';
|
|
5
6
|
import * as plugins from './plugins/index.js';
|
|
6
7
|
import { arrayify, compact } from './util/array.js';
|
|
7
8
|
import parsedArgs from './util/cli-arguments.js';
|
|
@@ -11,6 +12,7 @@ import { findFile, loadJSON } from './util/fs.js';
|
|
|
11
12
|
import { getIncludedIssueTypes } from './util/get-included-issue-types.js';
|
|
12
13
|
import { _dirGlob } from './util/glob.js';
|
|
13
14
|
import { _load } from './util/loader.js';
|
|
15
|
+
import { getKeysByValue } from './util/object.js';
|
|
14
16
|
import { join, relative, toPosix } from './util/path.js';
|
|
15
17
|
import { toCamelCase } from './util/plugin.js';
|
|
16
18
|
import { byPathDepth } from './util/workspace.js';
|
|
@@ -28,6 +30,7 @@ const getDefaultWorkspaceConfig = (extensions) => {
|
|
|
28
30
|
};
|
|
29
31
|
};
|
|
30
32
|
const defaultConfig = {
|
|
33
|
+
rules: defaultRules,
|
|
31
34
|
include: [],
|
|
32
35
|
exclude: [],
|
|
33
36
|
ignore: [],
|
|
@@ -88,12 +91,16 @@ export class ConfigurationChief {
|
|
|
88
91
|
getCompilers() {
|
|
89
92
|
return [this.config.syncCompilers, this.config.asyncCompilers];
|
|
90
93
|
}
|
|
94
|
+
getRules() {
|
|
95
|
+
return this.config.rules;
|
|
96
|
+
}
|
|
91
97
|
normalize(rawLocalConfig) {
|
|
92
98
|
const initialWorkspaces = rawLocalConfig.workspaces ?? {
|
|
93
99
|
[ROOT_WORKSPACE_NAME]: {
|
|
94
100
|
...rawLocalConfig,
|
|
95
101
|
},
|
|
96
102
|
};
|
|
103
|
+
const rules = { ...defaultRules, ...rawLocalConfig.rules };
|
|
97
104
|
const include = rawLocalConfig.include ?? defaultConfig.include;
|
|
98
105
|
const exclude = rawLocalConfig.exclude ?? defaultConfig.exclude;
|
|
99
106
|
const ignore = arrayify(rawLocalConfig.ignore ?? defaultConfig.ignore);
|
|
@@ -140,6 +147,7 @@ export class ConfigurationChief {
|
|
|
140
147
|
return workspaces;
|
|
141
148
|
}, {});
|
|
142
149
|
return {
|
|
150
|
+
rules,
|
|
143
151
|
include,
|
|
144
152
|
exclude,
|
|
145
153
|
ignore,
|
|
@@ -241,9 +249,10 @@ export class ConfigurationChief {
|
|
|
241
249
|
}
|
|
242
250
|
getIssueTypesToReport() {
|
|
243
251
|
const cliArgs = { include, exclude, dependencies, exports };
|
|
252
|
+
const excludesFromRules = getKeysByValue(this.config.rules, 'off');
|
|
244
253
|
const config = {
|
|
245
254
|
include: this.config.include ?? [],
|
|
246
|
-
exclude: this.config.exclude
|
|
255
|
+
exclude: [...excludesFromRules, ...this.config.exclude],
|
|
247
256
|
isProduction: this.isProduction,
|
|
248
257
|
};
|
|
249
258
|
return getIncludedIssueTypes(cliArgs, config);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const ConfigurationValidator: z.ZodObject<{
|
|
3
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
4
|
+
rules: z.ZodOptional<z.ZodRecord<z.ZodUnion<[z.ZodLiteral<"files">, z.ZodLiteral<"dependencies">, z.ZodLiteral<"devDependencies">, z.ZodLiteral<"unlisted">, z.ZodLiteral<"unresolved">, z.ZodLiteral<"exports">, z.ZodLiteral<"types">, z.ZodLiteral<"nsExports">, z.ZodLiteral<"nsTypes">, z.ZodLiteral<"duplicates">, z.ZodLiteral<"enumMembers">, z.ZodLiteral<"classMembers">]>, z.ZodEnum<["error", "warn", "off"]>>>;
|
|
3
5
|
entry: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
4
6
|
project: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
5
7
|
paths: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
@@ -11,7 +13,6 @@ export declare const ConfigurationValidator: z.ZodObject<{
|
|
|
11
13
|
syncCompilers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodString>>>;
|
|
12
14
|
asyncCompilers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodFunction<z.ZodTuple<[z.ZodString], z.ZodUnknown>, z.ZodPromise<z.ZodString>>>>;
|
|
13
15
|
include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
14
|
-
exclude: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
15
16
|
workspaces: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
16
17
|
entry: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
17
18
|
project: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
@@ -1403,6 +1404,8 @@ export declare const ConfigurationValidator: z.ZodObject<{
|
|
|
1403
1404
|
project?: string | string[] | undefined;
|
|
1404
1405
|
}>]>>;
|
|
1405
1406
|
}, "strip", z.ZodTypeAny, {
|
|
1407
|
+
exclude?: string[] | undefined;
|
|
1408
|
+
rules?: Partial<Record<"files" | "dependencies" | "devDependencies" | "unlisted" | "unresolved" | "exports" | "types" | "nsExports" | "nsTypes" | "duplicates" | "enumMembers" | "classMembers", "error" | "warn" | "off">> | undefined;
|
|
1406
1409
|
entry?: string | string[] | undefined;
|
|
1407
1410
|
project?: string | string[] | undefined;
|
|
1408
1411
|
paths?: Record<string, string[]> | undefined;
|
|
@@ -1414,7 +1417,6 @@ export declare const ConfigurationValidator: z.ZodObject<{
|
|
|
1414
1417
|
syncCompilers?: Record<string, (args_0: string, ...args_1: unknown[]) => string> | undefined;
|
|
1415
1418
|
asyncCompilers?: Record<string, (args_0: string, ...args_1: unknown[]) => Promise<string>> | undefined;
|
|
1416
1419
|
include?: string[] | undefined;
|
|
1417
|
-
exclude?: string[] | undefined;
|
|
1418
1420
|
workspaces?: Record<string, {
|
|
1419
1421
|
entry?: string | string[] | undefined;
|
|
1420
1422
|
project?: string | string[] | undefined;
|
|
@@ -1804,6 +1806,8 @@ export declare const ConfigurationValidator: z.ZodObject<{
|
|
|
1804
1806
|
project?: string | string[] | undefined;
|
|
1805
1807
|
} | undefined;
|
|
1806
1808
|
}, {
|
|
1809
|
+
exclude?: string[] | undefined;
|
|
1810
|
+
rules?: Partial<Record<"files" | "dependencies" | "devDependencies" | "unlisted" | "unresolved" | "exports" | "types" | "nsExports" | "nsTypes" | "duplicates" | "enumMembers" | "classMembers", "error" | "warn" | "off">> | undefined;
|
|
1807
1811
|
entry?: string | string[] | undefined;
|
|
1808
1812
|
project?: string | string[] | undefined;
|
|
1809
1813
|
paths?: Record<string, string[]> | undefined;
|
|
@@ -1815,7 +1819,6 @@ export declare const ConfigurationValidator: z.ZodObject<{
|
|
|
1815
1819
|
syncCompilers?: Record<string, (args_0: string, ...args_1: unknown[]) => string> | undefined;
|
|
1816
1820
|
asyncCompilers?: Record<string, (args_0: string, ...args_1: unknown[]) => Promise<string>> | undefined;
|
|
1817
1821
|
include?: string[] | undefined;
|
|
1818
|
-
exclude?: string[] | undefined;
|
|
1819
1822
|
workspaces?: Record<string, {
|
|
1820
1823
|
entry?: string | string[] | undefined;
|
|
1821
1824
|
project?: string | string[] | undefined;
|
|
@@ -5,7 +5,23 @@ const syncCompilerSchema = z.function().args(z.string()).returns(z.string());
|
|
|
5
5
|
const asyncCompilerSchema = z.function().args(z.string()).returns(z.promise(z.string()));
|
|
6
6
|
const compilerSchema = z.union([syncCompilerSchema, asyncCompilerSchema]);
|
|
7
7
|
const compilersSchema = z.record(z.string(), compilerSchema);
|
|
8
|
+
const issueTypeSchema = z.union([
|
|
9
|
+
z.literal('files'),
|
|
10
|
+
z.literal('dependencies'),
|
|
11
|
+
z.literal('devDependencies'),
|
|
12
|
+
z.literal('unlisted'),
|
|
13
|
+
z.literal('unresolved'),
|
|
14
|
+
z.literal('exports'),
|
|
15
|
+
z.literal('types'),
|
|
16
|
+
z.literal('nsExports'),
|
|
17
|
+
z.literal('nsTypes'),
|
|
18
|
+
z.literal('duplicates'),
|
|
19
|
+
z.literal('enumMembers'),
|
|
20
|
+
z.literal('classMembers'),
|
|
21
|
+
]);
|
|
22
|
+
const rulesSchema = z.record(issueTypeSchema, z.enum(['error', 'warn', 'off']));
|
|
8
23
|
const rootConfigurationSchema = z.object({
|
|
24
|
+
rules: rulesSchema.optional(),
|
|
9
25
|
entry: globSchema.optional(),
|
|
10
26
|
project: globSchema.optional(),
|
|
11
27
|
paths: pathsSchema.optional(),
|