knip 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +183 -180
- package/dist/cli.js +3 -1
- package/dist/help.js +4 -3
- package/dist/index.js +65 -50
- package/dist/runner.js +16 -16
- package/dist/types.d.ts +2 -0
- package/dist/util/config.js +2 -0
- package/dist/util/debug.js +8 -8
- package/dist/util/fs.d.ts +1 -1
- package/dist/util/fs.js +6 -4
- package/dist/util/project.d.ts +1 -4
- package/dist/util/project.js +2 -6
- package/license +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
# ✂️ Knip
|
|
2
2
|
|
|
3
|
-
Knip
|
|
4
|
-
|
|
3
|
+
Knip finds **unused files, dependencies and exports** in your JavaScript and TypeScript projects. Less code leads to
|
|
4
|
+
improved performance, less maintenance and easier refactorings.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
7
|
export const myVar = true;
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
ESLint handles files in isolation, so the `export` keyword "blocks" further analysis. Unused files and dependencies will
|
|
11
|
-
also not be detected. You could think of Knip as going (far!) beyond the
|
|
12
|
-
|
|
11
|
+
also not be detected. You could think of Knip as going (far!) beyond the `no-unused-vars` rule of ESLint. Knip lints the
|
|
12
|
+
project as a whole (or parts of it).
|
|
13
13
|
|
|
14
|
-
It's only human to forget
|
|
15
|
-
|
|
16
|
-
start finding things that can be removed during maintenance and refactorings? Especially in larger projects all of this
|
|
17
|
-
can be tedious. This is where Knip comes in.
|
|
14
|
+
It's only human to forget removing things that you no longer use. But how do you find out? Where to even start finding
|
|
15
|
+
things that can be removed?
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
The dots don't connect themselves. This is where Knip comes in:
|
|
20
18
|
|
|
21
19
|
- [x] Finds **unused files, dependencies and exports**.
|
|
22
20
|
- [x] Finds dependencies not listed in `package.json`.
|
|
@@ -24,24 +22,21 @@ Boring stuff should be automated! Just let Knip have a crack at it:
|
|
|
24
22
|
- [x] Supports JavaScript inside TypeScript projects (`"allowJs": true`).
|
|
25
23
|
- [x] Finds duplicate exports of the same symbol.
|
|
26
24
|
- [x] Supports JavaScript ES Module-based projects without a `tsconfig.json`.
|
|
27
|
-
- [x] Features multiple [reporters]
|
|
25
|
+
- [x] Features multiple [reporters][1] and supports [custom reporters][2].
|
|
28
26
|
|
|
29
27
|
Knip really shines in larger projects. A little bit of configuration will pay off, I promise. A comparison with similar
|
|
30
|
-
tools answers the question
|
|
31
|
-
[why another unused file/dependency/export finder?](#why-yet-another-unused-filedependencyexport-finder)
|
|
28
|
+
tools answers the question [why another unused file/dependency/export finder?][3]
|
|
32
29
|
|
|
33
30
|
Knip is a fresh take on keeping your projects clean & tidy!
|
|
34
31
|
|
|
35
|
-
[![An orange cow with scissors, Van Gogh style]
|
|
36
|
-
|
|
32
|
+
[![An orange cow with scissors, Van Gogh style][5]][4] <sup>_“An orange cow with scissors, Van Gogh style” - generated
|
|
33
|
+
with OpenAI_</sup>
|
|
37
34
|
|
|
38
35
|
## Installation
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
npm install -D knip
|
|
42
|
-
```
|
|
37
|
+
npm install -D knip
|
|
43
38
|
|
|
44
|
-
Knip requires least Node.js v16.17 or v18
|
|
39
|
+
Knip requires at least Node.js v16.17 or v18. Knip is _cutting edge!_
|
|
45
40
|
|
|
46
41
|
## Usage
|
|
47
42
|
|
|
@@ -59,67 +54,67 @@ all files it should match them against, including potentially unused files.
|
|
|
59
54
|
|
|
60
55
|
Then run the checks:
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
npx knip
|
|
64
|
-
```
|
|
57
|
+
npx knip
|
|
65
58
|
|
|
66
59
|
This will analyze the project and output unused files, exports, types and duplicate exports.
|
|
67
60
|
|
|
68
|
-
Use `--include files` when configuring knip the first time for faster initial results.
|
|
69
|
-
|
|
70
61
|
## How It Works
|
|
71
62
|
|
|
72
63
|
Knip works by creating two sets of files:
|
|
73
64
|
|
|
74
|
-
1.
|
|
75
|
-
2.
|
|
76
|
-
3.
|
|
77
|
-
4.
|
|
65
|
+
1. Production code is the set of files resolved from the `entryFiles`.
|
|
66
|
+
2. They are matched against the set of `projectFiles`.
|
|
67
|
+
3. The subset of project files that is not production code will be reported as unused files (in red).
|
|
68
|
+
4. Then the production code (in blue) will be analyzed for unused exports.
|
|
78
69
|
|
|
79
|
-
![How it works]
|
|
70
|
+
![How it works][6]
|
|
80
71
|
|
|
81
72
|
## Options
|
|
82
73
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
$ knip
|
|
109
|
-
$ knip --
|
|
110
|
-
$ knip
|
|
111
|
-
$ knip --
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
74
|
+
❯ npx knip
|
|
75
|
+
knip [options]
|
|
76
|
+
|
|
77
|
+
Options:
|
|
78
|
+
-c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
|
|
79
|
+
-t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
|
|
80
|
+
--dir Working directory (default: current working directory)
|
|
81
|
+
--include Report only listed issue type(s) (see below)
|
|
82
|
+
--exclude Exclude issue type(s) from report (see below)
|
|
83
|
+
--ignore Ignore files matching this glob pattern (can be set multiple times)
|
|
84
|
+
--no-gitignore Don't use .gitignore
|
|
85
|
+
--dev Include `devDependencies` in report(s)
|
|
86
|
+
--no-progress Don't show dynamic progress updates
|
|
87
|
+
--max-issues Maximum number of issues before non-zero exit code (default: 0)
|
|
88
|
+
--reporter Select reporter: symbols, compact, codeowners (default: symbols)
|
|
89
|
+
--reporter-options Pass extra options to the reporter (as JSON string, see example)
|
|
90
|
+
--jsdoc Enable JSDoc parsing, with options: public
|
|
91
|
+
--debug Show debug output
|
|
92
|
+
--debug-level Set verbosity of debug output (default: 1, max: 2)
|
|
93
|
+
|
|
94
|
+
Issue types: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
|
|
95
|
+
|
|
96
|
+
Examples:
|
|
97
|
+
|
|
98
|
+
$ knip
|
|
99
|
+
$ knip --dir packages/client --include files
|
|
100
|
+
$ knip -c ./knip.js --reporter compact --jsdoc public
|
|
101
|
+
$ knip --ignore 'lib/**/*.ts' --ignore build
|
|
102
|
+
$ knip --reporter codeowners --reporter-options '{"path":".github/CODEOWNERS"}'
|
|
103
|
+
|
|
104
|
+
More info: https://github.com/webpro/knip
|
|
105
|
+
|
|
106
|
+
## Performance
|
|
116
107
|
|
|
117
|
-
🚀 Knip is considerably faster when only the `files` and/or `duplicates`
|
|
108
|
+
🚀 Knip is considerably faster when only the `files` and/or `duplicates` types are included. Finding unused exports
|
|
109
|
+
requires deeper analysis (`exports`, `nsExports`, `types`, `nsTypes`). The following example commands do the same:
|
|
110
|
+
|
|
111
|
+
knip --include files --include duplicates
|
|
112
|
+
knip --include files,duplicates
|
|
118
113
|
|
|
119
114
|
## Reading the report
|
|
120
115
|
|
|
121
116
|
After analyzing all the files resolved from the `entryFiles` against the `projectFiles`, the report contains the
|
|
122
|
-
following
|
|
117
|
+
following types of issues:
|
|
123
118
|
|
|
124
119
|
- `files` - Unused files: did not find references to this file
|
|
125
120
|
- `dependencies` - Unused dependencies: did not find references to this dependency
|
|
@@ -130,25 +125,26 @@ following groups of issues:
|
|
|
130
125
|
- `nsTypes` - Unused types in namespaces: did not find direct references to this exported variable (2)
|
|
131
126
|
- `duplicates` - Duplicate exports: the same thing is exported more than once with different names from the same file
|
|
132
127
|
|
|
133
|
-
|
|
128
|
+
1. This may also include dependencies that could not be resolved properly (such as non-relative `local/dir/file.ts` not
|
|
129
|
+
and `local` not being in `node_modules`).
|
|
130
|
+
2. The variable or type is not referenced directly, and has become a member of a namespace. That's why Knip is not sure
|
|
131
|
+
whether this export can be removed, so please look into it:
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
2. The variable or type is not referenced directly, and has become a member of a namespace. That's why Knip is not sure
|
|
138
|
-
whether this export can be removed, so please look into it:
|
|
133
|
+
You can `--include` or `--exclude` any of the types to slice & dice the report to your needs. Alternatively, they can be
|
|
134
|
+
added to the configuration (e.g. `"exclude": ["dependencies"]`).
|
|
139
135
|
|
|
140
136
|
## Now what?
|
|
141
137
|
|
|
142
138
|
As always, make sure to backup files or use Git before deleting files or making changes. Run tests to verify results.
|
|
143
139
|
|
|
144
|
-
- Unused files can be
|
|
140
|
+
- Unused files can be removed.
|
|
145
141
|
- Unused dependencies can be removed from `package.json`.
|
|
146
142
|
- Unlisted dependencies should be added to `package.json`.
|
|
147
143
|
- Unused exports and types: remove the `export` keyword in front of unused exports. Then you (or tools such as the
|
|
148
144
|
TypeScript language server in VS Code and/or ESLint) can see whether the variable or type is used within the same
|
|
149
145
|
file. If this is not the case, it can be removed.
|
|
150
146
|
|
|
151
|
-
🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to
|
|
147
|
+
🔁 Repeat the process to reveal new unused files and exports. Sometimes it's so liberating to remove things!
|
|
152
148
|
|
|
153
149
|
## Production versus non-production code
|
|
154
150
|
|
|
@@ -157,12 +153,12 @@ Feels like you're getting too many false positives? Let's talk about `entryFiles
|
|
|
157
153
|
### Production code
|
|
158
154
|
|
|
159
155
|
The default configuration for Knip is very strict and targets production code. Non-production files such as tests should
|
|
160
|
-
not be part of the `entryFiles`. Here's why: test and other non-production files often import
|
|
161
|
-
will prevent the production files from being reported as unused. For best results:
|
|
156
|
+
not be part of the `entryFiles` and `projectFiles`. Here's why: test and other non-production files often import
|
|
157
|
+
production files, which will prevent the production files from being reported as unused. For best results:
|
|
162
158
|
|
|
163
159
|
- Include only production entry files to the `entryFiles`.
|
|
164
160
|
- Include only and all production files to the `projectFiles`.
|
|
165
|
-
- If necessary, exclude non-production files from the `projectFiles` (using negation
|
|
161
|
+
- If necessary, add globs to exclude non-production files from the `projectFiles` (using negation pattern).
|
|
166
162
|
|
|
167
163
|
This will ensure Knip understands what production code can be removed.
|
|
168
164
|
|
|
@@ -175,8 +171,8 @@ To analyze the project as a whole:
|
|
|
175
171
|
|
|
176
172
|
- Include both production entry files and test files to the `entryFiles`.
|
|
177
173
|
- Include all production files to the `projectFiles`.
|
|
178
|
-
-
|
|
179
|
-
-
|
|
174
|
+
- If necessary, add globs for non-production files to the `projectFiles`.
|
|
175
|
+
- Set `dev: true` in the configuration or add `--dev` as a command line flag (to add `devDependencies`).
|
|
180
176
|
|
|
181
177
|
Here's an example:
|
|
182
178
|
|
|
@@ -213,6 +209,11 @@ Using the `--dev` flag will now switch to the non-production analysis.
|
|
|
213
209
|
|
|
214
210
|
Depending on the complexity of the project, be aware that it might require some fine-tuning on your end.
|
|
215
211
|
|
|
212
|
+
## Zero-config
|
|
213
|
+
|
|
214
|
+
Knip can work without any configuration. Then an existing `tsconfig.json` file is required. Since `entryFiles` and
|
|
215
|
+
`projectFiles` are now the same, Knip is unable to report unused files.
|
|
216
|
+
|
|
216
217
|
## More configuration examples
|
|
217
218
|
|
|
218
219
|
### Monorepos
|
|
@@ -233,18 +234,18 @@ packages can be configured using globs:
|
|
|
233
234
|
|
|
234
235
|
Packages can also be explicitly configured per package directory.
|
|
235
236
|
|
|
236
|
-
To
|
|
237
|
+
To analyze the packages separately, using the matching pattern from the configuration file:
|
|
237
238
|
|
|
238
|
-
|
|
239
|
-
knip --dir packages/
|
|
240
|
-
knip --dir packages/services
|
|
241
|
-
```
|
|
239
|
+
knip --dir packages/client
|
|
240
|
+
knip --dir packages/services
|
|
242
241
|
|
|
243
242
|
#### Connected projects
|
|
244
243
|
|
|
245
|
-
A good example of a large project setup is a monorepo
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
A good example of a large project setup is a monorepo. Let's take an example (Nx) project configuration using Next.js,
|
|
245
|
+
Jest and Storybook, which has multiple apps and libs. They are not published separately and don't have their own
|
|
246
|
+
`package.json`.
|
|
247
|
+
|
|
248
|
+
This configuration file can also be a JavaScript file, which allows to add logic and/or comments (e.g. `knip.js`):
|
|
248
249
|
|
|
249
250
|
```js
|
|
250
251
|
const entryFiles = ['apps/**/pages/**/*.{js,ts,tsx}'];
|
|
@@ -266,8 +267,8 @@ const projectFiles = [
|
|
|
266
267
|
module.exports = { entryFiles, projectFiles };
|
|
267
268
|
```
|
|
268
269
|
|
|
269
|
-
This should give good results about unused files and exports for the monorepo. After the first run, the
|
|
270
|
-
can be tweaked further to the project structure.
|
|
270
|
+
This should give good results about unused files, dependencies and exports for the monorepo. After the first run, the
|
|
271
|
+
configuration can be tweaked further to the project structure.
|
|
271
272
|
|
|
272
273
|
## Reporters
|
|
273
274
|
|
|
@@ -302,84 +303,78 @@ The data can then be used to write issues to `stdout`, a JSON or CSV file, or se
|
|
|
302
303
|
|
|
303
304
|
The default reporter shows the sorted symbols first:
|
|
304
305
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
src/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
type
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
ProductsList, default src/components/Products.tsx
|
|
328
|
-
```
|
|
306
|
+
$ knip
|
|
307
|
+
--- UNUSED FILES (2)
|
|
308
|
+
src/chat/helpers.ts
|
|
309
|
+
src/components/SideBar.tsx
|
|
310
|
+
--- UNUSED DEPENDENCIES (1)
|
|
311
|
+
moment
|
|
312
|
+
--- UNLISTED DEPENDENCIES (1)
|
|
313
|
+
react
|
|
314
|
+
--- UNUSED EXPORTS (5)
|
|
315
|
+
lowercaseFirstLetter src/common/src/string/index.ts
|
|
316
|
+
RegistrationBox src/components/Registration.tsx
|
|
317
|
+
clamp src/css.ts
|
|
318
|
+
restoreSession src/services/authentication.ts
|
|
319
|
+
PREFIX src/services/authentication.ts
|
|
320
|
+
--- UNUSED TYPES (4)
|
|
321
|
+
enum RegistrationServices src/components/Registration/registrationMachine.ts
|
|
322
|
+
type RegistrationAction src/components/Registration/registrationMachine.ts
|
|
323
|
+
type ComponentProps src/components/Registration.tsx
|
|
324
|
+
interface ProductDetail src/types/Product.ts
|
|
325
|
+
--- DUPLICATE EXPORTS (2)
|
|
326
|
+
Registration, default src/components/Registration.tsx
|
|
327
|
+
ProductsList, default src/components/Products.tsx
|
|
329
328
|
|
|
330
329
|
#### Compact
|
|
331
330
|
|
|
332
331
|
The compact reporter shows the sorted files first, and then a list of symbols:
|
|
333
332
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
src/
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
src/
|
|
345
|
-
src/
|
|
346
|
-
src/
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
src/components/Registration
|
|
350
|
-
src/
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
src/components/
|
|
354
|
-
src/components/Products.tsx: ProductsList, default
|
|
355
|
-
```
|
|
333
|
+
$ knip --reporter compact
|
|
334
|
+
--- UNUSED FILES (2)
|
|
335
|
+
src/chat/helpers.ts
|
|
336
|
+
src/components/SideBar.tsx
|
|
337
|
+
--- UNUSED DEPENDENCIES (1)
|
|
338
|
+
moment
|
|
339
|
+
--- UNLISTED DEPENDENCIES (1)
|
|
340
|
+
react
|
|
341
|
+
--- UNUSED EXPORTS (4)
|
|
342
|
+
src/common/src/string/index.ts: lowercaseFirstLetter
|
|
343
|
+
src/components/Registration.tsx: RegistrationBox
|
|
344
|
+
src/css.ts: clamp
|
|
345
|
+
src/services/authentication.ts: restoreSession, PREFIX
|
|
346
|
+
--- UNUSED TYPES (3)
|
|
347
|
+
src/components/Registration/registrationMachine.ts: RegistrationServices, RegistrationAction
|
|
348
|
+
src/components/Registration.tsx: ComponentProps
|
|
349
|
+
src/types/Product.ts: ProductDetail
|
|
350
|
+
--- DUPLICATE EXPORTS (2)
|
|
351
|
+
src/components/Registration.tsx: Registration, default
|
|
352
|
+
src/components/Products.tsx: ProductsList, default
|
|
356
353
|
|
|
357
354
|
#### Code Owners
|
|
358
355
|
|
|
359
356
|
The `codeowners` reporter is like `compact`, but shows the sorted code owners (according to `.github/CODEOWNERS`) first:
|
|
360
357
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
@org/
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
@org/
|
|
372
|
-
@org/owner src/
|
|
373
|
-
@org/owner src/
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
@org/owner src/components/Registration
|
|
377
|
-
@org/owner src/
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
@org/owner src/components/
|
|
381
|
-
@org/owner src/components/Products.tsx: ProductsList, default
|
|
382
|
-
```
|
|
358
|
+
$ knip --reporter codeowners
|
|
359
|
+
--- UNUSED FILES (2)
|
|
360
|
+
@org/team src/chat/helpers.ts
|
|
361
|
+
@org/owner src/components/SideBar.tsx
|
|
362
|
+
--- UNUSED DEPENDENCIES (1)
|
|
363
|
+
@org/admin moment
|
|
364
|
+
--- UNLISTED DEPENDENCIES (1)
|
|
365
|
+
@org/owner src/components/Registration.tsx react
|
|
366
|
+
--- UNUSED EXPORTS (4)
|
|
367
|
+
@org/team src/common/src/string/index.ts: lowercaseFirstLetter
|
|
368
|
+
@org/owner src/components/Registration.tsx: RegistrationBox
|
|
369
|
+
@org/owner src/css.ts: clamp
|
|
370
|
+
@org/owner src/services/authentication.ts: restoreSession, PREFIX
|
|
371
|
+
--- UNUSED TYPES (3)
|
|
372
|
+
@org/owner src/components/Registration/registrationMachine.ts: RegistrationServices, RegistrationAction
|
|
373
|
+
@org/owner src/components/Registration.tsx: ComponentProps
|
|
374
|
+
@org/owner src/types/Product.ts: ProductDetail
|
|
375
|
+
--- DUPLICATE EXPORTS (2)
|
|
376
|
+
@org/owner src/components/Registration.tsx: Registration, default
|
|
377
|
+
@org/owner src/components/Products.tsx: ProductsList, default
|
|
383
378
|
|
|
384
379
|
The owner of `package.json` is considered the owner of unused (dev) dependencies.
|
|
385
380
|
|
|
@@ -397,26 +392,26 @@ collect the various issues in one go?
|
|
|
397
392
|
|
|
398
393
|
This table is a work in progress, but here's a first impression. Based on their docs (please report any mistakes):
|
|
399
394
|
|
|
400
|
-
| Feature
|
|
401
|
-
|
|
|
402
|
-
| Unused files
|
|
403
|
-
| Unused dependencies
|
|
404
|
-
| Unlisted dependencies
|
|
405
|
-
| [Custom dependency resolvers][
|
|
406
|
-
| Unused exports
|
|
407
|
-
| Duplicate exports
|
|
408
|
-
| Search namespaces
|
|
409
|
-
| Custom reporters
|
|
410
|
-
| Pure JavaScript/ESM
|
|
411
|
-
| Configure entry files
|
|
412
|
-
| [Support monorepos][
|
|
413
|
-
| ESLint plugin available
|
|
395
|
+
| Feature | **knip** | [depcheck][7] | [unimported][8] | [ts-unused-exports][9] | [ts-prune][10] | [find-unused-exports][11] |
|
|
396
|
+
| --------------------------------- | :------: | :-----------: | :-------------: | :--------------------: | :------------: | :-----------------------: |
|
|
397
|
+
| Unused files | ✅ | - | ✅ | - | - | - |
|
|
398
|
+
| Unused dependencies | ✅ | ✅ | ✅ | - | - | - |
|
|
399
|
+
| Unlisted dependencies | ✅ | ✅ | ✅ | - | - | - |
|
|
400
|
+
| [Custom dependency resolvers][12] | ❌ | ✅ | ❌ | - | - | - |
|
|
401
|
+
| Unused exports | ✅ | - | - | ✅ | ✅ | ✅ |
|
|
402
|
+
| Duplicate exports | ✅ | - | - | ❌ | ❌ | ❌ |
|
|
403
|
+
| Search namespaces | ✅ | - | - | ✅ | ❌ | ❌ |
|
|
404
|
+
| Custom reporters | ✅ | - | - | - | - | - |
|
|
405
|
+
| Pure JavaScript/ESM | ✅ | ✅ | ✅ | - | - | ✅ |
|
|
406
|
+
| Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
407
|
+
| [Support monorepos][13] | 🟠 | - | - | - | - | - |
|
|
408
|
+
| ESLint plugin available | - | - | - | ✅ | - | - |
|
|
414
409
|
|
|
415
410
|
✅ = Supported, ❌ = Not supported, - = Out of scope
|
|
416
411
|
|
|
417
412
|
## Monorepos
|
|
418
413
|
|
|
419
|
-
Knip wants to [support monorepos]
|
|
414
|
+
Knip wants to [support monorepos][14] properly, the first steps in this direction are implemented.
|
|
420
415
|
|
|
421
416
|
## Custom dependency resolvers
|
|
422
417
|
|
|
@@ -425,8 +420,7 @@ directory of a monorepo is nice for DX. But Knip will need some help to find it
|
|
|
425
420
|
`eslint-plugin-cypress` _dependency_. Or see it is not listed in `package.json`. Or that the dependency is still listed,
|
|
426
421
|
but no longer in use. Many popular projects reference plugins in similar ways, such as Babel, Webpack and Storybook.
|
|
427
422
|
|
|
428
|
-
Big compliments to [depcheck]
|
|
429
|
-
"specials". [Knip has this ambition][6], too.
|
|
423
|
+
Big compliments to [depcheck][15] which already does this! They call this "specials". [Knip has this ambition][16], too.
|
|
430
424
|
|
|
431
425
|
unimported is strict in this regard and works based on production files and `dependencies`, so does not have custom
|
|
432
426
|
dependency resolvers which are usually only needed for `devDependencies`.
|
|
@@ -434,19 +428,28 @@ dependency resolvers which are usually only needed for `devDependencies`.
|
|
|
434
428
|
## TypeScript language services
|
|
435
429
|
|
|
436
430
|
TypeScript language services could play a major role in most of the "unused" areas, as they have an overview of the
|
|
437
|
-
project as a whole.
|
|
438
|
-
exports" or "custom dependency resolvers" are
|
|
431
|
+
project as a whole. This powers things in VS Code like "Find references" or the "Module "./some" declares 'Thing'
|
|
432
|
+
locally, but it is not exported" message. I think features like "duplicate exports" or "custom dependency resolvers" are
|
|
433
|
+
userland territory, much like code linters.
|
|
439
434
|
|
|
440
435
|
## Knip?!
|
|
441
436
|
|
|
442
437
|
Knip is Dutch for a "cut". A Dutch expression is "to be ge**knip**t for something", which means to be perfectly suited
|
|
443
438
|
for the job. I'm motivated to make knip perfectly suited for the job of cutting projects to perfection! ✂️
|
|
444
439
|
|
|
445
|
-
[1]:
|
|
446
|
-
[2]:
|
|
447
|
-
[3]:
|
|
448
|
-
[4]: https://
|
|
449
|
-
[5]:
|
|
450
|
-
[6]:
|
|
451
|
-
[7]:
|
|
452
|
-
[8]:
|
|
440
|
+
[1]: #reporters
|
|
441
|
+
[2]: #custom-reporters
|
|
442
|
+
[3]: #really-another-unused-filedependencyexport-finder
|
|
443
|
+
[4]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
|
|
444
|
+
[5]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
|
|
445
|
+
[6]: ./assets/how-it-works.drawio.svg
|
|
446
|
+
[7]: https://github.com/depcheck/depcheck
|
|
447
|
+
[8]: https://github.com/smeijer/unimported
|
|
448
|
+
[9]: https://github.com/pzavolinsky/ts-unused-exports
|
|
449
|
+
[10]: https://github.com/nadeesha/ts-prune
|
|
450
|
+
[11]: https://github.com/jaydenseric/find-unused-exports
|
|
451
|
+
[12]: #custom-dependency-resolvers
|
|
452
|
+
[13]: #monorepos-1
|
|
453
|
+
[14]: #monorepos
|
|
454
|
+
[15]: https://github.com/depcheck/depcheck#special
|
|
455
|
+
[16]: https://github.com/webpro/knip/issues/7
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const _1 = require(".");
|
|
|
10
10
|
const help_1 = require("./help");
|
|
11
11
|
const reporters_1 = __importDefault(require("./reporters"));
|
|
12
12
|
const errors_1 = require("./util/errors");
|
|
13
|
-
const { values: { help, dir, config: configFilePath
|
|
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
14
|
options: {
|
|
15
15
|
help: { type: 'boolean' },
|
|
16
16
|
config: { type: 'string', short: 'c' },
|
|
@@ -21,6 +21,7 @@ const { values: { help, dir, config: configFilePath = 'knip.json', tsConfig: tsC
|
|
|
21
21
|
ignore: { type: 'string', multiple: true },
|
|
22
22
|
'no-gitignore': { type: 'boolean' },
|
|
23
23
|
dev: { type: 'boolean' },
|
|
24
|
+
'include-entry-files': { type: 'boolean' },
|
|
24
25
|
'no-progress': { type: 'boolean' },
|
|
25
26
|
'max-issues': { type: 'string' },
|
|
26
27
|
reporter: { type: 'string' },
|
|
@@ -49,6 +50,7 @@ const run = async () => {
|
|
|
49
50
|
exclude,
|
|
50
51
|
ignore,
|
|
51
52
|
gitignore: !isNoGitIgnore,
|
|
53
|
+
isIncludeEntryFiles,
|
|
52
54
|
isDev,
|
|
53
55
|
isShowProgress,
|
|
54
56
|
jsDoc,
|
package/dist/help.js
CHANGED
|
@@ -8,11 +8,12 @@ Options:
|
|
|
8
8
|
-c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
|
|
9
9
|
-t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
|
|
10
10
|
--dir Working directory (default: current working directory)
|
|
11
|
-
--include Report only listed issue
|
|
12
|
-
--exclude Exclude issue
|
|
11
|
+
--include Report only listed issue type(s) (see below)
|
|
12
|
+
--exclude Exclude issue type(s) from report (see below)
|
|
13
13
|
--ignore Ignore files matching this glob pattern (can be set multiple times)
|
|
14
14
|
--no-gitignore Don't use .gitignore
|
|
15
15
|
--dev Include \`devDependencies\` in report(s)
|
|
16
|
+
--include-entry-files Report unused exports and types for entry files
|
|
16
17
|
--no-progress Don't show dynamic progress updates
|
|
17
18
|
--max-issues Maximum number of issues before non-zero exit code (default: 0)
|
|
18
19
|
--reporter Select reporter: symbols, compact, codeowners (default: symbols)
|
|
@@ -21,7 +22,7 @@ Options:
|
|
|
21
22
|
--debug Show debug output
|
|
22
23
|
--debug-level Set verbosity of debug output (default: 1, max: 2)
|
|
23
24
|
|
|
24
|
-
Issue
|
|
25
|
+
Issue types: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
|
|
25
26
|
|
|
26
27
|
Examples:
|
|
27
28
|
|
package/dist/index.js
CHANGED
|
@@ -14,74 +14,89 @@ const runner_1 = require("./runner");
|
|
|
14
14
|
const errors_1 = require("./util/errors");
|
|
15
15
|
const debug_1 = require("./util/debug");
|
|
16
16
|
const main = async (options) => {
|
|
17
|
-
const { cwd, workingDir, configFilePath
|
|
17
|
+
const { cwd, workingDir, configFilePath: configFilePathArg, tsConfigFilePath: tsConfigFilePathArg, include, exclude, ignore, gitignore, isIncludeEntryFiles, isDev, isShowProgress, jsDoc, debug, } = options;
|
|
18
18
|
(0, debug_1.debugLogObject)(options, 1, 'Unresolved onfiguration', options);
|
|
19
|
-
const
|
|
20
|
-
const manifestPath = await (0, fs_1.findFile)(workingDir, 'package.json');
|
|
21
|
-
const localConfiguration = localConfigurationPath && require(localConfigurationPath);
|
|
19
|
+
const manifestPath = await (0, fs_1.findFile)(cwd, workingDir, 'package.json');
|
|
22
20
|
const manifest = manifestPath && require(manifestPath);
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const configFilePath = configFilePathArg ?? 'knip.json';
|
|
22
|
+
const resolvedConfigFilePath = await (0, fs_1.findFile)(cwd, workingDir, configFilePath);
|
|
23
|
+
const localConfig = resolvedConfigFilePath && require(resolvedConfigFilePath);
|
|
24
|
+
if (configFilePathArg && !resolvedConfigFilePath) {
|
|
25
|
+
throw new errors_1.ConfigurationError(`Unable to find ${configFilePathArg}`);
|
|
26
26
|
}
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new errors_1.ConfigurationError('Unable to find `entryFiles` and/or `projectFiles` in configuration.');
|
|
27
|
+
const tsConfigFilePath = tsConfigFilePathArg ?? 'tsconfig.json';
|
|
28
|
+
const resolvedTsConfigFilePath = await (0, fs_1.findFile)(cwd, workingDir, tsConfigFilePath);
|
|
29
|
+
if (tsConfigFilePathArg && !resolvedTsConfigFilePath) {
|
|
30
|
+
throw new errors_1.ConfigurationError(`Unable to find ${tsConfigFilePathArg}`);
|
|
32
31
|
}
|
|
33
|
-
const report = (0, config_1.resolveIncludedIssueGroups)(include, exclude, resolvedConfig);
|
|
34
32
|
let tsConfigPaths = [];
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (tsConfigPath) {
|
|
40
|
-
const tsConfig = typescript_1.default.readConfigFile(tsConfigPath, typescript_1.default.sys.readFile);
|
|
41
|
-
tsConfigPaths = tsConfig.config.compilerOptions?.paths
|
|
42
|
-
? Object.keys(tsConfig.config.compilerOptions.paths).map(p => p.replace(/\*/g, '**'))
|
|
33
|
+
if (resolvedTsConfigFilePath) {
|
|
34
|
+
const config = typescript_1.default.readConfigFile(resolvedTsConfigFilePath, typescript_1.default.sys.readFile);
|
|
35
|
+
tsConfigPaths = config.config.compilerOptions?.paths
|
|
36
|
+
? Object.keys(config.config.compilerOptions.paths).map(p => p.replace(/\*/g, '**'))
|
|
43
37
|
: [];
|
|
44
|
-
if (
|
|
45
|
-
throw new errors_1.ConfigurationError(`
|
|
38
|
+
if (config.error) {
|
|
39
|
+
throw new errors_1.ConfigurationError(`Unable to read ${node_path_1.default.relative(cwd, resolvedTsConfigFilePath)}`);
|
|
46
40
|
}
|
|
47
41
|
}
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
42
|
+
const dir = node_path_1.default.relative(cwd, workingDir);
|
|
43
|
+
const resolvedConfig = (0, config_1.resolveConfig)(manifest.knip ?? localConfig, { workingDir: dir, isDev });
|
|
44
|
+
(0, debug_1.debugLogObject)(options, 1, 'Resolved onfiguration', resolvedConfig);
|
|
45
|
+
if (!resolvedConfigFilePath && !manifest.knip && !resolvedTsConfigFilePath) {
|
|
46
|
+
throw new errors_1.ConfigurationError(`Unable to find ${configFilePath} or package.json#knip or ${tsConfigFilePath}`);
|
|
47
|
+
}
|
|
48
|
+
const { entryFiles, productionFiles, projectFiles } = await (async () => {
|
|
49
|
+
if (resolvedConfig) {
|
|
50
|
+
const skipAddFiles = { skipAddingFilesFromTsConfig: true, skipFileDependencyResolution: true };
|
|
51
|
+
const projectOptions = resolvedTsConfigFilePath
|
|
52
|
+
? { tsConfigFilePath: resolvedTsConfigFilePath }
|
|
53
|
+
: { compilerOptions: { allowJs: true } };
|
|
54
|
+
const entryPaths = await (0, path_1.resolvePaths)({
|
|
55
|
+
cwd,
|
|
56
|
+
workingDir,
|
|
57
|
+
patterns: resolvedConfig.entryFiles,
|
|
58
|
+
ignore,
|
|
59
|
+
gitignore,
|
|
60
|
+
});
|
|
61
|
+
(0, debug_1.debugLogFiles)(options, 1, 'Globbed entry paths', entryPaths);
|
|
62
|
+
const production = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, entryPaths);
|
|
63
|
+
const entryFiles = production.getSourceFiles();
|
|
64
|
+
(0, debug_1.debugLogSourceFiles)(options, 1, 'Included entry source files', entryFiles);
|
|
65
|
+
production.resolveSourceFileDependencies();
|
|
66
|
+
const productionFiles = production.getSourceFiles();
|
|
67
|
+
(0, debug_1.debugLogSourceFiles)(options, 1, 'Included production source files', productionFiles);
|
|
68
|
+
const projectPaths = await (0, path_1.resolvePaths)({
|
|
69
|
+
cwd,
|
|
70
|
+
workingDir,
|
|
71
|
+
patterns: resolvedConfig.projectFiles,
|
|
72
|
+
ignore,
|
|
73
|
+
gitignore,
|
|
74
|
+
});
|
|
75
|
+
(0, debug_1.debugLogFiles)(options, 1, 'Globbed project paths', projectPaths);
|
|
76
|
+
const project = (0, project_1.createProject)({ ...projectOptions, ...skipAddFiles }, projectPaths);
|
|
77
|
+
const projectFiles = project.getSourceFiles();
|
|
78
|
+
(0, debug_1.debugLogSourceFiles)(options, 1, 'Included project source files', projectFiles);
|
|
79
|
+
return { entryFiles, productionFiles, projectFiles };
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
const project = (0, project_1.createProject)({ tsConfigFilePath: resolvedTsConfigFilePath });
|
|
83
|
+
const files = project.getSourceFiles();
|
|
84
|
+
return { entryFiles: files, productionFiles: files, projectFiles: files };
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
const report = (0, config_1.resolveIncludedIssueGroups)(include, resolvedConfig ? exclude : ['files'], resolvedConfig);
|
|
74
88
|
const config = {
|
|
75
89
|
workingDir,
|
|
76
90
|
report,
|
|
77
91
|
entryFiles,
|
|
78
92
|
productionFiles,
|
|
79
93
|
projectFiles,
|
|
94
|
+
isIncludeEntryFiles: !resolvedConfig || isIncludeEntryFiles,
|
|
80
95
|
dependencies: Object.keys(manifest.dependencies ?? {}),
|
|
81
96
|
peerDependencies: Object.keys(manifest.peerDependencies ?? {}),
|
|
82
97
|
optionalDependencies: Object.keys(manifest.optionalDependencies ?? {}),
|
|
83
98
|
devDependencies: Object.keys(manifest.devDependencies ?? {}),
|
|
84
|
-
isDev: typeof resolvedConfig
|
|
99
|
+
isDev: typeof resolvedConfig?.dev === 'boolean' ? resolvedConfig.dev : isDev,
|
|
85
100
|
tsConfigPaths,
|
|
86
101
|
isShowProgress,
|
|
87
102
|
jsDocOptions: {
|
package/dist/runner.js
CHANGED
|
@@ -15,7 +15,7 @@ const debug_1 = require("./util/debug");
|
|
|
15
15
|
const lineRewriter = new log_1.LineRewriter();
|
|
16
16
|
async function findIssues(configuration) {
|
|
17
17
|
const { workingDir, isShowProgress, report, isDev, jsDocOptions } = configuration;
|
|
18
|
-
const { entryFiles, productionFiles, projectFiles } = configuration;
|
|
18
|
+
const { entryFiles, productionFiles, projectFiles, isIncludeEntryFiles } = configuration;
|
|
19
19
|
const { getUnresolvedDependencies, getUnusedDependencies, getUnusedDevDependencies } = (0, dependencies_1.getDependencyAnalyzer)(configuration);
|
|
20
20
|
const [usedProductionFiles, unreferencedProductionFiles] = (0, project_1.partitionSourceFiles)(projectFiles, productionFiles);
|
|
21
21
|
const [usedEntryFiles, usedNonEntryFiles] = (0, project_1.partitionSourceFiles)(usedProductionFiles, entryFiles);
|
|
@@ -81,13 +81,6 @@ async function findIssues(configuration) {
|
|
|
81
81
|
}
|
|
82
82
|
updateProcessingOutput(issue);
|
|
83
83
|
};
|
|
84
|
-
if (report.dependencies || report.unlisted) {
|
|
85
|
-
usedEntryFiles.forEach(sourceFile => {
|
|
86
|
-
counters.processed++;
|
|
87
|
-
const unresolvedDependencies = getUnresolvedDependencies(sourceFile);
|
|
88
|
-
unresolvedDependencies.forEach(issue => addSymbolIssue('unresolved', issue));
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
84
|
if (report.dependencies ||
|
|
92
85
|
report.unlisted ||
|
|
93
86
|
report.exports ||
|
|
@@ -95,7 +88,7 @@ async function findIssues(configuration) {
|
|
|
95
88
|
report.nsExports ||
|
|
96
89
|
report.nsTypes ||
|
|
97
90
|
report.duplicates) {
|
|
98
|
-
|
|
91
|
+
usedProductionFiles.forEach(sourceFile => {
|
|
99
92
|
counters.processed++;
|
|
100
93
|
const filePath = sourceFile.getFilePath();
|
|
101
94
|
if (report.dependencies || report.unlisted) {
|
|
@@ -110,10 +103,14 @@ async function findIssues(configuration) {
|
|
|
110
103
|
addSymbolIssue('duplicates', { filePath, symbol, symbols });
|
|
111
104
|
});
|
|
112
105
|
}
|
|
106
|
+
if (!isIncludeEntryFiles && entryFiles.includes(sourceFile))
|
|
107
|
+
return;
|
|
113
108
|
if (report.exports || report.types || report.nsExports || report.nsTypes) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
if (!isIncludeEntryFiles) {
|
|
110
|
+
const uniqueExportedSymbols = new Set([...exportDeclarations.values()].flat());
|
|
111
|
+
if (uniqueExportedSymbols.size === 1)
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
117
114
|
exportDeclarations.forEach(declarations => {
|
|
118
115
|
declarations.forEach(declaration => {
|
|
119
116
|
const type = (0, type_1.getType)(declaration);
|
|
@@ -126,10 +123,13 @@ async function findIssues(configuration) {
|
|
|
126
123
|
if (jsDocOptions.isReadPublicTag && ts_morph_1.ts.getJSDocPublicTag(declaration.compilerNode))
|
|
127
124
|
return;
|
|
128
125
|
let identifier;
|
|
126
|
+
let fakeIdentifier;
|
|
129
127
|
if (declaration.isKind(ts_morph_1.ts.SyntaxKind.Identifier)) {
|
|
130
128
|
identifier = declaration;
|
|
131
129
|
}
|
|
132
|
-
else if (declaration.isKind(ts_morph_1.ts.SyntaxKind.ArrowFunction)
|
|
130
|
+
else if (declaration.isKind(ts_morph_1.ts.SyntaxKind.ArrowFunction) ||
|
|
131
|
+
declaration.isKind(ts_morph_1.ts.SyntaxKind.ObjectLiteralExpression)) {
|
|
132
|
+
fakeIdentifier = 'default';
|
|
133
133
|
}
|
|
134
134
|
else if (declaration.isKind(ts_morph_1.ts.SyntaxKind.FunctionDeclaration) ||
|
|
135
135
|
declaration.isKind(ts_morph_1.ts.SyntaxKind.ClassDeclaration) ||
|
|
@@ -144,8 +144,8 @@ async function findIssues(configuration) {
|
|
|
144
144
|
else {
|
|
145
145
|
identifier = declaration.getFirstDescendantByKind(ts_morph_1.ts.SyntaxKind.Identifier);
|
|
146
146
|
}
|
|
147
|
-
if (identifier) {
|
|
148
|
-
const identifierText = identifier
|
|
147
|
+
if (identifier || fakeIdentifier) {
|
|
148
|
+
const identifierText = fakeIdentifier ?? identifier?.getText() ?? '*';
|
|
149
149
|
if (report.exports && issues.exports[filePath]?.[identifierText])
|
|
150
150
|
return;
|
|
151
151
|
if (report.types && issues.types[filePath]?.[identifierText])
|
|
@@ -154,7 +154,7 @@ async function findIssues(configuration) {
|
|
|
154
154
|
return;
|
|
155
155
|
if (report.nsTypes && issues.nsTypes[filePath]?.[identifierText])
|
|
156
156
|
return;
|
|
157
|
-
const refs = identifier
|
|
157
|
+
const refs = identifier?.findReferences() ?? [];
|
|
158
158
|
if (refs.length === 0) {
|
|
159
159
|
addSymbolIssue('exports', { filePath, symbol: identifierText });
|
|
160
160
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare type UnresolvedConfiguration = {
|
|
|
44
44
|
exclude: string[];
|
|
45
45
|
ignore: string[];
|
|
46
46
|
gitignore: boolean;
|
|
47
|
+
isIncludeEntryFiles: boolean;
|
|
47
48
|
isDev: boolean;
|
|
48
49
|
isShowProgress: boolean;
|
|
49
50
|
jsDoc: string[];
|
|
@@ -61,6 +62,7 @@ export declare type Configuration = {
|
|
|
61
62
|
projectFiles: SourceFile[];
|
|
62
63
|
productionFiles: SourceFile[];
|
|
63
64
|
entryFiles: SourceFile[];
|
|
65
|
+
isIncludeEntryFiles: boolean;
|
|
64
66
|
dependencies: string[];
|
|
65
67
|
peerDependencies: string[];
|
|
66
68
|
optionalDependencies: string[];
|
package/dist/util/config.js
CHANGED
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.resolveIncludedIssueGroups = exports.resolveConfig = void 0;
|
|
7
7
|
const micromatch_1 = __importDefault(require("micromatch"));
|
|
8
8
|
const resolveConfig = (importedConfiguration, options) => {
|
|
9
|
+
if (!importedConfiguration)
|
|
10
|
+
return;
|
|
9
11
|
let resolvedConfig = importedConfiguration;
|
|
10
12
|
const { workingDir, isDev } = options ?? {};
|
|
11
13
|
const configKeys = Object.keys(importedConfiguration);
|
package/dist/util/debug.js
CHANGED
|
@@ -9,7 +9,7 @@ const logArray = (collection) => console.log(node_util_1.default.inspect(collect
|
|
|
9
9
|
const debugLogObject = (config, minimumLevel, name, obj) => {
|
|
10
10
|
if (minimumLevel > config.debug.level)
|
|
11
11
|
return;
|
|
12
|
-
console.log(`[
|
|
12
|
+
console.log(`[knip] ${name}:`);
|
|
13
13
|
console.log(node_util_1.default.inspect(obj, { depth: null, colors: true }));
|
|
14
14
|
};
|
|
15
15
|
exports.debugLogObject = debugLogObject;
|
|
@@ -18,11 +18,11 @@ const debugLogFiles = (config, minimumLevel, name, filePaths) => {
|
|
|
18
18
|
return;
|
|
19
19
|
const { debug } = config;
|
|
20
20
|
if (debug.level > 1) {
|
|
21
|
-
console.debug(`[
|
|
21
|
+
console.debug(`[knip] ${name} (${filePaths.length}):`);
|
|
22
22
|
logArray(filePaths);
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
|
-
console.debug(`[
|
|
25
|
+
console.debug(`[knip] ${name} (${filePaths.length})`);
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
exports.debugLogFiles = debugLogFiles;
|
|
@@ -31,11 +31,11 @@ const debugLogSourceFiles = (config, minimumLevel, name, sourceFiles) => {
|
|
|
31
31
|
return;
|
|
32
32
|
const { debug } = config;
|
|
33
33
|
if (debug.level > 1) {
|
|
34
|
-
console.debug(`[
|
|
34
|
+
console.debug(`[knip] ${name} (${sourceFiles.length}):`);
|
|
35
35
|
logArray(sourceFiles.map(sourceFile => sourceFile.getFilePath()));
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
|
-
console.debug(`[
|
|
38
|
+
console.debug(`[knip] ${name} (${sourceFiles.length})`);
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
exports.debugLogSourceFiles = debugLogSourceFiles;
|
|
@@ -44,11 +44,11 @@ const debugLogDiff = (config, minimumLevel, name, arrA, arrB) => {
|
|
|
44
44
|
return;
|
|
45
45
|
const onlyInA = arrA.filter(itemA => !arrB.includes(itemA)).sort();
|
|
46
46
|
const onlyInB = arrB.filter(itemB => !arrA.includes(itemB)).sort();
|
|
47
|
-
console.log(`[
|
|
48
|
-
console.log(`[
|
|
47
|
+
console.log(`[knip] ${name}`);
|
|
48
|
+
console.log(`[knip] Only in left:`);
|
|
49
49
|
logArray(onlyInA);
|
|
50
50
|
console.log();
|
|
51
|
-
console.log(`[
|
|
51
|
+
console.log(`[knip] Only in right:`);
|
|
52
52
|
logArray(onlyInB);
|
|
53
53
|
};
|
|
54
54
|
exports.debugLogDiff = debugLogDiff;
|
package/dist/util/fs.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const findFile: (cwd: string, fileName: string) => Promise<string | undefined>;
|
|
1
|
+
export declare const findFile: (cwd: string, workingDir: string, fileName: string) => Promise<string | undefined>;
|
package/dist/util/fs.js
CHANGED
|
@@ -15,14 +15,16 @@ const isFile = async (filePath) => {
|
|
|
15
15
|
return false;
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
-
const findFile = async (cwd, fileName) => {
|
|
19
|
-
const filePath = node_path_1.default.join(
|
|
18
|
+
const findFile = async (cwd, workingDir, fileName) => {
|
|
19
|
+
const filePath = node_path_1.default.join(workingDir, fileName);
|
|
20
20
|
if (await isFile(filePath)) {
|
|
21
21
|
return filePath;
|
|
22
22
|
}
|
|
23
23
|
else {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
if (cwd === workingDir)
|
|
25
|
+
return;
|
|
26
|
+
const parentDir = node_path_1.default.resolve(workingDir, '..');
|
|
27
|
+
return (0, exports.findFile)(cwd, parentDir, fileName);
|
|
26
28
|
}
|
|
27
29
|
};
|
|
28
30
|
exports.findFile = findFile;
|
package/dist/util/project.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { Project } from 'ts-morph';
|
|
2
2
|
import type { ProjectOptions, SourceFile } from 'ts-morph';
|
|
3
|
-
export declare const createProject: (
|
|
4
|
-
projectOptions: ProjectOptions;
|
|
5
|
-
paths?: string[] | undefined;
|
|
6
|
-
}) => Project;
|
|
3
|
+
export declare const createProject: (projectOptions: ProjectOptions, paths?: string[]) => Project;
|
|
7
4
|
export declare const partitionSourceFiles: (projectFiles: SourceFile[], productionFiles: SourceFile[]) => SourceFile[][];
|
package/dist/util/project.js
CHANGED
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.partitionSourceFiles = exports.createProject = void 0;
|
|
4
4
|
const ts_morph_1 = require("ts-morph");
|
|
5
|
-
const createProject = (
|
|
6
|
-
const workspace = new ts_morph_1.Project(
|
|
7
|
-
...projectOptions,
|
|
8
|
-
skipAddingFilesFromTsConfig: true,
|
|
9
|
-
skipFileDependencyResolution: true,
|
|
10
|
-
});
|
|
5
|
+
const createProject = (projectOptions, paths) => {
|
|
6
|
+
const workspace = new ts_morph_1.Project(projectOptions);
|
|
11
7
|
if (paths)
|
|
12
8
|
workspace.addSourceFilesAtPaths(paths);
|
|
13
9
|
return workspace;
|
package/license
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ISC License (ISC)
|
|
2
|
+
|
|
3
|
+
Copyright 2022 Lars Kappert
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
|
|
6
|
+
provided that the above copyright notice and this permission notice appear in all copies.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
9
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
11
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
12
|
+
THIS SOFTWARE.
|