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 CHANGED
@@ -1,22 +1,20 @@
1
1
  # ✂️ Knip
2
2
 
3
- Knip scans your JavaScript and TypeScript projects for **unused files, dependencies and exports**: things that can be
4
- removed! Less code leads to improved performance, less maintenance, easier refactorings.
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 "unused variable" rule of ESLint. Knip lints
12
- the project as a whole, or parts of it.
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 to delete files or dependencies that you no longer use. How to keep track of how many times
15
- something is actually used in a codebase? How to find out this number just dropped to zero for anything? Where to even
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
- Boring stuff should be automated! Just let Knip have a crack at it:
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](#reporters) and supports [custom reporters](#custom-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](./assets/cow-with-orange-scissors-van-gogh-style.webp)](https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC)
36
- <sup>_“An orange cow with scissors, Van Gogh style” - generated with OpenAI_</sup>
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 is required. Knip is _cutting edge!_
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. Production code is the set of files resolved from the `entryFiles`.
75
- 2. They are matched against the set of `projectFiles`.
76
- 3. The subset of project files that is not production code will be reported as unused files (in red).
77
- 4. Then the production code (in blue) will be scanned for unused exports.
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](./assets/how-it-works.drawio.svg)
70
+ ![How it works][6]
80
71
 
81
72
  ## Options
82
73
 
83
- ```
84
- npx knip
85
- knip [options]
86
-
87
- Options:
88
- -c/--config [file] Configuration file path (default: ./knip.json or package.json#knip)
89
- -t/--tsConfig [file] TypeScript configuration path (default: ./tsconfig.json)
90
- --dir Working directory (default: current working directory)
91
- --include Report only listed issue group(s) (see below)
92
- --exclude Exclude issue group(s) from report (see below)
93
- --ignore Ignore files matching this glob pattern (can be set multiple times)
94
- --no-gitignore Don't use .gitignore
95
- --dev Include `devDependencies` in report(s)
96
- --no-progress Don't show dynamic progress updates
97
- --max-issues Maximum number of issues before non-zero exit code (default: 0)
98
- --reporter Select reporter: symbols, compact, codeowners (default: symbols)
99
- --reporter-options Pass extra options to the reporter (as JSON string, see example)
100
- --jsdoc Enable JSDoc parsing, with options: public
101
- --debug Show debug output
102
- --debug-level Set verbosity of debug output (default: 1, max: 2)
103
-
104
- Issue groups: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
105
-
106
- Examples:
107
-
108
- $ knip
109
- $ knip --dir packages/client --include files
110
- $ knip -c ./knip.js --reporter compact --jsdoc public
111
- $ knip --ignore 'lib/**/*.ts' --ignore build
112
- $ knip --reporter codeowners --reporter-options '{"path":".github/CODEOWNERS"}'
113
-
114
- More info: https://github.com/webpro/knip
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` groups are included.
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 groups of issues:
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
- Each group type can be an `--include` or `--exclude` to slice & dice the report to your needs.
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
- 1. This may also include dependencies that could not be resolved properly (such as non-relative `local/dir/file.ts` not
136
- and `local` not being in `node_modules`).
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 deleted.
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 delete things!
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 production files, which
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 patterns).
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
- - Include non-production files from the `projectFiles`.
179
- - To include `devDependencies`, set `dev: true` in the configuration or add `--dev` as a command line flag.
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 scan the packages separately, using the matching pattern from the configuration file:
237
+ To analyze the packages separately, using the matching pattern from the configuration file:
237
238
 
