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
@@ -22,51 +22,39 @@ export class FileEntryCache {
22
22
  this.filePath = path.resolve(_path, cacheId);
23
23
  if (isFile(this.filePath))
24
24
  this.cache = create(this.filePath);
25
- this.removeNotFoundFiles();
26
- }
27
- removeNotFoundFiles() {
28
- for (const filePath of this.normalizedEntries.keys()) {
29
- try {
30
- fs.statSync(filePath);
31
- }
32
- catch (error) {
33
- if (error.code === 'ENOENT')
34
- this.cache.delete(filePath);
35
- }
36
- }
37
25
  }
38
26
  getFileDescriptor(filePath) {
27
+ if (!isAbsolute(filePath))
28
+ filePath = resolve(filePath);
29
+ const existing = this.normalizedEntries.get(filePath);
30
+ if (existing)
31
+ return existing;
39
32
  let fstat;
40
33
  try {
41
- if (!isAbsolute(filePath))
42
- filePath = resolve(filePath);
43
34
  fstat = fs.statSync(filePath);
44
35
  }
45
36
  catch (error) {
46
37
  this.removeEntry(filePath);
47
38
  return { key: filePath, notFound: true, err: error };
48
39
  }
49
- return this._getFileDescriptorUsingMtimeAndSize(filePath, fstat);
50
- }
51
- _getFileDescriptorUsingMtimeAndSize(filePath, fstat) {
52
40
  let meta = this.cache.get(filePath);
53
- const cacheExists = Boolean(meta);
54
41
  const cSize = fstat.size;
55
42
  const cTime = fstat.mtime.getTime();
56
- let isDifferentDate;
57
- let isDifferentSize;
43
+ let changed = false;
58
44
  if (meta) {
59
- isDifferentDate = cTime !== meta.mtime;
60
- isDifferentSize = cSize !== meta.size;
45
+ if (cTime !== meta.mtime || cSize !== meta.size) {
46
+ changed = true;
47
+ meta.data = undefined;
48
+ }
49
+ meta.mtime = cTime;
50
+ meta.size = cSize;
61
51
  }
62
52
  else {
53
+ changed = true;
63
54
  meta = { size: cSize, mtime: cTime };
55
+ this.cache.set(filePath, meta);
64
56
  }
65
- const fd = {
66
- key: filePath,
67
- changed: !cacheExists || isDifferentDate || isDifferentSize,
68
- meta,
69
- };
57
+ const fd = { key: filePath, changed, meta };
70
58
  this.normalizedEntries.set(filePath, fd);
71
59
  return fd;
72
60
  }
@@ -76,33 +64,7 @@ export class FileEntryCache {
76
64
  this.normalizedEntries.delete(entryName);
77
65
  this.cache.delete(entryName);
78
66
  }
