knip 6.43.0 → 6.44.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 (155) hide show
  1. package/dist/CacheConsultant.d.ts +1 -0
  2. package/dist/CacheConsultant.js +2 -0
  3. package/dist/CatalogCounselor.d.ts +1 -3
  4. package/dist/CatalogCounselor.js +18 -15
  5. package/dist/ConfigurationChief.d.ts +24 -0
  6. package/dist/DependencyDeputy.d.ts +2 -1
  7. package/dist/DependencyDeputy.js +9 -4
  8. package/dist/IssueCollector.js +1 -0
  9. package/dist/ProjectPrincipal.d.ts +3 -2
  10. package/dist/ProjectPrincipal.js +32 -10
  11. package/dist/WorkspaceWorker.d.ts +5 -2
  12. package/dist/WorkspaceWorker.js +20 -7
  13. package/dist/binaries/util.js +2 -1
  14. package/dist/cli.js +6 -1
  15. package/dist/compilers/index.d.ts +5 -0
  16. package/dist/compilers/index.js +1 -1
  17. package/dist/constants.d.ts +1 -0
  18. package/dist/constants.js +1 -0
  19. package/dist/graph/analyze.js +3 -2
  20. package/dist/graph/build.d.ts +2 -0
  21. package/dist/graph/build.js +37 -22
  22. package/dist/graph-explorer/cache.d.ts +2 -1
  23. package/dist/graph-explorer/cache.js +5 -1
  24. package/dist/graph-explorer/explorer.d.ts +3 -5
  25. package/dist/graph-explorer/explorer.js +24 -3
  26. package/dist/plugins/biome/index.js +1 -1
  27. package/dist/plugins/cspell/index.js +1 -0
  28. package/dist/plugins/dependency-cruiser/index.js +2 -2
  29. package/dist/plugins/docusaurus/helpers.js +4 -4
  30. package/dist/plugins/electron-vite/index.js +2 -2
  31. package/dist/plugins/expressive-code/index.d.ts +1 -1
  32. package/dist/plugins/expressive-code/index.js +2 -2
  33. package/dist/plugins/gatsby/index.js +6 -6
  34. package/dist/plugins/gatsby/types.d.ts +1 -1
  35. package/dist/plugins/github-actions/index.js +1 -1
  36. package/dist/plugins/i18next-parser/index.js +2 -2
  37. package/dist/plugins/index.d.ts +5 -1
  38. package/dist/plugins/index.js +8 -0
  39. package/dist/plugins/jasmine/index.js +2 -2
  40. package/dist/plugins/karma/index.js +4 -4
  41. package/dist/plugins/kysely-codegen/index.d.ts +3 -0
  42. package/dist/plugins/kysely-codegen/index.js +20 -0
  43. package/dist/plugins/lefthook/index.js +2 -2
  44. package/dist/plugins/lingui/index.d.ts +3 -0
  45. package/dist/plugins/lingui/index.js +16 -0
  46. package/dist/plugins/lockfile-lint/index.js +2 -2
  47. package/dist/plugins/lost-pixel/index.js +2 -2
  48. package/dist/plugins/lunaria/index.js +2 -2
  49. package/dist/plugins/marko/index.js +5 -6
  50. package/dist/plugins/mastra/index.d.ts +3 -0
  51. package/dist/plugins/mastra/index.js +57 -0
  52. package/dist/plugins/mcp/index.js +1 -1
  53. package/dist/plugins/mise/index.js +0 -3
  54. package/dist/plugins/mocha/index.js +42 -3
  55. package/dist/plugins/mocha/types.d.ts +1 -0
  56. package/dist/plugins/nx/index.js +3 -8
  57. package/dist/plugins/nyc/index.js +1 -1
  58. package/dist/plugins/openapi-ts/index.js +2 -2
  59. package/dist/plugins/oxfmt/index.js +2 -2
  60. package/dist/plugins/playwright/index.js +6 -11
  61. package/dist/plugins/pnpm/index.js +2 -4
  62. package/dist/plugins/purgecss/index.d.ts +3 -0
  63. package/dist/plugins/purgecss/index.js +16 -0
  64. package/dist/plugins/railway/index.js +1 -4
  65. package/dist/plugins/remix/index.js +1 -1
  66. package/dist/plugins/rstest/index.js +2 -2
  67. package/dist/plugins/serverless-framework/index.js +1 -1
  68. package/dist/plugins/storybook/index.js +5 -5
  69. package/dist/plugins/sveltekit/resolveFromAST.js +1 -1
  70. package/dist/plugins/syncpack/index.js +2 -2
  71. package/dist/plugins/tanstack-router/index.js +3 -2
  72. package/dist/plugins/taskfile/index.js +4 -7
  73. package/dist/plugins/textlint/helpers.js +2 -2
  74. package/dist/plugins/typescript/index.js +7 -3
  75. package/dist/plugins/unocss/index.js +2 -2
  76. package/dist/plugins/vite-plugin-pwa/index.js +1 -4
  77. package/dist/plugins/vite-plugin-pwa/resolveFromAST.d.ts +0 -2
  78. package/dist/plugins/vite-plugin-pwa/resolveFromAST.js +2 -2
  79. package/dist/plugins/vite-pwa-assets-generator/index.js +2 -2
  80. package/dist/plugins/vitest/helpers.js +4 -7
  81. package/dist/plugins/vitest/index.js +1 -1
  82. package/dist/plugins/webpack/index.js +4 -2
  83. package/dist/plugins/yarn/index.js +2 -4
  84. package/dist/reporters/codeclimate.d.ts +2 -2
  85. package/dist/reporters/codeclimate.js +2 -4
  86. package/dist/reporters/codeowners.d.ts +2 -2
  87. package/dist/reporters/codeowners.js +9 -9
  88. package/dist/reporters/compact.d.ts +2 -2
  89. package/dist/reporters/compact.js +8 -7
  90. package/dist/reporters/cycles.d.ts +2 -2
  91. package/dist/reporters/cycles.js +7 -6
  92. package/dist/reporters/disclosure.d.ts +2 -2
  93. package/dist/reporters/disclosure.js +6 -4
  94. package/dist/reporters/github-actions.d.ts +2 -2
  95. package/dist/reporters/github-actions.js +7 -5
  96. package/dist/reporters/index.d.ts +10 -10
  97. package/dist/reporters/json.d.ts +2 -2
  98. package/dist/reporters/json.js +2 -4
  99. package/dist/reporters/markdown.d.ts +2 -2
  100. package/dist/reporters/markdown.js +11 -9
  101. package/dist/reporters/sarif.d.ts +2 -2
  102. package/dist/reporters/sarif.js +2 -3
  103. package/dist/reporters/symbols.d.ts +2 -2
  104. package/dist/reporters/symbols.js +6 -4
  105. package/dist/run.d.ts +1 -1
  106. package/dist/run.js +3 -10
  107. package/dist/schema/configuration.d.ts +60 -0
  108. package/dist/schema/plugins.d.ts +20 -0
  109. package/dist/schema/plugins.js +4 -0
  110. package/dist/session/configuration.d.ts +12 -0
  111. package/dist/session/configuration.js +43 -0
  112. package/dist/session/incremental.d.ts +54 -0
  113. package/dist/session/incremental.js +49 -0
  114. package/dist/session/session.d.ts +1 -1
  115. package/dist/session/session.js +62 -27
  116. package/dist/session/watch.d.ts +2 -0
  117. package/dist/session/watch.js +41 -0
  118. package/dist/types/PluginNames.d.ts +2 -2
  119. package/dist/types/PluginNames.js +4 -0
  120. package/dist/types/config.d.ts +1 -0
  121. package/dist/types/issues.d.ts +4 -0
  122. package/dist/types/package-json.d.ts +0 -3
  123. package/dist/typescript/get-imports-and-exports.js +2 -0
  124. package/dist/typescript/visitors/calls.js +15 -11
  125. package/dist/typescript/visitors/imports.d.ts +2 -1
  126. package/dist/typescript/visitors/imports.js +25 -19
  127. package/dist/typescript/visitors/members.js +23 -26
  128. package/dist/typescript/visitors/walk.d.ts +3 -0
  129. package/dist/typescript/visitors/walk.js +42 -2
  130. package/dist/util/cli-arguments.d.ts +2 -1
  131. package/dist/util/cli-arguments.js +3 -0
  132. package/dist/util/create-input-handler.js +1 -1
  133. package/dist/util/create-options.d.ts +42 -1
  134. package/dist/util/create-options.js +13 -2
  135. package/dist/util/file-entry-cache.d.ts +2 -0
  136. package/dist/util/file-entry-cache.js +19 -1
  137. package/dist/util/gitignore-cache.d.ts +1 -0
  138. package/dist/util/gitignore-cache.js +1 -0
  139. package/dist/util/glob-core.js +4 -2
  140. package/dist/util/glob.d.ts +1 -0
  141. package/dist/util/glob.js +6 -0
  142. package/dist/util/input.d.ts +2 -0
  143. package/dist/util/loader.d.ts +1 -0
  144. package/dist/util/loader.js +38 -2
  145. package/dist/util/modules.js +3 -1
  146. package/dist/util/path.d.ts +1 -0
  147. package/dist/util/path.js +1 -0
  148. package/dist/util/reporter.d.ts +1 -1
  149. package/dist/util/reporter.js +53 -9
  150. package/dist/util/watch.d.ts +5 -11
  151. package/dist/util/watch.js +84 -68
  152. package/dist/version.d.ts +1 -1
  153. package/dist/version.js +1 -1
  154. package/package.json +1 -1
  155. package/schema.json +16 -0
