knip 2.21.2 → 2.23.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 +148 -134
- package/dist/DependencyDeputy.d.ts +4 -0
- package/dist/DependencyDeputy.js +30 -3
- package/dist/ProjectPrincipal.d.ts +0 -2
- package/dist/ProjectPrincipal.js +2 -9
- package/dist/binaries/bash-parser.js +4 -2
- package/dist/binaries/resolvers/pnpm.js +1 -1
- package/dist/constants.js +2 -0
- package/dist/index.js +8 -6
- package/dist/plugins/eslint/helpers.js +1 -1
- package/dist/plugins/husky/index.js +3 -8
- package/dist/plugins/lefthook/index.js +19 -11
- package/dist/plugins/release-it/index.js +6 -0
- package/dist/plugins/release-it/types.d.ts +6 -0
- package/dist/reporters/json.js +1 -0
- package/dist/types/exports.d.ts +2 -1
- package/dist/types/issues.d.ts +1 -0
- package/dist/types/workspace.d.ts +1 -0
- package/dist/typescript/ast-helpers.d.ts +1 -0
- package/dist/typescript/ast-helpers.js +1 -0
- package/dist/typescript/getImportsAndExports.js +13 -8
- package/dist/util/cli-arguments.d.ts +1 -1
- package/dist/util/cli-arguments.js +2 -2
- package/dist/util/get-included-issue-types.js +10 -3
- package/dist/util/git.d.ts +1 -0
- package/dist/util/git.js +20 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
- package/dist/plugins/husky/helpers.d.ts +0 -1
- package/dist/plugins/husky/helpers.js +0 -9
package/README.md
CHANGED
|
@@ -25,54 +25,56 @@ The dots don't connect themselves. This is where Knip comes in:
|
|
|
25
25
|
- [x] Finds duplicate exports
|
|
26
26
|
- [x] Supports any combination of JavaScript and TypeScript
|
|
27
27
|
- [x] Multiple built-in [reporters][4] (or use [custom reporters][5] and [preprocessors][6])
|
|
28
|
+
- [x] Understands [JSDoc/TSDoc tags][7] (e.g. `@public` and `@internal`)
|
|
28
29
|
- [x] Run Knip as part of your CI environment to detect issues and prevent regressions
|
|
29
30
|
|
|
30
31
|
Knip shines in both small and large projects. It's a fresh take on keeping your projects clean & tidy!
|
|
31
32
|
|
|
32
|
-
[![An orange cow with scissors, Van Gogh style][
|
|
33
|
+
[![An orange cow with scissors, Van Gogh style][9]][8] <sup>_“An orange cow with scissors, Van Gogh style” - generated
|
|
33
34
|
with OpenAI_</sup>
|
|
34
35
|
|
|
35
|
-
For updates or questions, come hang out in [The Knip Barn (Discord)][
|
|
36
|
-
[@webpro (fosstodon.org)][
|
|
36
|
+
For updates or questions, come hang out in [The Knip Barn (Discord)][10], or follow [@webprolific (Twitter)][11] or
|
|
37
|
+
[@webpro (fosstodon.org)][12]. Please use GitHub to [report issues][13].
|
|
37
38
|
|
|
38
39
|
## Contents
|
|
39
40
|
|
|
40
|
-
- [Getting Started][
|
|
41
|
-
- [Installation][
|
|
42
|
-
- [Default Configuration][
|
|
43
|
-
- [Let's Go!][
|
|
44
|
-
- [Configuration][
|
|
45
|
-
- [Entry Files][
|
|
41
|
+
- [Getting Started][14]
|
|
42
|
+
- [Installation][15]
|
|
43
|
+
- [Default Configuration][16]
|
|
44
|
+
- [Let's Go!][17]
|
|
45
|
+
- [Configuration][18]
|
|
46
|
+
- [Entry Files][19]
|
|
46
47
|
- [Workspaces][1]
|
|
47
48
|
- [Plugins][2]
|
|
48
49
|
- [Compilers][3]
|
|
49
|
-
- [Ignore files, binaries, dependencies and workspaces][
|
|
50
|
-
- [Public exports][
|
|
51
|
-
- [Ignore exports used in file][
|
|
52
|
-
- [Include exports in entry files][
|
|
53
|
-
- [Paths][
|
|
54
|
-
- [Production Mode][
|
|
55
|
-
- [Strict][
|
|
56
|
-
- [Ignore `@internal` exports][
|
|
57
|
-
- [Plugins][
|
|
58
|
-
- [Output][
|
|
59
|
-
- [Screenshots][
|
|
60
|
-
- [Reading the report][
|
|
61
|
-
- [Rules & Filters][
|
|
50
|
+
- [Ignore files, binaries, dependencies and workspaces][20]
|
|
51
|
+
- [Public exports][21]
|
|
52
|
+
- [Ignore exports used in file][22]
|
|
53
|
+
- [Include exports in entry files][23]
|
|
54
|
+
- [Paths][24]
|
|
55
|
+
- [Production Mode][25]
|
|
56
|
+
- [Strict][26]
|
|
57
|
+
- [Ignore `@internal` exports][27]
|
|
58
|
+
- [Plugins][28]
|
|
59
|
+
- [Output][29]
|
|
60
|
+
- [Screenshots][30]
|
|
61
|
+
- [Reading the report][31]
|
|
62
|
+
- [Rules & Filters][32]
|
|
62
63
|
- [Reporters][4]
|
|
63
|
-
- [Fixing Issues][
|
|
64
|
-
- [
|
|
65
|
-
- [
|
|
66
|
-
- [
|
|
67
|
-
|
|
68
|
-
- [
|
|
69
|
-
- [
|
|
70
|
-
- [ts-
|
|
71
|
-
- [
|
|
72
|
-
- [
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
64
|
+
- [Fixing Issues][33]
|
|
65
|
+
- [JSDoc tags][7]
|
|
66
|
+
- [Command Line Options][34]
|
|
67
|
+
- [Potential boost with `--no-gitignore`][35]
|
|
68
|
+
- [Comparison & Migration][36]
|
|
69
|
+
- [depcheck][37]
|
|
70
|
+
- [unimported][38]
|
|
71
|
+
- [ts-unused-exports][39]
|
|
72
|
+
- [ts-prune][40]
|
|
73
|
+
- [Projects using Knip][41]
|
|
74
|
+
- [Articles, etc.][42]
|
|
75
|
+
- [Why "Knip"?][43]
|
|
76
|
+
- [Really, another unused file/dependency/export finder?][44]
|
|
77
|
+
- [Contributors][45]
|
|
76
78
|
|
|
77
79
|
## Getting Started
|
|
78
80
|
|
|
@@ -93,7 +95,7 @@ Knip has good defaults and you can run it without any configuration. The (simpli
|
|
|
93
95
|
}
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
There's more, jump to [Entry Files][
|
|
98
|
+
There's more, jump to [Entry Files][19] for details.
|
|
97
99
|
|
|
98
100
|
Places where Knip looks for configuration (ordered by priority):
|
|
99
101
|
|
|
@@ -135,7 +137,7 @@ Run the checks with `npx knip`. Or first add this script to `package.json`:
|
|
|
135
137
|
Then use `npm run knip` to analyze the project and output unused files, dependencies and exports. Knip works just fine
|
|
136
138
|
with `yarn` or `pnpm` as well.
|
|
137
139
|
|
|
138
|
-
See [Command Line Options][
|
|
140
|
+
See [Command Line Options][34] for an overview of available CLI options.
|
|
139
141
|
|
|
140
142
|
## Configuration
|
|
141
143
|
|
|
@@ -198,7 +200,7 @@ Here's an example `knip.json` configuration with some custom `entry` and `projec
|
|
|
198
200
|
```
|
|
199
201
|
|
|
200
202
|
It might be useful to run Knip first with no or little configuration to see where it needs custom `entry` and/or
|
|
201
|
-
`project` files. Each workspace has the same [default configuration][
|
|
203
|
+
`project` files. Each workspace has the same [default configuration][18].
|
|
202
204
|
|
|
203
205
|
The root workspace is named `"."` under `workspaces` (like in the example).
|
|
204
206
|
|
|
@@ -321,7 +323,7 @@ has them at `e2e-tests/*.spec.ts`. Here's how to configure this:
|
|
|
321
323
|
#### Multi-project repositories
|
|
322
324
|
|
|
323
325
|
Some repositories have a single `package.json`, but consist of multiple projects with configuration files across the
|
|
324
|
-
repository (such as the [Nx "intregrated repo" style][
|
|
326
|
+
repository (such as the [Nx "intregrated repo" style][46]). Let's assume some of these projects are apps and have their
|
|
325
327
|
own Cypress configuration and test files. In that case, we could configure the Cypress plugin like this:
|
|
326
328
|
|
|
327
329
|
```json
|
|
@@ -338,7 +340,7 @@ In case a plugin causes issues, it can be disabled by using `false` as its value
|
|
|
338
340
|
|
|
339
341
|
#### Create a new plugin
|
|
340
342
|
|
|
341
|
-
Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][
|
|
343
|
+
Getting false positives because a plugin is missing? Want to help out? Please read more at [writing a plugin][47]. This
|
|
342
344
|
guide also contains more details if you want to learn more about plugins and why they are useful.
|
|
343
345
|
|
|
344
346
|
### Compilers
|
|
@@ -361,7 +363,7 @@ export default {
|
|
|
361
363
|
};
|
|
362
364
|
```
|
|
363
365
|
|
|
364
|
-
Read [Compilers][
|
|
366
|
+
Read [Compilers][48] for more details and examples.
|
|
365
367
|
|
|
366
368
|
### Ignore files, binaries, dependencies and workspaces
|
|
367
369
|
|
|
@@ -528,7 +530,7 @@ The report contains the following types of issues:
|
|
|
528
530
|
|
|
529
531
|
When an issue type has zero issues, it is not shown.
|
|
530
532
|
|
|
531
|
-
Getting too many reported issues and false positives? Read more about [handling issues][
|
|
533
|
+
Getting too many reported issues and false positives? Read more about [handling issues][49].
|
|
532
534
|
|
|
533
535
|
### Rules & Filters
|
|
534
536
|
|
|
@@ -555,7 +557,7 @@ Example:
|
|
|
555
557
|
}
|
|
556
558
|
```
|
|
557
559
|
|
|
558
|
-
See [reading the report][
|
|
560
|
+
See [reading the report][31] for the list of issue types.
|
|
559
561
|
|
|
560
562
|
The rules are modeled after the ESLint `rules` configuration, and could be extended in the future. For instance, to
|
|
561
563
|
apply filters or configurations only to a specific issue type.
|
|
@@ -576,7 +578,7 @@ Use `--exclude` to ignore reports you're not interested in:
|
|
|
576
578
|
|
|
577
579
|
Use `--dependencies` or `--exports` as shortcuts to combine groups of related types.
|
|
578
580
|
|
|
579
|
-
See [reading the report][
|
|
581
|
+
See [reading the report][31] for the list of issue types.
|
|
580
582
|
|
|
581
583
|
#### When to use rules or filters
|
|
582
584
|
|
|
@@ -600,13 +602,13 @@ Knip provides the following built-in reporters:
|
|
|
600
602
|
|
|
601
603
|
When the provided built-in reporters are not sufficient, a custom reporter can be implemented.
|
|
602
604
|
|
|
603
|
-
Find more details in [reporters and preprocessors][
|
|
605
|
+
Find more details in [reporters and preprocessors][50].
|
|
604
606
|
|
|
605
607
|
#### Preprocessers
|
|
606
608
|
|
|
607
609
|
Use preprocessers to modify the results before they're passed to the reporter(s).
|
|
608
610
|
|
|
609
|
-
Find more details in [reporters and preprocessors][
|
|
611
|
+
Find more details in [reporters and preprocessors][50].
|
|
610
612
|
|
|
611
613
|
## Fixing Issues
|
|
612
614
|
|
|
@@ -624,9 +626,20 @@ Tip: back up files or use an VCS like Git before deleting files or making change
|
|
|
624
626
|
|
|
625
627
|
Repeat the process to reveal new unused files and exports. It's so liberating to remove unused things!
|
|
626
628
|
|
|
627
|
-
Getting too many reported issues and false positives? Read more about [handling issues][
|
|
629
|
+
Getting too many reported issues and false positives? Read more about [handling issues][49] describing potential causes
|
|
628
630
|
for false positives, and how to handle them.
|
|
629
631
|
|
|
632
|
+
## JSDoc tags
|
|
633
|
+
|
|
634
|
+
Knip takes the following JSDoc/TSDoc tags into account:
|
|
635
|
+
|
|
636
|
+
| Tag | Description |
|
|
637
|
+
| :---------- | :------------------------------------------------------ |
|
|
638
|
+
| `@public` | Do not report this unused export, type or member |
|
|
639
|
+
| `@beta` | ^^ Idem |
|
|
640
|
+
| `@internal` | Do not report this unused export in `--production` mode |
|
|
641
|
+
| `@alias` | Do not report this duplicate export |
|
|
642
|
+
|
|
630
643
|
## Command Line Options
|
|
631
644
|
|
|
632
645
|
$ npx knip --help
|
|
@@ -640,7 +653,7 @@ for false positives, and how to handle them.
|
|
|
640
653
|
--production Analyze only production source files (e.g. no tests, devDependencies, exported types)
|
|
641
654
|
--strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)
|
|
642
655
|
--ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)
|
|
643
|
-
--workspace [dir]
|
|
656
|
+
-W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)
|
|
644
657
|
--no-gitignore Don't use .gitignore
|
|
645
658
|
--include Report only provided issue type(s), can be comma-separated or repeated (1)
|
|
646
659
|
--exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
|
|
@@ -675,13 +688,13 @@ for false positives, and how to handle them.
|
|
|
675
688
|
|
|
676
689
|
## Potential boost with `--no-gitignore`
|
|
677
690
|
|
|
678
|
-
To increase performance in a large monorepo, check out [Potential boost with `--no-gitignore`][
|
|
691
|
+
To increase performance in a large monorepo, check out [Potential boost with `--no-gitignore`][51].
|
|
679
692
|
|
|
680
693
|
## Comparison & Migration
|
|
681
694
|
|
|
682
695
|
This table is an ongoing comparison. Based on their docs (please report any mistakes):
|
|
683
696
|
|
|
684
|
-
| Feature | **knip** | [depcheck][
|
|
697
|
+
| Feature | **knip** | [depcheck][52] | [unimported][53] | [ts-unused-exports][54] | [ts-prune][55] |
|
|
685
698
|
| :---------------------- | :------: | :------------: | :--------------: | :---------------------: | :------------: |
|
|
686
699
|
| Unused files | ✅ | - | ✅ | - | - |
|
|
687
700
|
| Unused dependencies | ✅ | ✅ | ✅ | - | - |
|
|
@@ -717,7 +730,7 @@ The following commands are similar:
|
|
|
717
730
|
unimported
|
|
718
731
|
knip --production --dependencies --include files
|
|
719
732
|
|
|
720
|
-
Also see [production mode][
|
|
733
|
+
Also see [production mode][25].
|
|
721
734
|
|
|
722
735
|
### ts-unused-exports
|
|
723
736
|
|
|
@@ -739,25 +752,25 @@ The following commands are similar:
|
|
|
739
752
|
|
|
740
753
|
Many thanks to some of the early adopters of Knip:
|
|
741
754
|
|
|
742
|
-
- [Block Protocol][
|
|
743
|
-
- [DeepmergeTS][
|
|
744
|
-
- [eslint-plugin-functional][
|
|
745
|
-
- [freeCodeCamp.org][
|
|
746
|
-
- [is-immutable-type][
|
|
747
|
-
- [IsaacScript][
|
|
748
|
-
- [Nuxt][
|
|
749
|
-
- [Owncast][
|
|
750
|
-
- [release-it][
|
|
751
|
-
- [Template TypeScript Node Package][
|
|
752
|
-
- [Tipi][
|
|
755
|
+
- [Block Protocol][56]
|
|
756
|
+
- [DeepmergeTS][57]
|
|
757
|
+
- [eslint-plugin-functional][58]
|
|
758
|
+
- [freeCodeCamp.org][59]
|
|
759
|
+
- [is-immutable-type][60]
|
|
760
|
+
- [IsaacScript][61]
|
|
761
|
+
- [Nuxt][62]
|
|
762
|
+
- [Owncast][63]
|
|
763
|
+
- [release-it][64]
|
|
764
|
+
- [Template TypeScript Node Package][65]
|
|
765
|
+
- [Tipi][66]
|
|
753
766
|
|
|
754
767
|
## Articles, etc.
|
|
755
768
|
|
|
756
|
-
- Discord: hang out in [The Knip Barn][
|
|
757
|
-
- Ask your questions in the [Knip knowledge base][
|
|
758
|
-
- Smashing Magazine: [Knip: An Automated Tool For Finding Unused Files, Exports, And Dependencies][
|
|
759
|
-
- Effective TypeScript: [Recommendation Update: ✂️ Use knip to detect dead code and types][
|
|
760
|
-
- Josh Goldberg: [Speeding Up Centered Part 4: Unused Code Bloat][
|
|
769
|
+
- Discord: hang out in [The Knip Barn][10]
|
|
770
|
+
- Ask your questions in the [Knip knowledge base][67] (powered by OpenAI and [7-docs][68], experimental!)
|
|
771
|
+
- Smashing Magazine: [Knip: An Automated Tool For Finding Unused Files, Exports, And Dependencies][69]
|
|
772
|
+
- Effective TypeScript: [Recommendation Update: ✂️ Use knip to detect dead code and types][70]
|
|
773
|
+
- Josh Goldberg: [Speeding Up Centered Part 4: Unused Code Bloat][71]
|
|
761
774
|
|
|
762
775
|
## Why "Knip"?
|
|
763
776
|
|
|
@@ -775,7 +788,7 @@ each file, and traversing all of this, why not collect the various issues in one
|
|
|
775
788
|
|
|
776
789
|
Special thanks to the wonderful people who have contributed to this project:
|
|
777
790
|
|
|
778
|
-
[![Contributors][
|
|
791
|
+
[![Contributors][73]][72]
|
|
779
792
|
|
|
780
793
|
[1]: #workspaces
|
|
781
794
|
[2]: #plugins
|
|
@@ -783,72 +796,73 @@ Special thanks to the wonderful people who have contributed to this project:
|
|
|
783
796
|
[4]: #reporters
|
|
784
797
|
[5]: #custom-reporters
|
|
785
798
|
[6]: #preprocessers
|
|
786
|
-
[7]:
|
|
787
|
-
[8]:
|
|
788
|
-
[9]:
|
|
789
|
-
[10]: https://
|
|
790
|
-
[11]: https://
|
|
791
|
-
[12]: https://
|
|
792
|
-
[13]:
|
|
793
|
-
[14]: #
|
|
794
|
-
[15]: #
|
|
795
|
-
[16]: #
|
|
796
|
-
[17]: #
|
|
797
|
-
[18]: #
|
|
798
|
-
[19]: #
|
|
799
|
-
[20]: #
|
|
800
|
-
[21]: #
|
|
801
|
-
[22]: #
|
|
802
|
-
[23]: #
|
|
803
|
-
[24]: #
|
|
804
|
-
[25]: #
|
|
805
|
-
[26]: #
|
|
806
|
-
[27]: #
|
|
807
|
-
[28]: #
|
|
808
|
-
[29]: #
|
|
809
|
-
[30]: #
|
|
810
|
-
[31]: #
|
|
811
|
-
[32]: #
|
|
812
|
-
[33]: #
|
|
813
|
-
[34]: #
|
|
814
|
-
[35]: #
|
|
815
|
-
[36]: #
|
|
816
|
-
[37]: #
|
|
817
|
-
[38]: #
|
|
818
|
-
[39]: #ts-
|
|
819
|
-
[40]: #
|
|
820
|
-
[41]: #
|
|
821
|
-
[42]: #
|
|
822
|
-
[43]: #
|
|
823
|
-
[44]: #
|
|
824
|
-
[45]:
|
|
825
|
-
[46]:
|
|
826
|
-
[47]: ./docs/
|
|
827
|
-
[48]: ./docs/
|
|
828
|
-
[49]: ./docs/
|
|
829
|
-
[50]: ./docs/
|
|
830
|
-
[51]:
|
|
831
|
-
[52]: https://github.com/
|
|
832
|
-
[53]: https://github.com/
|
|
833
|
-
[54]: https://github.com/
|
|
834
|
-
[55]: https://github.com/
|
|
835
|
-
[56]: https://github.com/
|
|
836
|
-
[57]: https://github.com/
|
|
837
|
-
[58]: https://github.com/
|
|
838
|
-
[59]: https://github.com/
|
|
839
|
-
[60]: https://github.com/
|
|
840
|
-
[61]: https://github.com/
|
|
841
|
-
[62]: https://github.com/
|
|
842
|
-
[63]: https://github.com/
|
|
843
|
-
[64]: https://github.com/
|
|
844
|
-
[65]: https://github.com/
|
|
845
|
-
[66]: https://
|
|
846
|
-
[67]: https://
|
|
847
|
-
[68]: https://
|
|
848
|
-
[69]: https://
|
|
849
|
-
[70]: https://
|
|
850
|
-
[71]: https://
|
|
851
|
-
[72]: https://
|
|
799
|
+
[7]: #jsdoc-tags
|
|
800
|
+
[8]: https://labs.openai.com/s/xZQACaLepaKya0PRUPtIN5dC
|
|
801
|
+
[9]: ./assets/cow-with-orange-scissors-van-gogh-style.webp
|
|
802
|
+
[10]: https://discord.gg/ya5yktTq
|
|
803
|
+
[11]: https://twitter.com/webprolific
|
|
804
|
+
[12]: https://fosstodon.org/@webpro
|
|
805
|
+
[13]: https://github.com/webpro/knip/issues
|
|
806
|
+
[14]: #getting-started
|
|
807
|
+
[15]: #installation
|
|
808
|
+
[16]: #default-configuration
|
|
809
|
+
[17]: #lets-go
|
|
810
|
+
[18]: #configuration
|
|
811
|
+
[19]: #entry-files
|
|
812
|
+
[20]: #ignore-files-binaries-dependencies-and-workspaces
|
|
813
|
+
[21]: #public-exports
|
|
814
|
+
[22]: #ignore-exports-used-in-file
|
|
815
|
+
[23]: #include-exports-in-entry-files
|
|
816
|
+
[24]: #paths
|
|
817
|
+
[25]: #production-mode
|
|
818
|
+
[26]: #strict
|
|
819
|
+
[27]: #ignore-internal-exports
|
|
820
|
+
[28]: #plugins-1
|
|
821
|
+
[29]: #output
|
|
822
|
+
[30]: #screenshots
|
|
823
|
+
[31]: #reading-the-report
|
|
824
|
+
[32]: #rules--filters
|
|
825
|
+
[33]: #fixing-issues
|
|
826
|
+
[34]: #command-line-options
|
|
827
|
+
[35]: #potential-boost-with---no-gitignore
|
|
828
|
+
[36]: #comparison--migration
|
|
829
|
+
[37]: #depcheck
|
|
830
|
+
[38]: #unimported
|
|
831
|
+
[39]: #ts-unused-exports
|
|
832
|
+
[40]: #ts-prune
|
|
833
|
+
[41]: #projects-using-knip
|
|
834
|
+
[42]: #articles-etc
|
|
835
|
+
[43]: #why-knip
|
|
836
|
+
[44]: #really-another-unused-filedependencyexport-finder
|
|
837
|
+
[45]: #contributors
|
|
838
|
+
[46]: https://nx.dev/concepts/integrated-vs-package-based
|
|
839
|
+
[47]: ./docs/writing-a-plugin.md
|
|
840
|
+
[48]: ./docs/compilers.md
|
|
841
|
+
[49]: ./docs/handling-issues.md
|
|
842
|
+
[50]: ./docs/reporters-and-preprocessors.md
|
|
843
|
+
[51]: ./docs/perf-boost-with-no-gitignore.md
|
|
844
|
+
[52]: https://github.com/depcheck/depcheck
|
|
845
|
+
[53]: https://github.com/smeijer/unimported
|
|
846
|
+
[54]: https://github.com/pzavolinsky/ts-unused-exports
|
|
847
|
+
[55]: https://github.com/nadeesha/ts-prune
|
|
848
|
+
[56]: https://github.com/blockprotocol/blockprotocol
|
|
849
|
+
[57]: https://github.com/RebeccaStevens/deepmerge-ts
|
|
850
|
+
[58]: https://github.com/eslint-functional/eslint-plugin-functional
|
|
851
|
+
[59]: https://github.com/freeCodeCamp/freeCodeCamp
|
|
852
|
+
[60]: https://github.com/RebeccaStevens/is-immutable-type
|
|
853
|
+
[61]: https://github.com/IsaacScript/isaacscript
|
|
854
|
+
[62]: https://github.com/nuxt/nuxt
|
|
855
|
+
[63]: https://github.com/owncast/owncast
|
|
856
|
+
[64]: https://github.com/release-it/release-it
|
|
857
|
+
[65]: https://github.com/JoshuaKGoldberg/template-typescript-node-package
|
|
858
|
+
[66]: https://github.com/meienberger/runtipi
|
|
859
|
+
[67]: https://knip.deno.dev
|
|
860
|
+
[68]: https://github.com/7-docs/7-docs
|
|
861
|
+
[69]: https://www.smashingmagazine.com/2023/08/knip-automated-tool-find-unused-files-exports-dependencies/
|
|
862
|
+
[70]: https://effectivetypescript.com/2023/07/29/knip/
|
|
863
|
+
[71]: https://www.joshuakgoldberg.com/blog/speeding-up-centered-part-4-unused-code-bloat/
|
|
864
|
+
[72]: https://github.com/webpro/knip/graphs/contributors
|
|
865
|
+
[73]: https://contrib.rocks/image?repo=webpro/knip
|
|
852
866
|
[plugin-ava]: ./src/plugins/ava
|
|
853
867
|
[plugin-babel]: ./src/plugins/babel
|
|
854
868
|
[plugin-capacitor]: ./src/plugins/capacitor
|
|
@@ -32,6 +32,7 @@ export declare class DependencyDeputy {
|
|
|
32
32
|
scripts: string[];
|
|
33
33
|
dependencies: string[];
|
|
34
34
|
peerDependencies: string[];
|
|
35
|
+
optionalPeerDependencies: string[];
|
|
35
36
|
optionalDependencies: string[];
|
|
36
37
|
devDependencies: string[];
|
|
37
38
|
allDependencies: string[];
|
|
@@ -46,12 +47,15 @@ export declare class DependencyDeputy {
|
|
|
46
47
|
addReferencedBinary(workspaceName: string, binaryName: string): void;
|
|
47
48
|
addPeerDependencies(workspaceName: string, peerDependencies: Map<string, Set<string>>): void;
|
|
48
49
|
getPeerDependenciesOf(workspaceName: string, dependency: string): string[];
|
|
50
|
+
getPeerDependencies(workspaceName: string): string[];
|
|
51
|
+
getOptionalPeerDependencies(workspaceName: string): string[];
|
|
49
52
|
maybeAddReferencedExternalDependency(workspace: Workspace, packageName: string): boolean;
|
|
50
53
|
maybeAddReferencedBinary(workspace: Workspace, binaryName: string): boolean;
|
|
51
54
|
private isInDependencies;
|
|
52
55
|
settleDependencyIssues(): {
|
|
53
56
|
dependencyIssues: Issue[];
|
|
54
57
|
devDependencyIssues: Issue[];
|
|
58
|
+
optionalPeerDependencyIssues: Issue[];
|
|
55
59
|
};
|
|
56
60
|
getConfigurationHints(): {
|
|
57
61
|
configurationHints: ConfigurationHints;
|
package/dist/DependencyDeputy.js
CHANGED
|
@@ -22,6 +22,11 @@ export class DependencyDeputy {
|
|
|
22
22
|
const dependencies = Object.keys(manifest.dependencies ?? {});
|
|
23
23
|
const peerDependencies = Object.keys(manifest.peerDependencies ?? {});
|
|
24
24
|
const optionalDependencies = Object.keys(manifest.optionalDependencies ?? {});
|
|
25
|
+
const optionalPeerDependencies = manifest.peerDependenciesMeta
|
|
26
|
+
? peerDependencies.filter(peerDependency => manifest.peerDependenciesMeta &&
|
|
27
|
+
peerDependency in manifest.peerDependenciesMeta &&
|
|
28
|
+
manifest.peerDependenciesMeta[peerDependency].optional)
|
|
29
|
+
: [];
|
|
25
30
|
const devDependencies = Object.keys(manifest.devDependencies ?? {});
|
|
26
31
|
const allDependencies = [...dependencies, ...devDependencies, ...peerDependencies, ...optionalDependencies];
|
|
27
32
|
this._manifests.set(name, {
|
|
@@ -32,6 +37,7 @@ export class DependencyDeputy {
|
|
|
32
37
|
scripts,
|
|
33
38
|
dependencies,
|
|
34
39
|
peerDependencies,
|
|
40
|
+
optionalPeerDependencies,
|
|
35
41
|
optionalDependencies,
|
|
36
42
|
devDependencies,
|
|
37
43
|
allDependencies,
|
|
@@ -79,6 +85,18 @@ export class DependencyDeputy {
|
|
|
79
85
|
getPeerDependenciesOf(workspaceName, dependency) {
|
|
80
86
|
return Array.from(this.peerDependencies.get(workspaceName)?.get(dependency) ?? []);
|
|
81
87
|
}
|
|
88
|
+
getPeerDependencies(workspaceName) {
|
|
89
|
+
const manifest = this._manifests.get(workspaceName);
|
|
90
|
+
if (!manifest)
|
|
91
|
+
return [];
|
|
92
|
+
return manifest.peerDependencies;
|
|
93
|
+
}
|
|
94
|
+
getOptionalPeerDependencies(workspaceName) {
|
|
95
|
+
const manifest = this._manifests.get(workspaceName);
|
|
96
|
+
if (!manifest)
|
|
97
|
+
return [];
|
|
98
|
+
return manifest.optionalPeerDependencies;
|
|
99
|
+
}
|
|
82
100
|
maybeAddReferencedExternalDependency(workspace, packageName) {
|
|
83
101
|
if (isBuiltin(packageName))
|
|
84
102
|
return true;
|
|
@@ -133,6 +151,7 @@ export class DependencyDeputy {
|
|
|
133
151
|
settleDependencyIssues() {
|
|
134
152
|
const dependencyIssues = [];
|
|
135
153
|
const devDependencyIssues = [];
|
|
154
|
+
const optionalPeerDependencyIssues = [];
|
|
136
155
|
for (const [workspaceName, { manifestPath, ignoreDependencies, ignoreBinaries }] of this._manifests.entries()) {
|
|
137
156
|
const referencedDependencies = this.referencedDependencies.get(workspaceName);
|
|
138
157
|
const installedBinaries = this.getInstalledBinaries(workspaceName);
|
|
@@ -175,6 +194,7 @@ export class DependencyDeputy {
|
|
|
175
194
|
const isNotReferencedDependency = (dependency) => !isReferencedDependency(dependency);
|
|
176
195
|
const pd = this.getProductionDependencies(workspaceName);
|
|
177
196
|
const dd = this.getDevDependencies(workspaceName);
|
|
197
|
+
const od = this.getOptionalPeerDependencies(workspaceName);
|
|
178
198
|
pd.filter(isNotIgnoredDependency)
|
|
179
199
|
.filter(isNotIgnoredBinary)
|
|
180
200
|
.filter(isNotReferencedDependency)
|
|
@@ -183,8 +203,12 @@ export class DependencyDeputy {
|
|
|
183
203
|
.filter(isNotIgnoredBinary)
|
|
184
204
|
.filter(isNotReferencedDependency)
|
|
185
205
|
.forEach(symbol => devDependencyIssues.push({ type: 'devDependencies', filePath: manifestPath, symbol }));
|
|
206
|
+
od.filter(isNotIgnoredDependency)
|
|
207
|
+
.filter(isNotIgnoredBinary)
|
|
208
|
+
.filter(p => isReferencedDependency(p))
|
|
209
|
+
.forEach(symbol => optionalPeerDependencyIssues.push({ type: 'optionalPeerDependencies', filePath: manifestPath, symbol }));
|
|
186
210
|
}
|
|
187
|
-
return { dependencyIssues, devDependencyIssues };
|
|
211
|
+
return { dependencyIssues, devDependencyIssues, optionalPeerDependencyIssues };
|
|
188
212
|
}
|
|
189
213
|
getConfigurationHints() {
|
|
190
214
|
const configurationHints = new Set();
|
|
@@ -199,12 +223,13 @@ export class DependencyDeputy {
|
|
|
199
223
|
const dependencies = this.isStrict
|
|
200
224
|
? this.getProductionDependencies(workspaceName)
|
|
201
225
|
: [...this.getProductionDependencies(workspaceName), ...this.getDevDependencies(workspaceName)];
|
|
226
|
+
const peerDependencies = this.getPeerDependencies(workspaceName);
|
|
202
227
|
const isReferencedDep = (name) => referencedDependencies?.has(name) && dependencies.includes(name);
|
|
203
228
|
const isReferencedBin = (name) => referencedBinaries?.has(name) && installedBinaries?.has(name);
|
|
204
229
|
ignoreDependencies
|
|
205
230
|
.filter(packageName => IGNORED_DEPENDENCIES.includes(packageName) ||
|
|
206
231
|
(workspaceName !== ROOT_WORKSPACE_NAME && this.ignoreDependencies.includes(packageName)) ||
|
|
207
|
-
isReferencedDep(packageName))
|
|
232
|
+
(!peerDependencies.includes(packageName) && isReferencedDep(packageName)))
|
|
208
233
|
.forEach(identifier => configurationHints.add({ workspaceName, identifier, type: 'ignoreDependencies' }));
|
|
209
234
|
ignoreBinaries
|
|
210
235
|
.filter(binaryName => IGNORED_GLOBAL_BINARIES.includes(binaryName) ||
|
|
@@ -216,11 +241,13 @@ export class DependencyDeputy {
|
|
|
216
241
|
const dependencies = this.isStrict
|
|
217
242
|
? this.getProductionDependencies(ROOT_WORKSPACE_NAME)
|
|
218
243
|
: [...this.getProductionDependencies(ROOT_WORKSPACE_NAME), ...this.getDevDependencies(ROOT_WORKSPACE_NAME)];
|
|
244
|
+
const peerDependencies = this.getPeerDependencies(ROOT_WORKSPACE_NAME);
|
|
219
245
|
Object.keys(rootIgnoreBinaries)
|
|
220
246
|
.filter(key => IGNORED_GLOBAL_BINARIES.includes(key) || (rootIgnoreBinaries[key] !== 0 && installedBinaries?.has(key)))
|
|
221
247
|
.forEach(identifier => configurationHints.add({ workspaceName: ROOT_WORKSPACE_NAME, identifier, type: 'ignoreBinaries' }));
|
|
222
248
|
Object.keys(rootIgnoreDependencies)
|
|
223
|
-
.filter(key => IGNORED_DEPENDENCIES.includes(key) ||
|
|
249
|
+
.filter(key => IGNORED_DEPENDENCIES.includes(key) ||
|
|
250
|
+
(rootIgnoreDependencies[key] !== 0 && !peerDependencies.includes(key) && dependencies.includes(key)))
|
|
224
251
|
.forEach(identifier => configurationHints.add({ workspaceName: ROOT_WORKSPACE_NAME, identifier, type: 'ignoreDependencies' }));
|
|
225
252
|
return { configurationHints };
|
|
226
253
|
}
|
|
@@ -60,7 +60,5 @@ export declare class ProjectPrincipal {
|
|
|
60
60
|
};
|
|
61
61
|
findUnusedMembers(filePath: string, members: ExportItemMember[]): string[];
|
|
62
62
|
private findReferences;
|
|
63
|
-
isPublicExport(exportedItem: ExportItem): boolean;
|
|
64
|
-
getJSDocTags(exportedItem: ExportItem): string[];
|
|
65
63
|
}
|
|
66
64
|
export {};
|
package/dist/ProjectPrincipal.js
CHANGED
|
@@ -2,7 +2,7 @@ import { isGitIgnoredSync } from 'globby';
|
|
|
2
2
|
import ts from 'typescript';
|
|
3
3
|
import { DEFAULT_EXTENSIONS } from './constants.js';
|
|
4
4
|
import { IGNORED_FILE_EXTENSIONS } from './constants.js';
|
|
5
|
-
import { isInModuleBlock } from './typescript/ast-helpers.js';
|
|
5
|
+
import { getJSDocTags, isInModuleBlock } from './typescript/ast-helpers.js';
|
|
6
6
|
import { createHosts } from './typescript/createHosts.js';
|
|
7
7
|
import { getImportsAndExports } from './typescript/getImportsAndExports.js';
|
|
8
8
|
import { SourceFileManager } from './typescript/SourceFileManager.js';
|
|
@@ -178,7 +178,7 @@ export class ProjectPrincipal {
|
|
|
178
178
|
findUnusedMembers(filePath, members) {
|
|
179
179
|
return members
|
|
180
180
|
.filter(member => {
|
|
181
|
-
if (
|
|
181
|
+
if (getJSDocTags(member.node).includes('@public'))
|
|
182
182
|
return false;
|
|
183
183
|
const referencedSymbols = this.findReferences(filePath, member.pos);
|
|
184
184
|
const files = referencedSymbols
|
|
@@ -194,11 +194,4 @@ export class ProjectPrincipal {
|
|
|
194
194
|
findReferences(filePath, pos) {
|
|
195
195
|
return this.backend.lsFindReferences(filePath, pos) ?? [];
|
|
196
196
|
}
|
|
197
|
-
isPublicExport(exportedItem) {
|
|
198
|
-
const tags = this.getJSDocTags(exportedItem);
|
|
199
|
-
return tags.includes('@public');
|
|
200
|
-
}
|
|
201
|
-
getJSDocTags(exportedItem) {
|
|
202
|
-
return ts.getJSDocTags(exportedItem.node).map(node => node.getText().match(/@\S+/)[0]);
|
|
203
|
-
}
|
|
204
197
|
}
|
|
@@ -15,7 +15,7 @@ export const getBinariesFromScript = (script, { cwd, manifest, knownGlobalsOnly
|
|
|
15
15
|
if (commandExpansions.length > 0) {
|
|
16
16
|
return commandExpansions.flatMap(expansion => getBinariesFromNodes(expansion.commandAST.commands)) ?? [];
|
|
17
17
|
}
|
|
18
|
-
if (!binary || binary === '.' || binary === 'source')
|
|
18
|
+
if (!binary || binary === '.' || binary === 'source' || binary === '[')
|
|
19
19
|
return [];
|
|
20
20
|
if (binary.startsWith('-') || binary.startsWith('"') || binary.startsWith('..'))
|
|
21
21
|
return [];
|
|
@@ -34,13 +34,15 @@ export const getBinariesFromScript = (script, { cwd, manifest, knownGlobalsOnly
|
|
|
34
34
|
case 'LogicalExpression':
|
|
35
35
|
return getBinariesFromNodes([node.left, node.right]);
|
|
36
36
|
case 'If':
|
|
37
|
-
return getBinariesFromNodes([
|
|
37
|
+
return getBinariesFromNodes([node.clause, node.then, ...(node.else ? [node.else] : [])]);
|
|
38
38
|
case 'For':
|
|
39
39
|
return getBinariesFromNodes(node.do.commands);
|
|
40
40
|
case 'CompoundList':
|
|
41
41
|
return getBinariesFromNodes(node.commands);
|
|
42
42
|
case 'Pipeline':
|
|
43
43
|
return getBinariesFromNodes(node.commands);
|
|
44
|
+
case 'Function':
|
|
45
|
+
return getBinariesFromNodes(node.body.commands);
|
|
44
46
|
default:
|
|
45
47
|
return [];
|
|
46
48
|
}
|
|
@@ -53,7 +53,7 @@ const commands = [
|
|
|
53
53
|
];
|
|
54
54
|
export const resolve = (_binary, args, { manifest }) => {
|
|
55
55
|
const scripts = manifest.scripts ? Object.keys(manifest.scripts) : [];
|
|
56
|
-
const parsed = parseArgs(args, {});
|
|
56
|
+
const parsed = parseArgs(args, { alias: { recursive: 'r' }, boolean: ['recursive'] });
|
|
57
57
|
const [command, binary] = parsed._;
|
|
58
58
|
if (scripts.includes(command) || commands.includes(command))
|
|
59
59
|
return [];
|
package/dist/constants.js
CHANGED
|
@@ -60,6 +60,7 @@ export const ISSUE_TYPES = [
|
|
|
60
60
|
'files',
|
|
61
61
|
'dependencies',
|
|
62
62
|
'devDependencies',
|
|
63
|
+
'optionalPeerDependencies',
|
|
63
64
|
'unlisted',
|
|
64
65
|
'binaries',
|
|
65
66
|
'unresolved',
|
|
@@ -75,6 +76,7 @@ export const ISSUE_TYPE_TITLE = {
|
|
|
75
76
|
files: 'Unused files',
|
|
76
77
|
dependencies: 'Unused dependencies',
|
|
77
78
|
devDependencies: 'Unused devDependencies',
|
|
79
|
+
optionalPeerDependencies: 'Referenced optional peerDependencies',
|
|
78
80
|
unlisted: 'Unlisted dependencies',
|
|
79
81
|
binaries: 'Unlisted binaries',
|
|
80
82
|
unresolved: 'Unresolved imports',
|
package/dist/index.js
CHANGED
|
@@ -208,8 +208,10 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
duplicate.forEach(symbols => {
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
if (symbols.length > 1) {
|
|
212
|
+
const symbol = symbols.join('|');
|
|
213
|
+
collector.addIssue({ type: 'duplicates', filePath, symbol, symbols });
|
|
214
|
+
}
|
|
213
215
|
});
|
|
214
216
|
external.forEach(specifier => {
|
|
215
217
|
const packageName = getPackageNameFromModuleSpecifier(specifier);
|
|
@@ -276,10 +278,9 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
276
278
|
continue;
|
|
277
279
|
const importingModule = importedSymbols.get(filePath);
|
|
278
280
|
for (const [symbol, exportedItem] of exportItems.entries()) {
|
|
279
|
-
|
|
280
|
-
if (jsDocTags.includes('@public') || jsDocTags.includes('@beta'))
|
|
281
|
+
if (exportedItem.jsDocTags.includes('@public') || exportedItem.jsDocTags.includes('@beta'))
|
|
281
282
|
continue;
|
|
282
|
-
if (isIgnoreInternal && jsDocTags.includes('@internal'))
|
|
283
|
+
if (isIgnoreInternal && exportedItem.jsDocTags.includes('@internal'))
|
|
283
284
|
continue;
|
|
284
285
|
if (importingModule && isSymbolImported(symbol, importingModule)) {
|
|
285
286
|
if (importingModule.isReExport && isExportedInEntryFile(importingModule))
|
|
@@ -317,11 +318,12 @@ export const main = async (unresolvedConfiguration) => {
|
|
|
317
318
|
}
|
|
318
319
|
}
|
|
319
320
|
if (isReportDependencies) {
|
|
320
|
-
const { dependencyIssues, devDependencyIssues } = deputy.settleDependencyIssues();
|
|
321
|
+
const { dependencyIssues, devDependencyIssues, optionalPeerDependencyIssues } = deputy.settleDependencyIssues();
|
|
321
322
|
const { configurationHints } = deputy.getConfigurationHints();
|
|
322
323
|
dependencyIssues.forEach(issue => collector.addIssue(issue));
|
|
323
324
|
if (!isProduction)
|
|
324
325
|
devDependencyIssues.forEach(issue => collector.addIssue(issue));
|
|
326
|
+
optionalPeerDependencyIssues.forEach(issue => collector.addIssue(issue));
|
|
325
327
|
configurationHints.forEach(hint => collector.addConfigurationHint(hint));
|
|
326
328
|
}
|
|
327
329
|
const unusedIgnoredWorkspaces = chief.getUnusedIgnoredWorkspaces();
|
|
@@ -5,7 +5,7 @@ import { _resolve } from '../../util/require.js';
|
|
|
5
5
|
import { fallback } from './fallback.js';
|
|
6
6
|
const getDependencies = (config) => {
|
|
7
7
|
const extendsSpecifiers = config.extends ? [config.extends].flat().map(resolveExtendSpecifier) : [];
|
|
8
|
-
if (extendsSpecifiers.
|
|
8
|
+
if (extendsSpecifiers.some(specifier => specifier?.startsWith('eslint-plugin-prettier')))
|
|
9
9
|
extendsSpecifiers.push('eslint-config-prettier');
|
|
10
10
|
const plugins = config.plugins ? config.plugins.map(resolvePluginSpecifier) : [];
|
|
11
11
|
const parser = config.parser;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import { _getDependenciesFromScripts } from '../../binaries/index.js';
|
|
3
|
+
import { getGitHookPaths } from '../../util/git.js';
|
|
3
4
|
import { timerify } from '../../util/Performance.js';
|
|
4
5
|
import { hasDependency } from '../../util/plugin.js';
|
|
5
|
-
import { getGitHooksPath } from './helpers.js';
|
|
6
6
|
export const NAME = 'husky';
|
|
7
7
|
export const ENABLERS = ['husky'];
|
|
8
8
|
export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
|
|
9
|
-
const
|
|
10
|
-
export const CONFIG_FILE_PATTERNS = [
|
|
11
|
-
`${gitHooksPath}/prepare-commit-msg`,
|
|
12
|
-
`${gitHooksPath}/commit-msg`,
|
|
13
|
-
`${gitHooksPath}/pre-{applypatch,commit,merge-commit,push,rebase,receive}`,
|
|
14
|
-
`${gitHooksPath}/post-{checkout,commit,merge,rewrite}`,
|
|
15
|
-
];
|
|
9
|
+
const gitHookPaths = getGitHookPaths('.husky');
|
|
10
|
+
export const CONFIG_FILE_PATTERNS = [...gitHookPaths];
|
|
16
11
|
const findHuskyDependencies = async (configFilePath, { cwd, manifest }) => {
|
|
17
12
|
const script = readFileSync(configFilePath);
|
|
18
13
|
return _getDependenciesFromScripts(String(script), {
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
1
2
|
import { _getDependenciesFromScripts } from '../../binaries/index.js';
|
|
3
|
+
import { fromBinary } from '../../binaries/util.js';
|
|
4
|
+
import { getGitHookPaths } from '../../util/git.js';
|
|
2
5
|
import { getValuesByKeyDeep } from '../../util/object.js';
|
|
3
6
|
import { timerify } from '../../util/Performance.js';
|
|
4
7
|
import { hasDependency, load } from '../../util/plugin.js';
|
|
5
8
|
export const NAME = 'Lefthook';
|
|
6
|
-
export const ENABLERS = ['lefthook', '@arkweid/lefthook'];
|
|
9
|
+
export const ENABLERS = ['lefthook', '@arkweid/lefthook', '@evilmartians/lefthook'];
|
|
7
10
|
export const isEnabled = ({ dependencies }) => hasDependency(dependencies, ENABLERS);
|
|
8
|
-
|
|
11
|
+
const gitHookPaths = getGitHookPaths();
|
|
12
|
+
export const CONFIG_FILE_PATTERNS = ['lefthook.yml', ...gitHookPaths];
|
|
9
13
|
const findLefthookDependencies = async (configFilePath, { cwd, manifest }) => {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
knownGlobalsOnly: true
|
|
18
|
-
}
|
|
14
|
+
const dependencies = manifest.devDependencies ? Object.keys(manifest.devDependencies) : [];
|
|
15
|
+
if (configFilePath.endsWith('.yml')) {
|
|
16
|
+
const config = await load(configFilePath);
|
|
17
|
+
if (!config)
|
|
18
|
+
return [];
|
|
19
|
+
const scripts = getValuesByKeyDeep(config, 'run').filter((value) => typeof value === 'string');
|
|
20
|
+
const lefthook = process.env.CI ? ENABLERS.filter(dependency => dependencies.includes(dependency)) : [];
|
|
21
|
+
return [...lefthook, ..._getDependenciesFromScripts(scripts, { cwd, manifest, knownGlobalsOnly: true })];
|
|
22
|
+
}
|
|
23
|
+
const script = readFileSync(configFilePath, 'utf8');
|
|
24
|
+
const scriptDependencies = _getDependenciesFromScripts([script], { cwd, manifest, knownGlobalsOnly: false });
|
|
25
|
+
const matches = scriptDependencies.find(dep => dependencies.includes(fromBinary(dep)));
|
|
26
|
+
return matches ? [matches] : [];
|
|
19
27
|
};
|
|
20
28
|
export const findDependencies = timerify(findLefthookDependencies);
|
|
@@ -18,6 +18,12 @@ const findReleaseItDependencies = async (configFilePath, { cwd, manifest }) => {
|
|
|
18
18
|
return [];
|
|
19
19
|
const plugins = config.plugins ? Object.keys(config.plugins) : [];
|
|
20
20
|
const scripts = config.hooks ? Object.values(config.hooks).flat() : [];
|
|
21
|
+
if (typeof config.github?.releaseNotes === 'string') {
|
|
22
|
+
scripts.push(config.github.releaseNotes);
|
|
23
|
+
}
|
|
24
|
+
if (typeof config.gitlab?.releaseNotes === 'string') {
|
|
25
|
+
scripts.push(config.gitlab.releaseNotes);
|
|
26
|
+
}
|
|
21
27
|
const dependencies = _getDependenciesFromScripts(scripts, { cwd, manifest });
|
|
22
28
|
return [...plugins, ...dependencies];
|
|
23
29
|
};
|
package/dist/reporters/json.js
CHANGED
|
@@ -22,6 +22,7 @@ export default async ({ report, issues, options }) => {
|
|
|
22
22
|
...(report.files && { files: false }),
|
|
23
23
|
...(report.dependencies && { dependencies: [] }),
|
|
24
24
|
...(report.devDependencies && { devDependencies: [] }),
|
|
25
|
+
...(report.optionalPeerDependencies && { optionalPeerDependencies: [] }),
|
|
25
26
|
...(report.unlisted && { unlisted: [] }),
|
|
26
27
|
...(report.unresolved && { unresolved: [] }),
|
|
27
28
|
...((report.exports || report.nsExports) && { exports: [] }),
|
package/dist/types/exports.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type ExportItem = {
|
|
|
7
7
|
pos: number;
|
|
8
8
|
type: SymbolType;
|
|
9
9
|
members?: ExportItemMember[];
|
|
10
|
+
jsDocTags?: string[];
|
|
10
11
|
};
|
|
11
12
|
export type ExportItemMember = {
|
|
12
13
|
node: ts.Node;
|
|
@@ -14,6 +15,6 @@ export type ExportItemMember = {
|
|
|
14
15
|
pos: number;
|
|
15
16
|
type: SymbolType;
|
|
16
17
|
};
|
|
17
|
-
export type ExportItems = Map<string, ExportItem
|
|
18
|
+
export type ExportItems = Map<string, Required<ExportItem>>;
|
|
18
19
|
export type Exports = Map<FilePath, ExportItems>;
|
|
19
20
|
export {};
|
package/dist/types/issues.d.ts
CHANGED
|
@@ -23,4 +23,5 @@ export declare function findAncestor<T>(node: ts.Node | undefined, callback: (el
|
|
|
23
23
|
export declare function findDescendants<T>(node: ts.Node | undefined, callback: (element: ts.Node) => boolean | 'STOP'): T[];
|
|
24
24
|
export declare const isDeclarationFileExtension: (extension: string) => boolean;
|
|
25
25
|
export declare const isInModuleBlock: (node: ts.Node) => boolean;
|
|
26
|
+
export declare const getJSDocTags: (node: ts.Node) => string[];
|
|
26
27
|
export {};
|
|
@@ -3,7 +3,7 @@ import ts from 'typescript';
|
|
|
3
3
|
import { getOrSet } from '../util/map.js';
|
|
4
4
|
import { isMaybePackageName } from '../util/modules.js';
|
|
5
5
|
import { isInNodeModules } from '../util/path.js';
|
|
6
|
-
import { isDeclarationFileExtension, isAccessExpression, getAccessExpressionName } from './ast-helpers.js';
|
|
6
|
+
import { isDeclarationFileExtension, isAccessExpression, getAccessExpressionName, getJSDocTags, } from './ast-helpers.js';
|
|
7
7
|
import getExportVisitors from './visitors/exports/index.js';
|
|
8
8
|
import { getJSXImplicitImportBase } from './visitors/helpers.js';
|
|
9
9
|
import getImportVisitors from './visitors/imports/index.js';
|
|
@@ -86,20 +86,25 @@ export const getImportsAndExports = (sourceFile, options) => {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
-
const addExport = ({ node, identifier, type, pos, members }) => {
|
|
89
|
+
const addExport = ({ node, identifier, type, pos, members = [] }) => {
|
|
90
90
|
if (options.skipExports)
|
|
91
91
|
return;
|
|
92
|
+
const jsDocTags = getJSDocTags(node);
|
|
92
93
|
if (exports.has(identifier)) {
|
|
93
94
|
const item = exports.get(identifier);
|
|
94
|
-
|
|
95
|
+
const crew = [...item.members, ...members];
|
|
96
|
+
const tags = [...item.jsDocTags, ...jsDocTags];
|
|
97
|
+
exports.set(identifier, { ...item, node, type, pos, members: crew, jsDocTags: tags });
|
|
95
98
|
}
|
|
96
99
|
else {
|
|
97
|
-
exports.set(identifier, { node, type, pos, members });
|
|
100
|
+
exports.set(identifier, { node, type, pos, members, jsDocTags });
|
|
101
|
+
}
|
|
102
|
+
if (!jsDocTags.includes('@alias')) {
|
|
103
|
+
if (ts.isExportAssignment(node))
|
|
104
|
+
maybeAddAliasedExport(node.expression, 'default');
|
|
105
|
+
if (ts.isVariableDeclaration(node))
|
|
106
|
+
maybeAddAliasedExport(node.initializer, identifier);
|
|
98
107
|
}
|
|
99
|
-
if (ts.isExportAssignment(node))
|
|
100
|
-
maybeAddAliasedExport(node.expression, 'default');
|
|
101
|
-
if (ts.isVariableDeclaration(node))
|
|
102
|
-
maybeAddAliasedExport(node.initializer, identifier);
|
|
103
108
|
};
|
|
104
109
|
const maybeAddAliasedExport = (node, alias) => {
|
|
105
110
|
const identifier = node?.getText();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n --workspace [dir]
|
|
1
|
+
export declare const helpText = "\u2702\uFE0F Find unused files, dependencies and exports in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -c, --config [file] Configuration file path (default: [.]knip.json[c], knip.js, knip.ts or package.json#knip)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n --production Analyze only production source files (e.g. no tests, devDependencies, exported types)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)\n -W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)\n --no-gitignore Don't use .gitignore\n --include Report only provided issue type(s), can be comma-separated or repeated (1)\n --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)\n --dependencies Shortcut for --include dependencies,unlisted,unresolved\n --exports Shortcut for --include exports,nsExports,classMembers,types,nsTypes,enumMembers,duplicates\n --include-entry-exports Include entry files when reporting unused exports\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --reporter Select reporter: symbols, compact, codeowners, json, can be repeated (default: symbols)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --no-exit-code Always exit with code zero (0)\n --max-issues Maximum number of issues before non-zero exit code (default: 0)\n -d, --debug Show debug output\n --debug-file-filter Filter for files in debug output (regex as string)\n --performance Measure count and running time of expensive functions and display stats table\n -h, --help Print this help text\n -V, --version Print version\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, classMembers, types, nsTypes, enumMembers, duplicates\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --debug --debug-file-filter '(specific|particular)-module'\n\nMore documentation and bug reports: https://github.com/webpro/knip";
|
|
2
2
|
declare const _default: {
|
|
3
3
|
config: string | undefined;
|
|
4
4
|
debug: boolean | undefined;
|
|
@@ -9,7 +9,7 @@ Options:
|
|
|
9
9
|
--production Analyze only production source files (e.g. no tests, devDependencies, exported types)
|
|
10
10
|
--strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)
|
|
11
11
|
--ignore-internal Ignore exports with tag @internal (JSDoc/TSDoc)
|
|
12
|
-
--workspace [dir]
|
|
12
|
+
-W, --workspace [dir] Analyze a single workspace (default: analyze all configured workspaces)
|
|
13
13
|
--no-gitignore Don't use .gitignore
|
|
14
14
|
--include Report only provided issue type(s), can be comma-separated or repeated (1)
|
|
15
15
|
--exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
|
|
@@ -68,7 +68,7 @@ try {
|
|
|
68
68
|
strict: { type: 'boolean' },
|
|
69
69
|
tsConfig: { type: 'string', short: 't' },
|
|
70
70
|
version: { type: 'boolean', short: 'V' },
|
|
71
|
-
workspace: { type: 'string' },
|
|
71
|
+
workspace: { type: 'string', short: 'W' },
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
74
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { ISSUE_TYPES } from '../constants.js';
|
|
2
2
|
export const getIncludedIssueTypes = (cliArgs, { include = [], exclude = [], isProduction = false } = {}) => {
|
|
3
3
|
if (cliArgs.dependencies) {
|
|
4
|
-
cliArgs.include = [
|
|
4
|
+
cliArgs.include = [
|
|
5
|
+
...cliArgs.include,
|
|
6
|
+
'dependencies',
|
|
7
|
+
'optionalPeerDependencies',
|
|
8
|
+
'unlisted',
|
|
9
|
+
'binaries',
|
|
10
|
+
'unresolved',
|
|
11
|
+
];
|
|
5
12
|
}
|
|
6
13
|
if (cliArgs.exports) {
|
|
7
14
|
const exports = ['exports', 'nsExports', 'classMembers', 'types', 'nsTypes', 'enumMembers', 'duplicates'];
|
|
@@ -21,9 +28,9 @@ export const getIncludedIssueTypes = (cliArgs, { include = [], exclude = [], isP
|
|
|
21
28
|
}
|
|
22
29
|
else {
|
|
23
30
|
if (_include.includes('dependencies'))
|
|
24
|
-
_include.push('devDependencies');
|
|
31
|
+
_include.push('devDependencies', 'optionalPeerDependencies');
|
|
25
32
|
if (_exclude.includes('dependencies'))
|
|
26
|
-
_exclude.push('devDependencies');
|
|
33
|
+
_exclude.push('devDependencies', 'optionalPeerDependencies');
|
|
27
34
|
}
|
|
28
35
|
const included = (_include.length > 0 ? _include : ISSUE_TYPES).filter(group => !_exclude.includes(group));
|
|
29
36
|
return ISSUE_TYPES.reduce((types, group) => ((types[group] = included.includes(group)), types), {});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getGitHookPaths: (defaultPath?: string) => string[];
|
package/dist/util/git.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { join } from './path.js';
|
|
3
|
+
const hookFileNames = [
|
|
4
|
+
`prepare-commit-msg`,
|
|
5
|
+
`commit-msg`,
|
|
6
|
+
`pre-{applypatch,commit,merge-commit,push,rebase,receive}`,
|
|
7
|
+
`post-{checkout,commit,merge,rewrite}`,
|
|
8
|
+
];
|
|
9
|
+
const getGitHooksPath = (defaultPath = '.git/hooks') => {
|
|
10
|
+
try {
|
|
11
|
+
return execSync('git config --get core.hooksPath', { encoding: 'utf8' }).trim();
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
return defaultPath;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export const getGitHookPaths = (defaultPath = '.git/hooks') => {
|
|
18
|
+
const gitHooksPath = getGitHooksPath(defaultPath);
|
|
19
|
+
return hookFileNames.map(fileName => join(gitHooksPath, fileName));
|
|
20
|
+
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "2.
|
|
1
|
+
export declare const version = "2.23.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '2.
|
|
1
|
+
export const version = '2.23.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "knip",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.0",
|
|
4
4
|
"description": "Find unused files, dependencies and exports in your TypeScript and JavaScript projects",
|
|
5
5
|
"homepage": "https://github.com/webpro/knip",
|
|
6
6
|
"repository": "github:webpro/knip",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"pretest": "node rmdir.js tmp && swc src -d tmp/src && swc tests -d tmp/tests",
|
|
26
26
|
"test": "node --no-warnings --test tmp",
|
|
27
27
|
"coverage": "c8 --reporter html node --no-warnings --loader tsx --test tests/*.test.ts tests/*/*.test.ts",
|
|
28
|
-
"watch": "tsc --watch",
|
|
28
|
+
"watch": "rm $(which knip); npm link && tsc --watch",
|
|
29
29
|
"prebuild": "node rmdir.js dist",
|
|
30
30
|
"build": "tsc",
|
|
31
31
|
"docs": "npm run docs:cli && npm run docs:plugins && npm run docs:format",
|
|
@@ -65,21 +65,21 @@
|
|
|
65
65
|
"@npmcli/package-json": "5.0.0",
|
|
66
66
|
"@release-it/bumper": "5.1.0",
|
|
67
67
|
"@swc/cli": "0.1.62",
|
|
68
|
-
"@swc/core": "1.3.
|
|
68
|
+
"@swc/core": "1.3.84",
|
|
69
69
|
"@types/eslint": "8.44.2",
|
|
70
70
|
"@types/js-yaml": "4.0.5",
|
|
71
71
|
"@types/micromatch": "4.0.2",
|
|
72
72
|
"@types/minimist": "1.2.2",
|
|
73
|
-
"@types/node": "20.
|
|
73
|
+
"@types/node": "20.6.0",
|
|
74
74
|
"@types/npmcli__map-workspaces": "3.0.1",
|
|
75
75
|
"@types/webpack": "5.28.2",
|
|
76
|
-
"@typescript-eslint/eslint-plugin": "6.
|
|
77
|
-
"@typescript-eslint/parser": "6.
|
|
76
|
+
"@typescript-eslint/eslint-plugin": "6.7.0",
|
|
77
|
+
"@typescript-eslint/parser": "6.7.0",
|
|
78
78
|
"c8": "8.0.1",
|
|
79
|
-
"eslint": "8.
|
|
79
|
+
"eslint": "8.49.0",
|
|
80
80
|
"eslint-import-resolver-typescript": "3.6.0",
|
|
81
81
|
"eslint-plugin-import": "2.28.1",
|
|
82
|
-
"eslint-plugin-n": "16.0
|
|
82
|
+
"eslint-plugin-n": "16.1.0",
|
|
83
83
|
"prettier": "3.0.3",
|
|
84
84
|
"release-it": "16.1.5",
|
|
85
85
|
"remark-cli": "11.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const getGitHooksPath: () => string;
|