knip 6.13.0 → 6.14.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.
Files changed (53) hide show
  1. package/dist/CacheConsultant.d.ts +3 -3
  2. package/dist/CacheConsultant.js +13 -16
  3. package/dist/ConfigurationChief.d.ts +1 -0
  4. package/dist/ConfigurationChief.js +11 -2
  5. package/dist/DependencyDeputy.d.ts +3 -0
  6. package/dist/DependencyDeputy.js +28 -8
  7. package/dist/ProjectPrincipal.d.ts +1 -0
  8. package/dist/ProjectPrincipal.js +43 -52
  9. package/dist/WorkspaceWorker.js +3 -3
  10. package/dist/cli.js +1 -1
  11. package/dist/graph/analyze.js +4 -3
  12. package/dist/graph/build.js +1 -0
  13. package/dist/graph-explorer/explorer.d.ts +2 -1
  14. package/dist/graph-explorer/operations/is-referenced.d.ts +2 -1
  15. package/dist/graph-explorer/operations/is-referenced.js +6 -3
  16. package/dist/plugins/docusaurus/helpers.js +12 -5
  17. package/dist/plugins/docusaurus/index.js +2 -1
  18. package/dist/plugins/hardhat/index.js +1 -1
  19. package/dist/plugins/jest/index.js +1 -1
  20. package/dist/plugins/nitro/index.js +1 -1
  21. package/dist/plugins/nuxt/helpers.js +3 -3
  22. package/dist/plugins/nuxt/index.js +1 -1
  23. package/dist/plugins/nx/index.js +8 -2
  24. package/dist/plugins/sst/resolveFromAST.js +2 -2
  25. package/dist/plugins/tailwind/index.js +1 -1
  26. package/dist/plugins/wxt/index.js +1 -1
  27. package/dist/reporters/trace.js +6 -2
  28. package/dist/run.js +7 -2
  29. package/dist/typescript/ast-helpers.js +2 -2
  30. package/dist/typescript/ast-nodes.d.ts +1 -1
  31. package/dist/typescript/ast-nodes.js +3 -1
  32. package/dist/typescript/get-imports-and-exports.js +4 -4
  33. package/dist/typescript/visitors/walk.d.ts +2 -1
  34. package/dist/typescript/visitors/walk.js +3 -1
  35. package/dist/util/Performance.d.ts +3 -1
  36. package/dist/util/Performance.js +6 -2
  37. package/dist/util/cli-arguments.d.ts +2 -1
  38. package/dist/util/cli-arguments.js +52 -50
  39. package/dist/util/disk-cache.d.ts +10 -0
  40. package/dist/util/disk-cache.js +62 -0
  41. package/dist/util/file-entry-cache.d.ts +0 -7
  42. package/dist/util/file-entry-cache.js +15 -53
  43. package/dist/util/gitignore-cache.d.ts +12 -0
  44. package/dist/util/gitignore-cache.js +77 -0
  45. package/dist/util/glob-cache.d.ts +2 -2
  46. package/dist/util/glob-cache.js +9 -57
  47. package/dist/util/glob-core.d.ts +4 -0
  48. package/dist/util/glob-core.js +14 -1
  49. package/dist/util/glob.js +12 -6
  50. package/dist/util/module-graph.js +17 -13
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  import { Visitor } from 'oxc-parser';
2
2
  import { collectPropertyValues, getImportMap } from '../../typescript/ast-helpers.js';
3
- import { parseFile } from '../../typescript/ast-nodes.js';
3
+ import { _parseFile } from '../../typescript/ast-nodes.js';
4
4
  import { toDeferResolveProductionEntry } from '../../util/input.js';
5
5
  import { dirname } from '../../util/path.js';
6
6
  import { _resolveSync } from '../../util/resolve.js';
