@knip/mcp 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -0
- package/docs/blog/brief-history.md +30 -0
- package/docs/blog/knip-v3.mdx +88 -0
- package/docs/blog/knip-v4.mdx +149 -0
- package/docs/blog/knip-v5.mdx +190 -0
- package/docs/blog/migration-to-v1.md +65 -0
- package/docs/blog/release-notes-v2.md +46 -0
- package/docs/blog/slim-down-to-speed-up.md +269 -0
- package/docs/blog/state-of-knip.md +191 -0
- package/docs/blog/two-years.mdx +107 -0
- package/docs/docs/blog/brief-history.md +30 -0
- package/docs/docs/blog/for-editors-and-agents.md +109 -0
- package/docs/docs/blog/knip-v3.mdx +88 -0
- package/docs/docs/blog/knip-v4.mdx +149 -0
- package/docs/docs/blog/knip-v5.mdx +190 -0
- package/docs/docs/blog/migration-to-v1.md +65 -0
- package/docs/docs/blog/release-notes-v2.md +46 -0
- package/docs/docs/blog/slim-down-to-speed-up.md +269 -0
- package/docs/docs/blog/state-of-knip.md +191 -0
- package/docs/docs/blog/two-years.mdx +107 -0
- package/docs/docs/explanations/comparison-and-migration.md +129 -0
- package/docs/docs/explanations/entry-files.md +70 -0
- package/docs/docs/explanations/plugins.md +318 -0
- package/docs/docs/explanations/why-use-knip.md +128 -0
- package/docs/docs/features/auto-fix.mdx +333 -0
- package/docs/docs/features/compilers.md +172 -0
- package/docs/docs/features/integrated-monorepos.md +52 -0
- package/docs/docs/features/monorepos-and-workspaces.md +134 -0
- package/docs/docs/features/production-mode.md +95 -0
- package/docs/docs/features/reporters.md +302 -0
- package/docs/docs/features/rules-and-filters.md +102 -0
- package/docs/docs/features/script-parser.md +156 -0
- package/docs/docs/features/source-mapping.md +100 -0
- package/docs/docs/guides/configuring-project-files.md +205 -0
- package/docs/docs/guides/contributing.md +24 -0
- package/docs/docs/guides/handling-issues.mdx +646 -0
- package/docs/docs/guides/issue-reproduction.md +94 -0
- package/docs/docs/guides/namespace-imports.md +125 -0
- package/docs/docs/guides/performance.md +97 -0
- package/docs/docs/guides/troubleshooting.md +127 -0
- package/docs/docs/guides/using-knip-in-ci.md +54 -0
- package/docs/docs/guides/working-with-commonjs.md +72 -0
- package/docs/docs/index.mdx +160 -0
- package/docs/docs/overview/configuration.md +104 -0
- package/docs/docs/overview/features.md +66 -0
- package/docs/docs/overview/getting-started.mdx +195 -0
- package/docs/docs/overview/screenshots-videos.md +42 -0
- package/docs/docs/playground.mdx +38 -0
- package/docs/docs/reference/cli.md +481 -0
- package/docs/docs/reference/configuration.md +413 -0
- package/docs/docs/reference/dynamic-configuration.mdx +72 -0
- package/docs/docs/reference/faq.md +441 -0
- package/docs/docs/reference/issue-types.md +43 -0
- package/docs/docs/reference/jsdoc-tsdoc-tags.md +122 -0
- package/docs/docs/reference/known-issues.md +64 -0
- package/docs/docs/reference/plugins/.gitkeep +0 -0
- package/docs/docs/reference/plugins.md +238 -0
- package/docs/docs/reference/related-tooling.md +46 -0
- package/docs/docs/sponsors.mdx +65 -0
- package/docs/docs/typescript/unused-dependencies.md +86 -0
- package/docs/docs/typescript/unused-exports.md +87 -0
- package/docs/docs/writing-a-plugin/argument-parsing.md +202 -0
- package/docs/docs/writing-a-plugin/index.md +376 -0
- package/docs/docs/writing-a-plugin/inputs.md +162 -0
- package/docs/explanations/comparison-and-migration.md +129 -0
- package/docs/explanations/entry-files.md +70 -0
- package/docs/explanations/plugins.md +318 -0
- package/docs/explanations/why-use-knip.md +128 -0
- package/docs/features/auto-fix.mdx +333 -0
- package/docs/features/compilers.md +172 -0
- package/docs/features/integrated-monorepos.md +52 -0
- package/docs/features/monorepos-and-workspaces.md +134 -0
- package/docs/features/production-mode.md +95 -0
- package/docs/features/reporters.md +302 -0
- package/docs/features/rules-and-filters.md +102 -0
- package/docs/features/script-parser.md +156 -0
- package/docs/features/source-mapping.md +100 -0
- package/docs/guides/configuring-project-files.md +205 -0
- package/docs/guides/contributing.md +24 -0
- package/docs/guides/handling-issues.mdx +646 -0
- package/docs/guides/issue-reproduction.md +94 -0
- package/docs/guides/namespace-imports.md +125 -0
- package/docs/guides/performance.md +97 -0
- package/docs/guides/troubleshooting.md +127 -0
- package/docs/guides/using-knip-in-ci.md +54 -0
- package/docs/guides/working-with-commonjs.md +72 -0
- package/docs/index.mdx +156 -0
- package/docs/overview/configuration.md +104 -0
- package/docs/overview/features.md +66 -0
- package/docs/overview/getting-started.mdx +195 -0
- package/docs/overview/screenshots-videos.md +42 -0
- package/docs/playground.mdx +38 -0
- package/docs/reference/cli.md +481 -0
- package/docs/reference/configuration.md +413 -0
- package/docs/reference/dynamic-configuration.mdx +72 -0
- package/docs/reference/faq.md +441 -0
- package/docs/reference/issue-types.md +43 -0
- package/docs/reference/jsdoc-tsdoc-tags.md +122 -0
- package/docs/reference/known-issues.md +64 -0
- package/docs/reference/plugins/.gitkeep +0 -0
- package/docs/reference/plugins.md +238 -0
- package/docs/reference/related-tooling.md +46 -0
- package/docs/sponsors.mdx +65 -0
- package/docs/typescript/unused-dependencies.md +86 -0
- package/docs/typescript/unused-exports.md +87 -0
- package/docs/writing-a-plugin/argument-parsing.md +202 -0
- package/docs/writing-a-plugin/index.md +376 -0
- package/docs/writing-a-plugin/inputs.md +162 -0
- package/license +15 -0
- package/package.json +38 -0
- package/src/cli.js +13 -0
- package/src/curated-resources.js +62 -0
- package/src/server.js +129 -0
- package/src/texts.js +76 -0
- package/src/tools.js +68 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Slim down to speed up
|
|
3
|
+
date: 2023-12-14
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 5
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
_Published: 2023-12-14_
|
|
9
|
+
|
|
10
|
+
**tl;dr;** Memory usage is up to 50% lower, runs are up to 60% faster and you
|
|
11
|
+
can start using v4 canary today. No "unused class members" for the time being,
|
|
12
|
+
but this feature is planned to be restored.
|
|
13
|
+
|
|
14
|
+
## Introduction
|
|
15
|
+
|
|
16
|
+
Honestly, performance has always been a challenge for Knip. A longstanding
|
|
17
|
+
bottleneck has finally been eliminated and Knip is going to be a lot faster.
|
|
18
|
+
Skip straight to the bottom to install v4 canary and try it out! Or grab
|
|
19
|
+
yourself a nice drink and read on if you're interested in where we are coming
|
|
20
|
+
from, and where we are heading.
|
|
21
|
+
|
|
22
|
+
## Projects & Workspaces
|
|
23
|
+
|
|
24
|
+
From the start, Knip has relied on TypeScript for its robust parser for
|
|
25
|
+
JavaScript and TypeScript files. And on lots of machinery important to Knip,
|
|
26
|
+
like module resolution and accurately finding references to exported values.
|
|
27
|
+
Parts of it can be customized, such as the (virtual) file system and the module
|
|
28
|
+
resolver.
|
|
29
|
+
|
|
30
|
+
In TypeScript terms, a "project" is like a workspace in a monorepo. Same as each
|
|
31
|
+
workspace has a `package.json`, each project has a `tsconfig.json`. The
|
|
32
|
+
`ts.createProgram()` method is used to create a program based on a
|
|
33
|
+
`tsconfig.json` and the machinery starts to read and parse source code files,
|
|
34
|
+
resolve modules, and so on.
|
|
35
|
+
|
|
36
|
+
Up until v2, when Knip wanted to find unused things in a monorepo, all programs
|
|
37
|
+
for all workspaces were loaded into memory. Workspaces often depend on each
|
|
38
|
+
other, so Knip couldn't load one project, analyze it and dispose it. This way,
|
|
39
|
+
connections across workspaces would be lost.
|
|
40
|
+
|
|
41
|
+
## Shared Workspaces
|
|
42
|
+
|
|
43
|
+
Knip v2 said goodbye to this approach and implemented its own TypeScript backend
|
|
44
|
+
(after using `ts-morph` for this). Based on the compatibility of
|
|
45
|
+
`compilerOptions`, workspaces were merged into shared programs whenever
|
|
46
|
+
possible. Having less programs in memory led to significant performance
|
|
47
|
+
improvements. Yet ultimately it was still a stopgap, since everything was still
|
|
48
|
+
kept in memory for the duration of the process.
|
|
49
|
+
|
|
50
|
+
"Why does everything need to stay in memory?", you may wonder. The answer is
|
|
51
|
+
that Knip uses `findReferences` at the end of the process. Knip relied on this
|
|
52
|
+
TypeScript Language Server method for everything that's not easy to find. More
|
|
53
|
+
about that later in [the story of findReferences][1]
|
|
54
|
+
|
|
55
|
+
## Serialization
|
|
56
|
+
|
|
57
|
+
Fortunately, everything that's imported and exported from source files
|
|
58
|
+
(including things like members of namespaces and enums) can be found relatively
|
|
59
|
+
easily during AST traversal. This way, references to exports don't have to be
|
|
60
|
+
"traced back" later on.
|
|
61
|
+
|
|
62
|
+
It's mostly class members that are harder to find due to their dynamic nature.
|
|
63
|
+
Without these, all information can be serialized for storage and retrieval (in
|
|
64
|
+
memory or on disk). Slimming down by taking class members out of the equation
|
|
65
|
+
simplifies things a lot and paves the way for all sorts of improvements.
|
|
66
|
+
|
|
67
|
+
## We Have To Slim Down
|
|
68
|
+
|
|
69
|
+
The relevant part in the linting process can be summarized in 5 steps:
|
|
70
|
+
|
|
71
|
+
1. Collect entry files and feed them to TypeScript
|
|
72
|
+
2. Read files, resolve modules, and create ASTs
|
|
73
|
+
3. Traverse ASTs and collect imports & exports
|
|
74
|
+
4. Match exports against imports to determine what's unused
|
|
75
|
+
5. Find references to hard-to-find exported values and members
|
|
76
|
+
|
|
77
|
+
If we would hold on to reporting unused class members, then especially steps 2
|
|
78
|
+
and 5 are hard to decouple. The program and the language service containing the
|
|
79
|
+
source files used to eventually trace back references can't really be decoupled.
|
|
80
|
+
So class members had to go. Sometimes you have to slim down to keep moving. One
|
|
81
|
+
step back, two steps forward.
|
|
82
|
+
|
|
83
|
+
If you rely on this feature, fear not. I plan to bring it back before the final
|
|
84
|
+
v4, but possibly behind a flag.
|
|
85
|
+
|
|
86
|
+
## What's In Store?
|
|
87
|
+
|
|
88
|
+
So with this out of the way, everything becomes a lot clearer and we can finally
|
|
89
|
+
really start thinking about significant memory and performance improvements. So
|
|
90
|
+
what's in store here? A lot!
|
|
91
|
+
|
|
92
|
+
- We no longer need to keep everything in memory, so workspaces are read and
|
|
93
|
+
disposed in isolation, one at a time. Memory usage will be spread out more
|
|
94
|
+
even. This does not make it faster, but reducing "out of memory" issues is
|
|
95
|
+
definitely a Good Thing™️ in my book.
|
|
96
|
+
- Knip could recover from unexpected exits and continue from the last completed
|
|
97
|
+
workspace.
|
|
98
|
+
- The imports and exports are in a format that can be serialized for storage and
|
|
99
|
+
retrieval. This opens up interesting opportunities, such as local caching on
|
|
100
|
+
disk, skipping work in subsequent runs, remote caching, and so on.
|
|
101
|
+
- Handling workspaces in isolation and serialization result in parallelization
|
|
102
|
+
becoming a possibility. This becomes essential, as module resolution and AST
|
|
103
|
+
creation and traversal are now the slowest parts of the process and are not
|
|
104
|
+
easy to optimize significantly (unless perhaps switching to e.g Rust).
|
|
105
|
+
- No longer relying on `findReferences` speeds up the export/import matching
|
|
106
|
+
part part significantly. So far I've seen **improvements of up to 60% on total
|
|
107
|
+
runtime**, and my guess is that some larger codebases may profit even more.
|
|
108
|
+
- The serialization format is still being explored and there is no caching yet,
|
|
109
|
+
but having the steps more decoupled is another Good Thing™️ that future me
|
|
110
|
+
should be happy about.
|
|
111
|
+
|
|
112
|
+
## Back It Up, Please
|
|
113
|
+
|
|
114
|
+
I heard you. Here's some example data. You can get it directly from Knip using
|
|
115
|
+
the `--performance` flag when running it on any codebase. Below we have some
|
|
116
|
+
data after linting the [Remix monorepo][2].
|
|
117
|
+
|
|
118
|
+
### Knip v3
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
$ knip --performance
|
|
122
|
+
|
|
123
|
+
Name size min max median sum
|
|
124
|
+
----------------------------- ---- ------ ------- ------- -------
|
|
125
|
+
findReferences 223 0.55 2252.35 8.46 5826.95
|
|
126
|
+
createProgram 2 50.78 1959.92 1005.35 2010.70
|
|
127
|
+
getTypeChecker 2 5.04 667.45 336.24 672.48
|
|
128
|
+
getImportsAndExports 396 0.00 7.19 0.11 104.46
|
|
129
|
+
|
|
130
|
+
Total running time: 9.7s (mem: 1487.39MB)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Knip v4
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
$ knip --performance
|
|
137
|
+
|
|
138
|
+
...
|
|
139
|
+
|
|
140
|
+
Name size min max median sum
|
|
141
|
+
----------------------------- ---- ------ ------- ------- -------
|
|
142
|
+
createProgram 2 54.36 2138.45 1096.40 2192.81
|
|
143
|
+
getTypeChecker 2 7.40 664.83 336.12 672.23
|
|
144
|
+
getImportsAndExports 396 0.00 36.36 0.16 224.37
|
|
145
|
+
getSymbolAtLocation 2915 0.00 29.71 0.00 65.63
|
|
146
|
+
|
|
147
|
+
Total running time: 4.3s (mem: 729.67MB)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Takeaways
|
|
151
|
+
|
|
152
|
+
The main takeaways here:
|
|
153
|
+
|
|
154
|
+
- In v3,`findReferences` is where Knip potentially spends most of its time
|
|
155
|
+
- In v4, total running time is down over 50%
|
|
156
|
+
- In v4, memory usage is down 50% (calculated using
|
|
157
|
+
`process.memoryUsage().heapUsage`)
|
|
158
|
+
- In v4, `getImportsAndExports` is more comprehensive to compensate for the
|
|
159
|
+
absence of `findReferences` - more on that below
|
|
160
|
+
|
|
161
|
+
Remember, unused class members are no longer reported by default in v4.
|
|
162
|
+
|
|
163
|
+
## The story of `findReferences`
|
|
164
|
+
|
|
165
|
+
Did I mention Knip uses `findReferences`...? Knip relied on it for everything
|
|
166
|
+
that's not easy to find. Here's an example of an export/import match that **is**
|
|
167
|
+
easy to find:
|
|
168
|
+
|
|
169
|
+
```ts title="import.ts"
|
|
170
|
+
import { MyThing } from './thing.ts';
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
```ts title="export.ts"
|
|
174
|
+
export const MyThing = 'cool';
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
In v2 and v3, Knip collects many of such easy patterns. Other patterns are
|
|
178
|
+
harder to find with static analysis. This is especially true for class members.
|
|
179
|
+
Let's take a look at the next example:
|
|
180
|
+
|
|
181
|
+
```ts title="MyClass.ts"
|
|
182
|
+
class MyClass {
|
|
183
|
+
constructor() {
|
|
184
|
+
this.method();
|
|
185
|
+
}
|
|
186
|
+
method() {}
|
|
187
|
+
do() {}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const OtherName = MyClass;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```ts title="instance.ts"
|
|
194
|
+
import * as MyNamespace from './MyClass.ts';
|
|
195
|
+
|
|
196
|
+
const { OtherName } = MyNamespace;
|
|
197
|
+
|
|
198
|
+
const instance = new OtherName();
|
|
199
|
+
|
|
200
|
+
instance.do();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Without a call or `new` expression to instantiate `OtherName`, its `method`
|
|
204
|
+
member would not be used (since the constructor would not be executed). To
|
|
205
|
+
figure this out using static analysis goes a long way. Through export
|
|
206
|
+
declarations, import declarations, aliases, initializers, call expressions...
|
|
207
|
+
the list goes on and on. Yet all this magic is exactly what happens when you use
|
|
208
|
+
"Find all references" or "Go to definition" in VS Code.
|
|
209
|
+
|
|
210
|
+
Knip used `findReferences` extensively, but it's what makes a part of Knip
|
|
211
|
+
rather slow. TypeScript needs to wire things up (through
|
|
212
|
+
`ts.createLanguageService` and `program.getTypeChecker`) before it can use this,
|
|
213
|
+
and then it tries hard to find all references to anything you throw at it. It
|
|
214
|
+
does this very well, but the more class members, enum members and namespaced
|
|
215
|
+
imports your codebase has, the longer it inevitably takes to complete the
|
|
216
|
+
process.
|
|
217
|
+
|
|
218
|
+
Besides letting go of class members, a slightly more comprehensive AST traversal
|
|
219
|
+
is required to compensate for the absence of `findReferences` (it's the
|
|
220
|
+
`getImportsAndExports` function in the metrics above). I'd like to give you an
|
|
221
|
+
idea of what "more comprehensive" means here.
|
|
222
|
+
|
|
223
|
+
In the following example, `referencedExport` was stored as export from
|
|
224
|
+
`namespace.ts`, but it was not imported directly as such:
|
|
225
|
+
|
|
226
|
+
```ts title="namespace.ts"
|
|
227
|
+
export const referencedExport = () => {};
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```ts title="index.ts"
|
|
231
|
+
import * as NS from './namespace.ts';
|
|
232
|
+
|
|
233
|
+
NS.referencedExport();
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Previously, Knip used `findReferences()` to "trace back" the usage of the
|
|
237
|
+
exported `referencedExport`.
|
|
238
|
+
|
|
239
|
+
The gist of the optimization is to pre-determine all imports and exports. During
|
|
240
|
+
AST traversal of `index.ts` , Knip sees that `referencedExport` is attached to
|
|
241
|
+
the imported `NS` namespace, and stores that as an imported identifier of
|
|
242
|
+
`namespace.ts`. When matching exports against imports, this lookup comes at no
|
|
243
|
+
extra cost. Additionally, this can be stored as strings, so it can be serialized
|
|
244
|
+
too. And that means it can be cached.
|
|
245
|
+
|
|
246
|
+
Knip already did this for trivial cases as shown in the first example of this
|
|
247
|
+
article. This has now been extended to cover more patterns. This is also what
|
|
248
|
+
needs to be tested more extensively before v4 can be released. Its own test
|
|
249
|
+
suite and the projects in the integration tests are already covered so we're
|
|
250
|
+
well on our way.
|
|
251
|
+
|
|
252
|
+
For the record, `findReferences` is an absolute gem of functionality provided by
|
|
253
|
+
TypeScript. Knip is still backed by TypeScript, and tries to speed things up by
|
|
254
|
+
shaking things off. In the end it's all about trade-offs.
|
|
255
|
+
|
|
256
|
+
## Let's Go!
|
|
257
|
+
|
|
258
|
+
You can start using Knip v4 today, feel free to try it out! You might find a
|
|
259
|
+
false positive that wasn't there in v3, please [report this][3].
|
|
260
|
+
|
|
261
|
+
```sh
|
|
262
|
+
npm install -D knip@canary
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Remember, Knip it before you ship it! Have a great day ☀️
|
|
266
|
+
|
|
267
|
+
[1]: #the-story-of-findreferences
|
|
268
|
+
[2]: https://github.com/remix-run/remix
|
|
269
|
+
[3]: https://github.com/webpro-nl/knip/issues
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: The State of Knip
|
|
3
|
+
date: 2025-02-28
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
_Published: 2025-02-28_
|
|
9
|
+
|
|
10
|
+
Honestly, Knip was a bit of a "cursed" project from the get-go. Getting anywhere
|
|
11
|
+
near a level of being broadly-ish valuable requires a good amount of
|
|
12
|
+
~~foolishness~~ determination, and it has always been clear it would stay far
|
|
13
|
+
from perfect. It's telling that most of [similar projects][1] have been
|
|
14
|
+
abandoned.
|
|
15
|
+
|
|
16
|
+
And even though Knip is in its infancy, this update is meant as a sign we feel
|
|
17
|
+
we're still on to something. External indicators include increased usage looking
|
|
18
|
+
at numbers such as dependent repositories on GitHub and weekly downloads on npm,
|
|
19
|
+
and bug reports about increasingly less rudimentary issues.
|
|
20
|
+
|
|
21
|
+
## Two Cases
|
|
22
|
+
|
|
23
|
+
For those interested, let's take a look at two cases that hopefully give an
|
|
24
|
+
impression of how Knip works under the hood and the level of issues we're
|
|
25
|
+
currently dealing with. It's assumed you already have a basic understanding of
|
|
26
|
+
Knip (otherwise please consider to read at least [entry files][2] and
|
|
27
|
+
[plugins][3] first).
|
|
28
|
+
|
|
29
|
+
### Case 1: Next.js
|
|
30
|
+
|
|
31
|
+
Let's say this default configuration represents, greatly simplified, [the
|
|
32
|
+
default `entry` patterns][4] for projects using Next.js:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"next": {
|
|
37
|
+
"entry": ["next.config.ts", "src/pages/**/*.tsx"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Those files will be searched for and then statically analyzed to collect
|
|
43
|
+
`import` statements and find other local files and external dependencies. This
|
|
44
|
+
is the generic way Knip handles all source files.
|
|
45
|
+
|
|
46
|
+
However, the game changes if the project uses the following Next.js
|
|
47
|
+
configuration:
|
|
48
|
+
|
|
49
|
+
```ts title="next.config.ts"
|
|
50
|
+
const nextConfig = {
|
|
51
|
+
pageExtensions: ['page.tsx'],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default nextConfig;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Next.js will now look for files matching `src/pages/**/*.page.tsx` instead (note
|
|
58
|
+
the subtle change of the glob pattern). Knip should respect this to find used
|
|
59
|
+
and unused files properly.
|
|
60
|
+
|
|
61
|
+
Moving the burden to users for them to either not notice at all and get
|
|
62
|
+
incorrect results, or having to override the `next.entry` patterns and include
|
|
63
|
+
`src/pages/**/*.page.tsx` isn't good DX. Knip should take care of it.
|
|
64
|
+
|
|
65
|
+
To get the configuration object and the value of `pageExtensions`, Knip has to
|
|
66
|
+
actually load and execute `next.config.ts` ¹... and trouble is right around the
|
|
67
|
+
corner:
|
|
68
|
+
|
|
69
|
+
```ts title="next.config.ts"
|
|
70
|
+
const nextConfig = {
|
|
71
|
+
pageExtensions: ['page.tsx'],
|
|
72
|
+
env: {
|
|
73
|
+
BASE_URL: process.env.BASE_URL.toLowerCase(),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default nextConfig;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```shell
|
|
81
|
+
$ knip
|
|
82
|
+
💥 LoaderError: Error loading next.config.ts
|
|
83
|
+
💥 Reason: Cannot read properties of undefined (reading 'toLowerCase')
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Obviously a contrived example, but the gist is that lots of tooling
|
|
87
|
+
configuration expects environment variables to be defined. But when running Knip
|
|
88
|
+
there might not be a mechanism to set those. Clearly a breaking change when Knip
|
|
89
|
+
starts doing this, only for Next.js projects with a configuration file that
|
|
90
|
+
doesn't read environment variables safely (or has other contextual
|
|
91
|
+
dependencies).
|
|
92
|
+
|
|
93
|
+
By the way, [the ESLint v9 plugin][5] has a similar issue.
|
|
94
|
+
|
|
95
|
+
¹ Another approach could be to statically analyze the `next.config.ts`
|
|
96
|
+
configuration file. That would require some additional efforts and get us only
|
|
97
|
+
so far, but is definitely useful in some cases and on the radar.
|
|
98
|
+
|
|
99
|
+
**EDIT:** This has been solved in the Next.js plugin in v5.48.0.
|
|
100
|
+
|
|
101
|
+
### Case 2: Knip does that?!
|
|
102
|
+
|
|
103
|
+
To further bring down user configuration and the number of false positives, the
|
|
104
|
+
system required more components. New components have been introduced to keep
|
|
105
|
+
improving and nail it for an increasing number of projects. This case is an
|
|
106
|
+
illustration of some of those components.
|
|
107
|
+
|
|
108
|
+
Let's just dive into this example and find out what's happening:
|
|
109
|
+
|
|
110
|
+
```json title="package.json"
|
|
111
|
+
{
|
|
112
|
+
"scripts": {
|
|
113
|
+
"test": "yarn --cwd packages/frontend vitest -c vitest.components.config.ts"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Orchestration is necessary between various components within Knip, such as:
|
|
119
|
+
|
|
120
|
+
- Plugins, the Vitest plugin parses `vitest.components.config.ts`
|
|
121
|
+
- Custom CLI argument parsing for executables, e.g. `yarn --cwd [dir]` and
|
|
122
|
+
`vitest --config [file]`
|
|
123
|
+
- The workspace graph, to see `packages/frontend` is a descendant workspace of
|
|
124
|
+
the root workspace
|
|
125
|
+
|
|
126
|
+
Patterns like in the script above do not occur only in `package.json` files, but
|
|
127
|
+
could be anywhere. Here's a similar example in a GitHub Actions workflow:
|
|
128
|
+
|
|
129
|
+
```yaml title=".github/workflows/test.yml"
|
|
130
|
+
jobs:
|
|
131
|
+
integration:
|
|
132
|
+
runs-on: ubuntu-latest
|
|
133
|
+
steps:
|
|
134
|
+
- run: playwright test -c playwright.e2e.config.ts
|
|
135
|
+
working-directory: e2e
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The pattern is very similar, because Knip needs to assign a configuration file
|
|
139
|
+
to a specific workspace (assuming there's one in `./e2e`) and apply the Vitest
|
|
140
|
+
configuration to that particular workspace with its own set of directory and
|
|
141
|
+
entry file patterns.
|
|
142
|
+
|
|
143
|
+
An essential part of Knip is to build up the module graph for source files. With
|
|
144
|
+
the configuration files still in mind, this is the pattern Knip follows towards
|
|
145
|
+
this goal:
|
|
146
|
+
|
|
147
|
+
- Find configuration files at default and custom locations
|
|
148
|
+
- Assign them to the right workspace
|
|
149
|
+
- Run plugins in their own workspace to take entry file patterns from the
|
|
150
|
+
configuration objects
|
|
151
|
+
- Load and parse configuration files to get referenced dependencies
|
|
152
|
+
|
|
153
|
+
The referenced dependencies are stored in the `DependencyDeputy` class to
|
|
154
|
+
eventually determine what dependencies are unused or missing in `package.json`
|
|
155
|
+
in each workspace.
|
|
156
|
+
|
|
157
|
+
Both the configuration and entry files are then used to start building up the
|
|
158
|
+
module graph.
|
|
159
|
+
|
|
160
|
+
## Comprehensive
|
|
161
|
+
|
|
162
|
+
Discussing the two cases briefly covers only part of the whole process. This
|
|
163
|
+
might give a sense of the reason why Knip is pretty comprehensive. After all,
|
|
164
|
+
building the module graph for internal source files to find unused files and
|
|
165
|
+
exports requires the list of external dependencies including internal
|
|
166
|
+
workspaces. And on the other hand, a complete module graph is required to find
|
|
167
|
+
unused or missing external dependencies.
|
|
168
|
+
|
|
169
|
+
The comprehensiveness also requires a range of components in the system, such as
|
|
170
|
+
the aforementioned ones, [compilers for popular frameworks][6] and a [script
|
|
171
|
+
parser][7], and other affordances such as [auto-fix][8].
|
|
172
|
+
|
|
173
|
+
That said, code organization could be improved to make it more accessible for
|
|
174
|
+
contributions and, for instance, expose programmatic APIs to use the generated
|
|
175
|
+
module graph outside of Knip. Additionally, existing plugins can better take
|
|
176
|
+
advantage of existing components in the system, and new plugins can be developed
|
|
177
|
+
to further reduce user configuration and false positives.
|
|
178
|
+
|
|
179
|
+
## The End
|
|
180
|
+
|
|
181
|
+
That's all for today, thanks for reading! Have a great one, and don't forget:
|
|
182
|
+
Knip it before you ship it! ✂️
|
|
183
|
+
|
|
184
|
+
[1]: ../explanations/comparison-and-migration.md
|
|
185
|
+
[2]: ../explanations/entry-files.md
|
|
186
|
+
[3]: ../explanations/plugins.md
|
|
187
|
+
[4]: ../reference/plugins/next.md#default-configuration
|
|
188
|
+
[5]: ../reference/plugins/eslint.md#eslint-v9
|
|
189
|
+
[6]: ../features/compilers.md
|
|
190
|
+
[7]: ../features/script-parser.md
|
|
191
|
+
[8]: ../features/auto-fix.mdx
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Two Years
|
|
3
|
+
date: 2024-10-04
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 2
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
_Published: 2024-10-04_
|
|
9
|
+
|
|
10
|
+
import EmojiBlastButton from '../../../components/EmojiBlastButton.astro';
|
|
11
|
+
import Projects from '../../../components/Projects.astro';
|
|
12
|
+
import Sponsors from '../../../components/Sponsors.astro';
|
|
13
|
+
|
|
14
|
+
Exactly two years ago the first commit was pushed to GitHub and the first
|
|
15
|
+
version of Knip was published to the npm registry. The name was initially
|
|
16
|
+
[Exportman][1]! We've come a loooong way... The JavaScript ecosystem is highly
|
|
17
|
+
dynamic and I've been crazy enough to even start, try and keep up with it! But
|
|
18
|
+
here we are.
|
|
19
|
+
|
|
20
|
+
October 4th is World Animal Day, so there was really no choice but bring in the
|
|
21
|
+
crazy mascot that early adopters may remember:
|
|
22
|
+
|
|
23
|
+
![Crazy cow with orange scissors in Van Gogh style][2]
|
|
24
|
+
|
|
25
|
+
Today we celebrate an unknown but CRAZY amount of clutter removed from so many
|
|
26
|
+
codebases with Knip's help. Every single day I see many of those little red
|
|
27
|
+
blocks for thousands of lines of deleted code and dependencies. Call me crazy,
|
|
28
|
+
but to me this is pure joy and never gets old! 🟩 🟥 🟥 🟥 🟥
|
|
29
|
+
|
|
30
|
+
<EmojiBlastButton />
|
|
31
|
+
|
|
32
|
+
## Smiling faces
|
|
33
|
+
|
|
34
|
+
The actual amount of code and dependencies removed and the number of smiling
|
|
35
|
+
faces this brings is what matters most, but also remain a good mystery. Clearly
|
|
36
|
+
more and more projects add Knip to their projects and CI workflows to keep
|
|
37
|
+
ever-growing codebases tidy. It's wonderful to see if Knip plays its part in
|
|
38
|
+
today's ecosystem to help with that. Thanks for bearing with me, here's to a lot
|
|
39
|
+
more little red blocks in your PRs! 🟩 🟥 🟥 🟥 🟥
|
|
40
|
+
|
|
41
|
+
## Updates
|
|
42
|
+
|
|
43
|
+
Why not throw in some freshly cooked updates in [v5.31.0][3] for you while we're
|
|
44
|
+
at it:
|
|
45
|
+
|
|
46
|
+
- [The auto-fix feature][4] has been completely revamped, it's much better and a
|
|
47
|
+
lot more comprehensive! You have to see it to believe it.
|
|
48
|
+
- Knip has upgraded to [Jiti v2][5], resolving a bunch of known issues when
|
|
49
|
+
loading configuration files authored in TypeScript and ESM, such as:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Cannot use 'import.meta' outside a module
|
|
53
|
+
await is only valid in async functions and the top level bodies of modules
|
|
54
|
+
Unexpected identifier 'Promise'
|
|
55
|
+
Reflect.metadata is not a function
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
And that pesky "CJS build of Vite's Node API is deprecated" warning is finally
|
|
59
|
+
gone!
|
|
60
|
+
|
|
61
|
+
Thanks to everyone involved in making this happen, it's truly much appreciated.
|
|
62
|
+
|
|
63
|
+
## Stable
|
|
64
|
+
|
|
65
|
+
If you haven't tried Knip recently, it's worth taking another look! Version 5
|
|
66
|
+
was released 8 months ago, and even though there were no breaking changes, it
|
|
67
|
+
includes many enhancements. In fact, Knip has been largely stable since version
|
|
68
|
+
3, which came out a year ago. Many releases have a compound effect, as Knip has
|
|
69
|
+
kept the pace for two years now.
|
|
70
|
+
|
|
71
|
+
## Projects using Knip
|
|
72
|
+
|
|
73
|
+
This list of projects using Knip to keep their codebases tidy is something I
|
|
74
|
+
couldn't be more proud of:
|
|
75
|
+
|
|
76
|
+
:::section{.columns.min200}
|
|
77
|
+
|
|
78
|
+
<Projects />
|
|
79
|
+
|
|
80
|
+
:::
|
|
81
|
+
|
|
82
|
+
And so many more on and off the radar. Very, very cool!
|
|
83
|
+
|
|
84
|
+
## Sponsors
|
|
85
|
+
|
|
86
|
+
Last but not least, eternal gratitude for all the sponsors that have been
|
|
87
|
+
supporting me along the way. THANK YOU, THANK YOU, THANK YOU!
|
|
88
|
+
|
|
89
|
+
And eh.. gotta take my chances: how about [joining this awesome club][6]?
|
|
90
|
+
|
|
91
|
+
:::section{.columns.min300.mt}
|
|
92
|
+
|
|
93
|
+
<Sponsors />
|
|
94
|
+
|
|
95
|
+
:::
|
|
96
|
+
|
|
97
|
+
## Acknowledgements
|
|
98
|
+
|
|
99
|
+
Thanks to Joshua Goldberg for [emoji-blast][7]! 🎉
|
|
100
|
+
|
|
101
|
+
[1]: https://www.npmjs.com/package/exportman/v/0.0.1
|
|
102
|
+
[2]: /cow-with-orange-scissors-van-gogh-style.webp
|
|
103
|
+
[3]: https://github.com/webpro-nl/knip/releases/tag/5.31.0
|
|
104
|
+
[4]: ../features/auto-fix.md
|
|
105
|
+
[5]: https://github.com/unjs/jiti
|
|
106
|
+
[6]: /sponsors
|
|
107
|
+
[7]: https://www.emojiblast.dev
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: A Brief History Of Knip
|
|
3
|
+
date: 2023-10-15
|
|
4
|
+
sidebar:
|
|
5
|
+
order: 7
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
_Published: 2023-10-15_
|
|
9
|
+
|
|
10
|
+
If you are fond of short lists and brief histories, then this page was written
|
|
11
|
+
just for you!
|
|
12
|
+
|
|
13
|
+
- 2022-10-04: The [initial commit][1]. Still so tiny at that point, but the seed
|
|
14
|
+
was planted. Starting out with finding unused files and exports, the name was
|
|
15
|
+
Exportman! 🦸
|
|
16
|
+
- 2022-10-09: Big plans and a rename 5 days later, the first published version
|
|
17
|
+
of Knip was [v0.1.2][2].
|
|
18
|
+
- 2022-11-22: Unused dependencies and support for workspaces and plugins in the
|
|
19
|
+
[first alpha of v1][3].
|
|
20
|
+
- 2023-01-10: Lots of testing and fixes led to [Knip v1][4].
|
|
21
|
+
- 2023-03-22: [Knip v2][5] saw a full rewrite of the TypeScript backend.
|
|
22
|
+
- 2023-10-15: [Introduction of Knip v3][6].
|
|
23
|
+
|
|
24
|
+
[1]:
|
|
25
|
+
https://github.com/webpro-nl/knip/commit/9589dfe22608da7d89f2613383da6db5826226d2
|
|
26
|
+
[2]: https://github.com/webpro-nl/knip/tree/0.1.2
|
|
27
|
+
[3]: https://github.com/webpro-nl/knip/releases/tag/1.0.0-alpha.0
|
|
28
|
+
[4]: https://github.com/webpro-nl/knip/tree/1.0.0
|
|
29
|
+
[5]: https://github.com/webpro-nl/knip/issues/73
|
|
30
|
+
[6]: ./knip-v3.mdx
|