@@ -18,6 +18,7 @@ export class FileEntryCache {
18
18
  filePath;
19
19
  cache = new Map();
20
20
  normalizedEntries = new Map();
21
+ isDirty = false;
21
22
  constructor(cacheId, _path) {
22
23
  this.filePath = path.resolve(_path, cacheId);
23
24
  if (isFile(this.filePath))
@@ -58,22 +59,39 @@ export class FileEntryCache {
58
59
  meta = { size: cSize, mtime: cTime };
59
60
  this.cache.set(filePath, meta);
60
61
  }
62
+ if (changed)
63
+ this.isDirty = true;
61
64
  const fd = { key: filePath, changed, meta };
62
65
  this.normalizedEntries.set(filePath, fd);
63
66
  return fd;
64
67
  }
68
+ setFileData(filePath, data) {
69
+ const fd = this.getFileDescriptor(filePath);
70
+ if (!fd.meta)
71
+ return;
72
+ fd.meta.data = data;
73
+ this.isDirty = true;
74
+ }
65
75
  removeEntry(entryName) {
66
76
  if (!isAbsolute(entryName))
67
77
  entryName = resolve(entryName);
68
78
  this.normalizedEntries.delete(entryName);
69
- this.cache.delete(entryName);
79
+ if (this.cache.delete(entryName))
80
+ this.isDirty = true;
70
81
  }
71
82
  reconcile() {
83
+ for (const filePath of this.cache.keys()) {
84
+ if (!this.normalizedEntries.has(filePath))
85
+ this.removeEntry(filePath);
86
+ }
87
+ if (!this.isDirty)
88
+ return;
72
89
  try {
73
90
  const dir = path.dirname(this.filePath);
74
91
  if (!isDirectory(dir))
75
92
  fs.mkdirSync(dir, { recursive: true });
76
93
  fs.writeFileSync(this.filePath, serialize(this.cache));
94
+ this.isDirty = false;
77
95
  }
78
96
  catch (_err) {
79
97
  debugLog('*', `Error writing cache to ${this.filePath}`);
@@ -5,6 +5,7 @@ export interface CachedGitignoreResult {
5
5
  gitignoreFiles: string[];
6
6
  perDirIgnores: Map<string, Gitignores>;
7
7
  }
8
+ export declare const clearGitignoreCache: () => void;
8
9
  export declare const initGitignoreCache: (cacheLocation: string) => void;
9
10
  export declare const isGitignoreCacheEnabled: () => boolean;
10
11
  export declare const flushGitignoreCache: () => void;
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { createDiskCache, mtimeMatches } from './disk-cache.js';
4
4
  const store = createDiskCache('gitignore');
5
+ export const clearGitignoreCache = store.clear;
5
6
  export const initGitignoreCache = store.init;
6
7
  export const isGitignoreCacheEnabled = store.isEnabled;
7
8
  export const flushGitignoreCache = store.flush;
@@ -1,6 +1,6 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { readdir, readFileSync } from 'node:fs';
3
- import { escapePath, glob as tinyGlob } from 'tinyglobby';
3
+ import { escapePath, glob as tinyGlob, isDynamicPattern } from 'tinyglobby';
4
4
  import picomatch from 'picomatch';
5
5
  import { GLOBAL_IGNORE_PATTERNS } from '../constants.js';
6
6
  import { compact, partition } from './array.js';
@@ -292,7 +292,9 @@ export async function glob(_patterns, options) {
292
292
  }
293
293
  cachedGlobIgnores.set(options.dir, compact(_ignore));
294
294
  }
295
- const ignorePatterns = (cachedIgnores ?? _ignore).concat(negatedPatterns.map(pattern => pattern.slice(1)));
295
+ const ignores = cachedIgnores ?? _ignore;
296
+ const isStatic = options.gitignore && !patterns.some(pattern => isDynamicPattern(pattern));
297
+ const ignorePatterns = (isStatic ? expandIgnorePatterns(ignores) : ignores).concat(negatedPatterns.map(pattern => pattern.slice(1)));
296
298
  const { dir, label, ...fgOptions } = {
297
299
  ...options,
298
300
  ignore: ignorePatterns,
@@ -8,6 +8,7 @@ interface GlobOptions {
8
8
  }
9
9
  export declare const removeProductionSuffix: (pattern: string) => string;
10
10
  export declare const prependDirToPattern: (dir: string, pattern: string) => string;
11
+ export declare const rebasePattern: (pattern: string, base: string, dir: string) => string;
11
12
  export declare const negate: (pattern: string) => string;
12
13
  export declare const hasProductionSuffix: (pattern: string) => boolean;
13
14
  export declare const hasNoProductionSuffix: (pattern: string) => boolean;
package/dist/util/glob.js CHANGED
@@ -23,6 +23,12 @@ export const prependDirToPattern = (dir, pattern) => {
23
23
  return `!${join(dir, pattern.slice(1))}`;
24
24
  return join(dir, pattern);
25
25
  };
26
+ export const rebasePattern = (pattern, base, dir) => {
27
+ const isNegated = pattern.startsWith('!');
28
+ const id = isNegated ? pattern.slice(1) : pattern;
29
+ const rebased = relative(dir, isAbsolute(id) ? id : join(base, id));
30
+ return isNegated ? `!${rebased}` : rebased;
31
+ };
26
32
  export const negate = (pattern) => pattern.replace(/^!?/, '!');
27
33
  export const hasProductionSuffix = (pattern) => pattern.endsWith('!');
28
34
  export const hasNoProductionSuffix = (pattern) => !pattern.endsWith('!');
@@ -7,6 +7,7 @@ export interface Input {
7
7
  production?: boolean;
8
8
  optional?: boolean;
9
9
  isTypeOnly?: boolean;
10
+ alsoReferenceOwner?: boolean;
10
11
  dir?: string;
11
12
  containingFilePath?: string;
12
13
  allowIncludeExports?: boolean;
@@ -33,6 +34,7 @@ interface IgnoreInput extends Input {
33
34
  type Options = {
34
35
  optional?: boolean;
35
36
  isTypeOnly?: boolean;
37
+ alsoReferenceOwner?: boolean;
36
38
  dir?: string;
37
39
  containingFilePath?: string;
38
40
  allowIncludeExports?: boolean;
@@ -1,3 +1,4 @@
1
1
  declare const load: (filePath: string) => Promise<any>;
2
2
  export declare const _load: typeof load;
3
+ export declare const unload: (filePaths: Iterable<string>) => void;
3
4
  export {};
@@ -6,11 +6,36 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
6
6
  }
7
7
  return path;
8
8
  };
9
+ import { readFileSync } from 'node:fs';
10
+ import { createRequire } from 'node:module';
11
+ import { pathToFileURL } from 'node:url';
9
12
  import { LoaderError } from './errors.js';
10
- import { loadFile, loadJSON, loadJSONC, loadTOML, loadYAML, parseJSONC, parseYAML } from './fs.js';
13
+ import { findFile, loadFile, loadJSON, loadJSONC, loadTOML, loadYAML, parseJSONC, parseYAML } from './fs.js';
11
14
  import { jiti } from './jiti.js';
12
15
  import { timerify } from './Performance.js';
13
- import { extname, isInternal } from './path.js';
16
+ import { dirname, extname, isInternal, toNative } from './path.js';
17
+ const unloaded = new Map();
18
+ const isNativeModule = (filePath) => {
19
+ const ext = extname(filePath);
20
+ if (ext !== '.js')
21
+ return ext === '.mjs';
22
+ for (let dir = dirname(filePath); dir !== dirname(dir); dir = dirname(dir)) {
23
+ const manifestPath = findFile(dir, 'package.json');
24
+ if (manifestPath)
25
+ return JSON.parse(readFileSync(manifestPath, 'utf8')).type === 'module';
26
+ }
27
+ return false;
28
+ };
29
+ const reload = async (filePath, version) => {
30
+ let imported;
31
+ try {
32
+ imported = jiti(filePath);
33
+ }
34
+ catch {
35
+ imported = await import(__rewriteRelativeImportExtension(`${pathToFileURL(filePath).href}?v=${version}`));
36
+ }
37
+ return imported.default ?? imported;
38
+ };
14
39
  const load = async (filePath) => {
15
40
  try {
16
41
  const ext = extname(filePath);
@@ -42,6 +67,9 @@ const load = async (filePath) => {
42
67
  if (ext === '.toml') {
43
68
  return await loadTOML(filePath);
44
69
  }
70
+ const version = unloaded.get(filePath);
71
+ if (version && isNativeModule(filePath))
72
+ return await reload(filePath, version);
45
73
  return await jiti.import(filePath, { default: true });
46
74
  }
47
75
  catch (error) {
@@ -49,3 +77,11 @@ const load = async (filePath) => {
49
77
  }
50
78
  };
51
79
  export const _load = timerify(load);
80
+ const { cache } = createRequire(import.meta.url);
81
+ export const unload = (filePaths) => {
82
+ for (const filePath of filePaths) {
83
+ delete cache[filePath];
84
+ delete cache[toNative(filePath)];
85
+ unloaded.set(filePath, (unloaded.get(filePath) ?? 0) + 1);
86
+ }
87
+ };
@@ -53,7 +53,9 @@ const isPackageNameStart = (ch) => (ch >= 65 && ch <= 90) || (ch >= 97 && ch <=
53
53
  const isPackageScopeStart = (ch) => isPackageNameStart(ch) || ch === 46 || ch === 95 || ch === 126;
54
54
  export const isStartsLikePackageName = (specifier) => {
55
55
  const ch = specifier.charCodeAt(0);
56
- return ch === 64 ? isPackageScopeStart(specifier.charCodeAt(1)) : isPackageNameStart(ch);
56
+ return ch === 64
57
+ ? isPackageScopeStart(specifier.charCodeAt(1))
58
+ : isPackageNameStart(ch) && specifier.charCodeAt(1) !== 58;
57
59
  };
58
60
  const isTildePackageName = (specifier) => specifier.charCodeAt(0) === 126 && isPackageNameStart(specifier.charCodeAt(1));
59
61
  export const stripVersionFromSpecifier = (specifier) => specifier.replace(/(\S+)@.*/, '$1');
@@ -5,6 +5,7 @@ export declare const extname: typeof path.extname;
5
5
  export declare const basename: typeof path.basename;
6
6
  export declare const join: typeof path.join;
7
7
  export declare const toPosix: (value: string) => string;
8
+ export declare const toNative: (value: string) => string;
8
9
  export declare const resolve: typeof path.resolve;
9
10
  export declare const resolveCwd: (dir: string) => string;
10
11
  export declare const relative: (from: string, to: string) => string;
package/dist/util/path.js CHANGED
@@ -6,6 +6,7 @@ export const extname = path.posix.extname;
6
6
  export const basename = path.posix.basename;
7
7
  export const join = path.posix.join;
8
8
  export const toPosix = isWin ? (value) => value.split(path.sep).join(path.posix.sep) : (value) => value;
9
+ export const toNative = isWin ? path.win32.normalize : (value) => value;
9
10
  export const resolve = path.posix.resolve;
10
11
  export const resolveCwd = (dir) => toPosix(path.resolve(dir));
11
12
  export const relative = (from, to) => {
@@ -1,2 +1,2 @@
1
1
  import type { ReporterOptions } from '../types/issues.ts';
2
- export declare const runReporters: (reporter: string[], options: ReporterOptions) => Promise<void>;
2
+ export declare const runReporters: (reporter: string[], outputFiles: string[], options: ReporterOptions) => Promise<void>;
@@ -1,12 +1,56 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { stripVTControlCharacters } from 'node:util';
1
3
  import internalReporters from '../reporters/index.js';
4
+ import { ConfigurationError } from './errors.js';
2
5
  import { _load } from './loader.js';
3
- import { isAbsolute, isInternal, resolve } from './path.js';
4
- export const runReporters = async (reporter, options) => {
5
- const reporters = await Promise.all(reporter.map(async (reporter) => {
6
- return reporter in internalReporters
7
- ? internalReporters[reporter]
8
- : await _load(isInternal(reporter) && !isAbsolute(reporter) ? resolve(reporter) : reporter);
9
- }));
10
- for (const reporter of reporters)
11
- await reporter(options);
6
+ import { dirname, isAbsolute, isInternal, resolve, toAbsolute, toPosix } from './path.js';
7
+ const writeStdout = (output) => {
8
+ if (!output)
9
+ return;
10
+ process.stdout._handle?.setBlocking?.(true);
11
+ process.stdout.write(`${output}\n`);
12
+ };
13
+ const getOutputFiles = (reporters, outputFiles, cwd) => {
14
+ const targets = new Map();
15
+ for (const outputFile of outputFiles) {
16
+ const index = outputFile.indexOf('=');
17
+ const name = index === -1 ? reporters[0] : outputFile.slice(0, index);
18
+ const filePath = index === -1 ? outputFile : outputFile.slice(index + 1);
19
+ if (index === -1 && reporters.length > 1) {
20
+ throw new ConfigurationError(`Option --output-file needs a reporter prefix when using multiple reporters (${reporters.join(', ')}), e.g. --output-file ${reporters[0]}=${outputFile}`);
21
+ }
22
+ if (!reporters.includes(name)) {
23
+ throw new ConfigurationError(`Option --output-file refers to reporter "${name}", but it is not selected with --reporter`);
24
+ }
25
+ targets.set(name, toAbsolute(toPosix(filePath), cwd));
26
+ }
27
+ return targets;
28
+ };
29
+ const loadReporter = (reporter) => reporter in internalReporters
30
+ ? Promise.resolve(internalReporters[reporter])
31
+ : _load(isInternal(reporter) && !isAbsolute(reporter) ? resolve(reporter) : reporter);
32
+ export const runReporters = async (reporter, outputFiles, options) => {
33
+ const targets = getOutputFiles(reporter, outputFiles, options.cwd);
34
+ const reporters = await Promise.all(reporter.map(loadReporter));
35
+ for (let i = 0; i < reporters.length; i++) {
36
+ const target = targets.get(reporter[i]);
37
+ if (!target) {
38
+ await reporters[i]({ ...options, write: writeStdout });
39
+ continue;
40
+ }
41
+ let output = '';
42
+ const write = (text) => {
43
+ if (text)
44
+ output += `${text}\n`;
45
+ };
46
+ await reporters[i]({ ...options, write });
47
+ try {
48
+ await mkdir(dirname(target), { recursive: true });
49
+ await writeFile(target, stripVTControlCharacters(output));
50
+ }
51
+ catch (error) {
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ throw new ConfigurationError(`Option --output-file failed to write: ${message}`);
54
+ }
55
+ }
12
56
  };
@@ -1,15 +1,9 @@
1
- import type { WatchListener } from 'node:fs';
2
1
  import type { ConfigurationChief } from '../ConfigurationChief.ts';
3
2
  import type { IssueCollector } from '../IssueCollector.ts';
4
3
  import type { ProjectPrincipal } from '../ProjectPrincipal.ts';
5
- import type { Issues } from '../types/issues.ts';
4
+ import type { ConfigurationFiles } from '../session/configuration.ts';
6
5
  import type { ModuleGraph } from '../types/module-graph.ts';
7
6
  import type { MainOptions } from './create-options.ts';
8
- export type OnFileChange = (options: {
9
- issues: Issues;
10
- duration?: number;
11
- mem?: number;
12
- }) => void;
13
7
  export type WatchChange = {
14
8
  type: 'added' | 'deleted' | 'modified';
15
9
  filePath: string;
@@ -23,13 +17,12 @@ type WatchOptions = {
23
17
  analyze: () => Promise<void>;
24
18
  principal: ProjectPrincipal;
25
19
  graph: ModuleGraph;
20
+ configurationFiles?: ConfigurationFiles;
26
21
  isIgnored: (path: string) => boolean;
27
- onFileChange?: OnFileChange;
28
22
  unreferencedFiles: Set<string>;
29
23
  entryPaths: Set<string>;
30
24
  };
31
- export declare const getSessionHandler: (options: MainOptions, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, principal, graph, isIgnored, onFileChange, unreferencedFiles, entryPaths, }: WatchOptions) => Promise<{
32
- listener: WatchListener<string | Buffer<ArrayBufferLike>>;
25
+ export declare const getSessionHandler: (options: MainOptions, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, principal, graph, configurationFiles, isIgnored, unreferencedFiles, entryPaths, }: WatchOptions) => Promise<{
33
26
  handleFileChanges: (changes: WatchChange[]) => Promise<{
34
27
  duration: number;
35
28
  mem: number;
@@ -37,11 +30,12 @@ export declare const getSessionHandler: (options: MainOptions, { analyzedFiles,
37
30
  getEntryPaths: () => Set<string>;
38
31
  getGraph: () => ModuleGraph;
39
32
  getIssues: () => {
40
- issues: Issues;
33
+ issues: import("../types/issues.ts").Issues;
41
34
  counters: import("../types/issues.ts").Counters;
42
35
  tagHints: Set<import("../types/issues.ts").TagHint>;
43
36
  configurationHints: import("../types/issues.ts").ConfigurationHint[];
44
37
  hasConfigLoadErrors: boolean;
45
38
  };
39
+ configurationFiles: ConfigurationFiles | undefined;
46
40
  }>;
47
41
  export {};
@@ -1,10 +1,8 @@
1
1
  import { invalidateCache } from '../graph-explorer/cache.js';
2
+ import { getFileTopology, hasSameTopology, isIncrementalFile } from '../session/incremental.js';
2
3
  import { debugLog } from './debug.js';
3
- import { hasErrorCause } from './errors.js';
4
- import { isFile } from './fs.js';
5
- import { logError } from './log.js';
6
4
  import { updateImportMap } from './module-graph.js';
7
- import { toAbsolute, toPosix, toRelative } from './path.js';
5
+ import { toAbsolute, toRelative } from './path.js';
8
6
  import { clearModuleResolutionCaches } from '../typescript/resolve-module-names.js';
9
7
  import { clearGlobCache } from './glob-cache.js';
10
8
  import { clearResolverCache } from './resolve.js';
@@ -13,13 +11,18 @@ const createUpdate = (options) => {
13
11
  const mem = process.memoryUsage().heapUsed;
14
12
  return { duration, mem };
15
13
  };
16
- export const getSessionHandler = async (options, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, principal, graph, isIgnored, onFileChange, unreferencedFiles, entryPaths, }) => {
14
+ export const getSessionHandler = async (options, { analyzedFiles, analyzeSourceFile, chief, collector, analyze, principal, graph, configurationFiles, isIgnored, unreferencedFiles, entryPaths, }) => {
17
15
  const added = new Set();
18
16
  const deleted = new Set();
19
17
  const modified = new Set();
18
+ if (configurationFiles)
19
+ principal.collectConfigurationImports(configurationFiles.paths);
20
20
  const processFileChanges = async (changes) => {
21
21
  const startTime = performance.now();
22
22
  let hasRelevantChanges = false;
23
+ const configurationChange = configurationFiles?.getChange(changes, options.cwd);
24
+ if (configurationChange)
25
+ throw configurationChange;
23
26
  for (const change of changes) {
24
27
  const filePath = toAbsolute(change.filePath, options.cwd);
25
28
  const relativePath = toRelative(change.filePath, options.cwd);
@@ -60,76 +63,104 @@ export const getSessionHandler = async (options, { analyzedFiles, analyzeSourceF
60
63
  }
61
64
  if (!hasRelevantChanges || (added.size === 0 && deleted.size === 0 && modified.size === 0))
62
65
  return;
63
- clearResolverCache();
64
- clearModuleResolutionCaches();
65
- clearGlobCache();
66
- invalidateCache(graph);
67
- const filePaths = await principal.getUsedResolvedFiles();
68
- unreferencedFiles.clear();
69
- const cachedUnusedFiles = collector.purge();
70
- for (const filePath of added)
71
- cachedUnusedFiles.add(filePath);
72
- for (const filePath of deleted)
73
- cachedUnusedFiles.delete(filePath);
74
- if (added.size > 0 || deleted.size > 0) {
75
- graph.clear();
76
- for (const filePath of filePaths) {
77
- const workspace = chief.findWorkspaceByFilePath(filePath);
78
- if (workspace) {
79
- await analyzeSourceFile(filePath);
66
+ let incremental = added.size === 0 && deleted.size === 0;
67
+ if (incremental) {
68
+ for (const filePath of modified) {
69
+ const file = graph.get(filePath);
70
+ if (!file || !analyzedFiles.has(filePath) || !isIncrementalFile(file)) {
71
+ incremental = false;
72
+ break;
80
73
  }
81
74
  }
82
75
  }
83
- else {
84
- for (const [filePath, file] of graph) {
85
- if (filePaths.has(filePath)) {
86
- file.importedBy = undefined;
76
+ if (incremental) {
77
+ for (const filePath of modified) {
78
+ const file = graph.get(filePath);
79
+ if (!file) {
80
+ incremental = false;
81
+ break;
87
82
  }
88
- else {
89
- graph.delete(filePath);
90
- analyzedFiles.delete(filePath);
91
- const workspace = chief.findWorkspaceByFilePath(filePath);
92
- if (workspace && principal.projectPaths.has(filePath))
93
- cachedUnusedFiles.add(filePath);
83
+ const before = getFileTopology(file);
84
+ await analyzeSourceFile(filePath);
85
+ if (!hasSameTopology(before, file)) {
86
+ incremental = false;
87
+ break;
94
88
  }
95
89
  }
96
- for (const filePath of filePaths) {
97
- if (!graph.has(filePath)) {
90
+ }
91
+ if (incremental) {
92
+ invalidateCache(graph, true);
93
+ collector.purge();
94
+ }
95
+ else {
96
+ clearResolverCache();
97
+ clearModuleResolutionCaches();
98
+ clearGlobCache();
99
+ invalidateCache(graph);
100
+ const filePaths = await principal.getUsedResolvedFiles();
101
+ const cachedUnusedFiles = collector.purge();
102
+ for (const filePath of added)
103
+ cachedUnusedFiles.add(filePath);
104
+ for (const filePath of deleted)
105
+ cachedUnusedFiles.delete(filePath);
106
+ if (added.size > 0 || deleted.size > 0) {
107
+ graph.clear();
108
+ for (const filePath of filePaths) {
98
109
  const workspace = chief.findWorkspaceByFilePath(filePath);
99
110
  if (workspace) {
100
111
  await analyzeSourceFile(filePath);
101
112
  }
102
113
  }
103
114
  }
104
- for (const filePath of modified) {
105
- if (!cachedUnusedFiles.has(filePath)) {
106
- const workspace = chief.findWorkspaceByFilePath(filePath);
107
- if (workspace) {
108
- if (principal.projectPaths.has(filePath) || graph.has(filePath)) {
115
+ else {
116
+ for (const [filePath, file] of graph) {
117
+ if (filePaths.has(filePath)) {
118
+ file.importedBy = undefined;
119
+ }
120
+ else {
121
+ graph.delete(filePath);
122
+ analyzedFiles.delete(filePath);
123
+ const workspace = chief.findWorkspaceByFilePath(filePath);
124
+ if (workspace && principal.projectPaths.has(filePath))
125
+ cachedUnusedFiles.add(filePath);
126
+ }
127
+ }
128
+ for (const filePath of filePaths) {
129
+ if (!graph.has(filePath) || !analyzedFiles.has(filePath)) {
130
+ const workspace = chief.findWorkspaceByFilePath(filePath);
131
+ if (workspace) {
109
132
  await analyzeSourceFile(filePath);
110
133
  }
111
134
  }
112
135
  }
136
+ for (const filePath of modified) {
137
+ if (!cachedUnusedFiles.has(filePath)) {
138
+ const workspace = chief.findWorkspaceByFilePath(filePath);
139
+ if (workspace) {
140
+ if (principal.projectPaths.has(filePath) || graph.has(filePath)) {
141
+ await analyzeSourceFile(filePath);
142
+ }
143
+ }
144
+ }
145
+ }
146
+ for (const filePath of filePaths) {
147
+ const file = graph.get(filePath);
148
+ if (file?.internalImportCache)
149
+ updateImportMap(file, file.internalImportCache, graph);
150
+ }
113
151
  }
114
- for (const filePath of filePaths) {
115
- const file = graph.get(filePath);
116
- if (file?.internalImportCache)
117
- updateImportMap(file, file.internalImportCache, graph);
118
- }
152
+ const unusedFiles = [...cachedUnusedFiles].filter(filePath => !analyzedFiles.has(filePath));
153
+ unreferencedFiles.clear();
154
+ for (const filePath of unusedFiles)
155
+ unreferencedFiles.add(filePath);
119
156
  }
120
- await analyze();
121
- const unusedFiles = [...cachedUnusedFiles].filter(filePath => !analyzedFiles.has(filePath));
122
- collector.addFilesIssues(unusedFiles);
123
- collector.addFileCounts({ processed: analyzedFiles.size, unused: unusedFiles.length });
124
157
  for (const issue of collector.getRetainedIssues())
125
158
  collector.addIssue(issue);
159
+ await analyze();
126
160
  added.clear();
127
161
  deleted.clear();
128
162
  modified.clear();
129
- const update = createUpdate({ startTime });
130
- if (onFileChange)
131
- onFileChange(Object.assign({ issues: getIssues().issues }, update));
132
- return update;
163
+ return createUpdate({ startTime });
133
164
  };
134
165
  const clearSourceText = () => principal.fileManager.sourceTextCache.clear();
135
166
  let pending = Promise.resolve();
@@ -138,23 +169,8 @@ export const getSessionHandler = async (options, { analyzedFiles, analyzeSourceF
138
169
  pending = update.then(clearSourceText, clearSourceText);
139
170
  return update;
140
171
  };
141
- const listener = (eventType, filePath) => {
142
- debugLog('*', `(raw) ${eventType} ${filePath}`);
143
- if (typeof filePath === 'string') {
144
- const normalizedPath = toPosix(filePath);
145
- const type = eventType === 'rename' ? (isFile(options.cwd, normalizedPath) ? 'added' : 'deleted') : 'modified';
146
- handleFileChanges([{ type, filePath: normalizedPath }]).catch(error => {
147
- const message = error instanceof Error ? error.message : String(error);
148
- const reason = error instanceof Error && hasErrorCause(error) ? `\nReason: ${error.cause.message}` : '';
149
- logError(message + reason);
150
- process.exitCode = 2;
151
- });
152
- }
153
- };
154
172
  const getIssues = () => collector.getIssues();
155
173
  const getEntryPaths = () => entryPaths;
156
174
  const getGraph = () => graph;
157
- if (onFileChange)
158
- onFileChange({ issues: getIssues().issues });
159
- return { listener, handleFileChanges, getEntryPaths, getGraph, getIssues };
175
+ return { handleFileChanges, getEntryPaths, getGraph, getIssues, configurationFiles };
160
176
  };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.43.0";
1
+ export declare const version = "6.44.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.43.0';
1
+ export const version = '6.44.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.43.0",
3
+ "version": "6.44.0",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",
package/schema.json CHANGED
@@ -638,6 +638,10 @@
638
638
  "title": "knex plugin configuration (https://knip.dev/reference/plugins/knex)",
639
639
  "$ref": "#/definitions/plugin"
640
640
  },
641
+ "kysely-codegen": {
642
+ "title": "kysely-codegen plugin configuration (https://knip.dev/reference/plugins/kysely-codegen)",
643
+ "$ref": "#/definitions/plugin"
644
+ },
641
645
  "ladle": {
642
646
  "title": "ladle plugin configuration (https://knip.dev/reference/plugins/ladle)",
643
647
  "$ref": "#/definitions/plugin"
@@ -650,6 +654,10 @@
650
654
  "title": "lefthook plugin configuration (https://knip.dev/reference/plugins/lefthook)",
651
655
  "$ref": "#/definitions/plugin"
652
656
  },
657
+ "lingui": {
658
+ "title": "lingui plugin configuration (https://knip.dev/reference/plugins/lingui)",
659
+ "$ref": "#/definitions/plugin"
660
+ },
653
661
  "lint-staged": {
654
662
  "title": "lint-staged plugin configuration (https://knip.dev/reference/plugins/lint-staged)",
655
663
  "$ref": "#/definitions/plugin"
@@ -682,6 +690,10 @@
682
690
  "title": "marko plugin configuration (https://knip.dev/reference/plugins/marko)",
683
691
  "$ref": "#/definitions/plugin"
684
692
  },
693
+ "mastra": {
694
+ "title": "mastra plugin configuration (https://knip.dev/reference/plugins/mastra)",
695
+ "$ref": "#/definitions/plugin"
696
+ },
685
697
  "mcp": {
686
698
  "title": "mcp plugin configuration (https://knip.dev/reference/plugins/mcp)",
687
699
  "$ref": "#/definitions/plugin"
@@ -862,6 +874,10 @@
862
874
  "title": "Prisma plugin configuration (https://knip.dev/reference/plugins/prisma)",
863
875
  "$ref": "#/definitions/plugin"
864
876
  },
877
+ "purgecss": {
878
+ "title": "purgecss plugin configuration (https://knip.dev/reference/plugins/purgecss)",
879
+ "$ref": "#/definitions/plugin"
880
+ },
865
881
  "quasar": {
866
882
  "title": "quasar plugin configuration (https://knip.dev/reference/plugins/quasar)",
867
883
  "$ref": "#/definitions/plugin"