@@ -27,7 +27,7 @@ export const getInputsFromHandlers = (program, options) => {
27
27
  if (resolvedPath) {
28
28
  const stackText = options.readFile(resolvedPath);
29
29
  if (stackText) {
30
- const stackResult = parseFile('stack.ts', stackText);
30
+ const stackResult = _parseFile('stack.ts', stackText);
31
31
  addHandlers(collectPropertyValues(stackResult.program, 'handler'));
32
32
  }
33
33
  }
@@ -3,7 +3,7 @@ import compiler from './compiler.js';
3
3
  const title = 'Tailwind';
4
4
  const enablers = ['tailwindcss', '@tailwindcss/vite', '@tailwindcss/postcss', '@tailwindcss/cli'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
- const entry = ['tailwind.config.{js,cjs,mjs,ts}'];
6
+ const entry = ['tailwind.config.{js,cjs,mjs,ts,cts,mts}'];
7
7
  const registerCompilers = ({ registerCompiler, hasDependency }) => {
8
8
  if (enablers.some(enabler => hasDependency(enabler)))
9
9
  registerCompiler({ extension: '.css', compiler });
@@ -3,7 +3,7 @@ import { hasDependency } from '../../util/plugin.js';
3
3
  const title = 'WXT';
4
4
  const enablers = ['wxt'];
5
5
  const isEnabled = ({ dependencies }) => hasDependency(dependencies, enablers);
6
- const config = ['wxt.config.{js,mjs,ts}'];
6
+ const config = ['wxt.config.{js,cjs,mjs,ts,cts,mts}'];
7
7
  const production = ['entrypoints/**/*'];
8
8
  const resolveConfig = async (localConfig) => {
9
9
  const inputs = [];
@@ -35,7 +35,7 @@ export default ({ graph, explorer, options, workspaceFilePathFilter }) => {
35
35
  nodes.sort((a, b) => a.filePath.localeCompare(b.filePath) || a.identifier.localeCompare(b.identifier));
36
36
  const toRel = (path) => toRelative(path, options.cwd);
37
37
  const isReferenced = (node) => {
38
- if (explorer.isReferenced(node.filePath, node.identifier, { includeEntryExports: false })[0])
38
+ if (explorer.isReferenced(node.filePath, node.identifier, { traverseEntries: false })[0])
39
39
  return true;
40
40
  if (explorer.hasStrictlyNsReferences(node.filePath, node.identifier)[0])
41
41
  return true;
@@ -48,7 +48,11 @@ export default ({ graph, explorer, options, workspaceFilePathFilter }) => {
48
48
  memberStatuses = [];
49
49
  for (const m of exp.members) {
50
50
  const id = `${node.identifier}.${m.identifier}`;
51
- const referenced = m.hasRefsInFile || explorer.isReferenced(node.filePath, id, { includeEntryExports: true })[0];
51
+ const referenced = m.hasRefsInFile ||
52
+ explorer.isReferenced(node.filePath, id, {
53
+ traverseEntries: true,
54
+ treatStarAtEntryAsReferenced: true,
55
+ })[0];
52
56
  memberStatuses.push({ identifier: m.identifier, referenced });
53
57
  }
54
58
  }
package/dist/run.js CHANGED
@@ -9,6 +9,7 @@ import { IssueCollector } from './IssueCollector.js';
9
9
  import { ProjectPrincipal } from './ProjectPrincipal.js';
10
10
  import watchReporter from './reporters/watch.js';
11
11
  import { debugLogObject } from './util/debug.js';
12
+ import { flushGitignoreCache, initGitignoreCache } from './util/gitignore-cache.js';
12
13
  import { flushGlobCache, initGlobCache } from './util/glob-cache.js';
13
14
  import { getGitIgnoredHandler } from './util/glob-core.js';
14
15
  import { getModuleSourcePathHandler, getWorkspaceManifestHandler } from './util/to-source-path.js';
@@ -16,8 +17,10 @@ import { getSessionHandler } from './util/watch.js';
16
17
  export const run = async (options) => {
17
18
  debugLogObject('*', 'Unresolved configuration', options);
18
19
  debugLogObject('*', 'Included issue types', options.includedIssueTypes);
19
- if (options.isCache)
20
+ if (options.isCache) {
20
21
  initGlobCache(options.cacheLocation);
22
+ initGitignoreCache(options.cacheLocation);
23
+ }
21
24
  const chief = new ConfigurationChief(options);
22
25
  const deputy = new DependencyDeputy(options);
23
26
  const streamer = new ConsoleStreamer(options);
@@ -83,8 +86,10 @@ export const run = async (options) => {
83
86
  const { issues, counters, tagHints, configurationHints } = collector.getIssues();
84
87
  if (!options.isWatch)
85
88
  streamer.clear();
86
- if (options.isCache)
89
+ if (options.isCache) {
87
90
  flushGlobCache();
91
+ flushGitignoreCache();
92
+ }
88
93
  return {
89
94
  results: {
90
95
  issues,
@@ -1,7 +1,7 @@
1
1
  import { Visitor } from 'oxc-parser';
2
2
  import stripJsonComments from 'strip-json-comments';
3
3
  import { extname, isInternal } from '../util/path.js';
4
- import { getStringValue, isStringLiteral, parseFile } from './ast-nodes.js';
4
+ import { _parseFile, getStringValue, isStringLiteral } from './ast-nodes.js';
5
5
  export const getPropertyKey = (prop) => prop?.key?.type === 'Identifier' ? prop.key.name : getStringValue(prop?.key);
6
6
  export const getImportMap = (program) => {
7
7
  const importMap = new Map();
@@ -191,7 +191,7 @@ export const collectStringLiterals = (sourceText, filePath) => {
191
191
  collectJsonStringLiterals(JSON.parse(stripJsonComments(sourceText, { trailingCommas: true })), literals);
192
192
  return literals;
193
193
  }
194
- const result = parseFile(filePath, sourceText);
194
+ const result = _parseFile(filePath, sourceText);
195
195
  const visitor = new Visitor({
196
196
  Literal(node) {
197
197
  if (typeof node.value === 'string')
@@ -2,7 +2,7 @@ import { type TSEnumDeclaration, type TSModuleDeclaration } from 'oxc-parser';
2
2
  import type { GetImportsAndExportsOptions, IgnoreExportsUsedInFile } from '../types/config.ts';
3
3
  import type { SymbolType } from '../types/issues.ts';
4
4
  import type { ExportMember } from '../types/module-graph.ts';
5
- export declare const parseFile: (filePath: string, sourceText: string) => import("oxc-parser").ParseResult;
5
+ export declare const _parseFile: (filePath: string, sourceText: string) => import("oxc-parser").ParseResult;
6
6
  export type ResolveModule = (specifier: string, containingFile: string) => ResolvedModule | undefined;
7
7
  export interface ResolvedModule {
8
8
  resolvedFileName: string;
@@ -1,16 +1,18 @@
1
1
  import { parseSync, rawTransferSupported, } from 'oxc-parser';
2
2
  import { DEFAULT_EXTENSIONS, FIX_FLAGS, SYMBOL_TYPE } from '../constants.js';
3
3
  import { extname } from '../util/path.js';
4
+ import { timerify } from '../util/Performance.js';
4
5
  import { EMPTY_TAGS } from './visitors/jsdoc.js';
5
6
  const defaultParseOptions = {
6
7
  sourceType: 'unambiguous',
7
8
  experimentalRawTransfer: rawTransferSupported(),
8
9
  };
9
- export const parseFile = (filePath, sourceText) => {
10
+ const parseFile = (filePath, sourceText) => {
10
11
  const ext = extname(filePath);
11
12
  const parseFileName = DEFAULT_EXTENSIONS.has(ext) ? filePath : `${filePath}.ts`;
12
13
  return parseSync(parseFileName, sourceText, defaultParseOptions);
13
14
  };
15
+ export const _parseFile = timerify(parseFile);
14
16
  export const buildLineStarts = (sourceText) => {
15
17
  const starts = [0];
16
18
  for (let i = 0; i < sourceText.length; i++) {
@@ -6,9 +6,9 @@ import { timerify } from '../util/Performance.js';
6
6
  import { dirname, isInNodeModules, resolve } from '../util/path.js';
7
7
  import { shouldIgnore } from '../util/tag.js';
8
8
  import { extractImportsFromComments } from './comments.js';
9
- import { buildLineStarts, getLineAndCol, parseFile, shouldCountRefs, } from './ast-nodes.js';
9
+ import { _parseFile, buildLineStarts, getLineAndCol, shouldCountRefs, } from './ast-nodes.js';
10
10
  import { buildJSDocTagLookup } from './visitors/jsdoc.js';
11
- import { walkAST } from './visitors/walk.js';
11
+ import { _walkAST } from './visitors/walk.js';
12
12
  const getImportsAndExports = (filePath, sourceText, resolveModule, options, ignoreExportsUsedInFile, skipExportsForFile, visitor, pluginCtx, cachedParseResult) => {
13
13
  const skipExports = skipExportsForFile || !options.isReportExports;
14
14
  const isDts = filePath.endsWith('.d.ts') || filePath.endsWith('.d.cts') || filePath.endsWith('.d.mts');
@@ -159,7 +159,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
159
159
  }
160
160
  }
161
161
  };
162
- const result = cachedParseResult ?? parseFile(filePath, sourceText);
162
+ const result = cachedParseResult ?? _parseFile(filePath, sourceText);
163
163
  const lineStarts = buildLineStarts(sourceText);
164
164
  const getJSDocTags = buildJSDocTagLookup(result.comments, sourceText);
165
165
  let hasNodeModuleImport = false;
@@ -255,7 +255,7 @@ const getImportsAndExports = (filePath, sourceText, resolveModule, options, igno
255
255
  pluginCtx.addScript = (s) => scripts.add(s);
256
256
  pluginCtx.addImport = (spec, pos, mod) => addImport(spec, undefined, undefined, undefined, pos, mod);
257
257
  }
258
- const localRefs = walkAST(result.program, sourceText, filePath, {
258
+ const localRefs = _walkAST(result.program, sourceText, filePath, {
259
259
  lineStarts,
260
260
  skipExports,
261
261
  options,
@@ -81,5 +81,6 @@ export interface WalkState extends WalkContext {
81
81
  }
82
82
  export declare const isShadowed: (name: string, pos: number) => boolean;
83
83
  export declare function buildVisitor(pluginVisitorObjects: PluginVisitorObject[], includeLocalRefs?: boolean): Visitor;
84
- export declare function walkAST(program: Program, sourceText: string, filePath: string, ctx: WalkContext): Set<string> | undefined;
84
+ declare function walkAST(program: Program, sourceText: string, filePath: string, ctx: WalkContext): Set<string> | undefined;
85
+ export declare const _walkAST: typeof walkAST;
85
86
  export {};
@@ -2,6 +2,7 @@ import { Visitor, visitorKeys, } from 'oxc-parser';
2
2
  import { FIX_FLAGS, IMPORT_FLAGS, OPAQUE, SYMBOL_TYPE } from '../../constants.js';
3
3
  import { addValue } from '../../util/module-graph.js';
4
4
  import { isInNodeModules } from '../../util/path.js';
5
+ import { timerify } from '../../util/Performance.js';
5
6
  import { getLineAndCol, getStringValue, isStringLiteral } from '../ast-nodes.js';
6
7
  import { EMPTY_TAGS } from './jsdoc.js';
7
8
  import { handleCallExpression, handleNewExpression } from './calls.js';
@@ -673,7 +674,7 @@ export function buildVisitor(pluginVisitorObjects, includeLocalRefs) {
673
674
  }
674
675
  return new Visitor(merged);
675
676
  }
676
- export function walkAST(program, sourceText, filePath, ctx) {
677
+ function walkAST(program, sourceText, filePath, ctx) {
677
678
  const isJS = filePath.endsWith('.js') || filePath.endsWith('.mjs') || filePath.endsWith('.cjs') || filePath.endsWith('.jsx');
678
679
  state = {
679
680
  ...ctx,
@@ -780,3 +781,4 @@ export function walkAST(program, sourceText, filePath, ctx) {
780
781
  state = undefined;
781
782
  return localRefs;
782
783
  }
784
+ export const _walkAST = timerify(walkAST);
@@ -13,6 +13,7 @@ declare class Performance {
13
13
  isEnabled: boolean;
14
14
  isTimerifyFunctions: boolean;
15
15
  isMemoryUsageEnabled: boolean;
16
+ isDurationEnabled: boolean;
16
17
  startTime: number;
17
18
  endTime: number;
18
19
  perfEntries: PerformanceEntry[];
@@ -21,7 +22,8 @@ declare class Performance {
21
22
  memId?: string;
22
23
  fnObserver?: PerformanceObserver;
23
24
  memObserver?: PerformanceObserver;
24
- constructor({ isTimerifyFunctions, isMemoryUsageEnabled }: {
25
+ constructor({ isTimerifyFunctions, isMemoryUsageEnabled, isDurationEnabled }: {
26
+ isDurationEnabled?: boolean | undefined;
25
27
  isMemoryUsageEnabled?: boolean | undefined;
26
28
  isTimerifyFunctions?: boolean | undefined;
27
29
  });
@@ -10,12 +10,14 @@ const { values } = parseArgs({
10
10
  'performance-fn': { type: 'string', multiple: true },
11
11
  memory: { type: 'boolean' },
12
12
  'memory-realtime': { type: 'boolean' },
13
+ duration: { type: 'boolean' },
13
14
  },
14
15
  });
15
16
  const timerifyOnlyFnName = values['performance-fn'];
16
17
  const isMemoryRealtime = !!values['memory-realtime'];
17
18
  const isTimerifyFunctions = !!values.performance || !!timerifyOnlyFnName;
18
19
  const isMemoryUsageEnabled = !!values.memory || isMemoryRealtime;
20
+ const isDurationEnabled = !!values.duration;
19
21
  export const timerify = (fn, name = fn.name) => {
20
22
  if (!isTimerifyFunctions)
21
23
  return fn;
@@ -36,6 +38,7 @@ class Performance {
36
38
  isEnabled;
37
39
  isTimerifyFunctions;
38
40
  isMemoryUsageEnabled;
41
+ isDurationEnabled;
39
42
  startTime = 0;
40
43
  endTime = 0;
41
44
  perfEntries = [];
@@ -44,10 +47,11 @@ class Performance {
44
47
  memId;
45
48
  fnObserver;
46
49
  memObserver;
47
- constructor({ isTimerifyFunctions = false, isMemoryUsageEnabled = false }) {
50
+ constructor({ isTimerifyFunctions = false, isMemoryUsageEnabled = false, isDurationEnabled = false }) {
48
51
  this.isEnabled = isTimerifyFunctions || isMemoryUsageEnabled;
49
52
  this.isTimerifyFunctions = isTimerifyFunctions;
50
53
  this.isMemoryUsageEnabled = isMemoryUsageEnabled;
54
+ this.isDurationEnabled = isDurationEnabled;
51
55
  this.startTime = performance.now();
52
56
  const instanceId = Math.floor(performance.now() * 100);
53
57
  this.perfId = `perf-${instanceId}`;
@@ -172,4 +176,4 @@ class Performance {
172
176
  this.memObserver?.disconnect();
173
177
  }
174
178
  }
175
- export const perfObserver = new Performance({ isTimerifyFunctions, isMemoryUsageEnabled });
179
+ export const perfObserver = new Performance({ isTimerifyFunctions, isMemoryUsageEnabled, isDurationEnabled });
@@ -1,4 +1,4 @@
1
- export declare const helpText = "\u2702\uFE0F Find unused dependencies, exports and files in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -h, --help Print this help text\n -V, --version Print version\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n -c, --config [file] Configuration file path\n (default: [.]knip.json[c], knip.(js|ts), knip.config.(js|ts) or package.json#knip)\n --use-tsconfig-files Use tsconfig.json to define project files (override `project` patterns)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n\nMode\n --cache Enable caching\n --cache-location Change cache location (default: node_modules/.cache/knip)\n --include-entry-exports Include entry files when reporting unused exports\n --no-gitignore Don't respect .gitignore\n --production Analyze only production source files (e.g. no test files, devDependencies)\n --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n --watch Watch mode\n\nScope\n -W, --workspace [filter] Filter workspaces by name, directory, or glob (can be repeated)\n --directory [dir] Run process from a different directory (default: cwd)\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,binaries,unresolved,catalog\n --exports Shortcut for --include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates\n --files Shortcut for --include files\n --tags Include or exclude tagged exports\n\nFix\n --fix Fix issues (modifies files in your repo)\n --fix-type Fix only issues of type, can be comma-separated or repeated (2)\n --allow-remove-files Allow Knip to remove files (with --fix)\n --format Format modified files after --fix using the local formatter\n\nOutput\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter (default: symbols), can be repeated (3)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --treat-config-hints-as-errors Exit with non-zero code (1) if there are any configuration hints\n --max-issues Maximum number of total issues before non-zero exit code (default: 0)\n --max-show-issues Maximum number of issues to display per type\n --no-exit-code Always exit with code zero (0)\n\nTroubleshooting\n -d, --debug Show debug output\n --memory Measure memory usage and display data table\n --memory-realtime Log memory usage in realtime\n --performance Measure count and running time of key functions and display stats table\n --performance-fn [name] Measure only function [name]\n --trace Show trace output\n --trace-dependency [name] Show files that import the named dependency\n --trace-export [name] Show trace output for named export(s)\n --trace-file [file] Show trace output for exports in file\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, types, nsTypes, enumMembers, namespaceMembers, duplicates, catalog\n(2) Fixable issue types: dependencies, exports, types, files, catalog\n(3) Built-in reporters: symbols (default), compact, codeowners, json, codeclimate, markdown, disclosure, github-actions\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip --workspace @myorg/* --workspace '!@myorg/legacy'\n$ knip --workspace './apps/*' --workspace '@shared/utils'\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --tags=-lintignore\n\nWebsite: https://knip.dev";
1
+ export declare const helpText = "\u2702\uFE0F Find unused dependencies, exports and files in your JavaScript and TypeScript projects\n\nUsage: knip [options]\n\nOptions:\n -h, --help Print this help text\n -V, --version Print version\n -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)\n -c, --config [file] Configuration file path\n (default: [.]knip.json[c], knip.(js|ts), knip.config.(js|ts) or package.json#knip)\n --use-tsconfig-files Use tsconfig.json to define project files (override `project` patterns)\n -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)\n\nMode\n --cache Enable caching\n --cache-location Change cache location (default: node_modules/.cache/knip)\n --include-entry-exports Include entry files when reporting unused exports\n --no-gitignore Don't respect .gitignore\n -p, --production Analyze only production source files (e.g. no test files, devDependencies)\n -s, --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)\n -w, --watch Watch mode\n\nScope\n -W, --workspace [filter] Filter workspaces by name, directory, or glob (can be repeated)\n -D, --directory [dir] Run process from a different directory (default: cwd)\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,binaries,unresolved,catalog\n --exports Shortcut for --include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates\n --files Shortcut for --include files\n --tags Include or exclude tagged exports\n\nFix\n -f, --fix Fix issues (modifies files in your repo)\n --fix-type Fix only issues of type, can be comma-separated or repeated (2)\n --allow-remove-files Allow Knip to remove files (with --fix)\n -F, --format Format modified files after --fix using the local formatter\n\nOutput\n --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated\n --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)\n --reporter Select reporter (default: symbols), can be repeated (3)\n --reporter-options Pass extra options to the reporter (as JSON string, see example)\n --no-config-hints Suppress configuration hints\n --treat-config-hints-as-errors Exit with non-zero code (1) if there are any configuration hints\n --max-issues Maximum number of total issues before non-zero exit code (default: 0)\n --max-show-issues Maximum number of issues to display per type\n --no-exit-code Always exit with code zero (0)\n\nTroubleshooting\n -d, --debug Show debug output\n --memory Measure memory usage and display data table\n --memory-realtime Log memory usage in realtime\n --performance Measure count and running time of key functions and display stats table\n --performance-fn [name] Measure only function [name]\n -u, --duration Print total running time (zero overhead, no instrumentation)\n --trace Show trace output\n --trace-dependency [name] Show files that import the named dependency\n --trace-export [name] Show trace output for named export(s)\n --trace-file [file] Show trace output for exports in file\n\n(1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, types, nsTypes, enumMembers, namespaceMembers, duplicates, catalog\n(2) Fixable issue types: dependencies, exports, types, files, catalog\n(3) Built-in reporters: symbols (default), compact, codeowners, json, codeclimate, markdown, disclosure, github-actions\n\nExamples:\n\n$ knip\n$ knip --production\n$ knip --workspace packages/client --include files,dependencies\n$ knip --workspace @myorg/* --workspace '!@myorg/legacy'\n$ knip --workspace './apps/*' --workspace '@shared/utils'\n$ knip -c ./config/knip.json --reporter compact\n$ knip --reporter codeowners --reporter-options '{\"path\":\".github/CODEOWNERS\"}'\n$ knip --tags=-lintignore\n\nWebsite: https://knip.dev";
2
2
  export type ParsedCLIArgs = ReturnType<typeof parseCLIArgs>;
3
3
  export default function parseCLIArgs(): {
4
4
  cache?: boolean | undefined;
@@ -33,6 +33,7 @@ export default function parseCLIArgs(): {
33
33
  'preprocessor-options'?: string | undefined;
34
34
  reporter?: string[] | undefined;
35
35
  'reporter-options'?: string | undefined;
36
+ duration?: boolean | undefined;
36
37
  strict?: boolean | undefined;
37
38
  trace?: boolean | undefined;
38
39
  'trace-dependency'?: string | undefined;
@@ -4,60 +4,61 @@ export const helpText = `✂️ Find unused dependencies, exports and files in
4
4
  Usage: knip [options]
5
5
 
6
6
  Options:
7
- -h, --help Print this help text
8
- -V, --version Print version
9
- -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
10
- -c, --config [file] Configuration file path
11
- (default: [.]knip.json[c], knip.(js|ts), knip.config.(js|ts) or package.json#knip)
12
- --use-tsconfig-files Use tsconfig.json to define project files (override \`project\` patterns)
13
- -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)
7
+ -h, --help Print this help text
8
+ -V, --version Print version
9
+ -n, --no-progress Don't show dynamic progress updates (automatically enabled in CI environments)
10
+ -c, --config [file] Configuration file path
11
+ (default: [.]knip.json[c], knip.(js|ts), knip.config.(js|ts) or package.json#knip)
12
+ --use-tsconfig-files Use tsconfig.json to define project files (override \`project\` patterns)
13
+ -t, --tsConfig [file] TypeScript configuration path (default: tsconfig.json)
14
14
 
15
15
  Mode
16
- --cache Enable caching
17
- --cache-location Change cache location (default: node_modules/.cache/knip)
18
- --include-entry-exports Include entry files when reporting unused exports
19
- --no-gitignore Don't respect .gitignore
20
- --production Analyze only production source files (e.g. no test files, devDependencies)
21
- --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)
22
- --watch Watch mode
16
+ --cache Enable caching
17
+ --cache-location Change cache location (default: node_modules/.cache/knip)
18
+ --include-entry-exports Include entry files when reporting unused exports
19
+ --no-gitignore Don't respect .gitignore
20
+ -p, --production Analyze only production source files (e.g. no test files, devDependencies)
21
+ -s, --strict Consider only direct dependencies of workspace (not devDependencies, not other workspaces)
22
+ -w, --watch Watch mode
23
23
 
24
24
  Scope
25
- -W, --workspace [filter] Filter workspaces by name, directory, or glob (can be repeated)
26
- --directory [dir] Run process from a different directory (default: cwd)
27
- --include Report only provided issue type(s), can be comma-separated or repeated (1)
28
- --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
29
- --dependencies Shortcut for --include dependencies,unlisted,binaries,unresolved,catalog
30
- --exports Shortcut for --include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates
31
- --files Shortcut for --include files
32
- --tags Include or exclude tagged exports
25
+ -W, --workspace [filter] Filter workspaces by name, directory, or glob (can be repeated)
26
+ -D, --directory [dir] Run process from a different directory (default: cwd)
27
+ --include Report only provided issue type(s), can be comma-separated or repeated (1)
28
+ --exclude Exclude provided issue type(s) from report, can be comma-separated or repeated (1)
29
+ --dependencies Shortcut for --include dependencies,unlisted,binaries,unresolved,catalog
30
+ --exports Shortcut for --include exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates
31
+ --files Shortcut for --include files
32
+ --tags Include or exclude tagged exports
33
33
 
34
34
  Fix
35
- --fix Fix issues (modifies files in your repo)
36
- --fix-type Fix only issues of type, can be comma-separated or repeated (2)
37
- --allow-remove-files Allow Knip to remove files (with --fix)
38
- --format Format modified files after --fix using the local formatter
35
+ -f, --fix Fix issues (modifies files in your repo)
36
+ --fix-type Fix only issues of type, can be comma-separated or repeated (2)
37
+ --allow-remove-files Allow Knip to remove files (with --fix)
38
+ -F, --format Format modified files after --fix using the local formatter
39
39
 
40
40
  Output
41
- --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
42
- --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
43
- --reporter Select reporter (default: symbols), can be repeated (3)
44
- --reporter-options Pass extra options to the reporter (as JSON string, see example)
45
- --no-config-hints Suppress configuration hints
46
- --treat-config-hints-as-errors Exit with non-zero code (1) if there are any configuration hints
47
- --max-issues Maximum number of total issues before non-zero exit code (default: 0)
48
- --max-show-issues Maximum number of issues to display per type
49
- --no-exit-code Always exit with code zero (0)
41
+ --preprocessor Preprocess the results before providing it to the reporter(s), can be repeated
42
+ --preprocessor-options Pass extra options to the preprocessor (as JSON string, see --reporter-options example)
43
+ --reporter Select reporter (default: symbols), can be repeated (3)
44
+ --reporter-options Pass extra options to the reporter (as JSON string, see example)
45
+ --no-config-hints Suppress configuration hints
46
+ --treat-config-hints-as-errors Exit with non-zero code (1) if there are any configuration hints
47
+ --max-issues Maximum number of total issues before non-zero exit code (default: 0)
48
+ --max-show-issues Maximum number of issues to display per type
49
+ --no-exit-code Always exit with code zero (0)
50
50
 
51
51
  Troubleshooting
52
- -d, --debug Show debug output
53
- --memory Measure memory usage and display data table
54
- --memory-realtime Log memory usage in realtime
55
- --performance Measure count and running time of key functions and display stats table
56
- --performance-fn [name] Measure only function [name]
57
- --trace Show trace output
58
- --trace-dependency [name] Show files that import the named dependency
59
- --trace-export [name] Show trace output for named export(s)
60
- --trace-file [file] Show trace output for exports in file
52
+ -d, --debug Show debug output
53
+ --memory Measure memory usage and display data table
54
+ --memory-realtime Log memory usage in realtime
55
+ --performance Measure count and running time of key functions and display stats table
56
+ --performance-fn [name] Measure only function [name]
57
+ -u, --duration Print total running time (zero overhead, no instrumentation)
58
+ --trace Show trace output
59
+ --trace-dependency [name] Show files that import the named dependency
60
+ --trace-export [name] Show trace output for named export(s)
61
+ --trace-file [file] Show trace output for exports in file
61
62
 
62
63
  (1) Issue types: files, dependencies, unlisted, unresolved, exports, nsExports, types, nsTypes, enumMembers, namespaceMembers, duplicates, catalog
63
64
  (2) Fixable issue types: dependencies, exports, types, files, catalog
@@ -83,14 +84,14 @@ export default function parseCLIArgs() {
83
84
  config: { type: 'string', short: 'c' },
84
85
  debug: { type: 'boolean', short: 'd' },
85
86
  dependencies: { type: 'boolean' },
86
- directory: { type: 'string' },
87
+ directory: { type: 'string', short: 'D' },
87
88
  exclude: { type: 'string', multiple: true },
88
89
  exports: { type: 'boolean' },
89
90
  tags: { type: 'string', multiple: true },
90
91
  files: { type: 'boolean' },
91
- fix: { type: 'boolean' },
92
+ fix: { type: 'boolean', short: 'f' },
92
93
  'fix-type': { type: 'string', multiple: true },
93
- format: { type: 'boolean' },
94
+ format: { type: 'boolean', short: 'F' },
94
95
  'allow-remove-files': { type: 'boolean' },
95
96
  help: { type: 'boolean', short: 'h' },
96
97
  include: { type: 'string', multiple: true },
@@ -105,12 +106,13 @@ export default function parseCLIArgs() {
105
106
  'no-progress': { type: 'boolean', short: 'n' },
106
107
  performance: { type: 'boolean' },
107
108
  'performance-fn': { type: 'string' },
108
- production: { type: 'boolean' },
109
+ production: { type: 'boolean', short: 'p' },
109
110
  preprocessor: { type: 'string', multiple: true },
110
111
  'preprocessor-options': { type: 'string' },
111
112
  reporter: { type: 'string', multiple: true },
112
113
  'reporter-options': { type: 'string' },
113
- strict: { type: 'boolean' },
114
+ duration: { type: 'boolean', short: 'u' },
115
+ strict: { type: 'boolean', short: 's' },
114
116
  trace: { type: 'boolean' },
115
117
  'trace-dependency': { type: 'string' },
116
118
  'trace-export': { type: 'string' },
@@ -119,7 +121,7 @@ export default function parseCLIArgs() {
119
121
  tsConfig: { type: 'string', short: 't' },
120
122
  'use-tsconfig-files': { type: 'boolean' },
121
123
  version: { type: 'boolean', short: 'V' },
122
- watch: { type: 'boolean' },
124
+ watch: { type: 'boolean', short: 'w' },
123
125
  workspace: { type: 'string', short: 'W', multiple: true },
124
126
  },
125
127
  }).values;
@@ -0,0 +1,10 @@
1
+ export interface DiskCache<V> {
2
+ init: (cacheLocation: string) => void;
3
+ isEnabled: () => boolean;
4
+ get: (key: string) => V | undefined;
5
+ set: (key: string, value: V) => void;
6
+ delete: (key: string) => void;
7
+ clear: () => void;
8
+ flush: () => void;
9
+ }
10
+ export declare const createDiskCache: <V>(name: string) => DiskCache<V>;
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { deserialize, serialize } from 'node:v8';
4
+ import { version } from '../version.js';
5
+ import { debugLog } from './debug.js';
6
+ import { isDirectory, isFile } from './fs.js';
7
+ import { dirname } from './path.js';
8
+ export const createDiskCache = (name) => {
9
+ const filename = `${name}-${version}.cache`;
10
+ let cacheFilePath;
11
+ let cache;
12
+ let isDirty = false;
13
+ return {
14
+ init(cacheLocation) {
15
+ cacheFilePath = path.resolve(cacheLocation, filename);
16
+ if (isFile(cacheFilePath)) {
17
+ try {
18
+ cache = deserialize(fs.readFileSync(cacheFilePath));
19
+ }
20
+ catch {
21
+ debugLog('*', `Error reading cache from ${cacheFilePath}`);
22
+ cache = new Map();
23
+ }
24
+ }
25
+ else {
26
+ cache = new Map();
27
+ }
28
+ },
29
+ isEnabled: () => cache !== undefined,
30
+ get: key => cache?.get(key),
31
+ set(key, value) {
32
+ if (!cache)
33
+ return;
34
+ cache.set(key, value);
35
+ isDirty = true;
36
+ },
37
+ delete(key) {
38
+ if (cache?.delete(key))
39
+ isDirty = true;
40
+ },
41
+ clear() {
42
+ if (cache && cache.size > 0) {
43
+ cache.clear();
44
+ isDirty = true;
45
+ }
46
+ },
47
+ flush() {
48
+ if (!cache || !cacheFilePath || !isDirty)
49
+ return;
50
+ try {
51
+ const dir = dirname(cacheFilePath);
52
+ if (!isDirectory(dir))
53
+ fs.mkdirSync(dir, { recursive: true });
54
+ fs.writeFileSync(cacheFilePath, serialize(cache));
55
+ isDirty = false;
56
+ }
57
+ catch {
58
+ debugLog('*', `Error writing cache to ${cacheFilePath}`);
59
+ }
60
+ },
61
+ };
62
+ };
@@ -1,4 +1,3 @@
1
- import fs from 'node:fs';
2
1
  type MetaData<T> = {
3
2
  size: number;
4
3
  mtime: number;
@@ -16,14 +15,8 @@ export declare class FileEntryCache<T> {
16
15
  cache: Map<string, MetaData<T>>;
17
16
  normalizedEntries: Map<string, FileDescriptor<T>>;
18
17
  constructor(cacheId: string, _path: string);
19
- removeNotFoundFiles(): void;
20
18
  getFileDescriptor(filePath: string): FileDescriptor<T>;
21
- _getFileDescriptorUsingMtimeAndSize(filePath: string, fstat: fs.Stats): FileDescriptor<T>;
22
19
  removeEntry(entryName: string): void;
23
- _getMetaForFileUsingMtimeAndSize(cacheEntry: FileDescriptor<T>): {
24
- size: number;
25
- mtime: number;
26
- };
27
20
  reconcile(): void;
28
21
  }
29
22
  export {};