@knip/mcp 0.0.4 → 0.0.6

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.
Files changed (55) hide show
  1. package/docs/docs/blog/brief-history.md +30 -0
  2. package/docs/docs/blog/for-editors-and-agents.md +127 -0
  3. package/docs/docs/blog/knip-v3.mdx +88 -0
  4. package/docs/docs/blog/knip-v4.mdx +149 -0
  5. package/docs/docs/blog/knip-v5.mdx +190 -0
  6. package/docs/docs/blog/migration-to-v1.md +65 -0
  7. package/docs/docs/blog/release-notes-v2.md +46 -0
  8. package/docs/docs/blog/slim-down-to-speed-up.md +269 -0
  9. package/docs/docs/blog/state-of-knip.md +191 -0
  10. package/docs/docs/blog/two-years.mdx +107 -0
  11. package/docs/docs/explanations/comparison-and-migration.md +129 -0
  12. package/docs/docs/explanations/entry-files.md +70 -0
  13. package/docs/docs/explanations/plugins.md +319 -0
  14. package/docs/docs/explanations/why-use-knip.md +128 -0
  15. package/docs/docs/features/auto-fix.mdx +333 -0
  16. package/docs/docs/features/compilers.md +172 -0
  17. package/docs/docs/features/integrated-monorepos.md +61 -0
  18. package/docs/docs/features/monorepos-and-workspaces.md +134 -0
  19. package/docs/docs/features/production-mode.md +95 -0
  20. package/docs/docs/features/reporters.md +302 -0
  21. package/docs/docs/features/rules-and-filters.md +102 -0
  22. package/docs/docs/features/script-parser.md +156 -0
  23. package/docs/docs/features/source-mapping.md +100 -0
  24. package/docs/docs/guides/configuring-project-files.md +205 -0
  25. package/docs/docs/guides/contributing.md +24 -0
  26. package/docs/docs/guides/handling-issues.mdx +646 -0
  27. package/docs/docs/guides/issue-reproduction.md +94 -0
  28. package/docs/docs/guides/namespace-imports.md +125 -0
  29. package/docs/docs/guides/performance.md +97 -0
  30. package/docs/docs/guides/troubleshooting.md +136 -0
  31. package/docs/docs/guides/using-knip-in-ci.md +54 -0
  32. package/docs/docs/guides/working-with-commonjs.md +72 -0
  33. package/docs/docs/index.mdx +160 -0
  34. package/docs/docs/overview/configuration.md +104 -0
  35. package/docs/docs/overview/features.md +66 -0
  36. package/docs/docs/overview/getting-started.mdx +195 -0
  37. package/docs/docs/overview/screenshots-videos.md +42 -0
  38. package/docs/docs/playground.mdx +38 -0
  39. package/docs/docs/reference/cli.md +485 -0
  40. package/docs/docs/reference/configuration.md +413 -0
  41. package/docs/docs/reference/dynamic-configuration.mdx +72 -0
  42. package/docs/docs/reference/faq.md +441 -0
  43. package/docs/docs/reference/issue-types.md +43 -0
  44. package/docs/docs/reference/jsdoc-tsdoc-tags.md +122 -0
  45. package/docs/docs/reference/known-issues.md +64 -0
  46. package/docs/docs/reference/plugins/.gitkeep +0 -0
  47. package/docs/docs/reference/plugins.md +238 -0
  48. package/docs/docs/reference/related-tooling.md +46 -0
  49. package/docs/docs/sponsors.mdx +65 -0
  50. package/docs/docs/typescript/unused-dependencies.md +86 -0
  51. package/docs/docs/typescript/unused-exports.md +87 -0
  52. package/docs/docs/writing-a-plugin/argument-parsing.md +202 -0
  53. package/docs/docs/writing-a-plugin/index.md +376 -0
  54. package/docs/docs/writing-a-plugin/inputs.md +162 -0
  55. package/package.json +5 -3