79
- _getMetaForFileUsingMtimeAndSize(cacheEntry) {
80
- const stat = fs.statSync(cacheEntry.key);
81
- const meta = Object.assign(cacheEntry.meta ?? {}, {
82
- size: stat.size,
83
- mtime: stat.mtime.getTime(),
84
- });
85
- return meta;
86
- }
87
67
  reconcile() {
88
- for (const [entryName, cacheEntry] of this.normalizedEntries) {
89
- try {
90
- const stat = fs.statSync(entryName);
91
- const meta = Object.assign(cacheEntry.meta ?? {}, {
92
- size: stat.size,
93
- mtime: stat.mtime.getTime(),
94
- });
95
- this.cache.set(entryName, meta);
96
- }
97
- catch (error) {
98
- if (error.code === 'ENOENT') {
99
- this.normalizedEntries.delete(entryName);
100
- this.cache.delete(entryName);
101
- }
102
- else
103
- throw error;
104
- }
105
- }
106
68
  try {
107
69
  const dir = dirname(this.filePath);
108
70
  if (!isDirectory(dir))
@@ -0,0 +1,12 @@
1
+ import type { Gitignores } from './glob-core.ts';
2
+ export interface CachedGitignoreResult {
3
+ ignores: Set<string>;
4
+ unignores: Set<string>;
5
+ gitignoreFiles: string[];
6
+ perDirIgnores: Map<string, Gitignores>;
7
+ }
8
+ export declare const initGitignoreCache: (cacheLocation: string) => void;
9
+ export declare const isGitignoreCacheEnabled: () => boolean;
10
+ export declare const flushGitignoreCache: () => void;
11
+ export declare const getCachedGitignore: (cwd: string, workspaceDirs?: Set<string>) => CachedGitignoreResult | undefined;
12
+ export declare const setCachedGitignore: (cwd: string, workspaceDirs: Set<string> | undefined, gitignoreFiles: string[], ignores: Set<string>, unignores: Set<string>, perDirIgnores: Map<string, Gitignores>) => void;
@@ -0,0 +1,77 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createDiskCache } from './disk-cache.js';
4
+ const store = createDiskCache('gitignore');
5
+ export const initGitignoreCache = store.init;
6
+ export const isGitignoreCacheEnabled = store.isEnabled;
7
+ export const flushGitignoreCache = store.flush;
8
+ const workspaceDirsKey = (workspaceDirs) => {
9
+ if (!workspaceDirs || workspaceDirs.size === 0)
10
+ return '';
11
+ return [...workspaceDirs].sort().join('\0');
12
+ };
13
+ const validateEntry = (entry, wsKey, cwd) => {
14
+ if (entry.workspaceDirsKey !== wsKey)
15
+ return false;
16
+ const files = entry.gitignoreFiles;
17
+ const mtimes = entry.mtimes;
18
+ for (let i = 0; i < files.length; i++) {
19
+ const abs = path.isAbsolute(files[i]) ? files[i] : path.resolve(cwd, files[i]);
20
+ try {
21
+ if (fs.statSync(abs).mtimeMs !== mtimes[i])
22
+ return false;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ return true;
29
+ };
30
+ export const getCachedGitignore = (cwd, workspaceDirs) => {
31
+ const entry = store.get(cwd);
32
+ if (!entry)
33
+ return undefined;
34
+ const wsKey = workspaceDirsKey(workspaceDirs);
35
+ if (!validateEntry(entry, wsKey, cwd)) {
36
+ store.delete(cwd);
37
+ return undefined;
38
+ }
39
+ const perDirIgnores = new Map();
40
+ for (const dir in entry.perDirIgnores) {
41
+ const data = entry.perDirIgnores[dir];
42
+ perDirIgnores.set(dir, { ignores: new Set(data.ignores), unignores: new Set(data.unignores) });
43
+ }
44
+ return {
45
+ ignores: new Set(entry.ignores),
46
+ unignores: new Set(entry.unignores),
47
+ gitignoreFiles: entry.gitignoreFiles,
48
+ perDirIgnores,
49
+ };
50
+ };
51
+ export const setCachedGitignore = (cwd, workspaceDirs, gitignoreFiles, ignores, unignores, perDirIgnores) => {
52
+ if (!store.isEnabled())
53
+ return;
54
+ const mtimes = [];
55
+ const validFiles = [];
56
+ for (const file of gitignoreFiles) {
57
+ const abs = path.isAbsolute(file) ? file : path.resolve(cwd, file);
58
+ try {
59
+ mtimes.push(fs.statSync(abs).mtimeMs);
60
+ validFiles.push(file);
61
+ }
62
+ catch {
63
+ }
64
+ }
65
+ const perDir = {};
66
+ for (const [dir, data] of perDirIgnores) {
67
+ perDir[dir] = { ignores: [...data.ignores], unignores: [...data.unignores] };
68
+ }
69
+ store.set(cwd, {
70
+ gitignoreFiles: validFiles,
71
+ mtimes,
72
+ ignores: [...ignores],
73
+ unignores: [...unignores],
74
+ perDirIgnores: perDir,
75
+ workspaceDirsKey: workspaceDirsKey(workspaceDirs),
76
+ });
77
+ };
@@ -1,5 +1,7 @@
1
1
  export declare const initGlobCache: (cacheLocation: string) => void;
2
2
  export declare const isGlobCacheEnabled: () => boolean;
3
+ export declare const flushGlobCache: () => void;
4
+ export declare const clearGlobCache: () => void;
3
5
  export declare const computeGlobCacheKey: (input: {
4
6
  patterns: string[];
5
7
  cwd: string;
@@ -8,5 +10,3 @@ export declare const computeGlobCacheKey: (input: {
8
10
  }) => string;
9
11
  export declare const getCachedGlob: (key: string) => string[] | undefined;
10
12
  export declare const setCachedGlob: (key: string, paths: string[], baseDir: string) => void;
11
- export declare const clearGlobCache: () => void;
12
- export declare const flushGlobCache: () => void;
@@ -1,31 +1,12 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { deserialize, serialize } from 'node:v8';
5
- import { version } from '../version.js';
6
- import { debugLog } from './debug.js';
7
- import { isDirectory, isFile } from './fs.js';
3
+ import { createDiskCache } from './disk-cache.js';
8
4
  import { dirname } from './path.js';
9
- const CACHE_FILENAME = `glob-${version}.cache`;
10
- let cacheFilePath;
11
- let cache;
12
- let isDirty = false;
13
- export const initGlobCache = (cacheLocation) => {
14
- cacheFilePath = path.resolve(cacheLocation, CACHE_FILENAME);
15
- if (isFile(cacheFilePath)) {
16
- try {
17
- cache = deserialize(fs.readFileSync(cacheFilePath));
18
- }
19
- catch {
20
- debugLog('*', `Error reading glob cache from ${cacheFilePath}`);
21
- cache = new Map();
22
- }
23
- }
24
- else {
25
- cache = new Map();
26
- }
27
- };
28
- export const isGlobCacheEnabled = () => cache !== undefined;
5
+ const store = createDiskCache('glob');
6
+ export const initGlobCache = store.init;
7
+ export const isGlobCacheEnabled = store.isEnabled;
8
+ export const flushGlobCache = store.flush;
9
+ export const clearGlobCache = store.clear;
29
10
  export const computeGlobCacheKey = (input) => {
30
11
  const h = createHash('sha1');
31
12
  h.update(input.cwd);
@@ -54,14 +35,11 @@ const validateEntry = (entry) => {
54
35
  return true;
55
36
  };
56
37
  export const getCachedGlob = (key) => {
57
- if (!cache)
58
- return undefined;
59
- const entry = cache.get(key);
38
+ const entry = store.get(key);
60
39
  if (!entry)
61
40
  return undefined;
62
41
  if (!validateEntry(entry)) {
63
- cache.delete(key);
64
- isDirty = true;
42
+ store.delete(key);
65
43
  return undefined;
66
44
  }
67
45
  return entry.paths;
@@ -94,31 +72,5 @@ const captureDirMtimes = (paths, baseDir) => {
94
72
  return result;
95
73
  };
96
74
  export const setCachedGlob = (key, paths, baseDir) => {
97
- if (!cache)
98
- return;
99
- cache.set(key, {
100
- paths,
101
- dirMtimes: captureDirMtimes(paths, baseDir),
102
- });
103
- isDirty = true;
104
- };
105
- export const clearGlobCache = () => {
106
- if (cache) {
107
- cache.clear();
108
- isDirty = true;
109
- }
110
- };
111
- export const flushGlobCache = () => {
112
- if (!cache || !cacheFilePath || !isDirty)
113
- return;
114
- try {
115
- const dir = dirname(cacheFilePath);
116
- if (!isDirectory(dir))
117
- fs.mkdirSync(dir, { recursive: true });
118
- fs.writeFileSync(cacheFilePath, serialize(cache));
119
- isDirty = false;
120
- }
121
- catch {
122
- debugLog('*', `Error writing glob cache to ${cacheFilePath}`);
123
- }
75
+ store.set(key, { paths, dirMtimes: captureDirMtimes(paths, baseDir) });
124
76
  };
@@ -9,6 +9,10 @@ interface GlobOptions extends TinyGlobOptions {
9
9
  dir: string;
10
10
  label?: string;
11
11
  }
12
+ export type Gitignores = {
13
+ ignores: Set<string>;
14
+ unignores: Set<string>;
15
+ };
12
16
  export declare const findAndParseGitignores: (cwd: string, workspaceDirs?: Set<string>) => Promise<{
13
17
  gitignoreFiles: string[];
14
18
  ignores: Set<string>;
@@ -7,6 +7,7 @@ import { GLOBAL_IGNORE_PATTERNS } from '../constants.js';
7
7
  import { compact, partition } from './array.js';
8
8
  import { debugLogObject } from './debug.js';
9
9
  import { isDirectory, isFile } from './fs.js';
10
+ import { getCachedGitignore, isGitignoreCacheEnabled, setCachedGitignore } from './gitignore-cache.js';
10
11
  import { timerify } from './Performance.js';
11
12
  import { expandIgnorePatterns, parseAndConvertGitignorePatterns } from './parse-and-convert-gitignores.js';
12
13
  import { dirname, join, relative, toPosix } from './path.js';
@@ -44,6 +45,15 @@ const findAncestorGitignoreFiles = (cwd) => {
44
45
  return gitignorePaths;
45
46
  };
46
47
  export const findAndParseGitignores = async (cwd, workspaceDirs) => {
48
+ if (isGitignoreCacheEnabled()) {
49
+ const cached = getCachedGitignore(cwd, workspaceDirs);
50
+ if (cached) {
51
+ for (const [dir, data] of cached.perDirIgnores)
52
+ cachedGitIgnores.set(dir, data);
53
+ debugLogObject('*', 'Parsed gitignore files (cached)', { gitignoreFiles: cached.gitignoreFiles });
54
+ return { gitignoreFiles: cached.gitignoreFiles, ignores: cached.ignores, unignores: cached.unignores };
55
+ }
56
+ }
47
57
  const ignores = new Set(GLOBAL_IGNORE_PATTERNS);
48
58
  const unignores = new Set();
49
59
  const gitignoreFiles = [];
@@ -171,7 +181,7 @@ export const findAndParseGitignores = async (cwd, workspaceDirs) => {
171
181
  .crawl(cwd)
172
182
  .withPromise();
173
183
  };
174
- await timerify(walkGitignores)();
184
+ await walkGitignores();
175
185
  if (unignores.size > 0) {
176
186
  const unignorePaths = new Set();
177
187
  for (const u of unignores) {
@@ -197,6 +207,9 @@ export const findAndParseGitignores = async (cwd, workspaceDirs) => {
197
207
  }
198
208
  }
199
209
  debugLogObject('*', 'Parsed gitignore files', { gitignoreFiles });
210
+ if (isGitignoreCacheEnabled()) {
211
+ setCachedGitignore(cwd, workspaceDirs, gitignoreFiles, ignores, unignores, cachedGitIgnores);
212
+ }
200
213
  return { gitignoreFiles, ignores, unignores };
201
214
  };
202
215
  const _parseFindGitignores = timerify(findAndParseGitignores);
package/dist/util/glob.js CHANGED
@@ -42,12 +42,18 @@ const defaultGlob = async ({ cwd, dir = cwd, patterns, gitignore = true, label }
42
42
  return paths;
43
43
  };
44
44
  const syncGlob = ({ cwd, patterns }) => {
45
- return globSync(patterns, {
46
- cwd,
47
- absolute: true,
48
- followSymbolicLinks: false,
49
- expandDirectories: false,
50
- });
45
+ const cacheEnabled = isGlobCacheEnabled();
46
+ const patternList = Array.isArray(patterns) ? patterns : [patterns];
47
+ const cacheKey = cacheEnabled ? computeGlobCacheKey({ patterns: patternList, cwd, dir: cwd, gitignore: false }) : '';
48
+ if (cacheEnabled) {
49
+ const cached = getCachedGlob(cacheKey);
50
+ if (cached)
51
+ return cached;
52
+ }
53
+ const paths = globSync(patterns, { cwd, absolute: true, followSymbolicLinks: false, expandDirectories: false });
54
+ if (cacheEnabled && paths.length > 0)
55
+ setCachedGlob(cacheKey, paths, cwd);
56
+ return paths;
51
57
  };
52
58
  const dirGlob = async ({ cwd, patterns, gitignore = true }) => glob(patterns, {
53
59
  cwd,
@@ -61,33 +61,37 @@ export const createImports = () => ({
61
61
  reExportNs: new Map(),
62
62
  });
63
63
  export const addValue = (map, id, value) => {
64
- if (map.has(id))
65
- map.get(id)?.add(value);
64
+ const existing = map.get(id);
65
+ if (existing)
66
+ existing.add(value);
66
67
  else
67
68
  map.set(id, new Set([value]));
68
69
  };
69
70
  export const addNsValue = (map, id, ns, value) => {
70
- if (map.has(id)) {
71
- if (map.get(id)?.has(ns))
72
- map.get(id)?.get(ns)?.add(value);
73
- else
74
- map.get(id)?.set(ns, new Set([value]));
75
- }
76
- else {
71
+ const inner = map.get(id);
72
+ if (!inner) {
77
73
  map.set(id, new Map([[ns, new Set([value])]]));
74
+ return;
78
75
  }
76
+ const set = inner.get(ns);
77
+ if (set)
78
+ set.add(value);
79
+ else
80
+ inner.set(ns, new Set([value]));
79
81
  };
80
82
  const addValues = (map, id, values) => {
81
- if (map.has(id))
83
+ const existing = map.get(id);
84
+ if (existing)
82
85
  for (const v of values)
83
- map.get(id)?.add(v);
86
+ existing.add(v);
84
87
  else
85
88
  map.set(id, values);
86
89
  };
87
90
  const addNsValues = (map, id, value) => {
88
- if (map.has(id))
91
+ const existing = map.get(id);
92
+ if (existing)
89
93
  for (const [ns, v] of value)
90
- addValues(map.get(id), ns, v);
94
+ addValues(existing, ns, v);
91
95
  else
92
96
  map.set(id, value);
93
97
  };
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "6.13.0";
1
+ export declare const version = "6.14.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '6.13.0';
1
+ export const version = '6.14.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "knip",
3
- "version": "6.13.0",
3
+ "version": "6.14.0",
4
4
  "description": "Find and fix unused dependencies, exports and files in your TypeScript and JavaScript projects",
5
5
  "keywords": [
6
6
  "analysis",