@knip/mcp 0.0.22 → 0.0.24

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.
@@ -251,20 +251,20 @@ This is why plugins can implement the `resolveFromAST` function.
251
251
  ### 8. resolveFromAST
252
252
 
253
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.
254
+ familiarity with Abstract Syntax Trees (AST). Knip provides AST helpers to make
255
+ implementing plugins more fun and a little less tedious.
257
256
 
258
257
  Anyway, let's dive in. Here's how we're adding the Starlight `components` paths
259
258
  to the default `production` file patterns:
260
259
 
261
260
  ```ts
262
- import ts from 'typescript';
261
+ import type { Program } from 'oxc-parser';
263
262
  import {
263
+ findCallArg,
264
264
  getDefaultImportName,
265
265
  getImportMap,
266
266
  getPropertyValues,
267
- } from '../../typescript/ast-helpers.js';
267
+ } from '../../typescript/ast-helpers.ts';
268
268
 
269
269
  const title = 'Astro';
270
270
 
@@ -275,44 +275,24 @@ const production = [
275
275
  'src/actions/index.{js,ts}',
276
276
  ];
277
277
 
278
- const getComponentPathsFromSourceFile = (sourceFile: ts.SourceFile) => {
279
- const componentPaths: Set<string> = new Set();
280
- const importMap = getImportMap(sourceFile);
278
+ const getComponentPaths = (program: Program) => {
279
+ const importMap = getImportMap(program);
281
280
  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;
281
+ if (!importName) return new Set<string>();
282
+ const starlightConfig = findCallArg(program, importName);
283
+ return getPropertyValues(starlightConfig, 'components');
302
284
  };
303
285
 
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);
286
+ const resolveFromAST: ResolveFromAST = (program: Program) => {
287
+ const componentPaths = getComponentPaths(program);
308
288
  return [...production, ...componentPaths].map(id => toProductionEntry(id));
309
289
  };
310
290
 
311
- const plugin: Plugin {
291
+ const plugin: Plugin = {
312
292
  title,
313
293
  production,
314
294
  resolveFromAST,
315
- }
295
+ };
316
296
 
317
297
  export default plugin;
318
298
  ```
@@ -386,6 +366,6 @@ plugin, this might be the right time to open a pull request!
386
366
  [2]: ../reference/plugins.md
387
367
  [3]: ../explanations/plugins.md#entry-files-from-config-files
388
368
  [4]: https://github.com/webpro-nl/knip/blob/6a6954386b33ee8a2919005230a4bc094e11bc03/knip.json#L12
389
- [5]: ./inputs.md
369
+ [5]: ../writing-a-plugin/inputs.md
390
370
  [6]: ../features/script-parser.md
391
- [7]: ./argument-parsing.md
371
+ [7]: ../writing-a-plugin/argument-parsing.md
package/src/texts.js CHANGED
@@ -56,13 +56,16 @@ export const ERROR_HINT = `For unexpected errors (exit code 2) such as "error lo
56
56
  - If no config file exists, create knip.json in the project root
57
57
  - Run knip again`;
58
58
 
59
+ export const UNCONFIGURED_HINT =
60
+ 'Issues are suppressed because the project is not yet configured. Reported issues might be false positives. Address configuration hints first, then re-run to get the actual issues.';
61
+
59
62
  export const CONFIG_REVIEW_HINT = `Review the existing configuration for potential improvements:
60
63
 
61
- - Never use "ignore" patterns (hides real issues!), always prefer specific solutions, other ignore* options are allowed
62
- - Many unused exported types? Add: ignoreExportsUsedInFile: { interface: true, type: true } (prefer this over other ignore* options)
64
+ - Never use "ignore" patterns (hides real issues!), always prefer specific solutions; other ignore* options are allowed
65
+ - Many unused exported types try ignoreExportsUsedInFile: { interface: true, type: true }
63
66
  - Remove ignore patterns that don't match any files
64
- - Remove redundant ignore patterns: Knip respects .gitignore (node_modules, dist, build, .git)
67
+ - Remove redundant ignore patterns Knip respects .gitignore (node_modules, dist, build, .git)
65
68
  - Remove entry patterns covered by config defaults and auto-detected plugins
66
- - Config files (e.g. vite.config.ts) showing as unused? Enable/disable the plugin explicitly
69
+ - Config files (e.g. vite.config.ts) showing as unused → try enable or disable the plugin explicitly (vite: true)
67
70
  - Dependencies matching Node.js builtins: add to ignoreDependencies (e.g. buffer, process)
68
71
  - Unresolved imports from path aliases: add paths to Knip config (tsconfig.json semantics)`;
package/src/tools.js CHANGED
@@ -3,7 +3,7 @@ import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { createOptions, createSession, finalizeConfigurationHints, KNIP_CONFIG_LOCATIONS } from 'knip/session';
5
5
  import { CURATED_RESOURCES } from './curated-resources.js';
6
- import { CONFIG_REVIEW_HINT } from './texts.js';
6
+ import { CONFIG_REVIEW_HINT, UNCONFIGURED_HINT } from './texts.js';
7
7
 
8
8
  export { ERROR_HINT } from './texts.js';
9
9
 
@@ -30,15 +30,30 @@ const docsDir = join(__dirname, './docs');
30
30
  * @param {{ cwd: string, configFilePath: string | undefined }} options
31
31
  */
32
32
  export function buildResults(results, options) {
33
+ const configurationHints = finalizeConfigurationHints(results, options);
34
+
35
+ const isSuppressIssues =
36
+ results.counters.total >= 10 &&
37
+ configurationHints.some(hint => hint.type === 'top-level-unconfigured' || hint.type === 'workspace-unconfigured');
38
+
39
+ const configFile = options.configFilePath
40
+ ? { exists: true, filePath: options.configFilePath }
41
+ : { exists: false, locations: KNIP_CONFIG_LOCATIONS };
42
+
43
+ const reviewHint = isSuppressIssues ? UNCONFIGURED_HINT : options.configFilePath ? CONFIG_REVIEW_HINT : undefined;
44
+ const files = isSuppressIssues ? [] : Array.from(results.issues.files);
45
+ const issues = isSuppressIssues
46
+ ? []
47
+ : Object.fromEntries(Object.entries(results.issues).filter(([key]) => key !== 'files' && key !== '_files'));
48
+
33
49
  return {
34
- configFile: options.configFilePath
35
- ? { exists: true, filePath: options.configFilePath, reviewHint: CONFIG_REVIEW_HINT }
36
- : { exists: false, locations: KNIP_CONFIG_LOCATIONS },
37
- configurationHints: finalizeConfigurationHints(results, options),
50
+ reviewHint,
51
+ configFile,
52
+ configurationHints,
38
53
  counters: results.counters,
39
54
  enabledPlugins: results.enabledPlugins,
40
- files: Array.from(results.issues.files),
41
- issues: Object.fromEntries(Object.entries(results.issues).filter(([key]) => key !== 'files' && key !== '_files')),
55
+ files,
56
+ issues,
42
57
  };
43
58
  }
44
59