knip 2.4.0 → 2.6.1

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 (40) hide show
  1. package/README.md +139 -133
  2. package/dist/ConfigurationChief.d.ts +15 -0
  3. package/dist/ConfigurationChief.js +10 -1
  4. package/dist/ConfigurationValidator.d.ts +6 -3
  5. package/dist/ConfigurationValidator.js +16 -0
  6. package/dist/IssueCollector.d.ts +12 -7
  7. package/dist/IssueCollector.js +7 -3
  8. package/dist/PrincipalFactory.d.ts +1 -3
  9. package/dist/PrincipalFactory.js +6 -6
  10. package/dist/ProjectPrincipal.d.ts +4 -5
  11. package/dist/ProjectPrincipal.js +2 -5
  12. package/dist/WorkspaceWorker.js +4 -3
  13. package/dist/binaries/resolvers/fallback.js +3 -2
  14. package/dist/binaries/resolvers/yarn.js +1 -0
  15. package/dist/cli.js +2 -2
  16. package/dist/constants.js +2 -3
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.js +48 -43
  19. package/dist/issues/initializers.d.ts +2 -1
  20. package/dist/issues/initializers.js +1 -0
  21. package/dist/plugins/eslint/helpers.js +3 -3
  22. package/dist/plugins/eslint/types.d.ts +1 -1
  23. package/dist/plugins/jest/index.js +2 -3
  24. package/dist/reporters/codeowners.js +2 -2
  25. package/dist/reporters/compact.js +1 -1
  26. package/dist/reporters/symbols.js +7 -5
  27. package/dist/reporters/util.d.ts +4 -1
  28. package/dist/reporters/util.js +6 -4
  29. package/dist/types/config.d.ts +2 -0
  30. package/dist/types/issues.d.ts +3 -0
  31. package/dist/util/compilers.d.ts +2 -1
  32. package/dist/util/object.d.ts +1 -0
  33. package/dist/util/object.js +8 -0
  34. package/dist/util/path.d.ts +5 -4
  35. package/dist/util/path.js +6 -4
  36. package/dist/util/tsconfig-loader.js +2 -2
  37. package/dist/version.d.ts +1 -1
  38. package/dist/version.js +1 -1
  39. package/package.json +7 -6
  40. 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 leads to improved performance, less maintenance and easier refactorings.
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, and finds unused exports, files and dependencies.
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 removing things that you no longer use. But how do you find out? Where do you start finding
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 the default locations above, but also in other places:
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 define entry files so you don't have to.
60
- - The `scripts` in package.json may also provide entry files that Knip can use.
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 excusively and have all source files only in the `src` directory:
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
- - **Unused files**: did not find references to this file
175
- - **Unused dependencies**: did not find references to this dependency
176
- - **Unused devDependencies**: did not find references to this dependency
177
- - **Unlisted dependencies**: used dependencies, but not listed in package.json _(1)_
178
- - **Unresolved imports**: import specifiers that could not be resolved
179
- - **Unused exports**: did not find references to this exported variable
180
- - **Unused exports in namespaces**: did not find direct references to this exported variable _(2)_
181
- - **Unused exported types**: did not find references to this exported type
182
- - **Unused exported types in namespaces**: did not find direct references to this exported variable _(2)_
183
- - **Unused exported enum members**: did not find references to this member of the exported enum
184
- - **Unused exported class members**: did not find references to this member of the exported class
185
- - **Duplicate exports**: the same thing is exported more than once
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
- _(1)_ If an unlisted dependency is prefixed with `bin:` it means a binary is missing. This often equals the package
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
- _(2)_ The variable or type is not referenced directly, and has become a member of a namespace. Knip can't find a
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
- ### Output filters
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
- You can `--include` or `--exclude` any of the types to slice & dice the report to your needs. Alternatively, they can be
198
- added to the configuration (e.g. `"exclude": ["dependencies"]`).
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
- Still not happy with the results? Getting too much output/false positives? The [FAQ][8] may be useful. Feel free to open
212
- an issue and I'm happy to look into it. Also see the next section on how to [ignore][9] certain false positives:
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
- ## Now what?
269
+ ## What's next?
230
270
 
231
271
  This is the fun part! Knip, knip, knip ✂️
232
272
 
233
- As always, make sure to backup files or use Git before deleting files or making changes. Run tests to verify results.
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
- ## Workspaces (monorepos)
285
+ Getting too many reported issues and false positives? Read more about [handling issues][8].
286
+
287
+ ## Workspaces
246
288
 
247
- Workspaces and monorepos are handled out-of-the-box by Knip. Every workspace is part of the analysis.
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. The default configuration of each workspace is the same as for a regular project.
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
- Root workspaces must be named `"."` under `workspaces` (like in the example).
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 are listed in `package.json`. Once enabled, they add a set of `config` files for
357
- itself and/or `entry` files for Knip to analyze.
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 allows
378
- to override any defaults. Let's take Cypress for example. By default it uses `cypress.config.js`, but your project uses
379
- `config/cypress.js`. Also, the default pattern for test files is `cypress/e2e/**/*.cy.js`, but your project has them at
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 potentially lots of configuration
393
- files (such as the [Nx "intregrated repo" style][10]). Let's assume some of these projects are apps and have their own
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][11]. This
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 which allows to include files that are not JavaScript or TypeScript in the process of
416
- finding unused or missing dependencies. For instance, `.mdx`, `.vue` and `.svelte` files come to mind.
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 analyis:
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][12] for more details and examples.
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 its exports from being reported as unused, while
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 for plugins (such as Next.js and Gatsby).
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 is an array of relative paths.
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-line. The data can then be used to write issues to `stdout`, a JSON or
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][13].
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, and should not be reported as unused.
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
- ## FAQ
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
- When unused dependencies are related to dependencies having a Knip [plugin][1], maybe the `config` and/or `entry` files
556
- for that dependency are at custom locations. The default values are at the plugin's documentation, and can be overridden
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
- - [DeepmergeTS][22]
645
- - [eslint-plugin-functional][23]
646
- - [freeCodeCamp.org][24]
647
- - [is-immutable-type][25]
648
- - [release-it][26]
649
- - [Template TypeScript Node Package][27]
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 knip perfectly suited for the job of cutting projects to perfection! ✂️
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][29]][28]
665
+ [![Contributors][30]][29]
661
666
 
662
- [1]: #workspaces-monorepos
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]: #faq
670
- [9]: #ignore
671
- [10]: https://nx.dev/concepts/integrated-vs-package-based
672
- [11]: ./docs/writing-a-plugin.md
673
- [12]: ./docs/compilers.md
674
- [13]: ./docs/custom-reporters.md
675
- [14]: #create-a-new-plugin
676
- [15]: #public-exports
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/RebeccaStevens/deepmerge-ts
684
- [23]: https://github.com/eslint-functional/eslint-plugin-functional
685
- [24]: https://github.com/freeCodeCamp/freeCodeCamp
686
- [25]: https://github.com/RebeccaStevens/is-immutable-type
687
- [26]: https://github.com/release-it/release-it
688
- [27]: https://github.com/JoshuaKGoldberg/template-typescript-node-package
689
- [28]: https://github.com/webpro/knip/graphs/contributors
690
- [29]: https://contrib.rocks/image?repo=webpro/knip
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(),