deslop-js 0.6.2 → 0.7.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/analyzed-inputs.cjs +5 -0
- package/dist/analyzed-inputs.d.cts +5 -0
- package/dist/analyzed-inputs.d.mts +5 -0
- package/dist/analyzed-inputs.mjs +3 -0
- package/dist/constants-CD9NXHxi.cjs +530 -0
- package/dist/constants-oYYofMhb.mjs +344 -0
- package/dist/entries-worker.mjs +3736 -0
- package/dist/index.cjs +672 -476
- package/dist/index.d.cts +22 -0
- package/dist/index.d.mts +22 -0
- package/dist/index.mjs +625 -430
- package/dist/parse-worker.mjs +1 -2212
- package/dist/parse-xkrZfjHl.mjs +2308 -0
- package/package.json +11 -1
package/dist/index.mjs
CHANGED
|
@@ -1,326 +1,19 @@
|
|
|
1
|
+
import { A as STANDALONE_PROJECT_LOCKFILES, C as RESOLVER_EXTENSIONS, D as SCRIPT_FILE_PATTERN, E as SCRIPT_EXTENSIONLESS_FILE_PATTERN, O as SEMANTIC_MAX_PROGRAM_FILES, S as REACT_NATIVE_PLATFORM_EXTENSIONS, T as SCRIPT_ENTRY_PATTERNS, _ as MINIFIED_DETECTION_MIN_BYTES, a as DEFAULT_EXCLUSIONS, b as OUTPUT_DIRECTORIES, c as DEFAULT_SEMANTIC_TSCONFIG_NAMES, d as IMPLICIT_DEPENDENCIES, f as KNOWN_CONFIG_PREFIXES, g as MAX_PARSE_FILE_SIZE_BYTES, h as MAX_ERROR_DETAIL_LENGTH, i as DEFAULT_ENTRY_GLOBS, j as SUMMARY_CACHE_MAX_BYTES, k as SOURCE_EXTENSIONS$3, l as GIT_CHECK_IGNORE_MAX_BUFFER_BYTES, m as MAX_ANALYSIS_ERRORS, n as BINARY_DETECTION_SAMPLE_BYTES, o as DEFAULT_EXTENSIONS, p as LOCKFILE_MARKERS, r as BUILTIN_MODULES, s as DEFAULT_SEMANTIC_DECORATOR_ALLOWLIST, t as ANALYZED_MANIFEST_FILENAMES, u as HIDDEN_DIRECTORY_ALLOWLIST, v as MIN_NUMERIC_LITERAL_MAGNITUDE_FOR_DUPLICATE, w as SCRIPT_CONFIG_FILE_PATTERN, x as PLATFORM_SUFFIXES, y as MONOREPO_ROOT_MARKERS } from "./constants-oYYofMhb.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
1
3
|
import path, { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
2
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
3
5
|
import fg from "fast-glob";
|
|
4
6
|
import { readFile } from "node:fs/promises";
|
|
5
7
|
import { parseSync } from "oxc-parser";
|
|
6
8
|
import ts from "typescript";
|
|
7
9
|
import { ResolverFactory } from "oxc-resolver";
|
|
10
|
+
import os, { devNull } from "node:os";
|
|
8
11
|
import { Worker } from "node:worker_threads";
|
|
9
12
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import
|
|
11
|
-
import
|
|
13
|
+
import { Minimatch, minimatch } from "minimatch";
|
|
14
|
+
import crypto from "node:crypto";
|
|
12
15
|
import { spawnSync } from "node:child_process";
|
|
13
16
|
|
|
14
|
-
//#region src/constants.ts
|
|
15
|
-
const DEFAULT_EXTENSIONS = [
|
|
16
|
-
".ts",
|
|
17
|
-
".tsx",
|
|
18
|
-
".js",
|
|
19
|
-
".jsx",
|
|
20
|
-
".mts",
|
|
21
|
-
".mjs",
|
|
22
|
-
".cts",
|
|
23
|
-
".cjs",
|
|
24
|
-
".mdx",
|
|
25
|
-
".astro",
|
|
26
|
-
".graphql",
|
|
27
|
-
".gql",
|
|
28
|
-
".css",
|
|
29
|
-
".scss",
|
|
30
|
-
".vue",
|
|
31
|
-
".svelte"
|
|
32
|
-
];
|
|
33
|
-
const HIDDEN_DIRECTORY_ALLOWLIST = [
|
|
34
|
-
".storybook",
|
|
35
|
-
".vitepress",
|
|
36
|
-
".well-known",
|
|
37
|
-
".changeset",
|
|
38
|
-
".github",
|
|
39
|
-
".client",
|
|
40
|
-
".server"
|
|
41
|
-
];
|
|
42
|
-
const OUTPUT_DIRECTORIES = [
|
|
43
|
-
"dist",
|
|
44
|
-
"build",
|
|
45
|
-
"out",
|
|
46
|
-
"esm",
|
|
47
|
-
"cjs"
|
|
48
|
-
];
|
|
49
|
-
const SOURCE_EXTENSIONS$3 = [
|
|
50
|
-
"ts",
|
|
51
|
-
"tsx",
|
|
52
|
-
"mts",
|
|
53
|
-
"cts",
|
|
54
|
-
"js",
|
|
55
|
-
"jsx",
|
|
56
|
-
"mjs",
|
|
57
|
-
"cjs"
|
|
58
|
-
];
|
|
59
|
-
const DEFAULT_EXCLUSIONS = [
|
|
60
|
-
"**/node_modules/**",
|
|
61
|
-
"**/.git/**",
|
|
62
|
-
"**/coverage/**",
|
|
63
|
-
"**/*.min.js",
|
|
64
|
-
"**/*.min.mjs",
|
|
65
|
-
"**/mockServiceWorker.js"
|
|
66
|
-
];
|
|
67
|
-
const SCRIPT_FILE_PATTERN = /(?:^|\s)(?:node|tsx|ts-node|tsc|npx|bun|esr|esno|jiti|babel-node|zx)\s+(?:\S+\s+)*?([\w./@-]+\.(?:ts|tsx|js|jsx|mts|mjs|cts|cjs))(?:\s|$)/;
|
|
68
|
-
const SCRIPT_EXTENSIONLESS_FILE_PATTERN = /(?:^|\s)(?:node|tsx|ts-node|bun|esr|esno|jiti|babel-node|zx)\s+(?:\S+\s+)*?((?:[./]|[\w@][\w@-]*\/)[\w./@-]+)(?:\s|$)/;
|
|
69
|
-
const SCRIPT_CONFIG_FILE_PATTERN = /--config\s+([\w./@-]+\.(?:ts|tsx|js|jsx|mts|mjs|cts|cjs))/;
|
|
70
|
-
const SCRIPT_ENTRY_PATTERNS = [];
|
|
71
|
-
const DEFAULT_ENTRY_GLOBS = [
|
|
72
|
-
"src/index.{ts,tsx,js,jsx}",
|
|
73
|
-
"src/main.{ts,tsx,js,jsx}",
|
|
74
|
-
"index.{ts,tsx,js,jsx}",
|
|
75
|
-
"main.{ts,tsx,js,jsx}"
|
|
76
|
-
];
|
|
77
|
-
const KNOWN_CONFIG_PREFIXES = [
|
|
78
|
-
"babel.config.",
|
|
79
|
-
"rollup.config.",
|
|
80
|
-
"webpack.config.",
|
|
81
|
-
"postcss.config.",
|
|
82
|
-
"stencil.config.",
|
|
83
|
-
"remotion.config.",
|
|
84
|
-
"metro.config.",
|
|
85
|
-
"tsup.config.",
|
|
86
|
-
"tsdown.config.",
|
|
87
|
-
"unbuild.config.",
|
|
88
|
-
"esbuild.config.",
|
|
89
|
-
"swc.config.",
|
|
90
|
-
"turbo.",
|
|
91
|
-
"jest.config.",
|
|
92
|
-
"jest.setup.",
|
|
93
|
-
"vitest.config.",
|
|
94
|
-
"vitest.ci.config.",
|
|
95
|
-
"vitest.setup.",
|
|
96
|
-
"vitest.workspace.",
|
|
97
|
-
"playwright.config.",
|
|
98
|
-
"cypress.config.",
|
|
99
|
-
"karma.conf.",
|
|
100
|
-
"eslint.config.",
|
|
101
|
-
"prettier.config.",
|
|
102
|
-
"stylelint.config.",
|
|
103
|
-
"lint-staged.config.",
|
|
104
|
-
"commitlint.config.",
|
|
105
|
-
"next.config.",
|
|
106
|
-
"next-sitemap.config.",
|
|
107
|
-
"nuxt.config.",
|
|
108
|
-
"astro.config.",
|
|
109
|
-
"sanity.config.",
|
|
110
|
-
"vite.config.",
|
|
111
|
-
"tailwind.config.",
|
|
112
|
-
"drizzle.config.",
|
|
113
|
-
"knexfile.",
|
|
114
|
-
"sentry.client.config.",
|
|
115
|
-
"sentry.server.config.",
|
|
116
|
-
"sentry.edge.config.",
|
|
117
|
-
"react-router.config.",
|
|
118
|
-
"typedoc.",
|
|
119
|
-
"deslop.config.",
|
|
120
|
-
"i18next-parser.config.",
|
|
121
|
-
"codegen.config.",
|
|
122
|
-
"graphql.config.",
|
|
123
|
-
"npmpackagejsonlint.config.",
|
|
124
|
-
"release-it.",
|
|
125
|
-
"release.config.",
|
|
126
|
-
"contentlayer.config.",
|
|
127
|
-
"rspack.config.",
|
|
128
|
-
"rsbuild.config.",
|
|
129
|
-
"module-federation.config.",
|
|
130
|
-
"vercel.",
|
|
131
|
-
"next-env.d.",
|
|
132
|
-
"env.d.",
|
|
133
|
-
"vite-env.d."
|
|
134
|
-
];
|
|
135
|
-
const IMPLICIT_DEPENDENCIES = new Set([
|
|
136
|
-
"typescript",
|
|
137
|
-
"@types/node",
|
|
138
|
-
"@types/react",
|
|
139
|
-
"@types/react-dom",
|
|
140
|
-
"eslint",
|
|
141
|
-
"prettier",
|
|
142
|
-
"husky",
|
|
143
|
-
"lint-staged",
|
|
144
|
-
"tslib",
|
|
145
|
-
"@babel/core",
|
|
146
|
-
"@babel/runtime",
|
|
147
|
-
"babel-core",
|
|
148
|
-
"babel-jest",
|
|
149
|
-
"babel-loader",
|
|
150
|
-
"postcss",
|
|
151
|
-
"cross-env",
|
|
152
|
-
"sass",
|
|
153
|
-
"node-sass",
|
|
154
|
-
"less",
|
|
155
|
-
"oxlint",
|
|
156
|
-
"biome",
|
|
157
|
-
"@biomejs/biome",
|
|
158
|
-
"patch-package",
|
|
159
|
-
"simple-git-hooks",
|
|
160
|
-
"lefthook",
|
|
161
|
-
"ts-node",
|
|
162
|
-
"ts-jest",
|
|
163
|
-
"tsx",
|
|
164
|
-
"jsdom",
|
|
165
|
-
"rimraf",
|
|
166
|
-
"concurrently",
|
|
167
|
-
"npm-run-all",
|
|
168
|
-
"npm-run-all2",
|
|
169
|
-
"dotenv-cli",
|
|
170
|
-
"webpack",
|
|
171
|
-
"rollup",
|
|
172
|
-
"terser",
|
|
173
|
-
"autoprefixer",
|
|
174
|
-
"tailwindcss",
|
|
175
|
-
"react-test-renderer",
|
|
176
|
-
"esbuild",
|
|
177
|
-
"typedoc",
|
|
178
|
-
"commitizen",
|
|
179
|
-
"cz-conventional-changelog"
|
|
180
|
-
]);
|
|
181
|
-
const BUILTIN_MODULES = new Set([
|
|
182
|
-
"assert",
|
|
183
|
-
"async_hooks",
|
|
184
|
-
"buffer",
|
|
185
|
-
"child_process",
|
|
186
|
-
"cluster",
|
|
187
|
-
"console",
|
|
188
|
-
"constants",
|
|
189
|
-
"crypto",
|
|
190
|
-
"dgram",
|
|
191
|
-
"diagnostics_channel",
|
|
192
|
-
"dns",
|
|
193
|
-
"domain",
|
|
194
|
-
"events",
|
|
195
|
-
"fs",
|
|
196
|
-
"http",
|
|
197
|
-
"http2",
|
|
198
|
-
"https",
|
|
199
|
-
"inspector",
|
|
200
|
-
"module",
|
|
201
|
-
"net",
|
|
202
|
-
"os",
|
|
203
|
-
"path",
|
|
204
|
-
"perf_hooks",
|
|
205
|
-
"process",
|
|
206
|
-
"punycode",
|
|
207
|
-
"querystring",
|
|
208
|
-
"readline",
|
|
209
|
-
"repl",
|
|
210
|
-
"stream",
|
|
211
|
-
"string_decoder",
|
|
212
|
-
"sys",
|
|
213
|
-
"timers",
|
|
214
|
-
"tls",
|
|
215
|
-
"trace_events",
|
|
216
|
-
"tty",
|
|
217
|
-
"url",
|
|
218
|
-
"util",
|
|
219
|
-
"v8",
|
|
220
|
-
"vm",
|
|
221
|
-
"wasi",
|
|
222
|
-
"worker_threads",
|
|
223
|
-
"zlib"
|
|
224
|
-
]);
|
|
225
|
-
const PLATFORM_SUFFIXES = [
|
|
226
|
-
".web",
|
|
227
|
-
".react-native",
|
|
228
|
-
".native",
|
|
229
|
-
".ios",
|
|
230
|
-
".android",
|
|
231
|
-
".desktop",
|
|
232
|
-
".windows",
|
|
233
|
-
".macos",
|
|
234
|
-
".any",
|
|
235
|
-
".react-server",
|
|
236
|
-
".server",
|
|
237
|
-
".client"
|
|
238
|
-
];
|
|
239
|
-
const REACT_NATIVE_PLATFORM_EXTENSIONS = [
|
|
240
|
-
".web.ts",
|
|
241
|
-
".web.tsx",
|
|
242
|
-
".web.js",
|
|
243
|
-
".web.jsx",
|
|
244
|
-
".native.ts",
|
|
245
|
-
".native.tsx",
|
|
246
|
-
".native.js",
|
|
247
|
-
".native.jsx",
|
|
248
|
-
".ios.ts",
|
|
249
|
-
".ios.tsx",
|
|
250
|
-
".ios.js",
|
|
251
|
-
".ios.jsx",
|
|
252
|
-
".android.ts",
|
|
253
|
-
".android.tsx",
|
|
254
|
-
".android.js",
|
|
255
|
-
".android.jsx"
|
|
256
|
-
];
|
|
257
|
-
const RESOLVER_EXTENSIONS = [
|
|
258
|
-
...DEFAULT_EXTENSIONS,
|
|
259
|
-
".d.ts",
|
|
260
|
-
".d.mts",
|
|
261
|
-
".d.cts",
|
|
262
|
-
".json",
|
|
263
|
-
".node",
|
|
264
|
-
".css",
|
|
265
|
-
".scss",
|
|
266
|
-
".less",
|
|
267
|
-
".svg",
|
|
268
|
-
".png",
|
|
269
|
-
".jpg",
|
|
270
|
-
".graphql",
|
|
271
|
-
".gql"
|
|
272
|
-
];
|
|
273
|
-
const SEMANTIC_MAX_PROGRAM_FILES = 5e3;
|
|
274
|
-
const MAX_PARSE_FILE_SIZE_BYTES = 2e6;
|
|
275
|
-
const MAX_ANALYSIS_ERRORS = 5e3;
|
|
276
|
-
const MAX_ERROR_DETAIL_LENGTH = 1e3;
|
|
277
|
-
const BINARY_DETECTION_SAMPLE_BYTES = 2048;
|
|
278
|
-
const MINIFIED_DETECTION_MIN_BYTES = 5e3;
|
|
279
|
-
/**
|
|
280
|
-
* Numeric literals below 1000 are dominated by indices, counters, small
|
|
281
|
-
* ranges, ports, percentages, and array sizes that coincide by accident
|
|
282
|
-
* (every `MAX_RETRIES = 3` is not a duplicate of every `LIMIT = 3`).
|
|
283
|
-
* 1000 admits real shared constants (timeouts in ms, byte sizes, polling
|
|
284
|
-
* intervals) without producing the noise floor that smaller magnitudes do.
|
|
285
|
-
* NOTE: even at 1000, the rule still produces medium-confidence false
|
|
286
|
-
* positives when constants share a value coincidentally with different
|
|
287
|
-
* names (e.g. `STEP_DELAY_MS` vs `MINIMUM_TOKENS`); the report explicitly
|
|
288
|
-
* downgrades those to `confidence: "medium"`.
|
|
289
|
-
*/
|
|
290
|
-
const MIN_NUMERIC_LITERAL_MAGNITUDE_FOR_DUPLICATE = 1e3;
|
|
291
|
-
const DEFAULT_SEMANTIC_DECORATOR_ALLOWLIST = [
|
|
292
|
-
"Component",
|
|
293
|
-
"Injectable",
|
|
294
|
-
"NgModule",
|
|
295
|
-
"Pipe",
|
|
296
|
-
"Directive",
|
|
297
|
-
"Controller",
|
|
298
|
-
"Module",
|
|
299
|
-
"Resolver",
|
|
300
|
-
"Query",
|
|
301
|
-
"Mutation",
|
|
302
|
-
"Get",
|
|
303
|
-
"Post",
|
|
304
|
-
"Put",
|
|
305
|
-
"Patch",
|
|
306
|
-
"Delete",
|
|
307
|
-
"Head",
|
|
308
|
-
"Options",
|
|
309
|
-
"All",
|
|
310
|
-
"Sse",
|
|
311
|
-
"WebSocketGateway",
|
|
312
|
-
"SubscribeMessage"
|
|
313
|
-
];
|
|
314
|
-
const DEFAULT_SEMANTIC_TSCONFIG_NAMES = [
|
|
315
|
-
"tsconfig.json",
|
|
316
|
-
"tsconfig.app.json",
|
|
317
|
-
"tsconfig.build.json",
|
|
318
|
-
"tsconfig.src.json",
|
|
319
|
-
"jsconfig.json"
|
|
320
|
-
];
|
|
321
|
-
const GIT_CHECK_IGNORE_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
322
|
-
|
|
323
|
-
//#endregion
|
|
324
17
|
//#region src/errors.ts
|
|
325
18
|
const truncateDetail = (text) => {
|
|
326
19
|
if (text.length <= 1e3) return text;
|
|
@@ -2612,12 +2305,6 @@ const resolveWorkspaces = (rootDir) => {
|
|
|
2612
2305
|
};
|
|
2613
2306
|
};
|
|
2614
2307
|
const IMPLICIT_SUB_PROJECT_SEARCH_DEPTH = 3;
|
|
2615
|
-
const STANDALONE_PROJECT_LOCKFILES = [
|
|
2616
|
-
"package-lock.json",
|
|
2617
|
-
"yarn.lock",
|
|
2618
|
-
"pnpm-lock.yaml",
|
|
2619
|
-
"bun.lockb"
|
|
2620
|
-
];
|
|
2621
2308
|
const isStandaloneProject = (directory) => STANDALONE_PROJECT_LOCKFILES.some((lockfile) => existsSync(join(directory, lockfile)));
|
|
2622
2309
|
const discoverImplicitSubProjects = (rootDir, alreadyDiscoveredDirectories) => {
|
|
2623
2310
|
const knownDirectories = new Set(alreadyDiscoveredDirectories);
|
|
@@ -3444,21 +3131,6 @@ const resolveWithIndexPrefix = (stemPath, directory) => {
|
|
|
3444
3131
|
|
|
3445
3132
|
//#endregion
|
|
3446
3133
|
//#region src/utils/find-monorepo-root.ts
|
|
3447
|
-
const MONOREPO_ROOT_MARKERS = [
|
|
3448
|
-
"pnpm-workspace.yaml",
|
|
3449
|
-
"pnpm-workspace.yml",
|
|
3450
|
-
"lerna.json",
|
|
3451
|
-
"nx.json",
|
|
3452
|
-
"turbo.json",
|
|
3453
|
-
"rush.json"
|
|
3454
|
-
];
|
|
3455
|
-
const LOCKFILE_MARKERS = [
|
|
3456
|
-
"pnpm-lock.yaml",
|
|
3457
|
-
"yarn.lock",
|
|
3458
|
-
"package-lock.json",
|
|
3459
|
-
"bun.lockb",
|
|
3460
|
-
"bun.lock"
|
|
3461
|
-
];
|
|
3462
3134
|
const MAX_MONOREPO_WALK_DEPTH = 5;
|
|
3463
3135
|
const findMonorepoRoot = (rootDir) => {
|
|
3464
3136
|
let currentDirectory = resolve(rootDir);
|
|
@@ -7224,6 +6896,18 @@ const resolveAvailableConcurrency = () => {
|
|
|
7224
6896
|
return clampParseConcurrency(available);
|
|
7225
6897
|
};
|
|
7226
6898
|
|
|
6899
|
+
//#endregion
|
|
6900
|
+
//#region src/collect/launch-worker.ts
|
|
6901
|
+
/**
|
|
6902
|
+
* Launches a worker module that sits next to `fromModuleUrl`: the TypeScript
|
|
6903
|
+
* source (via tsx) when the caller itself runs from source — tests and direct
|
|
6904
|
+
* `tsx` execution — and the built `.mjs` sibling when running from dist.
|
|
6905
|
+
*/
|
|
6906
|
+
const launchSiblingWorker = (fromModuleUrl, workerBaseName) => {
|
|
6907
|
+
const isTypeScriptSource = fromModuleUrl.endsWith(".ts");
|
|
6908
|
+
return new Worker(fileURLToPath(new URL(isTypeScriptSource ? `./${workerBaseName}.ts` : `./${workerBaseName}.mjs`, fromModuleUrl)), { ...isTypeScriptSource ? { execArgv: ["--import", "tsx"] } : {} });
|
|
6909
|
+
};
|
|
6910
|
+
|
|
7227
6911
|
//#endregion
|
|
7228
6912
|
//#region src/collect/parallel-parse.ts
|
|
7229
6913
|
const deserializeErrors = (serializedErrors) => serializedErrors.map((errorJson) => new DeslopError({
|
|
@@ -7250,14 +6934,6 @@ const deserializeParsedSource = (serialized) => ({
|
|
|
7250
6934
|
duplicateConstantCandidates: serialized.duplicateConstantCandidates,
|
|
7251
6935
|
errors: deserializeErrors(serialized.errors)
|
|
7252
6936
|
});
|
|
7253
|
-
const resolveWorkerPath = () => {
|
|
7254
|
-
const currentUrl = import.meta.url;
|
|
7255
|
-
if (currentUrl.endsWith(".ts")) return fileURLToPath(new URL("./parse-worker.ts", currentUrl));
|
|
7256
|
-
return fileURLToPath(new URL("./parse-worker.mjs", currentUrl));
|
|
7257
|
-
};
|
|
7258
|
-
const createWorker = (workerPath) => {
|
|
7259
|
-
return new Worker(workerPath, { ...workerPath.endsWith(".ts") ? { execArgv: ["--import", "tsx"] } : {} });
|
|
7260
|
-
};
|
|
7261
6937
|
const waitForReady = (worker) => new Promise((resolve, reject) => {
|
|
7262
6938
|
const onMessage = (message) => {
|
|
7263
6939
|
if (message.type === "ready") {
|
|
@@ -7275,11 +6951,10 @@ const waitForReady = (worker) => new Promise((resolve, reject) => {
|
|
|
7275
6951
|
worker.on("error", onError);
|
|
7276
6952
|
});
|
|
7277
6953
|
const parseFilesWithWorkerPool = async (files, workerCount) => {
|
|
7278
|
-
const workerPath = resolveWorkerPath();
|
|
7279
6954
|
const results = new Array(files.length);
|
|
7280
6955
|
const workers = [];
|
|
7281
6956
|
try {
|
|
7282
|
-
for (let workerIndex = 0; workerIndex < workerCount; workerIndex++) workers.push(
|
|
6957
|
+
for (let workerIndex = 0; workerIndex < workerCount; workerIndex++) workers.push(launchSiblingWorker(import.meta.url, "parse-worker"));
|
|
7283
6958
|
await Promise.all(workers.map(waitForReady));
|
|
7284
6959
|
} catch {
|
|
7285
6960
|
for (const worker of workers) worker.terminate();
|
|
@@ -7799,10 +7474,16 @@ const detectDeadExports = (graph, config) => {
|
|
|
7799
7474
|
const buildUsageMap = (graph) => {
|
|
7800
7475
|
const usedExportKeys = /* @__PURE__ */ new Set();
|
|
7801
7476
|
const sourceToTargetMap = buildSourceToTargetsMap(graph);
|
|
7477
|
+
const reExportEdgesBySource = /* @__PURE__ */ new Map();
|
|
7478
|
+
for (const edge of graph.edges) {
|
|
7479
|
+
if (!edge.isReExportEdge) continue;
|
|
7480
|
+
const existingEdges = reExportEdgesBySource.get(edge.source);
|
|
7481
|
+
if (existingEdges) existingEdges.push(edge);
|
|
7482
|
+
else reExportEdgesBySource.set(edge.source, [edge]);
|
|
7483
|
+
}
|
|
7802
7484
|
for (const module of graph.modules) {
|
|
7803
7485
|
if (!module.isEntryPoint) continue;
|
|
7804
|
-
for (const edge of
|
|
7805
|
-
if (edge.source !== module.fileId.index || !edge.isReExportEdge) continue;
|
|
7486
|
+
for (const edge of reExportEdgesBySource.get(module.fileId.index) ?? []) {
|
|
7806
7487
|
const targetModule = graph.modules[edge.target];
|
|
7807
7488
|
if (!targetModule) continue;
|
|
7808
7489
|
if (edge.reExportedNames.includes("*")) markAllExportsUsedRecursive(targetModule, graph, sourceToTargetMap, usedExportKeys, /* @__PURE__ */ new Set());
|
|
@@ -8049,12 +7730,29 @@ const matchesPackageTokenReference = (command, packageName) => {
|
|
|
8049
7730
|
|
|
8050
7731
|
//#endregion
|
|
8051
7732
|
//#region src/report/packages.ts
|
|
8052
|
-
const
|
|
7733
|
+
const globPackageFiles = (cwd, patterns, options, summaryCache) => summaryCache?.matchWalkedFiles({
|
|
7734
|
+
cwd,
|
|
7735
|
+
patterns,
|
|
7736
|
+
...options
|
|
7737
|
+
}) ?? fg.sync([...patterns], {
|
|
7738
|
+
cwd,
|
|
7739
|
+
absolute: true,
|
|
7740
|
+
onlyFiles: true,
|
|
7741
|
+
ignore: [...options.ignore],
|
|
7742
|
+
deep: options.deep,
|
|
7743
|
+
...options.dot === void 0 ? {} : { dot: options.dot }
|
|
7744
|
+
});
|
|
7745
|
+
const containsPackageName = (content, packageName) => content.includes(packageName);
|
|
7746
|
+
const matchPackageNamesInFile = (filePath, kind, names, matcher, summaryCache) => {
|
|
7747
|
+
if (summaryCache !== void 0) return summaryCache.matchPackageNames(filePath, kind, names, matcher);
|
|
7748
|
+
const content = readFileSync(filePath, "utf-8");
|
|
7749
|
+
const matchedNames = [];
|
|
7750
|
+
for (const packageName of names) if (matcher(content, packageName)) matchedNames.push(packageName);
|
|
7751
|
+
return matchedNames;
|
|
7752
|
+
};
|
|
7753
|
+
const discoverAllPackageJsonPaths = (rootDir, summaryCache) => {
|
|
8053
7754
|
const paths = [join(rootDir, "package.json")];
|
|
8054
|
-
const workspacePackageJsons =
|
|
8055
|
-
cwd: rootDir,
|
|
8056
|
-
absolute: true,
|
|
8057
|
-
onlyFiles: true,
|
|
7755
|
+
const workspacePackageJsons = globPackageFiles(rootDir, ["**/package.json"], {
|
|
8058
7756
|
ignore: [
|
|
8059
7757
|
"**/node_modules/**",
|
|
8060
7758
|
"**/dist/**",
|
|
@@ -8062,11 +7760,11 @@ const discoverAllPackageJsonPaths = (rootDir) => {
|
|
|
8062
7760
|
"**/.git/**"
|
|
8063
7761
|
],
|
|
8064
7762
|
deep: 5
|
|
8065
|
-
});
|
|
7763
|
+
}, summaryCache);
|
|
8066
7764
|
for (const workspacePath of workspacePackageJsons) if (workspacePath !== paths[0] && !paths.includes(workspacePath)) paths.push(workspacePath);
|
|
8067
7765
|
return paths;
|
|
8068
7766
|
};
|
|
8069
|
-
const detectStalePackages = (graph, config) => {
|
|
7767
|
+
const detectStalePackages = (graph, config, summaryCache) => {
|
|
8070
7768
|
const packageJsonPath = resolve(config.rootDir, "package.json");
|
|
8071
7769
|
let packageJson;
|
|
8072
7770
|
try {
|
|
@@ -8084,7 +7782,7 @@ const detectStalePackages = (graph, config) => {
|
|
|
8084
7782
|
const usedPackageNames = collectUsedPackages(graph);
|
|
8085
7783
|
const monorepoRoot = findMonorepoRoot(config.rootDir);
|
|
8086
7784
|
const nodeModulesSearchRoots = monorepoRoot && monorepoRoot !== config.rootDir ? [config.rootDir, monorepoRoot] : [config.rootDir];
|
|
8087
|
-
const allPackageJsonPaths = discoverAllPackageJsonPaths(config.rootDir);
|
|
7785
|
+
const allPackageJsonPaths = discoverAllPackageJsonPaths(config.rootDir, summaryCache);
|
|
8088
7786
|
if (monorepoRoot) {
|
|
8089
7787
|
const monorepoPackageJson = join(monorepoRoot, "package.json");
|
|
8090
7788
|
if (!allPackageJsonPaths.includes(monorepoPackageJson) && existsSync(monorepoPackageJson)) allPackageJsonPaths.push(monorepoPackageJson);
|
|
@@ -8097,13 +7795,13 @@ const detectStalePackages = (graph, config) => {
|
|
|
8097
7795
|
const packageJsonConfigReferenced = collectPackageJsonConfigReferences(workspacePackageJsonPath, declaredNames);
|
|
8098
7796
|
for (const packageName of packageJsonConfigReferenced) usedPackageNames.add(packageName);
|
|
8099
7797
|
}
|
|
8100
|
-
const nxProjectReferenced = collectNxProjectJsonReferences(config.rootDir, declaredNames, binToPackage);
|
|
7798
|
+
const nxProjectReferenced = collectNxProjectJsonReferences(config.rootDir, declaredNames, binToPackage, summaryCache);
|
|
8101
7799
|
for (const packageName of nxProjectReferenced) usedPackageNames.add(packageName);
|
|
8102
7800
|
const configSearchRoots = monorepoRoot && monorepoRoot !== config.rootDir ? [config.rootDir, monorepoRoot] : [config.rootDir];
|
|
8103
7801
|
for (const configSearchRoot of configSearchRoots) {
|
|
8104
|
-
const configReferenced = collectConfigReferencedPackages(configSearchRoot, graph, declaredNames);
|
|
7802
|
+
const configReferenced = collectConfigReferencedPackages(configSearchRoot, graph, declaredNames, summaryCache);
|
|
8105
7803
|
for (const packageName of configReferenced) usedPackageNames.add(packageName);
|
|
8106
|
-
const tsconfigReferenced = collectTsconfigReferencedPackages(configSearchRoot);
|
|
7804
|
+
const tsconfigReferenced = collectTsconfigReferencedPackages(configSearchRoot, summaryCache);
|
|
8107
7805
|
for (const packageName of tsconfigReferenced) usedPackageNames.add(packageName);
|
|
8108
7806
|
const { packageNames: expoPluginPackageNames } = extractExpoConfigPluginEntries(configSearchRoot, {
|
|
8109
7807
|
...dependencies,
|
|
@@ -8155,7 +7853,7 @@ const detectStalePackages = (graph, config) => {
|
|
|
8155
7853
|
candidateUnused.add(dependencyName);
|
|
8156
7854
|
}
|
|
8157
7855
|
if (candidateUnused.size > 0) {
|
|
8158
|
-
const sourceFileRescued = scanSourceFilesForPackageImports(config.rootDir, candidateUnused);
|
|
7856
|
+
const sourceFileRescued = scanSourceFilesForPackageImports(config.rootDir, candidateUnused, summaryCache);
|
|
8159
7857
|
for (const packageName of sourceFileRescued) {
|
|
8160
7858
|
usedPackageNames.add(packageName);
|
|
8161
7859
|
candidateUnused.delete(packageName);
|
|
@@ -8323,35 +8021,26 @@ const CONFIG_FILE_GLOBS = [
|
|
|
8323
8021
|
".dumirc.js",
|
|
8324
8022
|
"dumi.config.{ts,js}"
|
|
8325
8023
|
];
|
|
8326
|
-
const collectConfigReferencedPackages = (rootDir, graph, declaredNames) => {
|
|
8024
|
+
const collectConfigReferencedPackages = (rootDir, graph, declaredNames, summaryCache) => {
|
|
8327
8025
|
const referenced = /* @__PURE__ */ new Set();
|
|
8328
|
-
|
|
8329
|
-
if (!module.isConfigFile) continue;
|
|
8026
|
+
const addMatchesFromFile = (filePath, kind, matcher) => {
|
|
8330
8027
|
try {
|
|
8331
|
-
const
|
|
8332
|
-
for (const packageName of declaredNames) if (content.includes(packageName)) referenced.add(packageName);
|
|
8028
|
+
for (const packageName of matchPackageNamesInFile(filePath, kind, declaredNames, matcher, summaryCache)) referenced.add(packageName);
|
|
8333
8029
|
} catch {
|
|
8334
|
-
|
|
8030
|
+
return;
|
|
8335
8031
|
}
|
|
8032
|
+
};
|
|
8033
|
+
for (const module of graph.modules) {
|
|
8034
|
+
if (!module.isConfigFile) continue;
|
|
8035
|
+
addMatchesFromFile(module.fileId.path, "substring", containsPackageName);
|
|
8336
8036
|
}
|
|
8337
|
-
const configFiles =
|
|
8338
|
-
cwd: rootDir,
|
|
8339
|
-
absolute: true,
|
|
8340
|
-
onlyFiles: true,
|
|
8037
|
+
const configFiles = globPackageFiles(rootDir, CONFIG_FILE_GLOBS, {
|
|
8341
8038
|
ignore: ["**/node_modules/**"],
|
|
8342
8039
|
dot: true,
|
|
8343
8040
|
deep: 3
|
|
8344
|
-
});
|
|
8345
|
-
for (const configPath of configFiles)
|
|
8346
|
-
|
|
8347
|
-
for (const packageName of declaredNames) if (content.includes(packageName)) referenced.add(packageName);
|
|
8348
|
-
} catch {
|
|
8349
|
-
continue;
|
|
8350
|
-
}
|
|
8351
|
-
const documentationFiles = fg.sync(["**/*.{mdx,md}"], {
|
|
8352
|
-
cwd: rootDir,
|
|
8353
|
-
absolute: true,
|
|
8354
|
-
onlyFiles: true,
|
|
8041
|
+
}, summaryCache);
|
|
8042
|
+
for (const configPath of configFiles) addMatchesFromFile(configPath, "substring", containsPackageName);
|
|
8043
|
+
const documentationFiles = globPackageFiles(rootDir, ["**/*.{mdx,md}"], {
|
|
8355
8044
|
ignore: [
|
|
8356
8045
|
"**/node_modules/**",
|
|
8357
8046
|
"**/dist/**",
|
|
@@ -8359,13 +8048,8 @@ const collectConfigReferencedPackages = (rootDir, graph, declaredNames) => {
|
|
|
8359
8048
|
"**/CHANGELOG.md"
|
|
8360
8049
|
],
|
|
8361
8050
|
deep: 6
|
|
8362
|
-
});
|
|
8363
|
-
for (const documentationPath of documentationFiles)
|
|
8364
|
-
const content = readFileSync(documentationPath, "utf-8");
|
|
8365
|
-
for (const packageName of declaredNames) if (matchesPackageImportReference(content, packageName)) referenced.add(packageName);
|
|
8366
|
-
} catch {
|
|
8367
|
-
continue;
|
|
8368
|
-
}
|
|
8051
|
+
}, summaryCache);
|
|
8052
|
+
for (const documentationPath of documentationFiles) addMatchesFromFile(documentationPath, "importReference", matchesPackageImportReference);
|
|
8369
8053
|
return referenced;
|
|
8370
8054
|
};
|
|
8371
8055
|
const PACKAGE_JSON_CONFIG_SECTIONS = [
|
|
@@ -8436,19 +8120,16 @@ const collectPackageJsonConfigReferences = (packageJsonPath, declaredNames) => {
|
|
|
8436
8120
|
}
|
|
8437
8121
|
return referenced;
|
|
8438
8122
|
};
|
|
8439
|
-
const collectNxProjectJsonReferences = (rootDir, declaredNames, binToPackage) => {
|
|
8123
|
+
const collectNxProjectJsonReferences = (rootDir, declaredNames, binToPackage, summaryCache) => {
|
|
8440
8124
|
const referenced = /* @__PURE__ */ new Set();
|
|
8441
|
-
const projectJsonPaths =
|
|
8442
|
-
cwd: rootDir,
|
|
8443
|
-
absolute: true,
|
|
8444
|
-
onlyFiles: true,
|
|
8125
|
+
const projectJsonPaths = globPackageFiles(rootDir, ["project.json", "**/project.json"], {
|
|
8445
8126
|
ignore: [
|
|
8446
8127
|
"**/node_modules/**",
|
|
8447
8128
|
"**/dist/**",
|
|
8448
8129
|
"**/build/**"
|
|
8449
8130
|
],
|
|
8450
8131
|
deep: 5
|
|
8451
|
-
});
|
|
8132
|
+
}, summaryCache);
|
|
8452
8133
|
for (const projectJsonPath of projectJsonPaths) try {
|
|
8453
8134
|
const content = readFileSync(projectJsonPath, "utf-8");
|
|
8454
8135
|
const projectJson = JSON.parse(content);
|
|
@@ -8476,16 +8157,13 @@ const TSCONFIG_GLOBS = [
|
|
|
8476
8157
|
"**/tsconfig.json",
|
|
8477
8158
|
"**/tsconfig.*.json"
|
|
8478
8159
|
];
|
|
8479
|
-
const collectTsconfigReferencedPackages = (rootDir) => {
|
|
8160
|
+
const collectTsconfigReferencedPackages = (rootDir, summaryCache) => {
|
|
8480
8161
|
const referenced = /* @__PURE__ */ new Set();
|
|
8481
|
-
const tsconfigFiles =
|
|
8482
|
-
cwd: rootDir,
|
|
8483
|
-
absolute: true,
|
|
8484
|
-
onlyFiles: true,
|
|
8162
|
+
const tsconfigFiles = globPackageFiles(rootDir, TSCONFIG_GLOBS, {
|
|
8485
8163
|
ignore: ["**/node_modules/**"],
|
|
8486
8164
|
dot: false,
|
|
8487
8165
|
deep: 4
|
|
8488
|
-
});
|
|
8166
|
+
}, summaryCache);
|
|
8489
8167
|
for (const tsconfigPath of tsconfigFiles) try {
|
|
8490
8168
|
const cleaned = readFileSync(tsconfigPath, "utf-8").replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
8491
8169
|
const parsed = JSON.parse(cleaned);
|
|
@@ -8531,16 +8209,22 @@ const SOURCE_FILE_IGNORES = [
|
|
|
8531
8209
|
"**/*.min.js",
|
|
8532
8210
|
"**/*.d.ts"
|
|
8533
8211
|
];
|
|
8534
|
-
const scanSourceFilesForPackageImports = (rootDir, candidatePackages) => {
|
|
8212
|
+
const scanSourceFilesForPackageImports = (rootDir, candidatePackages, summaryCache) => {
|
|
8535
8213
|
const found = /* @__PURE__ */ new Set();
|
|
8536
8214
|
if (candidatePackages.size === 0) return found;
|
|
8537
|
-
const sourceFiles =
|
|
8538
|
-
cwd: rootDir,
|
|
8539
|
-
absolute: true,
|
|
8540
|
-
onlyFiles: true,
|
|
8215
|
+
const sourceFiles = globPackageFiles(rootDir, SOURCE_FILE_GLOBS, {
|
|
8541
8216
|
ignore: SOURCE_FILE_IGNORES,
|
|
8542
8217
|
deep: 15
|
|
8543
|
-
});
|
|
8218
|
+
}, summaryCache);
|
|
8219
|
+
if (summaryCache !== void 0) {
|
|
8220
|
+
for (const filePath of sourceFiles) try {
|
|
8221
|
+
for (const packageName of summaryCache.matchPackageNames(filePath, "importReference", candidatePackages, matchesPackageImportReference)) found.add(packageName);
|
|
8222
|
+
} catch {
|
|
8223
|
+
continue;
|
|
8224
|
+
}
|
|
8225
|
+
for (const packageName of found) candidatePackages.delete(packageName);
|
|
8226
|
+
return found;
|
|
8227
|
+
}
|
|
8544
8228
|
for (const filePath of sourceFiles) {
|
|
8545
8229
|
if (candidatePackages.size === 0) break;
|
|
8546
8230
|
try {
|
|
@@ -12510,7 +12194,7 @@ const safeReportDetector = (detectorName, detector, fallback, errorSink) => runS
|
|
|
12510
12194
|
module: "report",
|
|
12511
12195
|
contextDescription: "while building findings"
|
|
12512
12196
|
});
|
|
12513
|
-
const generateReport = (graph, config) => {
|
|
12197
|
+
const generateReport = (graph, config, summaryCache) => {
|
|
12514
12198
|
const analysisStartTime = performance.now();
|
|
12515
12199
|
const errorSink = [];
|
|
12516
12200
|
for (const module of graph.modules) {
|
|
@@ -12522,7 +12206,7 @@ const generateReport = (graph, config) => {
|
|
|
12522
12206
|
}
|
|
12523
12207
|
const unusedFiles = safeReportDetector("detectOrphanFiles", () => detectOrphanFiles(graph), [], errorSink);
|
|
12524
12208
|
const unusedExports = safeReportDetector("detectDeadExports", () => detectDeadExports(graph, config), [], errorSink);
|
|
12525
|
-
const unusedDependencies = safeReportDetector("detectStalePackages", () => detectStalePackages(graph, config), [], errorSink);
|
|
12209
|
+
const unusedDependencies = safeReportDetector("detectStalePackages", () => detectStalePackages(graph, config, summaryCache), [], errorSink);
|
|
12526
12210
|
const circularDependencies = safeReportDetector("detectCycles", () => detectCycles(graph), [], errorSink);
|
|
12527
12211
|
const syntacticRedundantAliases = config.reportRedundancy ? [...safeReportDetector("detectRedundantAliases", () => detectRedundantAliases(graph), [], errorSink), ...safeReportDetector("detectUselessAliasedReExports", () => detectUselessAliasedReExports(graph), [], errorSink)] : [];
|
|
12528
12212
|
const duplicateExports = config.reportRedundancy ? safeReportDetector("detectDuplicateExports", () => detectDuplicateExports(graph), [], errorSink) : [];
|
|
@@ -12616,6 +12300,478 @@ const generateReport = (graph, config) => {
|
|
|
12616
12300
|
};
|
|
12617
12301
|
};
|
|
12618
12302
|
|
|
12303
|
+
//#endregion
|
|
12304
|
+
//#region src/collect/entries-in-worker.ts
|
|
12305
|
+
/**
|
|
12306
|
+
* Runs `resolveEntries` on a dedicated worker thread so its ~90%-synchronous
|
|
12307
|
+
* fs work overlaps the main-thread analysis phases — the incremental-cache
|
|
12308
|
+
* warm path has no long parse `await` left to hide it behind. Entry content
|
|
12309
|
+
* reads stay live (fresh every run) exactly as inline. Worker infrastructure
|
|
12310
|
+
* failures fall back to the inline call (same result, just serialized); an
|
|
12311
|
+
* error thrown by `resolveEntries` itself rejects with the same message an
|
|
12312
|
+
* inline throw would, so the caller's fallback-to-empty-entries handling is
|
|
12313
|
+
* unchanged.
|
|
12314
|
+
*/
|
|
12315
|
+
const resolveEntriesInWorker = async (config) => {
|
|
12316
|
+
let worker;
|
|
12317
|
+
try {
|
|
12318
|
+
worker = launchSiblingWorker(import.meta.url, "entries-worker");
|
|
12319
|
+
} catch {
|
|
12320
|
+
return resolveEntries(config);
|
|
12321
|
+
}
|
|
12322
|
+
try {
|
|
12323
|
+
const outcome = await new Promise((resolveOutcome) => {
|
|
12324
|
+
worker.on("message", (message) => {
|
|
12325
|
+
if (message.type === "ready") worker.postMessage({
|
|
12326
|
+
type: "resolve-entries",
|
|
12327
|
+
config
|
|
12328
|
+
});
|
|
12329
|
+
else if (message.type === "result") resolveOutcome({
|
|
12330
|
+
kind: "result",
|
|
12331
|
+
entries: message.entries
|
|
12332
|
+
});
|
|
12333
|
+
else if (message.type === "error") resolveOutcome({
|
|
12334
|
+
kind: "analysis-error",
|
|
12335
|
+
errorMessage: message.errorMessage
|
|
12336
|
+
});
|
|
12337
|
+
});
|
|
12338
|
+
worker.on("error", () => resolveOutcome({ kind: "infra-failure" }));
|
|
12339
|
+
worker.on("exit", () => resolveOutcome({ kind: "infra-failure" }));
|
|
12340
|
+
});
|
|
12341
|
+
if (outcome.kind === "result") return outcome.entries;
|
|
12342
|
+
if (outcome.kind === "analysis-error") throw new Error(outcome.errorMessage);
|
|
12343
|
+
return resolveEntries(config);
|
|
12344
|
+
} finally {
|
|
12345
|
+
worker.terminate();
|
|
12346
|
+
}
|
|
12347
|
+
};
|
|
12348
|
+
|
|
12349
|
+
//#endregion
|
|
12350
|
+
//#region src/summary-cache.ts
|
|
12351
|
+
const WALK_SKIP_DIRECTORY_NAMES = new Set(["node_modules", ".git"]);
|
|
12352
|
+
const MANIFEST_LIKE_FILENAMES = new Set(ANALYZED_MANIFEST_FILENAMES);
|
|
12353
|
+
const isManifestLikeFileName = (fileName) => MANIFEST_LIKE_FILENAMES.has(fileName) || (fileName.startsWith("tsconfig") || fileName.startsWith("jsconfig")) && fileName.endsWith(".json");
|
|
12354
|
+
const isResolverConfigFileName = (fileName) => fileName.includes("webpack") || fileName.startsWith("vite.config.") || fileName.startsWith("vitest.config.") || fileName.startsWith("babel.config.") || fileName.startsWith(".babelrc") || fileName.startsWith("jest.config.");
|
|
12355
|
+
const sha1Hex = (text) => crypto.createHash("sha1").update(text).digest("hex");
|
|
12356
|
+
const sha1OfFileBytes = (filePath) => {
|
|
12357
|
+
try {
|
|
12358
|
+
return crypto.createHash("sha1").update(readFileSync(filePath)).digest("hex");
|
|
12359
|
+
} catch {
|
|
12360
|
+
return null;
|
|
12361
|
+
}
|
|
12362
|
+
};
|
|
12363
|
+
const fileNameOfPosixPath = (posixPath) => posixPath.slice(posixPath.lastIndexOf("/") + 1);
|
|
12364
|
+
const deslopRequire = createRequire(import.meta.url);
|
|
12365
|
+
const resolveOwnPackageVersion = () => {
|
|
12366
|
+
try {
|
|
12367
|
+
const packageJson = deslopRequire("deslop-js/package.json");
|
|
12368
|
+
return typeof packageJson?.version === "string" ? packageJson.version : "unknown";
|
|
12369
|
+
} catch {
|
|
12370
|
+
return "unknown";
|
|
12371
|
+
}
|
|
12372
|
+
};
|
|
12373
|
+
const walkTreeStats = (rootDirectory) => {
|
|
12374
|
+
const collected = /* @__PURE__ */ new Map();
|
|
12375
|
+
const followedLinkTargets = /* @__PURE__ */ new Set();
|
|
12376
|
+
const walk = (directory) => {
|
|
12377
|
+
let entries;
|
|
12378
|
+
try {
|
|
12379
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
12380
|
+
} catch {
|
|
12381
|
+
return;
|
|
12382
|
+
}
|
|
12383
|
+
for (const entry of entries) {
|
|
12384
|
+
const entryPath = `${directory}/${entry.name}`;
|
|
12385
|
+
if (entry.isDirectory()) {
|
|
12386
|
+
if (!WALK_SKIP_DIRECTORY_NAMES.has(entry.name)) walk(entryPath);
|
|
12387
|
+
continue;
|
|
12388
|
+
}
|
|
12389
|
+
if (entry.isSymbolicLink()) {
|
|
12390
|
+
if (WALK_SKIP_DIRECTORY_NAMES.has(entry.name)) continue;
|
|
12391
|
+
try {
|
|
12392
|
+
const linkStat = statSync(entryPath);
|
|
12393
|
+
if (linkStat.isDirectory()) {
|
|
12394
|
+
const linkTarget = realpathSync(entryPath);
|
|
12395
|
+
if (!followedLinkTargets.has(linkTarget)) {
|
|
12396
|
+
followedLinkTargets.add(linkTarget);
|
|
12397
|
+
walk(entryPath);
|
|
12398
|
+
}
|
|
12399
|
+
} else if (linkStat.isFile()) collected.set(entryPath, {
|
|
12400
|
+
m: linkStat.mtimeMs,
|
|
12401
|
+
s: linkStat.size
|
|
12402
|
+
});
|
|
12403
|
+
} catch {
|
|
12404
|
+
continue;
|
|
12405
|
+
}
|
|
12406
|
+
continue;
|
|
12407
|
+
}
|
|
12408
|
+
if (!entry.isFile()) continue;
|
|
12409
|
+
try {
|
|
12410
|
+
const fileStat = statSync(entryPath);
|
|
12411
|
+
collected.set(entryPath, {
|
|
12412
|
+
m: fileStat.mtimeMs,
|
|
12413
|
+
s: fileStat.size
|
|
12414
|
+
});
|
|
12415
|
+
} catch {
|
|
12416
|
+
continue;
|
|
12417
|
+
}
|
|
12418
|
+
}
|
|
12419
|
+
};
|
|
12420
|
+
walk(toPosixPath(resolve(rootDirectory)));
|
|
12421
|
+
return collected;
|
|
12422
|
+
};
|
|
12423
|
+
const compileGlobAlternation = (patterns, matchDotNames) => {
|
|
12424
|
+
const regexSources = [];
|
|
12425
|
+
for (const pattern of patterns) {
|
|
12426
|
+
const compiled = new Minimatch(pattern, { dot: matchDotNames }).makeRe();
|
|
12427
|
+
if (compiled === false) return null;
|
|
12428
|
+
regexSources.push(compiled.source);
|
|
12429
|
+
}
|
|
12430
|
+
return regexSources.length === 0 ? null : new RegExp(regexSources.join("|"));
|
|
12431
|
+
};
|
|
12432
|
+
const countPathSegments = (relativePath) => {
|
|
12433
|
+
let segmentCount = 1;
|
|
12434
|
+
for (let charIndex = 0; charIndex < relativePath.length; charIndex++) if (relativePath.charCodeAt(charIndex) === 47) segmentCount++;
|
|
12435
|
+
return segmentCount;
|
|
12436
|
+
};
|
|
12437
|
+
const contentFingerprintOf = (filePath, fileStat) => `${filePath}:${fileStat.s}:${sha1OfFileBytes(filePath) ?? `stat-${fileStat.m}`}`;
|
|
12438
|
+
const computeCollectHash = (walkedStats) => {
|
|
12439
|
+
const fingerprintLines = [];
|
|
12440
|
+
for (const [filePath, fileStat] of walkedStats) fingerprintLines.push(isManifestLikeFileName(fileNameOfPosixPath(filePath)) ? contentFingerprintOf(filePath, fileStat) : filePath);
|
|
12441
|
+
fingerprintLines.sort();
|
|
12442
|
+
return sha1Hex(fingerprintLines.join("\n"));
|
|
12443
|
+
};
|
|
12444
|
+
const computeResolutionHash = (collectHash, walkedStats) => {
|
|
12445
|
+
const fingerprintLines = [];
|
|
12446
|
+
for (const [filePath, fileStat] of walkedStats) if (isResolverConfigFileName(fileNameOfPosixPath(filePath))) fingerprintLines.push(contentFingerprintOf(filePath, fileStat));
|
|
12447
|
+
fingerprintLines.sort();
|
|
12448
|
+
return sha1Hex(`${collectHash}\n${fingerprintLines.join("\n")}`);
|
|
12449
|
+
};
|
|
12450
|
+
const computeScopeHash = (config) => sha1Hex(JSON.stringify({
|
|
12451
|
+
deslopVersion: resolveOwnPackageVersion(),
|
|
12452
|
+
rootDir: toPosixPath(resolve(config.rootDir)),
|
|
12453
|
+
entryPatterns: config.entryPatterns,
|
|
12454
|
+
ignorePatterns: config.ignorePatterns,
|
|
12455
|
+
includeExtensions: config.includeExtensions,
|
|
12456
|
+
reportTypes: config.reportTypes,
|
|
12457
|
+
includeEntryExports: config.includeEntryExports,
|
|
12458
|
+
reportRedundancy: config.reportRedundancy,
|
|
12459
|
+
tsConfigPath: config.tsConfigPath ?? null,
|
|
12460
|
+
paths: config.paths ?? null
|
|
12461
|
+
}));
|
|
12462
|
+
const isRecordValue = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12463
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
12464
|
+
const isOptionalArray = (value) => value === void 0 || Array.isArray(value);
|
|
12465
|
+
const emptyStore = (scopeHash) => ({
|
|
12466
|
+
version: 2,
|
|
12467
|
+
scopeHash,
|
|
12468
|
+
fileList: null,
|
|
12469
|
+
resolutions: null,
|
|
12470
|
+
summaries: {},
|
|
12471
|
+
packageFacts: {}
|
|
12472
|
+
});
|
|
12473
|
+
const readPersistedStore = (cachePath, scopeHash) => {
|
|
12474
|
+
try {
|
|
12475
|
+
const parsed = JSON.parse(readFileSync(cachePath, "utf-8"));
|
|
12476
|
+
if (isRecordValue(parsed) && parsed.version === 2 && parsed.scopeHash === scopeHash && isRecordValue(parsed.summaries) && isRecordValue(parsed.packageFacts)) {
|
|
12477
|
+
const persisted = parsed;
|
|
12478
|
+
return {
|
|
12479
|
+
version: 2,
|
|
12480
|
+
scopeHash,
|
|
12481
|
+
fileList: isRecordValue(persisted.fileList) ? persisted.fileList : null,
|
|
12482
|
+
resolutions: isRecordValue(persisted.resolutions) && typeof persisted.resolutions.hash === "string" && isRecordValue(persisted.resolutions.entries) ? persisted.resolutions : null,
|
|
12483
|
+
summaries: persisted.summaries,
|
|
12484
|
+
packageFacts: persisted.packageFacts
|
|
12485
|
+
};
|
|
12486
|
+
}
|
|
12487
|
+
} catch {}
|
|
12488
|
+
return emptyStore(scopeHash);
|
|
12489
|
+
};
|
|
12490
|
+
const intersectLocalReferencesWithOwnExports = (parsed) => {
|
|
12491
|
+
if (parsed.localIdentifierReferences.length === 0) return [];
|
|
12492
|
+
const ownExportNames = new Set(parsed.exports.map((exportInfo) => exportInfo.name));
|
|
12493
|
+
return parsed.localIdentifierReferences.filter((identifierName) => ownExportNames.has(identifierName));
|
|
12494
|
+
};
|
|
12495
|
+
const toPersistedArray = (values) => values.length > 0 ? values : void 0;
|
|
12496
|
+
const serializeParsedSource = (parsed, shouldPersistDryPatternFields) => ({
|
|
12497
|
+
imports: toPersistedArray(parsed.imports),
|
|
12498
|
+
exports: toPersistedArray(parsed.exports),
|
|
12499
|
+
memberAccesses: toPersistedArray(parsed.memberAccesses),
|
|
12500
|
+
wholeObjectUses: toPersistedArray(parsed.wholeObjectUses),
|
|
12501
|
+
localIdentifierReferences: toPersistedArray(intersectLocalReferencesWithOwnExports(parsed)),
|
|
12502
|
+
referencedFilenames: toPersistedArray(parsed.referencedFilenames),
|
|
12503
|
+
...shouldPersistDryPatternFields ? {
|
|
12504
|
+
redundantTypePatterns: toPersistedArray(parsed.redundantTypePatterns),
|
|
12505
|
+
identityWrappers: toPersistedArray(parsed.identityWrappers),
|
|
12506
|
+
typeDefinitionHashes: toPersistedArray(parsed.typeDefinitionHashes),
|
|
12507
|
+
inlineTypeLiterals: toPersistedArray(parsed.inlineTypeLiterals),
|
|
12508
|
+
simplifiableFunctions: toPersistedArray(parsed.simplifiableFunctions),
|
|
12509
|
+
simplifiableExpressions: toPersistedArray(parsed.simplifiableExpressions),
|
|
12510
|
+
duplicateConstantCandidates: toPersistedArray(parsed.duplicateConstantCandidates)
|
|
12511
|
+
} : {},
|
|
12512
|
+
errors: toPersistedArray(parsed.errors.map((deslopError) => deslopError.toJSON()))
|
|
12513
|
+
});
|
|
12514
|
+
const isPersistedErrorJson = (value) => isRecordValue(value) && typeof value.code === "string" && typeof value.module === "string" && typeof value.message === "string";
|
|
12515
|
+
const PERSISTED_SOURCE_ARRAY_FIELDS = [
|
|
12516
|
+
"imports",
|
|
12517
|
+
"exports",
|
|
12518
|
+
"memberAccesses",
|
|
12519
|
+
"wholeObjectUses",
|
|
12520
|
+
"localIdentifierReferences",
|
|
12521
|
+
"referencedFilenames",
|
|
12522
|
+
"redundantTypePatterns",
|
|
12523
|
+
"identityWrappers",
|
|
12524
|
+
"typeDefinitionHashes",
|
|
12525
|
+
"inlineTypeLiterals",
|
|
12526
|
+
"simplifiableFunctions",
|
|
12527
|
+
"simplifiableExpressions",
|
|
12528
|
+
"duplicateConstantCandidates",
|
|
12529
|
+
"errors"
|
|
12530
|
+
];
|
|
12531
|
+
const reviveParsedSource = (persisted) => {
|
|
12532
|
+
if (!isRecordValue(persisted)) return null;
|
|
12533
|
+
for (const fieldName of PERSISTED_SOURCE_ARRAY_FIELDS) if (!isOptionalArray(persisted[fieldName])) return null;
|
|
12534
|
+
const source = persisted;
|
|
12535
|
+
const persistedErrors = source.errors ?? [];
|
|
12536
|
+
if (!persistedErrors.every(isPersistedErrorJson)) return null;
|
|
12537
|
+
return {
|
|
12538
|
+
imports: source.imports ?? [],
|
|
12539
|
+
exports: source.exports ?? [],
|
|
12540
|
+
memberAccesses: source.memberAccesses ?? [],
|
|
12541
|
+
wholeObjectUses: source.wholeObjectUses ?? [],
|
|
12542
|
+
localIdentifierReferences: source.localIdentifierReferences ?? [],
|
|
12543
|
+
referencedFilenames: source.referencedFilenames ?? [],
|
|
12544
|
+
redundantTypePatterns: source.redundantTypePatterns ?? [],
|
|
12545
|
+
identityWrappers: source.identityWrappers ?? [],
|
|
12546
|
+
typeDefinitionHashes: source.typeDefinitionHashes ?? [],
|
|
12547
|
+
inlineTypeLiterals: source.inlineTypeLiterals ?? [],
|
|
12548
|
+
simplifiableFunctions: source.simplifiableFunctions ?? [],
|
|
12549
|
+
simplifiableExpressions: source.simplifiableExpressions ?? [],
|
|
12550
|
+
duplicateConstantCandidates: source.duplicateConstantCandidates ?? [],
|
|
12551
|
+
errors: persistedErrors.map((errorJson) => new DeslopError({
|
|
12552
|
+
code: errorJson.code,
|
|
12553
|
+
module: errorJson.module,
|
|
12554
|
+
severity: errorJson.severity,
|
|
12555
|
+
message: errorJson.message,
|
|
12556
|
+
path: errorJson.path,
|
|
12557
|
+
detail: errorJson.detail
|
|
12558
|
+
}))
|
|
12559
|
+
};
|
|
12560
|
+
};
|
|
12561
|
+
const atomicWriteFile = (filePath, contents) => {
|
|
12562
|
+
try {
|
|
12563
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
12564
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
12565
|
+
writeFileSync(temporaryPath, contents);
|
|
12566
|
+
renameSync(temporaryPath, filePath);
|
|
12567
|
+
} catch {}
|
|
12568
|
+
};
|
|
12569
|
+
const createSummaryCache = (cachePath, config) => {
|
|
12570
|
+
const scopeHash = computeScopeHash(config);
|
|
12571
|
+
const store = readPersistedStore(cachePath, scopeHash);
|
|
12572
|
+
const walkRoot = toPosixPath(resolve(config.rootDir));
|
|
12573
|
+
const walkedStats = walkTreeStats(config.rootDir);
|
|
12574
|
+
const collectHash = computeCollectHash(walkedStats);
|
|
12575
|
+
const resolutionHash = computeResolutionHash(collectHash, walkedStats);
|
|
12576
|
+
const shouldPersistDryPatternFields = config.reportRedundancy;
|
|
12577
|
+
if (store.resolutions === null || store.resolutions.hash !== resolutionHash) store.resolutions = {
|
|
12578
|
+
hash: resolutionHash,
|
|
12579
|
+
entries: {}
|
|
12580
|
+
};
|
|
12581
|
+
const resolutionEntries = store.resolutions.entries;
|
|
12582
|
+
let isDirty = false;
|
|
12583
|
+
const activeSummaryPaths = /* @__PURE__ */ new Set();
|
|
12584
|
+
const activeResolutionKeys = /* @__PURE__ */ new Set();
|
|
12585
|
+
const activeFactPaths = /* @__PURE__ */ new Set();
|
|
12586
|
+
const sortedNameSetMemo = /* @__PURE__ */ new WeakMap();
|
|
12587
|
+
const statOfLive = (filePath) => {
|
|
12588
|
+
try {
|
|
12589
|
+
const fileStat = statSync(filePath);
|
|
12590
|
+
return {
|
|
12591
|
+
m: fileStat.mtimeMs,
|
|
12592
|
+
s: fileStat.size
|
|
12593
|
+
};
|
|
12594
|
+
} catch {
|
|
12595
|
+
return null;
|
|
12596
|
+
}
|
|
12597
|
+
};
|
|
12598
|
+
const statOf = (filePath) => walkedStats.get(filePath) ?? statOfLive(filePath);
|
|
12599
|
+
return {
|
|
12600
|
+
lookupFileList: () => {
|
|
12601
|
+
const cachedFileList = store.fileList;
|
|
12602
|
+
if (cachedFileList === null || cachedFileList.hash !== collectHash || !isStringArray(cachedFileList.files)) return null;
|
|
12603
|
+
return cachedFileList.files.map((filePath, fileIndex) => ({
|
|
12604
|
+
index: fileIndex,
|
|
12605
|
+
path: filePath
|
|
12606
|
+
}));
|
|
12607
|
+
},
|
|
12608
|
+
storeFileList: (files) => {
|
|
12609
|
+
store.fileList = {
|
|
12610
|
+
hash: collectHash,
|
|
12611
|
+
files: files.map((file) => file.path)
|
|
12612
|
+
};
|
|
12613
|
+
isDirty = true;
|
|
12614
|
+
},
|
|
12615
|
+
lookupSummary: (filePath) => {
|
|
12616
|
+
const cachedSummary = store.summaries[filePath];
|
|
12617
|
+
if (!isRecordValue(cachedSummary)) return null;
|
|
12618
|
+
const fileStat = statOf(filePath);
|
|
12619
|
+
if (!fileStat) return null;
|
|
12620
|
+
if (fileStat.m !== cachedSummary.m || fileStat.s !== cachedSummary.s) {
|
|
12621
|
+
if (fileStat.s !== cachedSummary.s || typeof cachedSummary.h !== "string") return null;
|
|
12622
|
+
const contentHash = sha1OfFileBytes(filePath);
|
|
12623
|
+
if (contentHash === null || contentHash !== cachedSummary.h) return null;
|
|
12624
|
+
cachedSummary.m = fileStat.m;
|
|
12625
|
+
cachedSummary.s = fileStat.s;
|
|
12626
|
+
isDirty = true;
|
|
12627
|
+
}
|
|
12628
|
+
const revived = reviveParsedSource(cachedSummary.p);
|
|
12629
|
+
if (revived === null) return null;
|
|
12630
|
+
activeSummaryPaths.add(filePath);
|
|
12631
|
+
return revived;
|
|
12632
|
+
},
|
|
12633
|
+
storeSummary: (filePath, parsed) => {
|
|
12634
|
+
const walkStat = statOf(filePath);
|
|
12635
|
+
if (!walkStat) return;
|
|
12636
|
+
const contentHash = sha1OfFileBytes(filePath);
|
|
12637
|
+
if (contentHash === null) return;
|
|
12638
|
+
const liveStat = statOfLive(filePath);
|
|
12639
|
+
if (liveStat === null || liveStat.m !== walkStat.m || liveStat.s !== walkStat.s) return;
|
|
12640
|
+
store.summaries[filePath] = {
|
|
12641
|
+
m: walkStat.m,
|
|
12642
|
+
s: walkStat.s,
|
|
12643
|
+
h: contentHash,
|
|
12644
|
+
p: serializeParsedSource(parsed, shouldPersistDryPatternFields)
|
|
12645
|
+
};
|
|
12646
|
+
activeSummaryPaths.add(filePath);
|
|
12647
|
+
isDirty = true;
|
|
12648
|
+
},
|
|
12649
|
+
lookupResolution: (specifier, fromFile) => {
|
|
12650
|
+
const resolutionKey = `${dirname(fromFile)}::${specifier}`;
|
|
12651
|
+
const cachedResolution = resolutionEntries[resolutionKey];
|
|
12652
|
+
if (!isRecordValue(cachedResolution) || typeof cachedResolution.e !== "boolean") return null;
|
|
12653
|
+
activeResolutionKeys.add(resolutionKey);
|
|
12654
|
+
return {
|
|
12655
|
+
resolvedPath: typeof cachedResolution.p === "string" ? cachedResolution.p : void 0,
|
|
12656
|
+
isExternal: cachedResolution.e,
|
|
12657
|
+
packageName: typeof cachedResolution.n === "string" ? cachedResolution.n : void 0
|
|
12658
|
+
};
|
|
12659
|
+
},
|
|
12660
|
+
storeResolution: (specifier, fromFile, resolved) => {
|
|
12661
|
+
const resolutionKey = `${dirname(fromFile)}::${specifier}`;
|
|
12662
|
+
resolutionEntries[resolutionKey] = {
|
|
12663
|
+
p: resolved.resolvedPath ?? null,
|
|
12664
|
+
e: resolved.isExternal,
|
|
12665
|
+
n: resolved.packageName ?? null
|
|
12666
|
+
};
|
|
12667
|
+
activeResolutionKeys.add(resolutionKey);
|
|
12668
|
+
isDirty = true;
|
|
12669
|
+
},
|
|
12670
|
+
matchPackageNames: (filePath, kind, names, matcher) => {
|
|
12671
|
+
let nameSet = sortedNameSetMemo.get(names);
|
|
12672
|
+
if (nameSet === void 0) {
|
|
12673
|
+
const sortedNames = [...names].sort();
|
|
12674
|
+
nameSet = {
|
|
12675
|
+
sortedNames,
|
|
12676
|
+
hash: sha1Hex(sortedNames.join("\n"))
|
|
12677
|
+
};
|
|
12678
|
+
sortedNameSetMemo.set(names, nameSet);
|
|
12679
|
+
}
|
|
12680
|
+
activeFactPaths.add(filePath);
|
|
12681
|
+
const fileStat = statOf(filePath);
|
|
12682
|
+
const existingEntry = isRecordValue(store.packageFacts[filePath]) ? store.packageFacts[filePath] : null;
|
|
12683
|
+
let validEntry = fileStat !== null && existingEntry !== null && existingEntry.m === fileStat.m && existingEntry.s === fileStat.s ? existingEntry : null;
|
|
12684
|
+
const existingMatch = validEntry?.[kind];
|
|
12685
|
+
if (isRecordValue(existingMatch) && existingMatch.h === nameSet.hash && isStringArray(existingMatch.matched)) return existingMatch.matched;
|
|
12686
|
+
const content = readFileSync(filePath, "utf-8");
|
|
12687
|
+
const contentHash = sha1Hex(content);
|
|
12688
|
+
if (validEntry === null && fileStat !== null && existingEntry?.h === contentHash) {
|
|
12689
|
+
existingEntry.m = fileStat.m;
|
|
12690
|
+
existingEntry.s = fileStat.s;
|
|
12691
|
+
isDirty = true;
|
|
12692
|
+
validEntry = existingEntry;
|
|
12693
|
+
const repairedMatch = validEntry[kind];
|
|
12694
|
+
if (isRecordValue(repairedMatch) && repairedMatch.h === nameSet.hash && isStringArray(repairedMatch.matched)) return repairedMatch.matched;
|
|
12695
|
+
}
|
|
12696
|
+
const matchedNames = nameSet.sortedNames.filter((packageName) => matcher(content, packageName));
|
|
12697
|
+
if (fileStat !== null) {
|
|
12698
|
+
const factEntry = validEntry ?? {
|
|
12699
|
+
m: fileStat.m,
|
|
12700
|
+
s: fileStat.s,
|
|
12701
|
+
h: contentHash
|
|
12702
|
+
};
|
|
12703
|
+
factEntry[kind] = {
|
|
12704
|
+
h: nameSet.hash,
|
|
12705
|
+
matched: matchedNames
|
|
12706
|
+
};
|
|
12707
|
+
store.packageFacts[filePath] = factEntry;
|
|
12708
|
+
isDirty = true;
|
|
12709
|
+
}
|
|
12710
|
+
return matchedNames;
|
|
12711
|
+
},
|
|
12712
|
+
matchWalkedFiles: (query) => {
|
|
12713
|
+
if (toPosixPath(resolve(query.cwd)) !== walkRoot) return null;
|
|
12714
|
+
const positiveMatcher = compileGlobAlternation(query.patterns, query.dot === true);
|
|
12715
|
+
if (positiveMatcher === null) return null;
|
|
12716
|
+
const ignoreMatcher = query.ignore.length > 0 ? compileGlobAlternation(query.ignore, true) : null;
|
|
12717
|
+
if (query.ignore.length > 0 && ignoreMatcher === null) return null;
|
|
12718
|
+
const rootPrefixLength = walkRoot.length + 1;
|
|
12719
|
+
const matchedPaths = [];
|
|
12720
|
+
for (const filePath of walkedStats.keys()) {
|
|
12721
|
+
const relativePath = filePath.slice(rootPrefixLength);
|
|
12722
|
+
if (countPathSegments(relativePath) > query.deep) continue;
|
|
12723
|
+
if (ignoreMatcher !== null && ignoreMatcher.test(relativePath)) continue;
|
|
12724
|
+
if (positiveMatcher.test(relativePath)) matchedPaths.push(filePath);
|
|
12725
|
+
}
|
|
12726
|
+
return matchedPaths.sort();
|
|
12727
|
+
},
|
|
12728
|
+
save: () => {
|
|
12729
|
+
if (!isDirty) return;
|
|
12730
|
+
const compactedSummaries = {};
|
|
12731
|
+
for (const filePath of activeSummaryPaths) {
|
|
12732
|
+
const summaryEntry = store.summaries[filePath];
|
|
12733
|
+
if (summaryEntry !== void 0) compactedSummaries[filePath] = summaryEntry;
|
|
12734
|
+
}
|
|
12735
|
+
const compactedFacts = {};
|
|
12736
|
+
for (const filePath of activeFactPaths) {
|
|
12737
|
+
const factEntry = store.packageFacts[filePath];
|
|
12738
|
+
if (factEntry !== void 0) compactedFacts[filePath] = factEntry;
|
|
12739
|
+
}
|
|
12740
|
+
const compactedResolutions = {};
|
|
12741
|
+
for (const resolutionKey of activeResolutionKeys) {
|
|
12742
|
+
const resolutionEntry = resolutionEntries[resolutionKey];
|
|
12743
|
+
if (resolutionEntry !== void 0) compactedResolutions[resolutionKey] = resolutionEntry;
|
|
12744
|
+
}
|
|
12745
|
+
const serialized = JSON.stringify({
|
|
12746
|
+
version: 2,
|
|
12747
|
+
scopeHash,
|
|
12748
|
+
fileList: store.fileList,
|
|
12749
|
+
resolutions: {
|
|
12750
|
+
hash: resolutionHash,
|
|
12751
|
+
entries: compactedResolutions
|
|
12752
|
+
},
|
|
12753
|
+
summaries: compactedSummaries,
|
|
12754
|
+
packageFacts: compactedFacts
|
|
12755
|
+
});
|
|
12756
|
+
if (serialized.length > 268435456) return;
|
|
12757
|
+
atomicWriteFile(cachePath, serialized);
|
|
12758
|
+
}
|
|
12759
|
+
};
|
|
12760
|
+
};
|
|
12761
|
+
/**
|
|
12762
|
+
* Loads the incremental cache configured at `config.incrementalCachePath`.
|
|
12763
|
+
* Returns `null` (analysis runs exactly as without a cache) when the path is
|
|
12764
|
+
* unset or anything about initialization fails.
|
|
12765
|
+
*/
|
|
12766
|
+
const loadSummaryCache = (config) => {
|
|
12767
|
+
if (!config.incrementalCachePath) return null;
|
|
12768
|
+
try {
|
|
12769
|
+
return createSummaryCache(config.incrementalCachePath, config);
|
|
12770
|
+
} catch {
|
|
12771
|
+
return null;
|
|
12772
|
+
}
|
|
12773
|
+
};
|
|
12774
|
+
|
|
12619
12775
|
//#endregion
|
|
12620
12776
|
//#region src/utils/collect-git-ignored-paths.ts
|
|
12621
12777
|
/**
|
|
@@ -12810,6 +12966,7 @@ const defineConfig = (options) => ({
|
|
|
12810
12966
|
includeExtensions: options.includeExtensions ?? DEFAULT_EXTENSIONS,
|
|
12811
12967
|
tsConfigPath: options.tsConfigPath,
|
|
12812
12968
|
paths: options.paths,
|
|
12969
|
+
incrementalCachePath: options.incrementalCachePath,
|
|
12813
12970
|
reportTypes: options.reportTypes ?? false,
|
|
12814
12971
|
includeEntryExports: options.includeEntryExports ?? false,
|
|
12815
12972
|
reportRedundancy: options.reportRedundancy ?? true,
|
|
@@ -12930,24 +13087,26 @@ const analyze = async (config) => {
|
|
|
12930
13087
|
...config,
|
|
12931
13088
|
ignorePatterns: [...config.ignorePatterns, ...allExclusionPatterns]
|
|
12932
13089
|
} : config;
|
|
13090
|
+
const entriesPromise = (configWithExclusions.incrementalCachePath ? resolveEntriesInWorker(configWithExclusions) : resolveEntries(configWithExclusions)).catch((entriesError) => {
|
|
13091
|
+
setupErrors.push(new WorkspaceError({
|
|
13092
|
+
code: "workspace-discovery-failed",
|
|
13093
|
+
message: "resolveEntries failed — defaulting to empty entry set",
|
|
13094
|
+
path: config.rootDir,
|
|
13095
|
+
detail: describeUnknownError(entriesError)
|
|
13096
|
+
}));
|
|
13097
|
+
return {
|
|
13098
|
+
productionEntries: [],
|
|
13099
|
+
testEntries: [],
|
|
13100
|
+
alwaysUsedFiles: []
|
|
13101
|
+
};
|
|
13102
|
+
});
|
|
13103
|
+
const summaryCache = loadSummaryCache(configWithExclusions);
|
|
12933
13104
|
let files;
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
message: "resolveEntries failed — defaulting to empty entry set",
|
|
12940
|
-
path: config.rootDir,
|
|
12941
|
-
detail: describeUnknownError(entriesError)
|
|
12942
|
-
}));
|
|
12943
|
-
return {
|
|
12944
|
-
productionEntries: [],
|
|
12945
|
-
testEntries: [],
|
|
12946
|
-
alwaysUsedFiles: []
|
|
12947
|
-
};
|
|
12948
|
-
})]);
|
|
12949
|
-
files = collectedFiles;
|
|
12950
|
-
discoveredEntries = resolvedEntries;
|
|
13105
|
+
const cachedFileList = summaryCache?.lookupFileList() ?? null;
|
|
13106
|
+
if (cachedFileList !== null) files = cachedFileList;
|
|
13107
|
+
else try {
|
|
13108
|
+
files = await collectSourceFiles(configWithExclusions);
|
|
13109
|
+
summaryCache?.storeFileList(files);
|
|
12951
13110
|
} catch (collectError) {
|
|
12952
13111
|
setupErrors.push(new WorkspaceError({
|
|
12953
13112
|
code: "workspace-discovery-failed",
|
|
@@ -12958,9 +13117,6 @@ const analyze = async (config) => {
|
|
|
12958
13117
|
}));
|
|
12959
13118
|
return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
|
|
12960
13119
|
}
|
|
12961
|
-
const productionEntrySet = new Set(discoveredEntries.productionEntries);
|
|
12962
|
-
const testEntrySet = new Set(discoveredEntries.testEntries);
|
|
12963
|
-
const alwaysUsedFileSet = new Set(discoveredEntries.alwaysUsedFiles);
|
|
12964
13120
|
const gitIgnoreResult = collectGitIgnoredPaths(resolve(config.rootDir), files.map((file) => file.path));
|
|
12965
13121
|
const gitIgnoredFileSet = gitIgnoreResult.ignoredPaths;
|
|
12966
13122
|
if (gitIgnoreResult.gitUnavailable) setupErrors.push(new WorkspaceError({
|
|
@@ -12992,7 +13148,41 @@ const analyze = async (config) => {
|
|
|
12992
13148
|
}));
|
|
12993
13149
|
return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
|
|
12994
13150
|
}
|
|
12995
|
-
const
|
|
13151
|
+
const resolveModuleThroughCache = (specifier, fromFile) => {
|
|
13152
|
+
if (summaryCache === null) return moduleResolver.resolveModule(specifier, fromFile);
|
|
13153
|
+
const cachedResolution = summaryCache.lookupResolution(specifier, fromFile);
|
|
13154
|
+
if (cachedResolution !== null) return cachedResolution;
|
|
13155
|
+
const resolved = moduleResolver.resolveModule(specifier, fromFile);
|
|
13156
|
+
summaryCache.storeResolution(specifier, fromFile, resolved);
|
|
13157
|
+
return resolved;
|
|
13158
|
+
};
|
|
13159
|
+
let parsedModules;
|
|
13160
|
+
let summaryMissCount = 0;
|
|
13161
|
+
if (summaryCache === null) parsedModules = await parseFilesInParallel(files);
|
|
13162
|
+
else {
|
|
13163
|
+
parsedModules = new Array(files.length);
|
|
13164
|
+
const missedFiles = [];
|
|
13165
|
+
const missedPositions = [];
|
|
13166
|
+
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
13167
|
+
const cachedSummary = summaryCache.lookupSummary(files[fileIndex].path);
|
|
13168
|
+
if (cachedSummary !== null) {
|
|
13169
|
+
parsedModules[fileIndex] = cachedSummary;
|
|
13170
|
+
continue;
|
|
13171
|
+
}
|
|
13172
|
+
missedFiles.push(files[fileIndex]);
|
|
13173
|
+
missedPositions.push(fileIndex);
|
|
13174
|
+
}
|
|
13175
|
+
summaryMissCount = missedFiles.length;
|
|
13176
|
+
const parsedMissedModules = await parseFilesInParallel(missedFiles);
|
|
13177
|
+
for (let missIndex = 0; missIndex < missedFiles.length; missIndex++) {
|
|
13178
|
+
parsedModules[missedPositions[missIndex]] = parsedMissedModules[missIndex];
|
|
13179
|
+
summaryCache.storeSummary(missedFiles[missIndex].path, parsedMissedModules[missIndex]);
|
|
13180
|
+
}
|
|
13181
|
+
}
|
|
13182
|
+
const discoveredEntries = await entriesPromise;
|
|
13183
|
+
const productionEntrySet = new Set(discoveredEntries.productionEntries);
|
|
13184
|
+
const testEntrySet = new Set(discoveredEntries.testEntries);
|
|
13185
|
+
const alwaysUsedFileSet = new Set(discoveredEntries.alwaysUsedFiles);
|
|
12996
13186
|
const graphInputs = [];
|
|
12997
13187
|
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
12998
13188
|
const file = files[fileIndex];
|
|
@@ -13000,7 +13190,7 @@ const analyze = async (config) => {
|
|
|
13000
13190
|
const resolvedImportMap = /* @__PURE__ */ new Map();
|
|
13001
13191
|
const safeResolveImport = (specifier) => {
|
|
13002
13192
|
try {
|
|
13003
|
-
return
|
|
13193
|
+
return resolveModuleThroughCache(specifier, file.path);
|
|
13004
13194
|
} catch (resolveError) {
|
|
13005
13195
|
setupErrors.push(new ResolverError({
|
|
13006
13196
|
severity: "warning",
|
|
@@ -13080,7 +13270,7 @@ const analyze = async (config) => {
|
|
|
13080
13270
|
for (const importInfo of parsedStyleModule.imports) {
|
|
13081
13271
|
let resolvedImport;
|
|
13082
13272
|
try {
|
|
13083
|
-
resolvedImport =
|
|
13273
|
+
resolvedImport = resolveModuleThroughCache(importInfo.specifier, styleFilePath);
|
|
13084
13274
|
} catch (styleResolveError) {
|
|
13085
13275
|
setupErrors.push(new ResolverError({
|
|
13086
13276
|
severity: "warning",
|
|
@@ -13144,7 +13334,7 @@ const analyze = async (config) => {
|
|
|
13144
13334
|
}
|
|
13145
13335
|
let analysisResult;
|
|
13146
13336
|
try {
|
|
13147
|
-
analysisResult = generateReport(moduleGraph, config);
|
|
13337
|
+
analysisResult = generateReport(moduleGraph, config, summaryCache ?? void 0);
|
|
13148
13338
|
} catch (reportError) {
|
|
13149
13339
|
setupErrors.push(new DetectorError({
|
|
13150
13340
|
module: "report",
|
|
@@ -13154,6 +13344,11 @@ const analyze = async (config) => {
|
|
|
13154
13344
|
}));
|
|
13155
13345
|
return buildEmptyScanResult(setupErrors, performance.now() - pipelineStartTime);
|
|
13156
13346
|
}
|
|
13347
|
+
summaryCache?.save();
|
|
13348
|
+
if (summaryCache !== null) analysisResult.incrementalCacheStats = {
|
|
13349
|
+
summaryHits: files.length - summaryMissCount,
|
|
13350
|
+
summaryMisses: summaryMissCount
|
|
13351
|
+
};
|
|
13157
13352
|
if (setupErrors.length > 0) analysisResult.analysisErrors = [...setupErrors, ...analysisResult.analysisErrors];
|
|
13158
13353
|
analysisResult.analysisTimeMs = performance.now() - pipelineStartTime;
|
|
13159
13354
|
return analysisResult;
|