knip 6.13.1 → 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.
- package/dist/CacheConsultant.d.ts +3 -3
- package/dist/CacheConsultant.js +13 -16
- package/dist/ConfigurationChief.d.ts +1 -0
- package/dist/ConfigurationChief.js +11 -2
- package/dist/DependencyDeputy.d.ts +3 -0
- package/dist/DependencyDeputy.js +28 -8
- package/dist/ProjectPrincipal.d.ts +1 -0
- package/dist/ProjectPrincipal.js +43 -52
- package/dist/WorkspaceWorker.js +3 -3
- package/dist/cli.js +1 -1
- package/dist/graph/build.js +1 -0
- package/dist/plugins/nuxt/helpers.js +3 -3
- package/dist/plugins/nx/index.js +1 -3
- package/dist/plugins/sst/resolveFromAST.js +2 -2
- package/dist/run.js +7 -2
- package/dist/typescript/ast-helpers.js +2 -2
- package/dist/typescript/ast-nodes.d.ts +1 -1
- package/dist/typescript/ast-nodes.js +3 -1
- package/dist/typescript/get-imports-and-exports.js +4 -4
- package/dist/typescript/visitors/walk.d.ts +2 -1
- package/dist/typescript/visitors/walk.js +3 -1
- package/dist/util/Performance.d.ts +3 -1
- package/dist/util/Performance.js +6 -2
- package/dist/util/cli-arguments.d.ts +2 -1
- package/dist/util/cli-arguments.js +52 -50
- package/dist/util/disk-cache.d.ts +10 -0
- package/dist/util/disk-cache.js +62 -0
- package/dist/util/file-entry-cache.d.ts +0 -7
- package/dist/util/file-entry-cache.js +15 -53
- package/dist/util/gitignore-cache.d.ts +12 -0
- package/dist/util/gitignore-cache.js +77 -0
- package/dist/util/glob-cache.d.ts +2 -2
- package/dist/util/glob-cache.js +9 -57
- package/dist/util/glob-core.d.ts +4 -0
- package/dist/util/glob-core.js +14 -1
- package/dist/util/glob.js +12 -6
- package/dist/util/module-graph.js +17 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/util/Performance.js
CHANGED
|
@@ -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
|
|
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
|
|
8
|
-
-V, --version
|
|
9
|
-
-n, --no-progress
|
|
10
|
-
-c, --config [file]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-t, --tsConfig [file]
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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]
|
|
26
|
-
--directory [dir] Run process from a different directory (default: cwd)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
--
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
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 {};
|
|
@@ -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
|
|
57
|
-
let isDifferentSize;
|
|
43
|
+
let changed = false;
|
|
58
44
|
if (meta) {
|
|
59
|
-
|
|
60
|
-
|
|
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;
|
package/dist/util/glob-cache.js
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
-
import
|
|
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
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|
package/dist/util/glob-core.d.ts
CHANGED
|
@@ -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>;
|