knip 0.11.2 → 0.12.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 (42) hide show
  1. package/README.md +116 -75
  2. package/dist/cli.js +15 -39
  3. package/dist/help.js +3 -7
  4. package/dist/index.d.ts +4 -4
  5. package/dist/index.js +43 -53
  6. package/dist/log.js +2 -7
  7. package/dist/progress.d.ts +1 -1
  8. package/dist/progress.js +14 -19
  9. package/dist/reporters/codeowners.d.ts +1 -1
  10. package/dist/reporters/codeowners.js +11 -16
  11. package/dist/reporters/compact.d.ts +1 -1
  12. package/dist/reporters/compact.js +6 -8
  13. package/dist/reporters/constants.js +1 -4
  14. package/dist/reporters/index.d.ts +4 -4
  15. package/dist/reporters/index.js +9 -14
  16. package/dist/reporters/json.d.ts +1 -1
  17. package/dist/reporters/json.js +8 -13
  18. package/dist/reporters/symbols.d.ts +1 -1
  19. package/dist/reporters/symbols.js +6 -8
  20. package/dist/runner.d.ts +1 -1
  21. package/dist/runner.js +46 -54
  22. package/dist/types.d.ts +0 -4
  23. package/dist/types.js +1 -2
  24. package/dist/util/config.d.ts +1 -1
  25. package/dist/util/config.js +19 -16
  26. package/dist/util/debug.js +6 -15
  27. package/dist/util/dependencies.d.ts +2 -2
  28. package/dist/util/dependencies.js +15 -17
  29. package/dist/util/errors.js +1 -5
  30. package/dist/util/fs.d.ts +1 -0
  31. package/dist/util/fs.js +12 -17
  32. package/dist/util/glob.d.ts +1 -1
  33. package/dist/util/glob.js +8 -20
  34. package/dist/util/parseArgs.d.ts +23 -0
  35. package/dist/util/parseArgs.js +22 -0
  36. package/dist/util/path.js +2 -9
  37. package/dist/util/performance.d.ts +22 -0
  38. package/dist/util/performance.js +81 -0
  39. package/dist/util/project.d.ts +9 -3
  40. package/dist/util/project.js +16 -9
  41. package/dist/util/type.js +5 -9
  42. package/package.json +15 -5
package/README.md CHANGED
@@ -17,12 +17,11 @@ things that can be removed?
17
17
  The dots don't connect themselves. This is where Knip comes in:
18
18
 
19
19
  - [x] Finds **unused files, dependencies and exports**.
20
- - [x] Finds dependencies not listed in `package.json`.
21
- - [x] Verifies that exported symbols are actually used in other files, even when part of an imported namespace.
22
- - [x] Supports JavaScript inside TypeScript projects (`"allowJs": true`).
23
- - [x] Finds duplicate exports of the same symbol.
24
- - [x] Supports JavaScript ES Module-based projects without a `tsconfig.json`.
25
- - [x] Features multiple [reporters][1] and supports [custom reporters][2].
20
+ - [x] Finds used dependencies not listed in `package.json`.
21
+ - [x] Finds duplicate exports.
22
+ - [x] Supports JavaScript (without `tsconfig.json`, or TypeScript `allowJs: true`).
23
+ - [x] Features multiple [reporters][1] and supports [custom reporters][2] (think JSON and `CODEOWNERS`)
24
+ - [x] Run Knip as part of your CI environment to detect issues and prevent regressions.
26
25
 
27
26
  Knip really shines in larger projects. A little bit of configuration will pay off, I promise. A comparison with similar
28
27
  tools answers the question [why another unused file/dependency/export finder?][3]
@@ -32,11 +31,27 @@ Knip is a fresh take on keeping your projects clean & tidy!
32
31
  [![An orange cow with scissors, Van Gogh style][5]][4] <sup>_“An orange cow with scissors, Van Gogh style” - generated
33
32
  with OpenAI_</sup>
34
33
 
