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.
- package/README.md +116 -75
- package/dist/cli.js +15 -39
- package/dist/help.js +3 -7
- package/dist/index.d.ts +4 -4
- package/dist/index.js +43 -53
- package/dist/log.js +2 -7
- package/dist/progress.d.ts +1 -1
- package/dist/progress.js +14 -19
- package/dist/reporters/codeowners.d.ts +1 -1
- package/dist/reporters/codeowners.js +11 -16
- package/dist/reporters/compact.d.ts +1 -1
- package/dist/reporters/compact.js +6 -8
- package/dist/reporters/constants.js +1 -4
- package/dist/reporters/index.d.ts +4 -4
- package/dist/reporters/index.js +9 -14
- package/dist/reporters/json.d.ts +1 -1
- package/dist/reporters/json.js +8 -13
- package/dist/reporters/symbols.d.ts +1 -1
- package/dist/reporters/symbols.js +6 -8
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +46 -54
- package/dist/types.d.ts +0 -4
- package/dist/types.js +1 -2
- package/dist/util/config.d.ts +1 -1
- package/dist/util/config.js +19 -16
- package/dist/util/debug.js +6 -15
- package/dist/util/dependencies.d.ts +2 -2
- package/dist/util/dependencies.js +15 -17
- package/dist/util/errors.js +1 -5
- package/dist/util/fs.d.ts +1 -0
- package/dist/util/fs.js +12 -17
- package/dist/util/glob.d.ts +1 -1
- package/dist/util/glob.js +8 -20
- package/dist/util/parseArgs.d.ts +23 -0
- package/dist/util/parseArgs.js +22 -0
- package/dist/util/path.js +2 -9
- package/dist/util/performance.d.ts +22 -0
- package/dist/util/performance.js +81 -0
- package/dist/util/project.d.ts +9 -3
- package/dist/util/project.js +16 -9
- package/dist/util/type.js +5 -9
- 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]
|
|
22
|
-
- [x] Supports JavaScript
|
|
23
|
-
- [x]
|
|
24
|
-
- [x]
|
|
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.
|
|
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][
|
|
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
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
|
|
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`]
|
|
281
|
-
- [`symbol`]
|
|
282
|
-
- [`compact`]
|
|
283
|
-
- [`codeowners`]
|
|
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]
|
|
377
|
+
The keys match the [known issue types][13].
|
|
339
378
|
|
|
340
379
|
#### Usage Ideas
|
|
341
380
|
|
|
342
|
-
Use tools like [miller]
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
src/
|
|
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
|
-
|
|
358
|
-
|
|
|
359
|
-
|
|
|
360
|
-
| src/
|
|
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 --
|
|
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][
|
|
467
|
-
| --------------------------------- | :------: |
|
|
468
|
-
| Unused files | ✅ | -
|
|
469
|
-
| Unused dependencies | ✅ |
|
|
470
|
-
| Unlisted dependencies | ✅ |
|
|
471
|
-
| [Custom dependency resolvers][
|
|
472
|
-
| Unused exports | ✅ | -
|
|
473
|
-
| Duplicate exports | ✅ | -
|
|
474
|
-
| Search namespaces | ✅ | -
|
|
475
|
-
| Custom reporters | ✅ | -
|
|
476
|
-
|
|
|
477
|
-
| Configure entry files | ✅ |
|
|
478
|
-
| [Support monorepos][
|
|
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][
|
|
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]:
|
|
517
|
-
[7]:
|
|
518
|
-
[8]:
|
|
519
|
-
[9]:
|
|
520
|
-
[10]:
|
|
521
|
-
[11]:
|
|
522
|
-
[12]: #
|
|
523
|
-
[13]: #
|
|
524
|
-
[14]:
|
|
525
|
-
[15]: https://github.com/
|
|
526
|
-
[16]: https://github.com/
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
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
|
-
|
|
11
|
+
printHelp();
|
|
36
12
|
process.exit(0);
|
|
37
13
|
}
|
|
38
14
|
const cwd = process.cwd();
|
|
39
|
-
const workingDir = dir ?
|
|
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
|
|
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
|
|
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
|
|
46
|
+
if (error instanceof ConfigurationError) {
|
|
71
47
|
console.error(error.message + '\n');
|
|
72
|
-
|
|
48
|
+
printHelp();
|
|
73
49
|
process.exit(1);
|
|
74
50
|
}
|
|
75
51
|
throw error;
|
package/dist/help.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
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
|
|
23
|
-
const manifest = 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
|
|
19
|
+
throw new ConfigurationError('Unable to find package.json');
|
|
26
20
|
}
|
|
27
21
|
const configFilePath = configFilePathArg ?? 'knip.json';
|
|
28
|
-
const resolvedConfigFilePath = await
|
|
29
|
-
const localConfig = resolvedConfigFilePath &&
|
|
22
|
+
const resolvedConfigFilePath = await findFile(cwd, workingDir, configFilePath);
|
|
23
|
+
const localConfig = resolvedConfigFilePath && (await loadJSON(resolvedConfigFilePath));
|
|
30
24
|
if (configFilePathArg && !resolvedConfigFilePath) {
|
|
31
|
-
throw new
|
|
25
|
+
throw new ConfigurationError(`Unable to find ${configFilePathArg}`);
|
|
32
26
|
}
|
|
33
27
|
const tsConfigFilePath = tsConfigFilePathArg ?? 'tsconfig.json';
|
|
34
|
-
const resolvedTsConfigFilePath = await
|
|
28
|
+
const resolvedTsConfigFilePath = await findFile(cwd, workingDir, tsConfigFilePath);
|
|
35
29
|
if (tsConfigFilePathArg && !resolvedTsConfigFilePath) {
|
|
36
|
-
throw new
|
|
30
|
+
throw new ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
|
|
37
31
|
}
|
|
38
32
|
let tsConfigPathGlobs = [];
|
|
39
33
|
if (resolvedTsConfigFilePath) {
|
|
40
|
-
const config =
|
|
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
|
|
39
|
+
throw new ConfigurationError(`Unable to read ${relative(resolvedTsConfigFilePath)}`);
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
|
-
const dir =
|
|
49
|
-
const resolvedConfig =
|
|
50
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
69
|
-
const production = (
|
|
62
|
+
debugLogFiles(debug, 1, 'Globbed entry paths', entryPaths);
|
|
63
|
+
const production = _createProject({ ...projectOptions, ...skipAddFiles }, entryPaths);
|
|
70
64
|
const entryFiles = production.getSourceFiles();
|
|
71
|
-
|
|
72
|
-
production
|
|
73
|
-
const productionFiles = (
|
|
74
|
-
|
|
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 (
|
|
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
|
-
|
|
84
|
-
const project = (
|
|
77
|
+
debugLogFiles(debug, 1, 'Globbed project paths', projectPaths);
|
|
78
|
+
const project = _createProject({ ...projectOptions, ...skipAddFiles }, projectPaths);
|
|
85
79
|
const projectFiles = project.getSourceFiles();
|
|
86
|
-
|
|
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 = (
|
|
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 =
|
|
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
|
|
118
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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;
|
package/dist/progress.d.ts
CHANGED