238
- ```
239
- knip --dir packages/client
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, such as created with Nx. Let's take an example project
246
- configuration for an Nx project using Next.js, Jest and Storybook. This configuration file can also be a JavaScript
247
- file, which allows to add logic and/or comments (e.g. `knip.js`):
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 configuration
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
- $ 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
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
- $ knip --reporter compact
336
- --- UNUSED FILES (2)
337
- src/chat/helpers.ts
338
- src/components/SideBar.tsx
339
- --- UNUSED DEPENDENCIES (1)
340
- moment
341
- --- UNLISTED DEPENDENCIES (1)
342
- react
343
- --- UNUSED EXPORTS (4)
344
- src/common/src/string/index.ts: lowercaseFirstLetter
345
- src/components/Registration.tsx: RegistrationBox
346
- src/css.ts: clamp
347
- src/services/authentication.ts: restoreSession, PREFIX
348
- --- UNUSED TYPES (3)
349
- src/components/Registration/registrationMachine.ts: RegistrationServices, RegistrationAction
350
- src/components/Registration.tsx: ComponentProps
351
- src/types/Product.ts: ProductDetail
352
- --- DUPLICATE EXPORTS (2)
353
- src/components/Registration.tsx: Registration, default
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
- $ knip --reporter codeowners
363
- --- UNUSED FILES (2)
364
- @org/team src/chat/helpers.ts
365
- @org/owner src/components/SideBar.tsx
366
- --- UNUSED DEPENDENCIES (1)
367
- @org/admin moment
368
- --- UNLISTED DEPENDENCIES (1)
369
- @org/owner src/components/Registration.tsx react
370
- --- UNUSED EXPORTS (4)
371
- @org/team src/common/src/string/index.ts: lowercaseFirstLetter
372
- @org/owner src/components/Registration.tsx: RegistrationBox
373
- @org/owner src/css.ts: clamp
374
- @org/owner src/services/authentication.ts: restoreSession, PREFIX
375
- --- UNUSED TYPES (3)
376
- @org/owner src/components/Registration/registrationMachine.ts: RegistrationServices, RegistrationAction
377
- @org/owner src/components/Registration.tsx: ComponentProps
378
- @org/owner src/types/Product.ts: ProductDetail
379
- --- DUPLICATE EXPORTS (2)
380
- @org/owner src/components/Registration.tsx: Registration, default
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 | **knip** | [depcheck][1] | [unimported][2] | [ts-unused-exports][3] | [ts-prune][4] | [find-unused-exports][5] |
401
- | -------------------------------- | :------: | :-----------: | :-------------: | :--------------------: | :-----------: | :----------------------: |
402
- | Unused files | ✅ | - | ✅ | - | - | - |
403
- | Unused dependencies | ✅ | ✅ | ✅ | - | - | - |
404
- | Unlisted dependencies | ✅ | ✅ | ✅ | - | - | - |
405
- | [Custom dependency resolvers][7] | ❌ | ✅ | ❌ | - | - | - |
406
- | Unused exports | ✅ | - | - | ✅ | ✅ | ✅ |
407
- | Duplicate exports | ✅ | - | - | ❌ | ❌ | ❌ |
408
- | Search namespaces | ✅ | - | - | ✅ | ❌ | ❌ |
409
- | Custom reporters | ✅ | - | - | - | - | - |
410
- | Pure JavaScript/ESM | ✅ | ✅ | ✅ | - | - | ✅ |
411
- | Configure entry files | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
412
- | [Support monorepos][8] | 🟠 | - | - | - | - | - |
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](#monorepos) properly, the first steps in this direction are implemented.
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](https://github.com/depcheck/depcheck#special) which already does this! They call this
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. For instance, this powers the "Find references" feature in VS Code. I think features like "duplicate
438
- exports" or "custom dependency resolvers" are userland territory, much like code linters.
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]: https://github.com/depcheck/depcheck
446
- [2]: https://github.com/smeijer/unimported
447
- [3]: https://github.com/pzavolinsky/ts-unused-exports
448
- [4]: https://github.com/nadeesha/ts-prune
449
- [5]: https://github.com/jaydenseric/find-unused-exports
450
- [6]: https://github.com/webpro/knip/issues/7
451
- [7]: #custom-dependency-resolvers
452
- [8]: #monorepos-1
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 = 'knip.json', tsConfig: tsConfigFilePath, include = [], exclude = [], ignore = [], 'no-gitignore': isNoGitIgnore = false, dev: isDev = 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)({
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 group(s) (see below)
12
- --exclude Exclude issue group(s) from report (see below)
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 groups: files, dependencies, unlisted, exports, nsExports, types, nsTypes, duplicates
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 = 'knip.json', tsConfigFilePath, include, exclude, ignore, gitignore, isDev, isShowProgress, jsDoc, debug, } = options;
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 localConfigurationPath = configFilePath && (await (0, fs_1.findFile)(workingDir, configFilePath));
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
- if (!localConfigurationPath && !manifest.knip) {
24
- const location = workingDir === cwd ? 'current directory' : `${node_path_1.default.relative(cwd, workingDir)} or up.`;
25
- throw new errors_1.ConfigurationError(`Unable to find ${configFilePath} or package.json#knip in ${location}`);
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 dir = node_path_1.default.relative(cwd, workingDir);
28
- const resolvedConfig = (0, config_1.resolveConfig)(manifest.knip ?? localConfiguration, { workingDir: dir, isDev });
29
- (0, debug_1.debugLogObject)(options, 1, 'Resolved onfiguration', resolvedConfig);
30
- if (!resolvedConfig) {
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
- const tsConfigPath = await (0, fs_1.findFile)(workingDir, tsConfigFilePath ?? 'tsconfig.json');
36
- if (tsConfigFilePath && !tsConfigPath) {
37
- throw new errors_1.ConfigurationError(`Unable to find ${tsConfigFilePath}`);
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 (tsConfig.error) {
45
- throw new errors_1.ConfigurationError(`An error occured when reading ${node_path_1.default.relative(cwd, tsConfigPath)}`);
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 projectOptions = tsConfigPath ? { tsConfigFilePath: tsConfigPath } : { compilerOptions: { allowJs: true } };
49
- const entryPaths = await (0, path_1.resolvePaths)({
50
- cwd,
51
- workingDir,
52
- patterns: resolvedConfig.entryFiles,
53
- ignore,
54
- gitignore,
55
- });
56
- (0, debug_1.debugLogFiles)(options, 1, 'Globbed entry paths', entryPaths);
57
- const production = (0, project_1.createProject)({ projectOptions, paths: entryPaths });
58
- const entryFiles = production.getSourceFiles();
59
- (0, debug_1.debugLogSourceFiles)(options, 1, 'Included entry source files', entryFiles);
60
- production.resolveSourceFileDependencies();
61
- const productionFiles = production.getSourceFiles();
62
- (0, debug_1.debugLogSourceFiles)(options, 1, 'Included production source files', productionFiles);
63
- const projectPaths = await (0, path_1.resolvePaths)({
64
- cwd,
65
- workingDir,
66
- patterns: resolvedConfig.projectFiles,
67
- ignore,
68
- gitignore,
69
- });
70
- (0, debug_1.debugLogFiles)(options, 1, 'Globbed project paths', projectPaths);
71
- const project = (0, project_1.createProject)({ projectOptions, paths: projectPaths });
72
- const projectFiles = project.getSourceFiles();
73
- (0, debug_1.debugLogSourceFiles)(options, 1, 'Included project source files', projectFiles);
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.dev === 'boolean' ? resolvedConfig.dev : isDev,
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
- usedNonEntryFiles.forEach(sourceFile => {
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
- const uniqueExportedSymbols = new Set([...exportDeclarations.values()].flat());
115
- if (uniqueExportedSymbols.size === 1)
116
- return;
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.getText();
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.findReferences();
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[];
@@ -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);
@@ -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(`[exportman] ${name}:`);
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(`[exportman] ${name} (${filePaths.length}):`);
21
+ console.debug(`[knip] ${name} (${filePaths.length}):`);
22
22
  logArray(filePaths);
23
23
  }
24
24
  else {
25
- console.debug(`[exportman] ${name} (${filePaths.length})`);
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(`[exportman] ${name} (${sourceFiles.length}):`);
34
+ console.debug(`[knip] ${name} (${sourceFiles.length}):`);
35
35
  logArray(sourceFiles.map(sourceFile => sourceFile.getFilePath()));
36
36
  }
37
37
  else {
38
- console.debug(`[exportman] ${name} (${sourceFiles.length})`);
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(`[exportman] ${name}`);
48
- console.log(`[exportman] Only in left:`);
47
+ console.log(`[knip] ${name}`);
48
+ console.log(`[knip] Only in left:`);
49
49
  logArray(onlyInA);
50
50
  console.log();
51
- console.log(`[exportman] Only in right:`);
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(cwd, fileName);
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
- const parentDir = node_path_1.default.resolve(cwd, '..');
25
- return parentDir === '/' ? undefined : (0, exports.findFile)(parentDir, fileName);
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;
@@ -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: ({ projectOptions, paths }: {
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[][];
@@ -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 = ({ projectOptions, paths }) => {
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "description": "Find unused files, dependencies and exports in your TypeScript and JavaScript project",
5
5
  "keywords": [
6
6
  "find",