34
+ ## Roadmap
35
+
36
+ Please report any false positives by [opening an issue in this repo][6]. Bonus points for adding a public repository or
37
+ opening a pull request with a directory and example files in `test/fixtures`. Correctness and bug fixes have priority
38
+ over new features:
39
+
40
+ ### Upcoming Features
41
+
42
+ - [ ] Find unused members of classes and enums (#11 and #20).
43
+ - [ ] Custom dependency resolvers: find dependencies used in npm scripts.
44
+ - [ ] Custom dependency resolvers: find unused and unlisted plugins for Webpack, ESLint & Babel, etc. (#7)
45
+ - [ ] Smart default configurations and more fine-grained configuration options.
46
+ - [ ] Full support for monorepos (partial [monorepos support][7] with `--dir` exists).
47
+ - [ ] Fix issues: remove `export` keyword, uninstall unused dependencies, delete files (like `--fix` of ESLint).
48
+ - [ ] Add more reporters and report customization options (#3).
49
+
35
50
  ## Installation
36
51
 
37
52
  npm install -D knip
38
53
 
39
- Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.3. Knip is _cutting edge!_
54
+ Knip supports LTS versions of Node.js, and currently requires at least Node.js v16.17 or v18.6. Knip is _cutting edge!_
40
55
 
41
56
  ## Usage
42
57
 
@@ -67,7 +82,7 @@ Knip works by creating two sets of files:
67
82
  3. The subset of project files that is not production code will be reported as unused files (in red).
68
83
  4. Then the production code (in blue) will be analyzed for unused exports.
69
84
 
70
- ![How it works][6]
85
+ ![How it works][8]
71
86
 
72
87
  ## Options
73
88
 
@@ -88,9 +103,9 @@ Knip works by creating two sets of files:
88
103
  --max-issues Maximum number of issues before non-zero exit code (default: 0)
89
104
  --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
90
105
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
91
- --jsdoc Enable JSDoc parsing, with options: public
92
106
  --debug Show debug output
93
107
  --debug-level Set verbosity of debug output (default: 1, max: 2)
108
+ --performance Measure running time of expensive functions and display stats table
94
109
 
95
110
  Issue types: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
96
111
 
@@ -98,20 +113,12 @@ Knip works by creating two sets of files:
98
113
 
99
114
  $ knip
100
115
  $ knip --dir packages/client --include files
101
- $ knip -c ./knip.js --reporter compact --jsdoc public
116
+ $ knip -c ./knip.js --reporter compact
102
117
  $ knip --ignore 'lib/**/*.ts' --ignore build
103
118
  $ knip --reporter codeowners --reporter-options '{"path":".github/CODEOWNERS"}'
104
119
 
105
120
  More info: https://github.com/webpro/knip
106
121
 
107
- ## Performance
108
-
109
- 🚀 Knip is considerably faster when only the `files` and/or `duplicates` types are included. Finding unused exports
110
- requires deeper analysis (`exports`, `nsExports`, `types`, `nsTypes`). The following example commands do the same:
111
-
112
- knip --include files --include duplicates
113
- knip --include files,duplicates
114
-
115
122
  ## Reading the report
116
123
 
117
124
  After analyzing all the files resolved from the `entryFiles` against the `projectFiles`, the report contains the
@@ -124,7 +131,9 @@ following types of issues:
124
131
  - `nsExports` - Unused exports in namespaces: did not find direct references to this exported variable (2)
125
132
  - `types` - Unused types: did not find references to this exported type
126
133
  - `nsTypes` - Unused types in namespaces: did not find direct references to this exported variable (2)
127
- - `duplicates` - Duplicate exports: the same thing is exported more than once with different names from the same file
134
+ - `duplicates` - Duplicate exports: the same thing is exported more than once with different names
135
+
136
+ Notes:
128
137
 
129
138
  1. This includes dependencies that could not be resolved. For instance, what does `unresolved/dir/module` mean?
130
139
  - To target something in the (missing) `node_modules/unresolved` package?
@@ -146,14 +155,45 @@ As always, make sure to backup files or use Git before deleting files or making
146
155
  - Unused exports and types: remove the `export` keyword in front of unused exports. Then you (or tools such as
147
156
  TypeScript language services in VS Code and/or ESLint) can see whether the variable or type is used within the same
148
157
  file. If this is not the case, it can be removed.
158
+ - Duplicate exports can be removed to export only once, make sure to import that everywhere.
149
159
 
150
160
  🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to remove things!
151
161
 
152
- ## Production versus non-production code
162
+ ## Performance
163
+
164
+ 🚀 Knip finds issues of type `files`, `dependencies`, `unlisted` and `duplicates` very fast. Finding unused exports
165
+ requires deeper analysis (`exports`, `nsExports`, `types`, `nsTypes`). The following example commands do the same:
166
+
167
+ knip --include files --include dependencies
168
+ knip --include files,dependencies
169
+
170
+ Use `--performance` to see where most of the time is spent.
171
+
172
+ ## Configuration
173
+
174
+ ### Libraries versus Applications
175
+
176
+ Libraries and applications are identical when it comes to files and dependencies: whatever is unused should be removed.
177
+ Yet libraries usually have exports meant to be used by other libraries or applications. Such public variables and types
178
+ in libraries can be marked with the JSDoc `@public` tag:
179
+
180
+ ```js
181
+ /**
182
+ * Merge two objects.
183
+ *
184
+ * @public
185
+ */
186
+
187
+ export const merge = function () {};
188
+ ```
189
+
190
+ Knip does not report public exports and types as unused.
191
+
192
+ ### Production versus non-production code
153
193
 
154
194
  Feels like you're getting too many false positives? Let's talk about `entryFiles` and `projectFiles`.
155
195
 
156
- ### Production code
196
+ #### Production code
157
197
 
158
198
  The default configuration for Knip is very strict and targets production code. Non-production files such as tests should
159
199
  not be part of the `entryFiles` and `projectFiles`. Here's why: test and other non-production files often import
@@ -165,7 +205,7 @@ production files, which will prevent the production files from being reported as
165
205
 
166
206
  This will ensure Knip understands what production code can be removed.
167
207
 
168
- ### Non-production code
208
+ #### Non-production code
169
209
 
170
210
  Non-production code includes files such as unit tests, end-to-end tests, tooling, scripts, Storybook stories, etc. Think
171
211
  of it the same way as the convention to split `dependencies` and `devDependencies` in `package.json`.
@@ -244,9 +284,8 @@ To analyze the packages separately, using the matching pattern from the configur
244
284
 
245
285
  #### Connected projects
246
286
 
247
- A good example of a large project setup is a monorepo. Let's take an example (Nx) project configuration using Next.js,
248
- Jest and Storybook, which has multiple apps and libs. They are not published separately and don't have their own
249
- `package.json`.
287
+ Let's take another example Nx project configuration using Next.js, Jest and Storybook, which has multiple apps and libs.
288
+ They are not published separately and don't have their own `package.json`.
250
289
 
251
290
  This configuration file can also be a JavaScript file, which allows to add logic and/or comments (e.g. `knip.js`):
252
291
 
@@ -277,10 +316,10 @@ configuration can be tweaked further to the project structure.
277
316
 
278
317
  Knip provides the following built-in reporters:
279
318
 
280
- - [`json`](#json)
281
- - [`symbol`](#symbol-default) (default)
282
- - [`compact`](#compact)
283
- - [`codeowners`](#code-owners)
319
+ - [`json`][9]
320
+ - [`symbol`][10] (default)
321
+ - [`compact`][11]
322
+ - [`codeowners`][12]
284
323
 
285
324
  ### Custom Reporters
286
325
 
@@ -335,38 +374,31 @@ per file like this:
335
374
  ]
336
375
  ```
337
376
 
338
- The keys match the [known issue types](#reading-the-report).
377
+ The keys match the [known issue types][13].
339
378
 
340
379
  #### Usage Ideas
341
380
 
342
- Use tools like [miller](https://github.com/johnkerl/miller) or [jtbl](https://github.com/kellyjonbrazil/jtbl) to consume
343
- the JSON and render a table in the terminal.
381
+ Use tools like [miller][14] or [jtbl][15] to consume the JSON and render a table in the terminal.
344
382
 
345
383
  ##### Table
346
384
 
347
- ```
348
- $ npx knip --reporter json | mlr --ijson --opprint --no-auto-flatten cat
349
- file owners files unlisted exports types duplicates
350
- src/Registration.tsx @org/owner true react lowercaseFirstLetter, RegistrationBox RegistrationServices, RegistrationAction Registration, default
351
- src/ProductsList.tsx @org/team false - - ProductDetail -
352
- ```
385
+ $ npx knip --reporter json | mlr --ijson --opprint --no-auto-flatten cat
386
+ file owners files unlisted exports types duplicates
387
+ src/Registration.tsx @org/owner true react lowercaseFirstLetter, RegistrationBox RegistrationServices, RegistrationAction Registration, default
388
+ src/ProductsList.tsx @org/team false - - ProductDetail -
353
389
 
354
390
  ##### Markdown Table
355
391
 
356
- ```
357
- $ npx knip --reporter json | mlr --ijson --omd --no-auto-flatten cat
358
- | file | owners | files | duplicates |
359
- | --- | --- | --- | --- |
360
- | src/Registration.tsx | @org/owner | true | Registration, default |
361
- | src/ProductsList.tsx | @org/team | false | |
362
- ```
392
+ $ npx knip --reporter json | mlr --ijson --omd --no-auto-flatten cat
393
+ | file | owners | files | duplicates |
394
+ | --- | --- | --- | --- |
395
+ | src/Registration.tsx | @org/owner | true | Registration, default |
396
+ | src/ProductsList.tsx | @org/team | false | |
363
397
 
364
398
  Include specific issue types and/or replace the `cat` command with `put` for clean output:
365
399
 
366
- ```
367
- npx knip --include files,duplicates --reporter json | mlr --ijson --opprint --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
368
- npx knip --reporter json | mlr --ijson --omd --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
369
- ```
400
+ npx knip --include files,duplicates --reporter json | mlr --ijson --opprint --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
401
+ npx knip --reporter json | mlr --ijson --omd --no-auto-flatten put 'for (e in $*) { if(is_array($[e])) { $[e] = joinv($[e], ", ") } }'
370
402
 
371
403
  ### More Output Examples
372
404
 
@@ -463,20 +495,20 @@ collect the various issues in one go?
463
495
 
464
496
  This table is a work in progress, but here's a first impression. Based on their docs (please report any mistakes):
465
497
 
466
- | Feature | **knip** | [depcheck][7] | [unimported][8] | [ts-unused-exports][9] | [ts-prune][10] | [find-unused-exports][11] |
467
- | --------------------------------- | :------: | :-----------: | :-------------: | :--------------------: | :------------: | :-----------------------: |
468
- | Unused files | ✅ | - | ✅ | - | - | - |
469
- | Unused dependencies | ✅ | ✅ | ✅ | - | - | - |
470
- | Unlisted dependencies | ✅ | ✅ | ✅ | - | - | - |
471
- | [Custom dependency resolvers][12] | ❌ | ✅ | ❌ | - | - | - |
472
- | Unused exports | ✅ | - | - | ✅ | ✅ | ✅ |
473
- | Duplicate exports | ✅ | - | - | ❌ | ❌ | ❌ |
474
- | Search namespaces | ✅ | - | - | ✅ | ❌ | ❌ |
475
- | Custom reporters | ✅ | - | - | - | - | - |
476
- | Pure JavaScript/ESM | ✅ | ✅ | ✅ | - | - | ✅ |
477
- | Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
478
- | [Support monorepos][13] | 🟠 | - | - | - | - | - |
479
- | ESLint plugin available | - | - | - | ✅ | - | - |
498
+ | Feature | **knip** | [depcheck][16] | [unimported][17] | [ts-unused-exports][18] | [ts-prune][19] | [find-unused-exports][20] |
499
+ | --------------------------------- | :------: | :------------: | :--------------: | :---------------------: | :------------: | :-----------------------: |
500
+ | Unused files | ✅ | - | ✅ | - | - | - |
501
+ | Unused dependencies | ✅ | ✅ | ✅ | - | - | - |
502
+ | Unlisted dependencies | ✅ | ✅ | ✅ | - | - | - |
503
+ | [Custom dependency resolvers][21] | ❌ | ✅ | ❌ | - | - | - |
504
+ | Unused exports | ✅ | - | - | ✅ | ✅ | ✅ |
505
+ | Duplicate exports | ✅ | - | - | ❌ | ❌ | ❌ |
506
+ | Search namespaces | ✅ | - | - | ✅ | ❌ | ❌ |
507
+ | Custom reporters | ✅ | - | - | - | - | - |
508
+ | JavaScript support | ✅ | ✅ | ✅ | - | - | ✅ |
509
+ | Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
510
+ | [Support monorepos][22] | 🟠 | - | - | - | - | - |
511
+ | ESLint plugin available | - | - | - | ✅ | - | - |
480
512
 
481
513
  ✅ = Supported, ❌ = Not supported, - = Out of scope
482
514
 
@@ -491,7 +523,8 @@ directory of a monorepo is nice for DX. But Knip will need some help to find it
491
523
  `eslint-plugin-cypress` _dependency_. Or see it is not listed in `package.json`. Or that the dependency is still listed,
492
524
  but no longer in use. Many popular projects reference plugins in similar ways, such as Babel, Webpack and Storybook.
493
525
 
494
- Big compliments to [depcheck][15] which already does this! They call this "specials". [Knip has this ambition][16], too.
526
+ Big compliments to [depcheck][23] which already does this! They call this "specials". This is on [Knip's roadmap][24],
527
+ as well, with the additional ambition to also find used dependencies that are not listed in `package.json`.
495
528
 
496
529
  unimported is strict in this regard and works based on production files and `dependencies`, so does not have custom
497
530
  dependency resolvers which are usually only needed for `devDependencies`.
@@ -513,14 +546,22 @@ for the job. I'm motivated to make knip perfectly suited for the job of cutting
513
546
  [3]: #really-another-unused-filedependencyexport-finder
514
547
  [4]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
515
548
  [5]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
516
- [6]: ./assets/how-it-works.drawio.svg
517
- [7]: https://github.com/depcheck/depcheck
518
- [8]: https://github.com/smeijer/unimported
519
- [9]: https://github.com/pzavolinsky/ts-unused-exports
520
- [10]: https://github.com/nadeesha/ts-prune
521
- [11]: https://github.com/jaydenseric/find-unused-exports
522
- [12]: #custom-dependency-resolvers
523
- [13]: #monorepos-1
524
- [14]: #monorepos
525
- [15]: https://github.com/depcheck/depcheck#special
526
- [16]: https://github.com/webpro/knip/issues/7
549
+ [6]: https://github.com/webpro/knip/issues
550
+ [7]: #monorepos
551
+ [8]: ./assets/how-it-works.drawio.svg
552
+ [9]: #json
553
+ [10]: #symbol-default
554
+ [11]: #compact
555
+ [12]: #code-owners
556
+ [13]: #reading-the-report
557
+ [14]: https://github.com/johnkerl/miller
558
+ [15]: https://github.com/kellyjonbrazil/jtbl
559
+ [16]: https://github.com/depcheck/depcheck
560
+ [17]: https://github.com/smeijer/unimported
561
+ [18]: https://github.com/pzavolinsky/ts-unused-exports
562
+ [19]: https://github.com/nadeesha/ts-prune
563
+ [20]: https://github.com/jaydenseric/find-unused-exports
564
+ [21]: #custom-dependency-resolvers
565
+ [22]: #monorepos-1
566
+ [23]: https://github.com/depcheck/depcheck#special
567
+ [24]: #roadmap
package/dist/cli.js CHANGED
@@ -1,47 +1,23 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __importDefault = (this && this.__importDefault) || function (mod) {
4
- return (mod && mod.__esModule) ? mod : { "default": mod };
5
- };
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- const node_path_1 = __importDefault(require("node:path"));
8
- const node_util_1 = require("node:util");
9
- const _1 = require(".");
10
- const help_1 = require("./help");
11
- const reporters_1 = __importDefault(require("./reporters"));
12
- const errors_1 = require("./util/errors");
13
- const { values: { help, dir, config: configFilePath, tsConfig: tsConfigFilePath, include = [], exclude = [], ignore = [], 'no-gitignore': isNoGitIgnore = false, dev: isDev = false, 'include-entry-files': isIncludeEntryFiles = false, 'no-progress': noProgress = false, reporter = 'symbols', 'reporter-options': reporterOptions = '', 'max-issues': maxIssues = '0', jsdoc: jsDoc = [], debug: isDebug = false, 'debug-level': debugLevel = '1', }, } = (0, node_util_1.parseArgs)({
14
- options: {
15
- help: { type: 'boolean' },
16
- config: { type: 'string', short: 'c' },
17
- tsConfig: { type: 'string', short: 't' },
18
- dir: { type: 'string' },
19
- include: { type: 'string', multiple: true },
20
- exclude: { type: 'string', multiple: true },
21
- ignore: { type: 'string', multiple: true },
22
- 'no-gitignore': { type: 'boolean' },
23
- dev: { type: 'boolean' },
24
- 'include-entry-files': { type: 'boolean' },
25
- 'no-progress': { type: 'boolean' },
26
- 'max-issues': { type: 'string' },
27
- reporter: { type: 'string' },
28
- 'reporter-options': { type: 'string' },
29
- jsdoc: { type: 'string', multiple: true },
30
- debug: { type: 'boolean' },
31
- 'debug-level': { type: 'string' },
32
- },
33
- });
2
+ import path from 'node:path';
3
+ import parsedArgs from './util/parseArgs.js';
4
+ import { main } from './index.js';
5
+ import { printHelp } from './help.js';
6
+ import reporters from './reporters/index.js';
7
+ import { ConfigurationError } from './util/errors.js';
8
+ import { measure } from './util/performance.js';
9
+ const { values: { help, dir, config: configFilePath, tsConfig: tsConfigFilePath, include = [], exclude = [], ignore = [], 'no-gitignore': isNoGitIgnore = false, dev: isDev = false, 'include-entry-files': isIncludeEntryFiles = false, 'no-progress': noProgress = false, reporter = 'symbols', 'reporter-options': reporterOptions = '', 'max-issues': maxIssues = '0', debug: isDebug = false, 'debug-level': debugLevel = '1', }, } = parsedArgs;
34
10
  if (help) {
35
- (0, help_1.printHelp)();
11
+ printHelp();
36
12
  process.exit(0);
37
13
  }
38
14
  const cwd = process.cwd();
39
- const workingDir = dir ? node_path_1.default.resolve(dir) : cwd;
15
+ const workingDir = dir ? path.resolve(dir) : cwd;
40
16
  const isShowProgress = !isDebug && noProgress === false && process.stdout.isTTY && typeof process.stdout.cursorTo === 'function';
41
- const printReport = reporter in reporters_1.default ? reporters_1.default[reporter] : require(node_path_1.default.join(workingDir, reporter));
17
+ const printReport = reporter in reporters ? reporters[reporter] : await import(path.join(workingDir, reporter));
42
18
  const run = async () => {
43
19
  try {
44
- const { report, issues, counters } = await (0, _1.main)({
20
+ const { report, issues, counters } = await main({
45
21
  cwd,
46
22
  workingDir,
47
23
  configFilePath,
@@ -53,7 +29,6 @@ const run = async () => {
53
29
  isIncludeEntryFiles,
54
30
  isDev,
55
31
  isShowProgress,
56
- jsDoc,
57
32
  debug: {
58
33
  isEnabled: isDebug,
59
34
  level: isDebug ? Number(debugLevel) : 0,
@@ -63,13 +38,14 @@ const run = async () => {
63
38
  const totalErrorCount = Object.keys(report)
64
39
  .filter(reportGroup => report[reportGroup])
65
40
  .reduce((errorCount, reportGroup) => errorCount + counters[reportGroup], 0);
41
+ await measure.print();
66
42
  if (totalErrorCount > Number(maxIssues))
67
43
  process.exit(totalErrorCount);
68
44
  }
69
45
  catch (error) {
70
- if (error instanceof errors_1.ConfigurationError) {
46
+ if (error instanceof ConfigurationError) {
71
47
  console.error(error.message + '\n');
72
- (0, help_1.printHelp)();
48
+ printHelp();
73
49
  process.exit(1);
74
50
  }
75
51
  throw error;
package/dist/help.js CHANGED
@@ -1,7 +1,4 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.printHelp = void 0;
4
- const printHelp = () => {
1
+ export const printHelp = () => {
5
2
  console.log(`knip [options]
6
3
 
7
4
  Options:
@@ -18,9 +15,9 @@ Options:
18
15
  --max-issues Maximum number of issues before non-zero exit code (default: 0)
19
16
  --reporter Select reporter: symbols, compact, codeowners, json (default: symbols)
20
17
  --reporter-options Pass extra options to the reporter (as JSON string, see example)
21
- --jsdoc Enable JSDoc parsing, with options: public
22
18
  --debug Show debug output
23
19
  --debug-level Set verbosity of debug output (default: 1, max: 2)
20
+ --performance Measure running time of expensive functions and display stats table
24
21
 
25
22
  Issue types: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
26
23
 
@@ -28,10 +25,9 @@ Examples:
28
25
 
29
26
  $ knip
30
27
  $ knip --dir packages/client --include files
31
- $ knip -c ./knip.js --reporter compact --jsdoc public
28
+ $ knip -c ./knip.js --reporter compact
32
29
  $ knip --ignore 'lib/**/*.ts' --ignore build
33
30
  $ knip --reporter codeowners --reporter-options '{"path":".github/CODEOWNERS"}'
34
31
 
35
32
  More info: https://github.com/webpro/knip`);
36
33
  };
37
- exports.printHelp = printHelp;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { UnresolvedConfiguration } from './types';
1
+ import type { UnresolvedConfiguration } from './types.js';
2
2
  export declare const main: (unresolvedConfiguration: UnresolvedConfiguration) => Promise<{
3
- report: import("./types").Report;
4
- issues: import("./types").Issues;
5
- counters: import("./types").Counters;
3
+ report: import("./types.js").Report;
4
+ issues: import("./types.js").Issues;
5
+ counters: import("./types.js").Counters;
6
6
  }>;
package/dist/index.js CHANGED
@@ -1,55 +1,49 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.main = void 0;
7
- const typescript_1 = __importDefault(require("typescript"));
8
- const config_1 = require("./util/config");
9
- const fs_1 = require("./util/fs");
10
- const path_1 = require("./util/path");
11
- const glob_1 = require("./util/glob");
12
- const project_1 = require("./util/project");
13
- const runner_1 = require("./runner");
14
- const errors_1 = require("./util/errors");
15
- const debug_1 = require("./util/debug");
16
- const progress_1 = require("./progress");
17
- const main = async (unresolvedConfiguration) => {
18
- const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } = unresolvedConfiguration;
19
- const updateMessage = (0, progress_1.getMessageUpdater)(unresolvedConfiguration);
20
- (0, debug_1.debugLogObject)(debug, 1, 'Unresolved configuration', unresolvedConfiguration);
1
+ import ts from 'typescript';
2
+ import { resolveConfig, resolveIncludedIssueTypes } from './util/config.js';
3
+ import { findFile, loadJSON } from './util/fs.js';
4
+ import { relative } from './util/path.js';
5
+ import { _glob } from './util/glob.js';
6
+ import { _createProject, _resolveSourceFileDependencies, _removeExternalSourceFiles } from './util/project.js';
7
+ import { findIssues } from './runner.js';
8
+ import { ConfigurationError } from './util/errors.js';
9
+ import { debugLogObject, debugLogFiles, debugLogSourceFiles } from './util/debug.js';
10
+ import { getMessageUpdater } from './progress.js';
11
+ export const main = async (unresolvedConfiguration) => {
12
+ const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, debug, } = unresolvedConfiguration;
13
+ const updateMessage = getMessageUpdater(unresolvedConfiguration);
14
+ debugLogObject(debug, 1, 'Unresolved configuration', unresolvedConfiguration);
21
15
  updateMessage('Reading configuration and manifest files...');
22
- const manifestPath = await (0, fs_1.findFile)(cwd, workingDir, 'package.json');
23
- const manifest = manifestPath && require(manifestPath);
16
+ const manifestPath = await findFile(cwd, workingDir, 'package.json');
17
+ const manifest = manifestPath && (await loadJSON(manifestPath));
24
18
  if (!manifestPath || !manifest) {
25
- throw new errors_1.ConfigurationError('Unable to find package.json');
19
+ throw new ConfigurationError('Unable to find package.json');
26
20
  }
27
21
  const configFilePath = configFilePathArg ?? 'knip.json';
28
- const resolvedConfigFilePath = await (0, fs_1.findFile)(cwd, workingDir, configFilePath);
29
- const localConfig = resolvedConfigFilePath && require(resolvedConfigFilePath);
22
+ const resolvedConfigFilePath = await findFile(cwd, workingDir, configFilePath);
23
+ const localConfig = resolvedConfigFilePath && (await loadJSON(resolvedConfigFilePath));
30
24
  if (configFilePathArg && !resolvedConfigFilePath) {
31
- throw new errors_1.ConfigurationError(`Unable to find ${configFilePathArg}`);
25
+ throw new ConfigurationError(`Unable to find ${configFilePathArg}`);
32
26
  }
33
27
  const tsConfigFilePath = tsConfigFilePathArg ?? 'tsconfig.json';
34
- const resolvedTsConfigFilePath = await (0, fs_1.findFile)(cwd, workingDir, tsConfigFilePath);
28
+ const resolvedTsConfigFilePath = await findFile(cwd, workingDir, tsConfigFilePath);
35
29
  if (tsConfigFilePathArg && !resolvedTsConfigFilePath) {
36
- throw new errors_1.ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
30
+ throw new ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
37
31
  }
38
32
  let tsConfigPathGlobs = [];
39
33
  if (resolvedTsConfigFilePath) {
40
- const config = typescript_1.default.readConfigFile(resolvedTsConfigFilePath, typescript_1.default.sys.readFile);
34
+ const config = ts.readConfigFile(resolvedTsConfigFilePath, ts.sys.readFile);
41
35
  tsConfigPathGlobs = config.config.compilerOptions?.paths
42
36
  ? Object.keys(config.config.compilerOptions.paths).map(p => p.replace(/\*/g, '**'))
43
37
  : [];
44
38
  if (config.error) {
45
- throw new errors_1.ConfigurationError(`Unable to read ${(0, path_1.relative)(resolvedTsConfigFilePath)}`);
39
+ throw new ConfigurationError(`Unable to read ${relative(resolvedTsConfigFilePath)}`);
46
40
  }
47
41
  }
48
- const dir = (0, path_1.relative)(workingDir);
49
- const resolvedConfig = (0, config_1.resolveConfig)(manifest.knip ?? localConfig, { workingDir: dir, isDev });
50
- (0, debug_1.debugLogObject)(debug, 1, 'Resolved configuration', resolvedConfig);
42
+ const dir = relative(workingDir);
43
+ const resolvedConfig = resolveConfig(manifest.knip ?? localConfig, { workingDir: dir, isDev });
44
+ debugLogObject(debug, 1, 'Resolved configuration', resolvedConfig);
51
45
  if (!resolvedConfigFilePath && !manifest.knip && !resolvedTsConfigFilePath) {
52
- throw new errors_1.ConfigurationError(`Unable to find ${configFilePath} or package.json#knip or ${tsConfigFilePath}`);
46
+ throw new ConfigurationError(`Unable to find ${configFilePath} or package.json#knip or ${tsConfigFilePath}`);
53
47
  }
54
48
  const { entryFiles, productionFiles, projectFiles } = await (async () => {
55
49
  if (resolvedConfig) {
@@ -58,42 +52,42 @@ const main = async (unresolvedConfiguration) => {
58
52
  ? { tsConfigFilePath: resolvedTsConfigFilePath }
59
53
  : { compilerOptions: { allowJs: true } };
60
54
  updateMessage('Resolving entry files...');
61
- const entryPaths = await (0, glob_1.glob)({
55
+ const entryPaths = await _glob({
62
56
  cwd,
63
57
  workingDir,
64
58
  patterns: resolvedConfig.entryFiles,
65
59
  ignore,
66
60
  gitignore,
67
61
  });
68
- (0, debug_1.debugLogFiles)(debug, 1, 'Globbed entry paths', entryPaths);
69
- const production = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, entryPaths);
62
+ debugLogFiles(debug, 1, 'Globbed entry paths', entryPaths);
63
+ const production = _createProject({ ...projectOptions, ...skipAddFiles }, entryPaths);
70
64
  const entryFiles = production.getSourceFiles();
71
- (0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved entry source files', entryFiles);
72
- production.resolveSourceFileDependencies();
73
- const productionFiles = (0, project_1.removeExternalSourceFiles)(production);
74
- (0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved production source files', productionFiles);
65
+ debugLogSourceFiles(debug, 1, 'Resolved entry source files', entryFiles);
66
+ _resolveSourceFileDependencies(production);
67
+ const productionFiles = _removeExternalSourceFiles(production);
68
+ debugLogSourceFiles(debug, 1, 'Resolved production source files', productionFiles);
75
69
  updateMessage('Resolving project files...');
76
- const projectPaths = await (0, glob_1.glob)({
70
+ const projectPaths = await _glob({
77
71
  cwd,
78
72
  workingDir,
79
73
  patterns: resolvedConfig.projectFiles,
80
74
  ignore,
81
75
  gitignore,
82
76
  });
83
- (0, debug_1.debugLogFiles)(debug, 1, 'Globbed project paths', projectPaths);
84
- const project = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, projectPaths);
77
+ debugLogFiles(debug, 1, 'Globbed project paths', projectPaths);
78
+ const project = _createProject({ ...projectOptions, ...skipAddFiles }, projectPaths);
85
79
  const projectFiles = project.getSourceFiles();
86
- (0, debug_1.debugLogSourceFiles)(debug, 1, 'Resolved project source files', projectFiles);
80
+ debugLogSourceFiles(debug, 1, 'Resolved project source files', projectFiles);
87
81
  return { entryFiles, productionFiles, projectFiles };
88
82
  }
89
83
  else {
90
84
  updateMessage('Resolving project files...');
91
- const project = (0, project_1.createProject)({ tsConfigFilePath: resolvedTsConfigFilePath });
85
+ const project = _createProject({ tsConfigFilePath: resolvedTsConfigFilePath });
92
86
  const files = project.getSourceFiles();
93
87
  return { entryFiles: files, productionFiles: files, projectFiles: files };
94
88
  }
95
89
  })();
96
- const report = (0, config_1.resolveIncludedIssueTypes)(include, resolvedConfig ? exclude : ['files'], resolvedConfig);
90
+ const report = resolveIncludedIssueTypes(include, resolvedConfig ? exclude : ['files'], resolvedConfig);
97
91
  const config = {
98
92
  workingDir,
99
93
  report,
@@ -109,13 +103,9 @@ const main = async (unresolvedConfiguration) => {
109
103
  isDev: Boolean(resolvedConfig?.dev),
110
104
  tsConfigPathGlobs: tsConfigPathGlobs,
111
105
  isShowProgress,
112
- jsDocOptions: {
113
- isReadPublicTag: jsDoc.includes('public'),
114
- },
115
106
  debug,
116
107
  };
117
- const { issues, counters } = await (0, runner_1.findIssues)(config);
118
- (0, debug_1.debugLogObject)(debug, 2, 'Issues', issues);
108
+ const { issues, counters } = await findIssues(config);
109
+ debugLogObject(debug, 2, 'Issues', issues);
119
110
  return { report, issues, counters };
120
111
  };
121
- exports.main = main;
package/dist/log.js CHANGED
@@ -1,9 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LineRewriter = exports.getLine = void 0;
4
- const getLine = (value, text) => `${String(value).padStart(5)} ${text}`;
5
- exports.getLine = getLine;
6
- class LineRewriter {
1
+ export const getLine = (value, text) => `${String(value).padStart(5)} ${text}`;
2
+ export class LineRewriter {
7
3
  lines = 0;
8
4
  clearLines(count) {
9
5
  if (count > 0) {
@@ -23,4 +19,3 @@ class LineRewriter {
23
19
  this.lines = messages.length;
24
20
  }
25
21
  }
26
- exports.LineRewriter = LineRewriter;
@@ -1,4 +1,4 @@
1
- import type { Configuration, Counters, Issue } from './types';
1
+ import type { Configuration, Counters, Issue } from './types.js';
2
2
  export declare const getMessageUpdater: (configuration: {
3
3
  isShowProgress: boolean;
4
4
  }) => (message: string) => void;