@@ -0,0 +1,202 @@
1
+ ---
2
+ title: Argument Parsing
3
+ sidebar:
4
+ order: 3
5
+ ---
6
+
7
+ Some plugins have an `arg` object in their implementation. It's a way for
8
+ plugins to customize how command-line arguments are parsed for their tool's
9
+ executables. Argument parsing in plugins help Knip identify dependencies and
10
+ entry files from scripts.
11
+
12
+ Knip uses [minimist][1] for argument parsing and some options are identical
13
+ ([alias][2], [boolean][3], [string][4]).
14
+
15
+ Also see [type definitions][5] and [examples in existing plugins][6].
16
+
17
+ - [alias][2]
18
+ - [args][7]
19
+ - [binaries][8]
20
+ - [boolean][3]
21
+ - [config][9]
22
+ - [fromArgs][10]
23
+ - [nodeImportArgs][11]
24
+ - [positional][12]
25
+ - [resolve][13]
26
+ - [resolveInputs][14]
27
+ - [string][4]
28
+
29
+ ## alias
30
+
31
+ Define aliases.
32
+
33
+ Example:
34
+
35
+ ```ts
36
+ {
37
+ require: ['r'];
38
+ }
39
+ ```
40
+
41
+ Also see [nodeImportArgs][11].
42
+
43
+ ## args
44
+
45
+ Modify or filter arguments before parsing. For edge cases preprocessing is
46
+ useful, e.g. if minimist has trouble parsing or to modify/discard arguments.
47
+
48
+ Example:
49
+
50
+ ```ts
51
+ {
52
+ args: (args: string[]) => args.filter(arg => arg !== 'omit');
53
+ }
54
+ ```
55
+
56
+ ## binaries
57
+
58
+ Executables for the dependency.
59
+
60
+ Example:
61
+
62
+ ```ts
63
+ {
64
+ binaries: ['tsc'];
65
+ }
66
+ ```
67
+
68
+ Default: plugin name, e.g. for the ESLint plugin the value is `["eslint"]`
69
+
70
+ ## boolean
71
+
72
+ Mark arguments as boolean. By default, arguments are expected to have string
73
+ values.
74
+
75
+ ## config
76
+
77
+ Define arguments that contain the configuration file path. Usually you'll want
78
+ to set aliases too. Use `true` for shorthand to set `alias` + `string` +
79
+ `config`.
80
+
81
+ Example:
82
+
83
+ ```ts
84
+ {
85
+ config: true;
86
+ }
87
+ ```
88
+
89
+ The `tsup` plugin has this. Now `tsup --config tsup.client.json` will have
90
+ `tsup.client.json` go through `resolveConfig` (also `-c` alias).
91
+
92
+ Example:
93
+
94
+ ```ts
95
+ {
96
+ config: ['p'];
97
+ }
98
+ ```
99
+
100
+ This will mark e.g. `tsc -p tsconfig.lib.json` as a configuration file and it
101
+ will be handled by `resolveConfig` of the (typescript) plugin.
102
+
103
+ ## fromArgs
104
+
105
+ Parse return value as a new script. Can be a an array of strings, or function
106
+ that returns an array of strings and those values will be parsed separately.
107
+
108
+ Example:
109
+
110
+ ```ts
111
+ {
112
+ fromArgs: ['exec'];
113
+ }
114
+ ```
115
+
116
+ Then this script:
117
+
118
+ ```sh
119
+ nodemon --exec "node index.js"
120
+ ```
121
+
122
+ Will have `"node index.js"` being parsed as a new script.
123
+
124
+ ## nodeImportArgs
125
+
126
+ Set to `true` as a shorthand for this [alias][2]:
127
+
128
+ ```ts
129
+ {
130
+ import: ['r', 'experimental-loader', 'require', 'loader']
131
+ }
132
+ ```
133
+
134
+ Example:
135
+
136
+ ```ts
137
+ {
138
+ nodeImportArgs: true;
139
+ }
140
+ ```
141
+
142
+ ## positional
143
+
144
+ Set to `true` to use the first positional argument as an entry point.
145
+
146
+ Example:
147
+
148
+ ```ts
149
+ {
150
+ positional: true;
151
+ }
152
+ ```
153
+
154
+ The `tsx` plugin has this and `"tsx script.ts"` as a script will result in the
155
+ `script.ts` file being an entry point.
156
+
157
+ ## resolve
158
+
159
+ List of arguments to resolve to a dependency or entry file path.
160
+
161
+ Example:
162
+
163
+ ```ts
164
+ {
165
+ resolve: ['plugin'];
166
+ }
167
+ ```
168
+
169
+ Now for a script like `"program --plugin package"` this will result in
170
+ `"package"` being resolved as a dependency.
171
+
172
+ ## resolveInputs
173
+
174
+ Return inputs from parsed arguments
175
+
176
+ ```ts
177
+ {
178
+ resolveInputs: (parsed: ParsedArgs) =>
179
+ parsed['flag'] ? [toDependency('package')] : [];
180
+ }
181
+ ```
182
+
183
+ ## string
184
+
185
+ Mark arguments as string. This is the default, but number-looking arguments are
186
+ returned as numbers by minimist.
187
+
188
+ [1]: https://www.npmjs.com/package/minimist
189
+ [2]: #alias
190
+ [3]: #boolean
191
+ [4]: #string
192
+ [5]: https://github.com/webpro-nl/knip/blob/main/packages/knip/src/types/args.ts
193
+ [6]:
194
+ https://github.com/search?q=repo%3Awebpro-nl%2Fknip++path%3Apackages%2Fknip%2Fsrc%2Fplugins+%22const+args+%3D%22&type=code
195
+ [7]: #args
196
+ [8]: #binaries
197
+ [9]: #config
198
+ [10]: #fromargs
199
+ [11]: #nodeimportargs
200
+ [12]: #positional
201
+ [13]: #resolve
202
+ [14]: #resolveinputs
@@ -0,0 +1,376 @@
1
+ ---
2
+ title: Writing A Plugin
3
+ sidebar:
4
+ order: 1
5
+ ---
6
+
7
+ Plugins provide Knip with entry files and dependencies it would be unable to
8
+ find otherwise. Plugins always do at least one of the following:
9
+
10
+ 1. Define entry file patterns
11
+ 2. Find dependencies in configuration files
12
+
13
+ Knip v5.1.0 introduced a new plugin API, which makes them a breeze to write and
14
+ maintain.
15
+
16
+ :::tip[The new plugin API]
17
+
18
+ Easy things should be easy, and complex things possible.
19
+
20
+ :::
21
+
22
+ This tutorial walks through example plugins so you'll be ready to write your
23
+ own! The following examples demonstrate the elements a plugin can implement.
24
+
25
+ There's a handy command available to easily [create a new plugin][1] and get
26
+ started right away.
27
+
28
+ ## Example 1: entry
29
+
30
+ Let's dive right in. Here's the entire source code of the Tailwind plugin:
31
+
32
+ ```ts
33
+ import type { IsPluginEnabled, Plugin } from '../../types/config.js';
34
+ import { hasDependency } from '../../util/plugin.js';
35
+
36
+ const title = 'Tailwind';
37
+
38
+ const enablers = ['tailwindcss'];
39
+
40
+ const isEnabled: IsPluginEnabled = ({ dependencies }) =>
41
+ hasDependency(dependencies, enablers);
42
+
43
+ const entry = ['tailwind.config.{js,cjs,mjs,ts}'];
44
+
45
+ const plugin: Plugin {
46
+ title,
47
+ enablers,
48
+ isEnabled,
49
+ entry,
50
+ };
51
+
52
+ export default plugin;
53
+ ```
54
+
55
+ Yes, that's the entire plugin! Let's go over each item one by one:
56
+
57
+ ### 1. `title`
58
+
59
+ The title of the plugin displayed in the [list of plugins][2] and in debug
60
+ output.
61
+
62
+ ### 2. `enablers`
63
+
64
+ An array of strings to match one or more dependencies in `package.json` so the
65
+ `isEnabled` function can determine whether the plugin should be enabled or not.
66
+ Regular expressions are allowed as well.
67
+
68
+ ### 3. `isEnabled`
69
+
70
+ This function checks whether a match is found in the `dependencies` or
71
+ `devDependencies` in `package.json`. The plugin is enabled if the dependency is
72
+ listed in `package.json`.
73
+
74
+ This function can be kept straightforward with the `hasDependency` helper.
75
+
76
+ ### 4. `entry`
77
+
78
+ This plugin exports `entry` file patterns. This means that if the Tailwind
79
+ plugin is enabled, then `tailwind.config.*` files are added as entry files. A
80
+ Tailwind configuration file does not contain anything particular, so adding it
81
+ as an `entry` to treat it as a regular source file is enough.
82
+
83
+ The next example shows how to handle a tool that has its own particular
84
+ configuration object.
85
+
86
+ ## Example 2: config
87
+
88
+ Here's the full source code of the `nyc` plugin:
89
+
90
+ ```ts
91
+ import { toDeferResolve } from '../../util/input.js';
92
+ import { hasDependency } from '../../util/plugin.js';
93
+ import type { NycConfig } from './types.js';
94
+ import type {
95
+ IsPluginEnabled,
96
+ Plugin,
97
+ ResolveConfig,
98
+ } from '../../types/config.js';
99
+
100
+ const title = 'nyc';
101
+
102
+ const enablers = ['nyc'];
103
+
104
+ const isEnabled: IsPluginEnabled = ({ dependencies }) =>
105
+ hasDependency(dependencies, enablers);
106
+
107
+ const config = [
108
+ '.nycrc',
109
+ '.nycrc.{json,yml,yaml}',
110
+ 'nyc.config.js',
111
+ 'package.json',
112
+ ];
113
+
114
+ const resolveConfig: ResolveConfig<NycConfig> = config => {
115
+ const extend = config?.extends ?? [];
116
+ const requires = config?.require ?? [];
117
+ return [extend, requires].flat().map(id => toDeferResolve(id));
118
+ };
119
+
120
+ const plugin: Plugin {
121
+ title,
122
+ enablers,
123
+ isEnabled,
124
+ config,
125
+ resolveConfig
126
+ };
127
+
128
+ export default plugin;
129
+ ```
130
+
131
+ Here's an example `config` file that will be handled by this plugin:
132
+
133
+ ```json title=".nycrc.json"
134
+ {
135
+ "extends": "@istanbuljs/nyc-config-typescript",
136
+ "check-coverage": true
137
+ }
138
+ ```
139
+
140
+ Compared to the first example, this plugin has two new variables:
141
+
142
+ ### 5. `config`
143
+
144
+ The `config` array contains all possible locations of the config file for the
145
+ tool. Knip loads matching files and passes the results (i.e. its default export)
146
+ into the `resolveConfig` function:
147
+
148
+ ### 6. `resolveConfig`
149
+
150
+ This function receives the exported value of the `config` file, and executes the
151
+ `resolveConfig` function with this object. The plugin should return the entry
152
+ paths and dependencies referenced in this object.
153
+
154
+ Knip supports JSON, YAML, TOML, JavaScript and TypeScript config files. Files
155
+ without an extension are provided as plain text strings.
156
+
157
+ :::tip[Should I implement resolveConfig?]
158
+
159
+ You should implement `resolveConfig` if any of these are true:
160
+
161
+ - The `config` file contains one or more options that represent [entry
162
+ points][3]
163
+ - The `config` file references dependencies by strings (not import statements)
164
+
165
+ :::
166
+
167
+ ## Example 3: entry paths
168
+
169
+ ### 7. entry and production
170
+
171
+ Some tools operate mostly on entry files, some examples:
172
+
173
+ - Mocha looks for test files at `test/*.{js,cjs,mjs}`
174
+ - Storybook looks for stories at `*.stories.@(mdx|js|jsx|tsx)`
175
+
176
+ And some of those tools allow to configure those locations and patterns in
177
+ configuration files, such as `next.config.js` or `vite.config.ts`. If that's the
178
+ case we can define `resolveConfig` in our plugin to take this from the
179
+ configuration object and return it to Knip:
180
+
181
+ Here's an example from the Mocha plugin:
182
+
183
+ ```ts
184
+ const entry = ['**/test/*.{js,cjs,mjs}'];
185
+
186
+ const resolveConfig: ResolveConfig<MochaConfig> = localConfig => {
187
+ const entryPatterns = localConfig.spec ? [localConfig.spec].flat() : entry;
188
+ return entryPatterns.map(id => toEntry(id));
189
+ };
190
+
191
+ export default {
192
+ entry,
193
+ resolveConfig,
194
+ };
195
+ ```
196
+
197
+ With Mocha, you can configure `spec` file patterns. The result of implementing
198
+ `resolveConfig` is that users don't need to duplicate this configuration in both
199
+ the tool (e.g. Mocha) and Knip.
200
+
201
+ Use `production` entries to target source files that represent production code.
202
+
203
+ :::tip
204
+
205
+ Regardless of the presence of `resolveConfig`, add `entry` and `production` to
206
+ the default export so they will be displayed in the plugin's documentation as
207
+ default values.
208
+
209
+ :::
210
+
211
+ ## Example 4: Use the AST directly
212
+
213
+ If the `resolveFromConfig` function is implemented, Knip loads the configuration
214
+ file and passes the default-exported object to this plugin function. However,
215
+ that object might then not contain the information we need.
216
+
217
+ Here's an example `astro.config.ts` configuration file with a Starlight
218
+ integration:
219
+
220
+ ```ts
221
+ import starlight from '@astrojs/starlight';
222
+ import { defineConfig } from 'astro/config';
223
+
224
+ export default defineConfig({
225
+ integrations: [
226
+ starlight({
227
+ components: {
228
+ Head: './src/components/Head.astro',
229
+ Footer: './src/components/Footer.astro',
230
+ },
231
+ }),
232
+ ],
233
+ });
234
+ ```
235
+
236
+ With Starlight, components can be defined to override the default internal ones.
237
+ They're not otherwise referenced in your source code, so you'd have to manually
238
+ add them as entry files ([Knip itself did this][4]).
239
+
240
+ In the Astro plugin, there's no way to access this object containing
241
+ `components` to add the component files as entry files if we were to try:
242
+
243
+ ```ts
244
+ const resolveConfig: ResolveConfig<AstroConfig> = async config => {
245
+ console.log(config); // ¯\_(ツ)_/¯
246
+ };
247
+ ```
248
+
249
+ This is why plugins can implement the `resolveFromAST` function.
250
+
251
+ ### 7. resolveFromAST
252
+
253
+ Let's take a look at the Astro plugin implementation. This example assumes some
254
+ familiarity with Abstract Syntax Trees (AST) and the TypeScript compiler API.
255
+ Knip will provide more and more AST helpers to make implementing plugins more
256
+ fun and a little less tedious.
257
+
258
+ Anyway, let's dive in. Here's how we're adding the Starlight `components` paths
259
+ to the default `production` file patterns:
260
+
261
+ ```ts
262
+ import ts from 'typescript';
263
+ import {
264
+ getDefaultImportName,
265
+ getImportMap,
266
+ getPropertyValues,
267
+ } from '../../typescript/ast-helpers.js';
268
+
269
+ const title = 'Astro';
270
+
271
+ const production = [
272
+ 'src/pages/**/*.{astro,mdx,js,ts}',
273
+ 'src/content/**/*.mdx',
274
+ 'src/middleware.{js,ts}',
275
+ 'src/actions/index.{js,ts}',
276
+ ];
277
+
278
+ const getComponentPathsFromSourceFile = (sourceFile: ts.SourceFile) => {
279
+ const componentPaths: Set<string> = new Set();
280
+ const importMap = getImportMap(sourceFile);
281
+ const importName = getDefaultImportName(importMap, '@astrojs/starlight');
282
+
283
+ function visit(node: ts.Node) {
284
+ if (
285
+ ts.isCallExpression(node) &&
286
+ ts.isIdentifier(node.expression) &&
287
+ node.expression.text === importName // match the starlight() function call
288
+ ) {
289
+ const starlightConfig = node.arguments[0];
290
+ if (ts.isObjectLiteralExpression(starlightConfig)) {
291
+ const values = getPropertyValues(starlightConfig, 'components');
292
+ for (const value of values) componentPaths.add(value);
293
+ }
294
+ }
295
+
296
+ ts.forEachChild(node, visit);
297
+ }
298
+
299
+ visit(sourceFile);
300
+
301
+ return componentPaths;
302
+ };
303
+
304
+ const resolveFromAST: ResolveFromAST = (sourceFile: ts.SourceFile) => {
305
+ // Include './src/components/Head.astro' and './src/components/Footer.astro'
306
+ // as production entry files so they're also part of the analysis
307
+ const componentPaths = getComponentPathsFromSourceFile(sourceFile);
308
+ return [...production, ...componentPaths].map(id => toProductionEntry(id));
309
+ };
310
+
311
+ const plugin: Plugin {
312
+ title,
313
+ production,
314
+ resolveFromAST,
315
+ }
316
+
317
+ export default plugin;
318
+ ```
319
+
320
+ ## Inputs
321
+
322
+ You may have noticed functions like `toDeferResolve` and `toEntry`. They're a
323
+ way for plugins to tell what they've found and how Knip should handle those. The
324
+ more precision a plugin can provide here, the better results and performance
325
+ will be.
326
+
327
+ Find all the details over at [Writing A Plugin → Inputs][5].
328
+
329
+ ## Argument Parsing
330
+
331
+ As part of the [script parser][6], Knip parses command-line arguments. Plugins
332
+ can implement the `arg` object to add custom argument parsing tailored to the
333
+ tool.
334
+
335
+ Read more in [Writing A Plugin → Argument Parsing][7].
336
+
337
+ ## Create a new plugin
338
+
339
+ The easiest way to create a new plugin is to use the `create-plugin` script:
340
+
341
+ ```sh
342
+ cd packages/knip
343
+ pnpm create-plugin --name tool
344
+ ```
345
+
346
+ This adds source and test files and fixtures to get you started. It also adds
347
+ the plugin to the JSON Schema and TypeScript types.
348
+
349
+ Run the test for your new plugin using one of the following commands:
350
+
351
+ ```sh
352
+ pnpm tsx --test test/plugins/tool.test.ts
353
+ bun test test/plugins/tool.test.ts
354
+ ```
355
+
356
+ You're ready to implement and submit a new Knip plugin! 🆕 🎉
357
+
358
+ ## Wrapping Up
359
+
360
+ Feel free to check out the implementation of other similar plugins, and borrow
361
+ ideas and code from those!
362
+
363
+ The documentation website takes care of generating the [plugin list and the
364
+ individual plugin pages][2] from the exported plugin values.
365
+
366
+ Thanks for reading. If you have been following this guide to create a new
367
+ plugin, this might be the right time to open a pull request!
368
+
369
+ [1]: #create-a-new-plugin
370
+ [2]: ../reference/plugins.md
371
+ [3]: ../explanations/plugins.md#entry-files-from-config-files
372
+ [4]:
373
+ https://github.com/webpro-nl/knip/blob/6a6954386b33ee8a2919005230a4bc094e11bc03/knip.json#L12
374
+ [5]: ./writing-a-plugin/inputs.md
375
+ [6]: ../features/script-parser.md
376
+ [7]: ./writing-a-plugin/argument-parsing.md