@animus-ui/extract 0.1.24 → 0.1.26
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/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
- package/crates/extract-v2/index.d.ts +107 -6
- package/dist/index.cjs +48 -5
- package/dist/index.mjs +2 -2
- package/dist/pipeline/alias-agreement.d.ts +107 -0
- package/dist/pipeline/alias-agreement.d.ts.map +1 -0
- package/dist/pipeline/analyze-project-args.d.ts +11 -2
- package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
- package/dist/pipeline/asset-copies.d.ts +20 -0
- package/dist/pipeline/asset-copies.d.ts.map +1 -0
- package/dist/pipeline/built-in-asset-types.d.ts +9 -0
- package/dist/pipeline/built-in-asset-types.d.ts.map +1 -0
- package/dist/pipeline/core-options.d.ts +31 -7
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/discover-packages.d.ts +98 -12
- package/dist/pipeline/discover-packages.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +41 -3
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/engine-retirement.d.ts +7 -1
- package/dist/pipeline/engine-retirement.d.ts.map +1 -1
- package/dist/pipeline/extension-policy.d.ts +95 -0
- package/dist/pipeline/extension-policy.d.ts.map +1 -0
- package/dist/pipeline/host-assets.d.ts +32 -0
- package/dist/pipeline/host-assets.d.ts.map +1 -0
- package/dist/pipeline/host-expansions.d.ts +92 -0
- package/dist/pipeline/host-expansions.d.ts.map +1 -0
- package/dist/pipeline/host-extensions.d.ts +64 -0
- package/dist/pipeline/host-extensions.d.ts.map +1 -0
- package/dist/pipeline/host-package-resolver.d.ts +40 -0
- package/dist/pipeline/host-package-resolver.d.ts.map +1 -0
- package/dist/pipeline/index.d.ts +32 -8
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/kit-publication.d.ts.map +1 -1
- package/dist/pipeline/manifest-diagnostics.d.ts +27 -6
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/manifest-schema.d.ts +16 -0
- package/dist/pipeline/manifest-schema.d.ts.map +1 -1
- package/dist/pipeline/output-targets.d.ts +52 -0
- package/dist/pipeline/output-targets.d.ts.map +1 -0
- package/dist/pipeline/probe-evidence.d.ts +58 -0
- package/dist/pipeline/probe-evidence.d.ts.map +1 -0
- package/dist/pipeline/run-analysis.d.ts +26 -0
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/runtime-import.d.ts +10 -0
- package/dist/pipeline/runtime-import.d.ts.map +1 -0
- package/dist/pipeline/served-files.d.ts +16 -0
- package/dist/pipeline/served-files.d.ts.map +1 -0
- package/dist/pipeline/source-corpus.d.ts +4 -0
- package/dist/pipeline/source-corpus.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +37 -5
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/system-config.d.ts +18 -0
- package/dist/pipeline/system-config.d.ts.map +1 -1
- package/dist/pipeline/system-instances.d.ts +27 -0
- package/dist/pipeline/system-instances.d.ts.map +1 -0
- package/dist/pipeline/tsconfig-paths.d.ts +4 -0
- package/dist/pipeline/tsconfig-paths.d.ts.map +1 -1
- package/dist/pipeline/vite-expansions.d.ts +40 -0
- package/dist/pipeline/vite-expansions.d.ts.map +1 -0
- package/dist/pipeline/webpack-expansions.d.ts +55 -0
- package/dist/pipeline/webpack-expansions.d.ts.map +1 -0
- package/dist/pipeline/workspace-packages.d.ts +13 -0
- package/dist/pipeline/workspace-packages.d.ts.map +1 -0
- package/dist/{pipeline-Bl20tbu_.mjs → pipeline-BzMQ_Z0M.mjs} +2293 -751
- package/dist/{pipeline-CAH9mxnK.cjs → pipeline-DyUf-eKZ.cjs} +2564 -746
- package/dist/session/coalescing-window.d.ts +34 -0
- package/dist/session/coalescing-window.d.ts.map +1 -0
- package/dist/session/extraction-session.d.ts +297 -35
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session/index.d.ts +7 -3
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/published-set.d.ts +6 -1
- package/dist/session/published-set.d.ts.map +1 -1
- package/dist/session/resolve-packages.d.ts +9 -5
- package/dist/session/resolve-packages.d.ts.map +1 -1
- package/dist/session/singleton.d.ts +218 -53
- package/dist/session/singleton.d.ts.map +1 -1
- package/dist/session/turbopack-orchestrator.d.ts.map +1 -1
- package/dist/session.cjs +1462 -510
- package/dist/session.mjs +1430 -489
- package/package.json +4 -4
package/dist/session.cjs
CHANGED
|
@@ -1,8 +1,107 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_pipeline = require("./pipeline-
|
|
2
|
+
const require_pipeline = require("./pipeline-DyUf-eKZ.cjs");
|
|
3
3
|
let crypto = require("crypto");
|
|
4
4
|
let fs = require("fs");
|
|
5
5
|
let path = require("path");
|
|
6
|
+
//#region session/coalescing-window.ts
|
|
7
|
+
/**
|
|
8
|
+
* The one coalescing window (SA19). It opens on the first pending event and
|
|
9
|
+
* fires a fixed interval later: a later event joins the open window and never
|
|
10
|
+
* pushes it back, so a steady stream of edits cannot starve the work it
|
|
11
|
+
* coalesces. Builds, startup and explicit flushes bypass it through `flush`.
|
|
12
|
+
* Hosts add no timers of their own.
|
|
13
|
+
*/
|
|
14
|
+
const COALESCING_WINDOW_MS = 75;
|
|
15
|
+
const SYSTEM_CLOCK = {
|
|
16
|
+
schedule: (fn, ms) => {
|
|
17
|
+
const timer = setTimeout(fn, ms);
|
|
18
|
+
timer.unref?.();
|
|
19
|
+
return timer;
|
|
20
|
+
},
|
|
21
|
+
cancel: (timer) => clearTimeout(timer)
|
|
22
|
+
};
|
|
23
|
+
var CoalescingWindow = class {
|
|
24
|
+
fire;
|
|
25
|
+
windowMs;
|
|
26
|
+
clock;
|
|
27
|
+
timer = null;
|
|
28
|
+
constructor(fire, windowMs = 75, clock = SYSTEM_CLOCK) {
|
|
29
|
+
this.fire = fire;
|
|
30
|
+
this.windowMs = windowMs;
|
|
31
|
+
this.clock = clock;
|
|
32
|
+
}
|
|
33
|
+
/** A pending event: opens the window, or joins the open one. */
|
|
34
|
+
note() {
|
|
35
|
+
if (this.timer !== null) return;
|
|
36
|
+
this.timer = this.clock.schedule(() => {
|
|
37
|
+
this.timer = null;
|
|
38
|
+
return this.fire();
|
|
39
|
+
}, this.windowMs);
|
|
40
|
+
}
|
|
41
|
+
/** Fires now, closing any open window: the bypass. */
|
|
42
|
+
flush() {
|
|
43
|
+
this.cancel();
|
|
44
|
+
return this.fire();
|
|
45
|
+
}
|
|
46
|
+
/** Closes an open window without firing it. */
|
|
47
|
+
cancel() {
|
|
48
|
+
if (this.timer === null) return;
|
|
49
|
+
this.clock.cancel(this.timer);
|
|
50
|
+
this.timer = null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region pipeline/system-instances.ts
|
|
55
|
+
/**
|
|
56
|
+
* The system and the analysis read one instance of each kit (spec §2.2
|
|
57
|
+
* A4). Discovery asks the host which instance a kit specifier names; the
|
|
58
|
+
* system loader resolves the system file's imports itself. Until the loader
|
|
59
|
+
* takes the host's instance, a kit whose analysed instance differs from the
|
|
60
|
+
* one the system evaluated is refused: its components would be extracted
|
|
61
|
+
* against another instance's vocabulary.
|
|
62
|
+
*/
|
|
63
|
+
function realPath(path$38) {
|
|
64
|
+
try {
|
|
65
|
+
return (0, fs.realpathSync)(path$38);
|
|
66
|
+
} catch {
|
|
67
|
+
return path$38;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One error for each system-file kit specifier whose analysed package
|
|
72
|
+
* instance is not the one the system load evaluated: the file the loader's
|
|
73
|
+
* own resolution names must be among the files the load read, and lie in
|
|
74
|
+
* another package root than the instance discovery read for its package.
|
|
75
|
+
*/
|
|
76
|
+
function systemInstanceMismatches(input) {
|
|
77
|
+
const evaluated = new Set(input.evaluated.map(realPath));
|
|
78
|
+
const shown = (path$39) => (0, path.relative)(input.rootDir, path$39).split(path.sep).join("/");
|
|
79
|
+
const diagnostics = [];
|
|
80
|
+
for (const specifier of input.specifiers) {
|
|
81
|
+
if (!require_pipeline.isPackageSpecifier(specifier)) continue;
|
|
82
|
+
const analysed = input.instances[require_pipeline.bareSpecifierPackageName(specifier)];
|
|
83
|
+
if (analysed === void 0) continue;
|
|
84
|
+
let file;
|
|
85
|
+
try {
|
|
86
|
+
file = input.loaderResolve(specifier);
|
|
87
|
+
} catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (file === null || !evaluated.has(realPath(file))) continue;
|
|
91
|
+
const system = realPath(require_pipeline.findPackageRoot(realPath(file)));
|
|
92
|
+
if (system === analysed) continue;
|
|
93
|
+
diagnostics.push({
|
|
94
|
+
file: specifier,
|
|
95
|
+
component: "kit",
|
|
96
|
+
kind: "error",
|
|
97
|
+
message: `the system evaluates '${specifier}' from ${shown(system)}, but the host bundler resolves it to ${shown(analysed)}, which extraction analyses, so its components would be extracted against another instance's vocabulary — make the bundler and the system resolve '${specifier}' to one instance`,
|
|
98
|
+
code: require_pipeline.SYSTEM_INSTANCE_MISMATCH,
|
|
99
|
+
severity: require_pipeline.severityFor(require_pipeline.SYSTEM_INSTANCE_MISMATCH)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return diagnostics;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
6
105
|
//#region session/session-paths.ts
|
|
7
106
|
/**
|
|
8
107
|
* Session artifact names, path derivations, and artifact shapes. Imports no
|
|
@@ -169,9 +268,11 @@ function readCliLockRecord(dir) {
|
|
|
169
268
|
if (!isPublishedSetRecord(candidate) || !isPublishedSetNumber(candidate.pid)) return { kind: "indeterminate" };
|
|
170
269
|
const startedAt = candidate.startedAt;
|
|
171
270
|
const heartbeatAt = candidate.heartbeatAt;
|
|
271
|
+
const build = candidate.build;
|
|
172
272
|
const record = { pid: candidate.pid };
|
|
173
273
|
if (isPublishedSetString(startedAt)) record.startedAt = startedAt;
|
|
174
274
|
if (isPublishedSetString(heartbeatAt)) record.heartbeatAt = heartbeatAt;
|
|
275
|
+
if (isPublishedSetString(build)) record.build = build;
|
|
175
276
|
return {
|
|
176
277
|
kind: "held",
|
|
177
278
|
record
|
|
@@ -202,17 +303,19 @@ function checkLockLiveness(record) {
|
|
|
202
303
|
reason: `holder pid ${record.pid} is running but its lock heartbeat (${record.heartbeatAt}) is ${Math.round(ageMs / 1e3)}s old, past the ${CLI_LOCK_STALE_AFTER_MS / 1e3}s staleness window — the pid names a different process now`
|
|
203
304
|
};
|
|
204
305
|
}
|
|
205
|
-
function lockRecordBytes(startedAt) {
|
|
206
|
-
|
|
306
|
+
function lockRecordBytes(startedAt, build) {
|
|
307
|
+
const record = {
|
|
207
308
|
pid: process.pid,
|
|
208
309
|
startedAt,
|
|
209
310
|
heartbeatAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
210
|
-
}
|
|
311
|
+
};
|
|
312
|
+
if (build !== void 0) record.build = build;
|
|
313
|
+
return JSON.stringify(record);
|
|
211
314
|
}
|
|
212
315
|
const HEARTBEAT_TEMP_PREFIX = ".lock-heartbeat-";
|
|
213
316
|
function holdDirectoryClaim(dir, options = {}) {
|
|
214
317
|
const startedAt = options.startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
215
|
-
const path$
|
|
318
|
+
const path$37 = (0, path.join)(dir, CLI_LOCK_ARTIFACT);
|
|
216
319
|
const tempPath = (0, path.join)(dir, `${HEARTBEAT_TEMP_PREFIX}${process.pid}`);
|
|
217
320
|
const rename = options.write === "rename";
|
|
218
321
|
let refresh = null;
|
|
@@ -225,13 +328,13 @@ function holdDirectoryClaim(dir, options = {}) {
|
|
|
225
328
|
const stamp = () => {
|
|
226
329
|
try {
|
|
227
330
|
if (rename) {
|
|
228
|
-
(0, fs.writeFileSync)(tempPath, lockRecordBytes(startedAt));
|
|
229
|
-
(0, fs.renameSync)(tempPath, path$
|
|
230
|
-
} else (0, fs.writeFileSync)(path$
|
|
331
|
+
(0, fs.writeFileSync)(tempPath, lockRecordBytes(startedAt, options.build));
|
|
332
|
+
(0, fs.renameSync)(tempPath, path$37);
|
|
333
|
+
} else (0, fs.writeFileSync)(path$37, lockRecordBytes(startedAt, options.build));
|
|
231
334
|
} catch (error) {
|
|
232
335
|
stop();
|
|
233
336
|
if (isMissingFile(error)) return;
|
|
234
|
-
console.error(`[animus] Could not write the directory claim ${path$
|
|
337
|
+
console.error(`[animus] Could not write the directory claim ${path$37}: ${String(error)}`);
|
|
235
338
|
}
|
|
236
339
|
};
|
|
237
340
|
stamp();
|
|
@@ -242,7 +345,7 @@ function holdDirectoryClaim(dir, options = {}) {
|
|
|
242
345
|
return () => {
|
|
243
346
|
stop();
|
|
244
347
|
if (rename) (0, fs.rmSync)(tempPath, { force: true });
|
|
245
|
-
(0, fs.rmSync)(path$
|
|
348
|
+
(0, fs.rmSync)(path$37, { force: true });
|
|
246
349
|
};
|
|
247
350
|
}
|
|
248
351
|
/** Snapshot the session's copied asset() files, sorted, BEFORE the session
|
|
@@ -257,320 +360,424 @@ function collectSessionAssets(sessionDir) {
|
|
|
257
360
|
}));
|
|
258
361
|
}
|
|
259
362
|
//#endregion
|
|
260
|
-
//#region session/resolve-packages.ts
|
|
261
|
-
/** An exports target under the `import` condition, then `default`, as the
|
|
262
|
-
* system loader reads it; an array offers its targets in order. */
|
|
263
|
-
function importTarget(value) {
|
|
264
|
-
if (require_pipeline.isJsonString(value)) return value;
|
|
265
|
-
if (Array.isArray(value)) {
|
|
266
|
-
for (const entry of value) {
|
|
267
|
-
const target = importTarget(entry);
|
|
268
|
-
if (target !== null) return target;
|
|
269
|
-
}
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
272
|
-
if (!require_pipeline.isJsonBlock(value)) return null;
|
|
273
|
-
return importTarget(value.import) ?? importTarget(value.default);
|
|
274
|
-
}
|
|
275
|
-
/** The exports entry for `subpath`: an exact key answers alone; otherwise
|
|
276
|
-
* the pattern with the longest prefix, then the longest suffix, wins. */
|
|
277
|
-
function exportsTarget(exports, subpath) {
|
|
278
|
-
const map = require_pipeline.isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
|
|
279
|
-
if (subpath in map) return importTarget(map[subpath]);
|
|
280
|
-
let best = null;
|
|
281
|
-
for (const [pattern, value] of Object.entries(map)) {
|
|
282
|
-
const star = pattern.indexOf("*");
|
|
283
|
-
if (star === -1 || pattern.indexOf("*", star + 1) !== -1) continue;
|
|
284
|
-
const prefix = pattern.slice(0, star);
|
|
285
|
-
const suffix = pattern.slice(star + 1);
|
|
286
|
-
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix) || subpath.length < prefix.length + suffix.length) continue;
|
|
287
|
-
if (!best || prefix.length > best.prefix.length || prefix.length === best.prefix.length && suffix.length > best.suffix.length) best = {
|
|
288
|
-
prefix,
|
|
289
|
-
suffix,
|
|
290
|
-
value
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
if (!best) return null;
|
|
294
|
-
const matched = subpath.slice(best.prefix.length, subpath.length - best.suffix.length);
|
|
295
|
-
return importTarget(best.value)?.replaceAll("*", matched) ?? null;
|
|
296
|
-
}
|
|
297
|
-
/** The entry an ES import of `name` resolves to through the package's
|
|
298
|
-
* `exports`, under the `import` condition, then `default`: what the Vite
|
|
299
|
-
* host and the system loader resolve. Null without an `exports` field, or
|
|
300
|
-
* when no target exists. */
|
|
301
|
-
function resolveImportEntry(rootDir, name) {
|
|
302
|
-
const packageName = name.split("/").slice(0, name.startsWith("@") ? 2 : 1).join("/");
|
|
303
|
-
const subpath = `.${name.slice(packageName.length)}`;
|
|
304
|
-
for (let dir = rootDir;; dir = (0, path.dirname)(dir)) {
|
|
305
|
-
const manifest = (0, path.join)(dir, "node_modules", packageName, "package.json");
|
|
306
|
-
if ((0, fs.existsSync)(manifest)) {
|
|
307
|
-
let exports;
|
|
308
|
-
try {
|
|
309
|
-
const parsed = require_pipeline.parseInternalWire((0, fs.readFileSync)(manifest, "utf-8"), `${manifest} (a package manifest)`);
|
|
310
|
-
exports = require_pipeline.isJsonBlock(parsed) ? parsed.exports : void 0;
|
|
311
|
-
} catch {
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
if (exports === void 0 || exports === null) return null;
|
|
315
|
-
const target = exportsTarget(exports, subpath);
|
|
316
|
-
if (target === null) return null;
|
|
317
|
-
const entryPath = (0, path.resolve)((0, path.dirname)(manifest), target);
|
|
318
|
-
return (0, fs.existsSync)(entryPath) ? (0, fs.realpathSync)(entryPath) : null;
|
|
319
|
-
}
|
|
320
|
-
if (dir === (0, path.dirname)(dir)) return null;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
function resolvePackagesByName(rootDir, names) {
|
|
324
|
-
if (names.length === 0) return {};
|
|
325
|
-
const nameSet = new Set(names);
|
|
326
|
-
const resolved = /* @__PURE__ */ new Set();
|
|
327
|
-
const packageMap = {};
|
|
328
|
-
try {
|
|
329
|
-
const rootPkg = JSON.parse((0, fs.readFileSync)((0, path.join)(rootDir, "package.json"), "utf-8"));
|
|
330
|
-
const workspaces = Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces?.packages ?? [];
|
|
331
|
-
for (const ws of workspaces) {
|
|
332
|
-
const wsDir = (0, path.resolve)(rootDir, ws);
|
|
333
|
-
if (!(0, fs.existsSync)(wsDir)) continue;
|
|
334
|
-
try {
|
|
335
|
-
const pkg = JSON.parse((0, fs.readFileSync)((0, path.join)(wsDir, "package.json"), "utf-8"));
|
|
336
|
-
const name = pkg.name || "";
|
|
337
|
-
if (nameSet.has(name)) {
|
|
338
|
-
const main = pkg.main || pkg.module || "index.ts";
|
|
339
|
-
const entryPath = (0, path.resolve)(wsDir, main);
|
|
340
|
-
if ((0, fs.existsSync)(entryPath)) {
|
|
341
|
-
packageMap[name] = (0, path.relative)(rootDir, entryPath);
|
|
342
|
-
resolved.add(name);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
} catch {}
|
|
346
|
-
}
|
|
347
|
-
} catch {}
|
|
348
|
-
for (const name of nameSet) {
|
|
349
|
-
if (resolved.has(name)) continue;
|
|
350
|
-
const importEntry = resolveImportEntry(rootDir, name);
|
|
351
|
-
if (importEntry !== null) {
|
|
352
|
-
packageMap[name] = (0, path.relative)(rootDir, importEntry);
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
try {
|
|
356
|
-
const entryPath = require.resolve(name, { paths: [rootDir] });
|
|
357
|
-
packageMap[name] = (0, path.relative)(rootDir, entryPath);
|
|
358
|
-
} catch {}
|
|
359
|
-
}
|
|
360
|
-
return packageMap;
|
|
361
|
-
}
|
|
362
|
-
//#endregion
|
|
363
363
|
//#region session/singleton.ts
|
|
364
364
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
365
|
+
* A build's shared state lives in one registry on `globalThis`, keyed by its
|
|
366
|
+
* `SessionKey`, so the ESM plugin copy and the CJS webpack-loader copy of
|
|
367
|
+
* this module find the same entry, and two builds of one root never share
|
|
368
|
+
* one. A reader of a build no participant has joined gets the empty value;
|
|
369
|
+
* a writer must have joined it.
|
|
367
370
|
*/
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
const SHARED_EXTERNAL_DIRS_KEY = "__animus_external_pkg_dirs__";
|
|
373
|
-
const SHARED_EXTERNAL_ENTRIES_KEY = "__animus_external_source_entries__";
|
|
374
|
-
const SHARED_EXTERNAL_SIDE_EFFECTS_KEY = "__animus_external_side_effects__";
|
|
375
|
-
const ANALYZED_HASHES_KEY = "__animus_analyzed_hashes__";
|
|
376
|
-
const REPLACEMENT_EPOCH_KEY = "__animus_replacement_epoch__";
|
|
377
|
-
const WATCH_TRANSACTION_KEY = "__animus_watch_transaction__";
|
|
378
|
-
const PROCESS_SESSION_ID_KEY = "__animus_process_session_id__";
|
|
379
|
-
const SESSION_ARTIFACT_DIR_KEY = "__animus_session_artifact_dir__";
|
|
380
|
-
const OWNING_WATCH_SESSION_KEY = "__animus_owning_watch_session__";
|
|
381
|
-
const EXCLUSIVE_SESSION_OWNER_KEY = "__animus_exclusive_session_owner__";
|
|
382
|
-
const ENGINE_KEY = "__animus_engine__";
|
|
383
|
-
const V2_ENGINE_KEY = "__animus_v2_engine__";
|
|
384
|
-
const V2_SENT_SOURCES_KEY = "__animus_v2_sent_sources__";
|
|
385
|
-
const V2_DRIFT_WARNED_KEY = "__animus_v2_drift_warned__";
|
|
386
|
-
const ENGINE_API_OVERRIDE_KEY = "__animus_engine_api_override__";
|
|
387
|
-
const singletonStore = globalThis;
|
|
388
|
-
const isSingletonString = (value) => {
|
|
389
|
-
if (Object(value) === value) return false;
|
|
390
|
-
try {
|
|
391
|
-
String.prototype.valueOf.call(value);
|
|
392
|
-
return true;
|
|
393
|
-
} catch {
|
|
394
|
-
return false;
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
function getManifestJson() {
|
|
398
|
-
return singletonStore[MANIFEST_KEY];
|
|
371
|
+
function joined(key) {
|
|
372
|
+
const state = findSession(key);
|
|
373
|
+
if (state === void 0) throw new Error(`[animus] no participant has joined build ${key.build} of ${key.root}`);
|
|
374
|
+
return state;
|
|
399
375
|
}
|
|
400
|
-
function
|
|
401
|
-
|
|
376
|
+
function getManifestJson(key) {
|
|
377
|
+
return findSession(key)?.manifestJson ?? null;
|
|
378
|
+
}
|
|
379
|
+
function setManifestJson(key, json) {
|
|
380
|
+
joined(key).manifestJson = json;
|
|
402
381
|
}
|
|
403
382
|
/**
|
|
404
383
|
* A latch over the most recently STARTED analysis, never cleared on settle:
|
|
405
384
|
* non-null means one was scheduled. In-flight lives in getWatchTransaction.
|
|
406
385
|
*/
|
|
407
|
-
function getAnalysisStartedPromise() {
|
|
408
|
-
return
|
|
386
|
+
function getAnalysisStartedPromise(key) {
|
|
387
|
+
return findSession(key)?.analysisStarted ?? null;
|
|
388
|
+
}
|
|
389
|
+
function setAnalysisStartedPromise(key, promise) {
|
|
390
|
+
joined(key).analysisStarted = promise;
|
|
409
391
|
}
|
|
410
|
-
function
|
|
411
|
-
|
|
392
|
+
function resetAnalysisStartedPromise(key) {
|
|
393
|
+
const state = findSession(key);
|
|
394
|
+
if (state !== void 0) state.analysisStarted = null;
|
|
412
395
|
}
|
|
413
|
-
|
|
414
|
-
|
|
396
|
+
/** True for the build's first caller only: the one that reports its
|
|
397
|
+
* unmatched user excludes. */
|
|
398
|
+
function claimUnmatchedExcludeReport(key) {
|
|
399
|
+
const state = joined(key);
|
|
400
|
+
if (state.unmatchedExcludesReported) return false;
|
|
401
|
+
state.unmatchedExcludesReported = true;
|
|
402
|
+
return true;
|
|
415
403
|
}
|
|
416
|
-
function getSharedCss() {
|
|
417
|
-
return
|
|
404
|
+
function getSharedCss(key) {
|
|
405
|
+
return findSession(key)?.sharedCss ?? "";
|
|
418
406
|
}
|
|
419
|
-
function setSharedCss(css) {
|
|
420
|
-
|
|
407
|
+
function setSharedCss(key, css) {
|
|
408
|
+
joined(key).sharedCss = css;
|
|
421
409
|
}
|
|
422
|
-
function getSharedSystemProps() {
|
|
423
|
-
return
|
|
410
|
+
function getSharedSystemProps(key) {
|
|
411
|
+
return findSession(key)?.sharedSystemProps ?? "";
|
|
424
412
|
}
|
|
425
|
-
function setSharedSystemProps(content) {
|
|
426
|
-
|
|
413
|
+
function setSharedSystemProps(key, content) {
|
|
414
|
+
joined(key).sharedSystemProps = content;
|
|
427
415
|
}
|
|
428
|
-
function getSharedExternalDirs() {
|
|
429
|
-
return
|
|
416
|
+
function getSharedExternalDirs(key) {
|
|
417
|
+
return findSession(key)?.externalDirs ?? [];
|
|
430
418
|
}
|
|
431
|
-
function setSharedExternalDirs(dirs) {
|
|
432
|
-
|
|
419
|
+
function setSharedExternalDirs(key, dirs) {
|
|
420
|
+
joined(key).externalDirs = dirs;
|
|
433
421
|
}
|
|
434
|
-
function getSharedExternalEntries() {
|
|
435
|
-
return
|
|
422
|
+
function getSharedExternalEntries(key) {
|
|
423
|
+
return findSession(key)?.externalEntries ?? /* @__PURE__ */ new Map();
|
|
436
424
|
}
|
|
437
|
-
function setSharedExternalEntries(entries) {
|
|
438
|
-
|
|
425
|
+
function setSharedExternalEntries(key, entries) {
|
|
426
|
+
joined(key).externalEntries = entries;
|
|
439
427
|
}
|
|
440
428
|
/** Specifier → its source entry's side effects, published with the shared
|
|
441
429
|
* entries; absent leaves the bundler's own classification. */
|
|
442
|
-
function getSharedExternalSideEffects() {
|
|
443
|
-
return
|
|
430
|
+
function getSharedExternalSideEffects(key) {
|
|
431
|
+
return findSession(key)?.externalSideEffects ?? /* @__PURE__ */ new Map();
|
|
444
432
|
}
|
|
445
|
-
function setSharedExternalSideEffects(sideEffects) {
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
function globalSlot(key) {
|
|
449
|
-
return {
|
|
450
|
-
get: () => singletonStore[key] ?? null,
|
|
451
|
-
set: (value) => {
|
|
452
|
-
singletonStore[key] = value;
|
|
453
|
-
}
|
|
454
|
-
};
|
|
433
|
+
function setSharedExternalSideEffects(key, sideEffects) {
|
|
434
|
+
joined(key).externalSideEffects = sideEffects;
|
|
455
435
|
}
|
|
456
|
-
const analyzedHashesSlot = globalSlot(ANALYZED_HASHES_KEY);
|
|
457
|
-
const replacementEpochSlot = globalSlot(REPLACEMENT_EPOCH_KEY);
|
|
458
|
-
const watchTransactionSlot = globalSlot(WATCH_TRANSACTION_KEY);
|
|
459
|
-
const sessionArtifactDirSlot = globalSlot(SESSION_ARTIFACT_DIR_KEY);
|
|
460
|
-
const owningWatchSessionSlot = globalSlot(OWNING_WATCH_SESSION_KEY);
|
|
461
436
|
/**
|
|
462
|
-
* The only session that can run watch analysis. A non-owning
|
|
463
|
-
*
|
|
437
|
+
* The only session of a build that can run watch analysis. A non-owning
|
|
438
|
+
* session's observations queue for it; dropping them loses files only that
|
|
439
|
+
* watcher sees.
|
|
464
440
|
*/
|
|
465
|
-
function getOwningWatchSession() {
|
|
466
|
-
return
|
|
441
|
+
function getOwningWatchSession(key) {
|
|
442
|
+
return findSession(key)?.authority ?? null;
|
|
467
443
|
}
|
|
468
|
-
function setOwningWatchSession(session) {
|
|
469
|
-
|
|
444
|
+
function setOwningWatchSession(key, session) {
|
|
445
|
+
joined(key).authority = session;
|
|
470
446
|
}
|
|
471
447
|
/**
|
|
472
448
|
* Per-file content hashes of the last published analysis (relPath → hash of
|
|
473
449
|
* the exact bytes analyzed), including files that produced zero entries.
|
|
474
450
|
*/
|
|
475
|
-
function getAnalyzedHashes() {
|
|
476
|
-
return
|
|
451
|
+
function getAnalyzedHashes(key) {
|
|
452
|
+
return findSession(key)?.analyzedHashes ?? null;
|
|
477
453
|
}
|
|
478
|
-
function setAnalyzedHashes(hashes) {
|
|
479
|
-
|
|
454
|
+
function setAnalyzedHashes(key, hashes) {
|
|
455
|
+
joined(key).analyzedHashes = hashes;
|
|
480
456
|
}
|
|
481
|
-
/**
|
|
482
|
-
*
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
const fresh = (0, crypto.randomUUID)();
|
|
489
|
-
singletonStore[PROCESS_SESSION_ID_KEY] = fresh;
|
|
490
|
-
return fresh;
|
|
457
|
+
/** What the build's last published analysis read of the discovered
|
|
458
|
+
* packages (spec §2.2 A4); null before one publishes. */
|
|
459
|
+
function getSharedPackageReadRecord(key) {
|
|
460
|
+
return findSession(key)?.packageReadRecord ?? null;
|
|
461
|
+
}
|
|
462
|
+
function setSharedPackageReadRecord(key, record) {
|
|
463
|
+
joined(key).packageReadRecord = record;
|
|
491
464
|
}
|
|
492
465
|
/**
|
|
493
|
-
*
|
|
494
|
-
*
|
|
466
|
+
* One participant publishes a build's analysis, holding this claim until it
|
|
467
|
+
* closes: a second publisher would clobber its artifacts. Two builds never
|
|
468
|
+
* exclude each other; sequential claim and release cycles are legal.
|
|
495
469
|
*/
|
|
496
|
-
function claimExclusiveSessionOwner(label) {
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
470
|
+
function claimExclusiveSessionOwner(key, label) {
|
|
471
|
+
const state = joined(key);
|
|
472
|
+
const active = state.activeAttempt;
|
|
473
|
+
if (active !== null) throw new Error(`[animus] "${label}" started an analysis of build ${key.build} of ${key.root}, which "${active}" publishes. A build's participants share one analysis; run it once and let the others read its results.`);
|
|
474
|
+
state.activeAttempt = label;
|
|
500
475
|
return () => {
|
|
501
|
-
if (
|
|
476
|
+
if (state.activeAttempt === label) state.activeAttempt = null;
|
|
502
477
|
};
|
|
503
478
|
}
|
|
504
479
|
/**
|
|
505
|
-
* Absolute artifact directory of the
|
|
480
|
+
* Absolute artifact directory of the build's last publication. The
|
|
506
481
|
* Turbopack loader runs out of process and receives it via options instead.
|
|
507
482
|
*/
|
|
508
|
-
function getSessionArtifactDir() {
|
|
509
|
-
return
|
|
483
|
+
function getSessionArtifactDir(key) {
|
|
484
|
+
return findSession(key)?.artifactDir ?? null;
|
|
510
485
|
}
|
|
511
|
-
function setSessionArtifactDir(dir) {
|
|
512
|
-
|
|
486
|
+
function setSessionArtifactDir(key, dir) {
|
|
487
|
+
joined(key).artifactDir = dir;
|
|
513
488
|
}
|
|
514
489
|
/**
|
|
515
490
|
* Replacement epoch of the last published analysis, written AFTER the
|
|
516
491
|
* manifest so an epoch reader always sees at least that generation's manifest.
|
|
517
492
|
*/
|
|
518
|
-
function getReplacementEpoch() {
|
|
519
|
-
return
|
|
493
|
+
function getReplacementEpoch(key) {
|
|
494
|
+
return findSession(key)?.replacementEpoch ?? null;
|
|
520
495
|
}
|
|
521
|
-
function setReplacementEpoch(epoch) {
|
|
522
|
-
|
|
496
|
+
function setReplacementEpoch(key, epoch) {
|
|
497
|
+
joined(key).replacementEpoch = epoch;
|
|
523
498
|
}
|
|
524
499
|
/**
|
|
525
|
-
* The one in-flight watch-analysis transaction: a
|
|
526
|
-
* it joins this promise rather than read a pre-transaction
|
|
500
|
+
* The build's one in-flight watch-analysis transaction: a participant that
|
|
501
|
+
* did not start it joins this promise rather than read a pre-transaction
|
|
502
|
+
* generation.
|
|
527
503
|
*/
|
|
528
|
-
function getWatchTransaction() {
|
|
529
|
-
return
|
|
504
|
+
function getWatchTransaction(key) {
|
|
505
|
+
return findSession(key)?.watchTransaction ?? null;
|
|
506
|
+
}
|
|
507
|
+
function observationQueue(key) {
|
|
508
|
+
return joined(key).observations;
|
|
530
509
|
}
|
|
531
|
-
function
|
|
532
|
-
|
|
510
|
+
function getSystemAuthority(key) {
|
|
511
|
+
return findSession(key)?.systemAuthority ?? null;
|
|
533
512
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
function setSharedEngine(engine) {
|
|
537
|
-
singletonStore[ENGINE_KEY] = engine;
|
|
513
|
+
function setSystemAuthority(key, authority) {
|
|
514
|
+
joined(key).systemAuthority = authority;
|
|
538
515
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
516
|
+
function setWatchTransaction(key, transaction) {
|
|
517
|
+
joined(key).watchTransaction = transaction;
|
|
541
518
|
}
|
|
542
519
|
/** Single choke-point for every native extraction call; the NAPI module's
|
|
543
520
|
* own `.d.ts` is the authoritative surface for what it returns. */
|
|
544
521
|
function requireEngine() {
|
|
545
522
|
return require("@animus-ui/extract");
|
|
546
523
|
}
|
|
524
|
+
const ENGINE_API_OVERRIDE_KEY = "__animus_engine_api_override__";
|
|
525
|
+
const overrideGlobal = globalThis;
|
|
526
|
+
/** The native engine module could not be loaded: an environment failure,
|
|
527
|
+
* never answered by another engine. */
|
|
528
|
+
var EnvironmentFailure = class extends Error {
|
|
529
|
+
code = "EnvironmentFailure";
|
|
530
|
+
constructor(cause) {
|
|
531
|
+
super(`[animus] EnvironmentFailure: the native extraction engine could not be loaded: ${String(cause)}`, { cause });
|
|
532
|
+
this.name = "EnvironmentFailure";
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
let loadEngineFactory = requireEngine;
|
|
536
|
+
/** The native factory, loaded once for every build in the process. A load
|
|
537
|
+
* failure is an EnvironmentFailure, and is not cached: the next use tries
|
|
538
|
+
* the load again. */
|
|
539
|
+
function engineFactory() {
|
|
540
|
+
const registry = sessionRegistry();
|
|
541
|
+
if (registry.factory === null) try {
|
|
542
|
+
registry.factory = loadEngineFactory();
|
|
543
|
+
} catch (error) {
|
|
544
|
+
throw new EnvironmentFailure(error instanceof Error ? error : String(error));
|
|
545
|
+
}
|
|
546
|
+
return registry.factory;
|
|
547
|
+
}
|
|
547
548
|
/**
|
|
548
|
-
*
|
|
549
|
-
*
|
|
549
|
+
* One engine-handle store (SA18): the engine API over `key`'s own engine
|
|
550
|
+
* state, made once per key in the registry every module copy joins. The
|
|
551
|
+
* native factory is shared; each key's engine instance, analyze-time
|
|
552
|
+
* sources and drift flag are its own, and its build's retirement releases
|
|
553
|
+
* them. A key need not be a joined build's: a Vite context or a Turbopack
|
|
554
|
+
* worker keys its own.
|
|
550
555
|
*/
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
556
|
+
function engineHandle(key, options) {
|
|
557
|
+
const engines = sessionRegistry().engines;
|
|
558
|
+
const id = sessionKeyId(key);
|
|
559
|
+
let api = engines.get(id);
|
|
560
|
+
if (api === void 0) {
|
|
561
|
+
let engine = null;
|
|
562
|
+
let sentSources = null;
|
|
563
|
+
let driftWarned = false;
|
|
564
|
+
api = require_pipeline.createV2EngineApi({
|
|
565
|
+
...options,
|
|
566
|
+
isV2: () => true,
|
|
567
|
+
loadNativeEngine: engineFactory,
|
|
568
|
+
store: {
|
|
569
|
+
getEngine: () => engine,
|
|
570
|
+
setEngine: (next) => {
|
|
571
|
+
engine = next;
|
|
572
|
+
},
|
|
573
|
+
getSentSources: () => sentSources,
|
|
574
|
+
setSentSources: (sources) => {
|
|
575
|
+
sentSources = sources;
|
|
576
|
+
},
|
|
577
|
+
getDriftWarned: () => driftWarned,
|
|
578
|
+
setDriftWarned: (value) => {
|
|
579
|
+
driftWarned = value;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
engines.set(id, api);
|
|
584
|
+
}
|
|
585
|
+
return api;
|
|
586
|
+
}
|
|
587
|
+
/** Releases a standalone handle, one no joined build retires: its host
|
|
588
|
+
* calls this when it closes. */
|
|
589
|
+
function releaseEngineHandle(key) {
|
|
590
|
+
sessionRegistry().engines.delete(sessionKeyId(key));
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* The build's engine API, from the one engine-handle store.
|
|
594
|
+
*/
|
|
595
|
+
function engineApi(key) {
|
|
596
|
+
const override = overrideGlobal[ENGINE_API_OVERRIDE_KEY];
|
|
597
|
+
if (override) return override();
|
|
598
|
+
joined(key);
|
|
599
|
+
return engineHandle(key, {
|
|
600
|
+
label: "animus",
|
|
601
|
+
passThroughUnknownPaths: true
|
|
602
|
+
})();
|
|
603
|
+
}
|
|
604
|
+
/** A fresh build id, created once by the owning host wrapper. */
|
|
605
|
+
function createBuildId() {
|
|
606
|
+
return (0, crypto.randomUUID)();
|
|
607
|
+
}
|
|
608
|
+
/** The key of build `build` of `root`, by the root's canonical path. */
|
|
609
|
+
function sessionKey(build, root) {
|
|
610
|
+
const absolute = (0, path.resolve)(root);
|
|
611
|
+
let canonical = absolute;
|
|
612
|
+
try {
|
|
613
|
+
canonical = (0, fs.realpathSync)(absolute);
|
|
614
|
+
} catch {}
|
|
615
|
+
return {
|
|
616
|
+
build,
|
|
617
|
+
root: canonical,
|
|
618
|
+
protocol: 1
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function sessionKeyId(key) {
|
|
622
|
+
return JSON.stringify([
|
|
623
|
+
key.protocol,
|
|
624
|
+
key.build,
|
|
625
|
+
key.root
|
|
626
|
+
]);
|
|
627
|
+
}
|
|
628
|
+
/** A participant joined a session with semantic options that differ from
|
|
629
|
+
* those the session was created with. */
|
|
630
|
+
var SessionConfigurationConflict = class extends Error {
|
|
631
|
+
key;
|
|
632
|
+
established;
|
|
633
|
+
joining;
|
|
634
|
+
differing;
|
|
635
|
+
code = "SessionConfigurationConflict";
|
|
636
|
+
constructor(key, established, joining, differing, requirement = "Every participant of one build must use the same Animus options.") {
|
|
637
|
+
super(`[animus] SessionConfigurationConflict: "${joining}" joined build ${key.build} of ${key.root} with ${differing.join(", ")} differing from "${established}". ${requirement}`);
|
|
638
|
+
this.key = key;
|
|
639
|
+
this.established = established;
|
|
640
|
+
this.joining = joining;
|
|
641
|
+
this.differing = differing;
|
|
642
|
+
this.name = "SessionConfigurationConflict";
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
function digestOf(field) {
|
|
646
|
+
return {
|
|
647
|
+
field,
|
|
648
|
+
text: (options) => {
|
|
649
|
+
const value = options[field];
|
|
650
|
+
return value === void 0 ? void 0 : require_pipeline.stableStringify(value);
|
|
568
651
|
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Every option, classified: one a build's analysis depends on, which every
|
|
656
|
+
* participant must agree on, or one deliberately excluded. `verbose` changes
|
|
657
|
+
* no output, and the root is the key's. The type checker rejects an option
|
|
658
|
+
* added to `AnimusCoreOptions` until it is classified here.
|
|
659
|
+
*/
|
|
660
|
+
const OPTION_CLASSES = {
|
|
661
|
+
system: digestOf("system"),
|
|
662
|
+
exclude: digestOf("exclude"),
|
|
663
|
+
extensions: digestOf("extensions"),
|
|
664
|
+
strict: digestOf("strict"),
|
|
665
|
+
diagnostics: digestOf("diagnostics"),
|
|
666
|
+
verbose: "excluded",
|
|
667
|
+
prefix: digestOf("prefix"),
|
|
668
|
+
prefixContextualVars: digestOf("prefixContextualVars"),
|
|
669
|
+
targets: digestOf("targets"),
|
|
670
|
+
minify: digestOf("minify"),
|
|
671
|
+
staticCss: digestOf("staticCss"),
|
|
672
|
+
layers: digestOf("layers"),
|
|
673
|
+
engine: digestOf("engine"),
|
|
674
|
+
mode: digestOf("mode"),
|
|
675
|
+
root: "excluded"
|
|
676
|
+
};
|
|
677
|
+
/** The options a build's analysis depends on, each as stable text; each is
|
|
678
|
+
* data by its type, so equal text is an equal value. A host's own options
|
|
679
|
+
* are not the build's. */
|
|
680
|
+
function semanticOptions(options) {
|
|
681
|
+
const present = [];
|
|
682
|
+
for (const [name, digest] of Object.entries(OPTION_CLASSES)) {
|
|
683
|
+
if (digest === "excluded") continue;
|
|
684
|
+
const text = digest.text(options);
|
|
685
|
+
if (text !== void 0) present.push([name, text]);
|
|
686
|
+
}
|
|
687
|
+
return new Map(present);
|
|
688
|
+
}
|
|
689
|
+
const SESSION_REGISTRY = Symbol.for("animus.session-registry.v1");
|
|
690
|
+
const registryGlobal = globalThis;
|
|
691
|
+
function sessionRegistry() {
|
|
692
|
+
return registryGlobal[SESSION_REGISTRY] ??= {
|
|
693
|
+
sessions: /* @__PURE__ */ new Map(),
|
|
694
|
+
engines: /* @__PURE__ */ new Map(),
|
|
695
|
+
factory: null
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Joins `participant` to the session of `key`, creating it with `options` on
|
|
700
|
+
* the first join. A later participant must bring the same semantic options.
|
|
701
|
+
*/
|
|
702
|
+
function joinSession(key, participant, options) {
|
|
703
|
+
const sessions = sessionRegistry().sessions;
|
|
704
|
+
const id = sessionKeyId(key);
|
|
705
|
+
const joining = semanticOptions(options);
|
|
706
|
+
const existing = sessions.get(id);
|
|
707
|
+
if (existing === void 0) {
|
|
708
|
+
const state = {
|
|
709
|
+
key,
|
|
710
|
+
established: participant,
|
|
711
|
+
options: joining,
|
|
712
|
+
participants: /* @__PURE__ */ new Set([participant]),
|
|
713
|
+
activeAttempt: null,
|
|
714
|
+
authority: null,
|
|
715
|
+
analysisStarted: null,
|
|
716
|
+
watchTransaction: null,
|
|
717
|
+
observations: {
|
|
718
|
+
revision: 0,
|
|
719
|
+
rescan: false,
|
|
720
|
+
untaken: /* @__PURE__ */ new Map(),
|
|
721
|
+
suppliedSources: /* @__PURE__ */ new Map(),
|
|
722
|
+
revisions: /* @__PURE__ */ new Map(),
|
|
723
|
+
active: null
|
|
724
|
+
},
|
|
725
|
+
systemAuthority: null,
|
|
726
|
+
claimRecord: null,
|
|
727
|
+
manifestJson: null,
|
|
728
|
+
sharedCss: "",
|
|
729
|
+
sharedSystemProps: "",
|
|
730
|
+
externalDirs: [],
|
|
731
|
+
externalEntries: /* @__PURE__ */ new Map(),
|
|
732
|
+
externalSideEffects: /* @__PURE__ */ new Map(),
|
|
733
|
+
analyzedHashes: null,
|
|
734
|
+
packageReadRecord: null,
|
|
735
|
+
replacementEpoch: null,
|
|
736
|
+
artifactDir: null,
|
|
737
|
+
unmatchedExcludesReported: false
|
|
738
|
+
};
|
|
739
|
+
sessions.set(id, state);
|
|
740
|
+
return state;
|
|
741
|
+
}
|
|
742
|
+
const differing = [.../* @__PURE__ */ new Set([...existing.options.keys(), ...joining.keys()])].filter((name) => existing.options.get(name) !== joining.get(name));
|
|
743
|
+
if (differing.length > 0) throw new SessionConfigurationConflict(key, existing.established, participant, differing.sort());
|
|
744
|
+
existing.participants.add(participant);
|
|
745
|
+
return existing;
|
|
746
|
+
}
|
|
747
|
+
/** The session of `key`, if a participant has joined it. */
|
|
748
|
+
function findSession(key) {
|
|
749
|
+
return sessionRegistry().sessions.get(sessionKeyId(key));
|
|
750
|
+
}
|
|
751
|
+
/** The build's on-disk claim record, which stops another process pruning
|
|
752
|
+
* its tree: held once by the build, whichever participant publishes, and
|
|
753
|
+
* released only when the build retires. */
|
|
754
|
+
function holdBuildClaimRecord(key, hold) {
|
|
755
|
+
const state = joined(key);
|
|
756
|
+
state.claimRecord ??= hold();
|
|
757
|
+
}
|
|
758
|
+
/** `participant` leaves the build of `key`. The build retires with its
|
|
759
|
+
* last participant, once no attempt is in flight: it leaves the registry
|
|
760
|
+
* and releases its claim record, so its tree is pruned once stale. */
|
|
761
|
+
function leaveSession(key, participant) {
|
|
762
|
+
const state = findSession(key);
|
|
763
|
+
if (state === void 0) return;
|
|
764
|
+
state.participants.delete(participant);
|
|
765
|
+
retireIfUnused(sessionKeyId(key), state);
|
|
766
|
+
}
|
|
767
|
+
function retireIfUnused(id, state) {
|
|
768
|
+
if (state.participants.size > 0) return;
|
|
769
|
+
const inflight = state.watchTransaction;
|
|
770
|
+
if (inflight !== null) {
|
|
771
|
+
const retry = () => retireIfUnused(id, state);
|
|
772
|
+
inflight.then(retry, retry);
|
|
773
|
+
return;
|
|
569
774
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
775
|
+
const sessions = sessionRegistry().sessions;
|
|
776
|
+
if (sessions.get(id) !== state) return;
|
|
777
|
+
sessions.delete(id);
|
|
778
|
+
sessionRegistry().engines.delete(id);
|
|
779
|
+
state.claimRecord?.();
|
|
780
|
+
state.claimRecord = null;
|
|
574
781
|
}
|
|
575
782
|
//#endregion
|
|
576
783
|
//#region session/timing.ts
|
|
@@ -606,6 +813,23 @@ function logBuildTimings(bt, rustTiming, log, verbose) {
|
|
|
606
813
|
}
|
|
607
814
|
//#endregion
|
|
608
815
|
//#region session/extraction-session.ts
|
|
816
|
+
/** An observation's state when its file is gone, or cannot be read; any
|
|
817
|
+
* other state is the content hash it was observed with. */
|
|
818
|
+
const REMOVED_OBSERVATION = "removed";
|
|
819
|
+
const UNREADABLE_OBSERVATION = "unreadable";
|
|
820
|
+
/** A loaded system's identity: its evaluated modules and a fingerprint of
|
|
821
|
+
* the whole load. */
|
|
822
|
+
function systemLoad(system) {
|
|
823
|
+
return {
|
|
824
|
+
identity: require_pipeline.contentHash(JSON.stringify(system)),
|
|
825
|
+
dependencies: system.dependencies ?? []
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const CONSOLE_LOGGER = {
|
|
829
|
+
info: (line) => console.error(line),
|
|
830
|
+
warn: (line) => console.warn(line),
|
|
831
|
+
error: (line) => console.error(line)
|
|
832
|
+
};
|
|
609
833
|
const SESSION_DIR_MAX_AGE_MS = 864e5;
|
|
610
834
|
const STATUS_WATCHDOG_MS = 2e3;
|
|
611
835
|
const LEGACY_FLAT_ARTIFACTS = [
|
|
@@ -624,19 +848,96 @@ function isDiskString(value) {
|
|
|
624
848
|
}
|
|
625
849
|
/** Rejection of an attempt whose corpus holds an aborted parse. */
|
|
626
850
|
var AbortedParseRejection = class extends Error {};
|
|
851
|
+
/** A reload refused because its full read would replace host-supplied
|
|
852
|
+
* bytes with the disk's: a system reload a parse hold deferred, or a full
|
|
853
|
+
* read that reaches a host-supplied file only under another spelling (a
|
|
854
|
+
* case-insensitive volume). Like a hold, it keeps the prior publication
|
|
855
|
+
* and the observed sources. */
|
|
856
|
+
var DeferredReloadRefusal = class extends Error {};
|
|
857
|
+
/** The file's canonical path, or null when it cannot be resolved. */
|
|
858
|
+
function canonicalPath(path$4) {
|
|
859
|
+
try {
|
|
860
|
+
return fs.realpathSync.native(path$4);
|
|
861
|
+
} catch {
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
/** The host sources a full read reaches only under another spelling: a key
|
|
866
|
+
* the read did not reach exactly, whose file is one the read reached by
|
|
867
|
+
* another path (a case-insensitive volume, a symlinked alias). A key with
|
|
868
|
+
* no such file is not read either way, and is left alone. */
|
|
869
|
+
function misspelledHostSources(hostSources, readPaths) {
|
|
870
|
+
const misses = [...hostSources.keys()].filter((path$5) => !readPaths.has(path$5));
|
|
871
|
+
if (misses.length === 0) return [];
|
|
872
|
+
const readByCanonical = /* @__PURE__ */ new Map();
|
|
873
|
+
for (const readPath of readPaths) {
|
|
874
|
+
const canonical = canonicalPath(readPath);
|
|
875
|
+
if (canonical === null) continue;
|
|
876
|
+
readByCanonical.set(canonical, [...readByCanonical.get(canonical) ?? [], readPath]);
|
|
877
|
+
}
|
|
878
|
+
return misses.flatMap((path$6) => {
|
|
879
|
+
const canonical = canonicalPath(path$6);
|
|
880
|
+
const read = canonical === null ? void 0 : readByCanonical.get(canonical);
|
|
881
|
+
return read === void 0 ? [] : [{
|
|
882
|
+
path: path$6,
|
|
883
|
+
read
|
|
884
|
+
}];
|
|
885
|
+
});
|
|
886
|
+
}
|
|
627
887
|
function isDiskNumber(value) {
|
|
628
888
|
return Object(value) !== value && Object.prototype.toString.call(value) === "[object Number]";
|
|
629
889
|
}
|
|
890
|
+
/** A file's content hash, or '' when it cannot be read. */
|
|
891
|
+
function fileHashOrEmpty(path$7) {
|
|
892
|
+
try {
|
|
893
|
+
return require_pipeline.contentHash((0, fs.readFileSync)(path$7, "utf-8"));
|
|
894
|
+
} catch {
|
|
895
|
+
return "";
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
/** The first file whose content no longer has its recorded hash: an
|
|
899
|
+
* unreadable or deleted file reads as '', which mismatches a real one. */
|
|
900
|
+
function firstChangedFile(hashes) {
|
|
901
|
+
for (const [path$8, hash] of hashes) if (fileHashOrEmpty(path$8) !== hash) return path$8;
|
|
902
|
+
}
|
|
630
903
|
/** Bundler-agnostic extraction pipeline: system load, source ingestion,
|
|
631
904
|
* analysis, emission, and watch-cycle diffing. Carries no bundler types. */
|
|
632
905
|
var ExtractionSession = class {
|
|
633
906
|
host;
|
|
634
907
|
/** Set by the adapter before any pipeline run. */
|
|
635
908
|
rootDir = null;
|
|
909
|
+
/** The module extracted terminals import their factories from; an
|
|
910
|
+
* override must supply every terminal the analysis extracts. */
|
|
911
|
+
runtimeImport = require_pipeline.DEFAULT_RUNTIME_IMPORT;
|
|
912
|
+
/** A host that resolves imports itself (Vite's `this.resolve`): package
|
|
913
|
+
* discovery asks it first, the loader only for a declared workspace it
|
|
914
|
+
* cannot resolve. Null resolves through the loader alone. */
|
|
915
|
+
hostResolve = null;
|
|
916
|
+
/** Each file's alias comparison, kept by content hash across analyses. */
|
|
917
|
+
aliasCache = new require_pipeline.AliasAgreementCache();
|
|
636
918
|
/** Serialized path aliases, harvested by the adapter from bundler config. */
|
|
637
919
|
pathAliasesJson = null;
|
|
920
|
+
/** The host's aliases are the project's tsconfig `paths`: each full pass
|
|
921
|
+
* re-reads them, so a tsconfig edit takes effect. */
|
|
922
|
+
aliasesFromTsconfig = false;
|
|
923
|
+
/** The root package.json and the tsconfig chain, absent files included:
|
|
924
|
+
* a change to any is a configuration change, which runs the full pass. */
|
|
925
|
+
configInputPaths = [];
|
|
926
|
+
configInputKeys = /* @__PURE__ */ new Set();
|
|
927
|
+
/** Each configuration input's content hash at the last full pass, ''
|
|
928
|
+
* for an absent one: the probe's basis when the host reports nothing. */
|
|
929
|
+
configInputHashes = /* @__PURE__ */ new Map();
|
|
638
930
|
/** The host's export conditions, in its order, for the system loader. */
|
|
639
931
|
conditions = [];
|
|
932
|
+
/** The host's extension order, as its adapter captured it; the built-in
|
|
933
|
+
* policy where no host resolves. */
|
|
934
|
+
extensionPolicy = require_pipeline.BUILT_IN_EXTENSION_POLICY;
|
|
935
|
+
/** Which files the host serves as assets (spec §2.2 A8); the built-in
|
|
936
|
+
* classification where no host's can be read. */
|
|
937
|
+
assetClassification = require_pipeline.builtInAssetClassification("noHost");
|
|
938
|
+
/** How the host's bundler expands computed loads (spec §6.3); without
|
|
939
|
+
* one, every computed load keeps its widest reading. */
|
|
940
|
+
loadExpander;
|
|
640
941
|
assetCopyCache = /* @__PURE__ */ new Map();
|
|
641
942
|
/** Physical asset files registered with the host watcher. */
|
|
642
943
|
assetDependencyPaths = /* @__PURE__ */ new Set();
|
|
@@ -644,9 +945,23 @@ var ExtractionSession = class {
|
|
|
644
945
|
/** System-props module id override. Turbopack rejects absolute-path
|
|
645
946
|
* imports, so its driver sets a virtual id; null injects the abs path. */
|
|
646
947
|
systemPropsModuleId = null;
|
|
948
|
+
/** The stylesheet module generated code imports; a host that serves the
|
|
949
|
+
* stylesheet itself names its own. */
|
|
950
|
+
cssModuleId = ANIMUS_CSS_MODULE_ID;
|
|
647
951
|
/** Whether the analysis-inputs hydration corpus is written to disk. Only
|
|
648
952
|
* isolated loader workers replay it; an in-process loader reads memory. */
|
|
649
953
|
persistAnalysisInputs = false;
|
|
954
|
+
/** The host substitutes asset() URLs itself (Vite: dev copies, build
|
|
955
|
+
* emitFile markers): the published sheets keep their placeholders, and
|
|
956
|
+
* the session makes no copy and tracks no asset file. */
|
|
957
|
+
hostSubstitutesAssets = false;
|
|
958
|
+
/** Whether the session keeps its artifacts on disk: status, commit,
|
|
959
|
+
* manifest, styles, the epoch witness, asset copies and its directory
|
|
960
|
+
* claim, with the sibling reconciliation they need. A host that serves
|
|
961
|
+
* every result from memory (Vite) turns it off; the shared state still
|
|
962
|
+
* publishes. Such a host also substitutes assets (above): asset copies
|
|
963
|
+
* are artifacts. */
|
|
964
|
+
persistArtifacts = true;
|
|
650
965
|
/** Whether this session serves development (a watch), where an error-level
|
|
651
966
|
* diagnostic is reported and the session keeps running. Only a build
|
|
652
967
|
* fails on one. */
|
|
@@ -694,29 +1009,69 @@ var ExtractionSession = class {
|
|
|
694
1009
|
/** Fires exactly once per settled `handleWatchUpdate` — the failure cause,
|
|
695
1010
|
* or null when clean. Observation never alters delivery. */
|
|
696
1011
|
onCycleSettled = null;
|
|
1012
|
+
/** Fires once per published generation, and per held attempt. */
|
|
1013
|
+
onOutcome = null;
|
|
1014
|
+
/** The last published generation; null before the first. */
|
|
1015
|
+
publication = null;
|
|
1016
|
+
/** The original sources (raw, before adaptation) the session holds, by
|
|
1017
|
+
* root-relative path: the published generation's, plus any that an
|
|
1018
|
+
* attempt in flight or held by an aborted parse has read. */
|
|
1019
|
+
get heldSources() {
|
|
1020
|
+
return this.fileCache;
|
|
1021
|
+
}
|
|
1022
|
+
/** The generation an emit produced, current once its ingestion
|
|
1023
|
+
* publishes. */
|
|
1024
|
+
stagedPublication = null;
|
|
697
1025
|
options;
|
|
698
1026
|
staticCssJson;
|
|
699
1027
|
system = null;
|
|
700
|
-
/** Configured
|
|
701
|
-
*
|
|
1028
|
+
/** Configured files that could not be read. Incremental passes replay
|
|
1029
|
+
* these instead of passing strict, and add a watched change they cannot
|
|
1030
|
+
* read. */
|
|
702
1031
|
ingestionFailureDiagnostics = [];
|
|
703
1032
|
/** Whether the corpus being analyzed left a file out, whose renders the
|
|
704
1033
|
* analysis cannot see. */
|
|
705
1034
|
skippedSources = [];
|
|
1035
|
+
/** Resolver calls ingestion ended under another spelling. */
|
|
1036
|
+
ingestionOpenings = [];
|
|
1037
|
+
/** The adapted originals of the ingestion being analysed: it publishes
|
|
1038
|
+
* only after its analysis does. */
|
|
1039
|
+
ingestionAdapted = /* @__PURE__ */ new Map();
|
|
706
1040
|
/** Full package-resolution map from the last full pipeline — replayed by
|
|
707
1041
|
* incremental passes (sourceEntries alone omits dist-resolved packages). */
|
|
708
1042
|
lastPackageMap = {};
|
|
709
1043
|
fileCache = /* @__PURE__ */ new Map();
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1044
|
+
/** Held sources whose bytes the host supplied (WatchChanges.sources),
|
|
1045
|
+
* by root-relative path; a read from disk drops the path. */
|
|
1046
|
+
hostSuppliedKeys = /* @__PURE__ */ new Set();
|
|
1047
|
+
corpus = require_pipeline.createSourceCorpus(this.corpusHost());
|
|
1048
|
+
/** The corpus reads the driver label when it reports, so a host that
|
|
1049
|
+
* sets `driverLabel` after construction is named in its messages. */
|
|
1050
|
+
corpusHost() {
|
|
1051
|
+
const label = () => this.driverLabel;
|
|
1052
|
+
return {
|
|
1053
|
+
engineApi: () => this.engine(),
|
|
1054
|
+
get prefix() {
|
|
1055
|
+
return `[${label()}]`;
|
|
1056
|
+
},
|
|
1057
|
+
strict: () => !!this.options.strict,
|
|
1058
|
+
warn: (message) => this.warn(message),
|
|
1059
|
+
reportErrors: () => this.reportErrors(),
|
|
1060
|
+
extensions: () => this.extensionPolicy,
|
|
1061
|
+
rootDir: () => this.rootDir
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
717
1064
|
systemDependencyKeys = /* @__PURE__ */ new Set();
|
|
1065
|
+
/** Whether `path` is the system module, one of its dependencies, or a
|
|
1066
|
+
* path its load found absent: a change to it reloads the system. */
|
|
1067
|
+
isSystemDependency(path$9) {
|
|
1068
|
+
return require_pipeline.toWatchKeys(path$9).some((key) => this.systemDependencyKeys.has(key));
|
|
1069
|
+
}
|
|
718
1070
|
/** Loader-reported dependency paths for bundler watch registration. */
|
|
719
1071
|
systemDependencyPaths = [];
|
|
1072
|
+
/** Paths the system load looked for and found absent: a host registers
|
|
1073
|
+
* them for creation, which reloads the system. */
|
|
1074
|
+
systemMissingPaths = [];
|
|
720
1075
|
systemDependencyHashes = /* @__PURE__ */ new Map();
|
|
721
1076
|
lastSystemPropsHash = null;
|
|
722
1077
|
artifactRecords = {
|
|
@@ -730,9 +1085,20 @@ var ExtractionSession = class {
|
|
|
730
1085
|
/** Release handle for the in-process owner slot AND the directory's
|
|
731
1086
|
* on-disk owner record, taken and released as one; null when unheld. */
|
|
732
1087
|
releasePublicationClaim = null;
|
|
733
|
-
/**
|
|
734
|
-
*
|
|
735
|
-
|
|
1088
|
+
/** The build this session takes part in: every participant of one build
|
|
1089
|
+
* shares its artifact tree and state; another build of the root never
|
|
1090
|
+
* does. */
|
|
1091
|
+
sessionId;
|
|
1092
|
+
/** The log tier, from the option and `ANIMUS_DEBUG` at construction. */
|
|
1093
|
+
verbosity;
|
|
1094
|
+
/** The emission mode at construction: explicit, else `NODE_ENV`'s. It
|
|
1095
|
+
* defaults minify. */
|
|
1096
|
+
startupMode;
|
|
1097
|
+
joinedKey = null;
|
|
1098
|
+
/** This instance as a participant of its build, once joined. */
|
|
1099
|
+
participant = null;
|
|
1100
|
+
/** Closed: it left its build and starts no attempt until it reattaches. */
|
|
1101
|
+
detached = false;
|
|
736
1102
|
/** Epoch of the last published analysis; null seeds from disk so a
|
|
737
1103
|
* same-session restart with unchanged plans rewrites no bytes. */
|
|
738
1104
|
lastEpochValue = null;
|
|
@@ -747,10 +1113,23 @@ var ExtractionSession = class {
|
|
|
747
1113
|
* the cache keeps every observed source so the repair publishes them in
|
|
748
1114
|
* one generation; a successful publication clears it. */
|
|
749
1115
|
heldRejection = null;
|
|
750
|
-
/**
|
|
751
|
-
*
|
|
752
|
-
*
|
|
1116
|
+
/** Reload debt: a full pass that failed with a committed system to
|
|
1117
|
+
* hold, in development or under an aborted parse, owed until one
|
|
1118
|
+
* succeeds. runFullPipeline alone sets and clears it. While it stands, every batch retries the full reload and
|
|
1119
|
+
* nothing publishes against the stale system. Under an aborted parse's
|
|
1120
|
+
* hold, the first batch that passes the parse check pays it instead. */
|
|
753
1121
|
systemReloadOwed = false;
|
|
1122
|
+
/** The system dependencies the last committed full pass recorded: a
|
|
1123
|
+
* failed reload watches these and its own, never an earlier failure's. */
|
|
1124
|
+
lastGoodSystemDependencies = {
|
|
1125
|
+
keys: /* @__PURE__ */ new Set(),
|
|
1126
|
+
paths: [],
|
|
1127
|
+
missing: [],
|
|
1128
|
+
hashes: /* @__PURE__ */ new Map()
|
|
1129
|
+
};
|
|
1130
|
+
/** Whether the current full pass's system load recorded its
|
|
1131
|
+
* dependencies. */
|
|
1132
|
+
attemptRecordedSystemDependencies = false;
|
|
754
1133
|
/** Monotonic readiness witness: flips true on the first complete
|
|
755
1134
|
* publication and never regresses; every later status write carries it. */
|
|
756
1135
|
firstEmissionComplete = false;
|
|
@@ -761,27 +1140,53 @@ var ExtractionSession = class {
|
|
|
761
1140
|
constructor(options, host = {}) {
|
|
762
1141
|
this.host = host;
|
|
763
1142
|
this.options = options;
|
|
1143
|
+
this.sessionId = host.build ?? createBuildId();
|
|
764
1144
|
this.staticCssJson = require_pipeline.serializeStaticCss(options.staticCss);
|
|
1145
|
+
this.verbosity = require_pipeline.resolveVerbosity(options.verbose, process.env.ANIMUS_DEBUG);
|
|
1146
|
+
this.startupMode = require_pipeline.resolveMode(options.mode, () => process.env.NODE_ENV === "production" ? "production" : "development").mode;
|
|
765
1147
|
}
|
|
766
1148
|
/** Forgets that `relPath` could not be read once an incremental pass reads
|
|
767
|
-
* or deletes it, so its cost stops holding pruning off.
|
|
1149
|
+
* or deletes it, so its cost stops holding pruning off. Whether one was
|
|
1150
|
+
* recorded. */
|
|
768
1151
|
clearIngestionFailure(relPath) {
|
|
1152
|
+
const before = this.ingestionFailureDiagnostics.length;
|
|
769
1153
|
this.ingestionFailureDiagnostics = this.ingestionFailureDiagnostics.filter((diagnostic) => diagnostic.file !== relPath);
|
|
1154
|
+
return this.ingestionFailureDiagnostics.length !== before;
|
|
770
1155
|
}
|
|
771
1156
|
get sessionDir() {
|
|
772
1157
|
return sessionArtifactDir(this.rootDir, this.sessionId);
|
|
773
1158
|
}
|
|
1159
|
+
/** This session's build of its root, joined on first use: a participant
|
|
1160
|
+
* whose options differ from the build's fails with
|
|
1161
|
+
* `SessionConfigurationConflict`. */
|
|
1162
|
+
get key() {
|
|
1163
|
+
if (this.joinedKey === null) {
|
|
1164
|
+
const key = sessionKey(this.sessionId, this.rootDir);
|
|
1165
|
+
const participant = `${this.driverLabel}#${(0, crypto.randomUUID)()}`;
|
|
1166
|
+
joinSession(key, participant, this.options);
|
|
1167
|
+
this.participant = participant;
|
|
1168
|
+
this.joinedKey = key;
|
|
1169
|
+
}
|
|
1170
|
+
return this.joinedKey;
|
|
1171
|
+
}
|
|
774
1172
|
get verbose() {
|
|
775
|
-
return
|
|
1173
|
+
return this.verbosity !== "quiet";
|
|
1174
|
+
}
|
|
1175
|
+
/** The engine of this session's build. */
|
|
1176
|
+
engine() {
|
|
1177
|
+
return this.host.engine?.() ?? engineApi(this.key);
|
|
1178
|
+
}
|
|
1179
|
+
get logger() {
|
|
1180
|
+
return this.host.logger ?? CONSOLE_LOGGER;
|
|
776
1181
|
}
|
|
777
1182
|
log(msg) {
|
|
778
|
-
if (this.verbose)
|
|
1183
|
+
if (this.verbose) this.logger.info(`[animus] ${msg}`);
|
|
779
1184
|
}
|
|
780
1185
|
warn(msg) {
|
|
781
|
-
|
|
1186
|
+
this.logger.warn(`[animus] ${msg}`);
|
|
782
1187
|
}
|
|
783
1188
|
reportErrors() {
|
|
784
|
-
return this.development ? (message) =>
|
|
1189
|
+
return this.development ? (message) => this.logger.error(message) : void 0;
|
|
785
1190
|
}
|
|
786
1191
|
/** A build-strictness check that failed: a build throws `message`, and
|
|
787
1192
|
* development reports it at error level and takes the non-strict path. */
|
|
@@ -790,6 +1195,11 @@ var ExtractionSession = class {
|
|
|
790
1195
|
if (!report) throw new Error(message);
|
|
791
1196
|
report(message);
|
|
792
1197
|
}
|
|
1198
|
+
/** A finding `strict` turns into a strict failure; otherwise a warning. */
|
|
1199
|
+
strictOrWarn(message, strictMessage = message) {
|
|
1200
|
+
if (this.options.strict) this.strictFailure(strictMessage);
|
|
1201
|
+
else this.warn(message);
|
|
1202
|
+
}
|
|
793
1203
|
now() {
|
|
794
1204
|
return this.verbose ? performance.now() : 0;
|
|
795
1205
|
}
|
|
@@ -804,6 +1214,11 @@ var ExtractionSession = class {
|
|
|
804
1214
|
lastExternalOutcomes = [];
|
|
805
1215
|
/** The analysed kits' descriptors, from the last discovery. */
|
|
806
1216
|
kitDescriptors = [];
|
|
1217
|
+
/** Package name → the real root of the instance discovery read. */
|
|
1218
|
+
packageInstances = {};
|
|
1219
|
+
/** What this attempt's analysis read of the discovered packages; the
|
|
1220
|
+
* build's shared record once the analysis publishes. */
|
|
1221
|
+
packageReadRecord = null;
|
|
807
1222
|
/** Component count of the last published analysis, null before the first —
|
|
808
1223
|
* read by drivers instead of re-parsing the manifest JSON every cycle. */
|
|
809
1224
|
lastComponentCount = null;
|
|
@@ -814,18 +1229,15 @@ var ExtractionSession = class {
|
|
|
814
1229
|
/** When superseded asset copies leave `assets/`: `'full-pipeline'` suits a
|
|
815
1230
|
* driver serving the dir in place, `'every-cycle'` one republishing it. */
|
|
816
1231
|
staleAssetPruning = "full-pipeline";
|
|
817
|
-
/**
|
|
818
|
-
*
|
|
1232
|
+
/** The host's output directories (`outputExclusion`): root-relative,
|
|
1233
|
+
* excluded as literal path prefixes from discovery and from package
|
|
1234
|
+
* collection, whatever the user's list. */
|
|
819
1235
|
structuralExclude = [];
|
|
820
1236
|
scanConfigMemo = null;
|
|
821
|
-
/** Per-pattern exclusion hit counts from this session's matcher —
|
|
822
|
-
* driver-consumed reporting surface (dead-pattern visibility). */
|
|
823
|
-
getExcludeStats() {
|
|
824
|
-
return this.resolveScanConfig().excludeMatcher.stats();
|
|
825
|
-
}
|
|
826
1237
|
/** Resolve the scan configuration from options — the single source of the
|
|
827
|
-
* exclude/extension policy shared by the full and incremental pipelines
|
|
828
|
-
|
|
1238
|
+
* exclude/extension policy shared by the full and incremental pipelines,
|
|
1239
|
+
* and by a host classifying its own file events. */
|
|
1240
|
+
get scanConfig() {
|
|
829
1241
|
if (this.scanConfigMemo === null) {
|
|
830
1242
|
const extensionsSet = new Set(this.options.extensions ?? require_pipeline.DEFAULT_EXTENSIONS);
|
|
831
1243
|
this.scanConfigMemo = {
|
|
@@ -835,8 +1247,8 @@ var ExtractionSession = class {
|
|
|
835
1247
|
}
|
|
836
1248
|
return this.scanConfigMemo;
|
|
837
1249
|
}
|
|
838
|
-
/**
|
|
839
|
-
*
|
|
1250
|
+
/** Records the batch before any await, so an attempt that rejects never
|
|
1251
|
+
* erases it, and settles with the attempt that takes it (SA20). */
|
|
840
1252
|
async handleWatchUpdate(changes) {
|
|
841
1253
|
try {
|
|
842
1254
|
await this.routeWatchUpdate(changes);
|
|
@@ -847,36 +1259,131 @@ var ExtractionSession = class {
|
|
|
847
1259
|
this.onCycleSettled?.(null);
|
|
848
1260
|
}
|
|
849
1261
|
async routeWatchUpdate(changes) {
|
|
850
|
-
|
|
851
|
-
|
|
1262
|
+
if ((changes.modifiedFiles?.size ?? 0) > 0 || (changes.removedFiles?.size ?? 0) > 0) {
|
|
1263
|
+
await this.awaitObservation(this.recordObservation(changes));
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
const inflight = getWatchTransaction(this.key);
|
|
852
1267
|
if (inflight) {
|
|
853
1268
|
await inflight;
|
|
854
|
-
if (!hasBatch) return;
|
|
855
|
-
}
|
|
856
|
-
if (!this.system) {
|
|
857
|
-
const owner = getOwningWatchSession();
|
|
858
|
-
if (owner && owner !== this && hasBatch) await owner.ingestForwardedBatch(changes);
|
|
859
1269
|
return;
|
|
860
1270
|
}
|
|
861
|
-
await this.
|
|
1271
|
+
if (this.system && !this.detached) await this.runWatchAttempt(changes);
|
|
1272
|
+
}
|
|
1273
|
+
/** Queues the batch's observations under a new revision, synchronously.
|
|
1274
|
+
* A batch equivalent to what the active attempt took joins that attempt:
|
|
1275
|
+
* a second compiler's report of the same edit never analyzes it twice. */
|
|
1276
|
+
recordObservation(changes) {
|
|
1277
|
+
const queue = observationQueue(this.key);
|
|
1278
|
+
const observed = /* @__PURE__ */ new Map();
|
|
1279
|
+
for (const path$10 of changes.modifiedFiles ?? []) {
|
|
1280
|
+
let state;
|
|
1281
|
+
try {
|
|
1282
|
+
state = require_pipeline.contentHash(changes.sources?.get(path$10) ?? (0, fs.readFileSync)(path$10, "utf-8"));
|
|
1283
|
+
} catch {
|
|
1284
|
+
state = UNREADABLE_OBSERVATION;
|
|
1285
|
+
}
|
|
1286
|
+
observed.set(path$10, state);
|
|
1287
|
+
}
|
|
1288
|
+
for (const path$11 of changes.removedFiles ?? []) observed.set(path$11, REMOVED_OBSERVATION);
|
|
1289
|
+
const revision = ++queue.revision;
|
|
1290
|
+
const active = queue.active;
|
|
1291
|
+
if (active !== null && [...observed].every(([path$12, state]) => !queue.untaken.has(path$12) && active.taken.get(path$12) === state)) {
|
|
1292
|
+
queue.revisions.set(revision, active.transaction);
|
|
1293
|
+
return revision;
|
|
1294
|
+
}
|
|
1295
|
+
for (const [path$13, state] of observed) {
|
|
1296
|
+
queue.untaken.set(path$13, state);
|
|
1297
|
+
const supplied = changes.sources?.get(path$13);
|
|
1298
|
+
if (supplied === void 0) queue.suppliedSources.delete(path$13);
|
|
1299
|
+
else queue.suppliedSources.set(path$13, supplied);
|
|
1300
|
+
}
|
|
1301
|
+
queue.revisions.set(revision, null);
|
|
1302
|
+
return revision;
|
|
862
1303
|
}
|
|
863
|
-
/**
|
|
864
|
-
*
|
|
865
|
-
async
|
|
866
|
-
|
|
1304
|
+
/** Settles with the attempt that takes `revision`. Another attempt's
|
|
1305
|
+
* rejection is its own callers': this observation runs after it. */
|
|
1306
|
+
async awaitObservation(revision) {
|
|
1307
|
+
const queue = observationQueue(this.key);
|
|
867
1308
|
for (;;) {
|
|
868
|
-
const
|
|
869
|
-
if (
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1309
|
+
const taken = queue.revisions.get(revision);
|
|
1310
|
+
if (taken !== null && taken !== void 0) {
|
|
1311
|
+
queue.revisions.delete(revision);
|
|
1312
|
+
await taken;
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
const inflight = getWatchTransaction(this.key);
|
|
1316
|
+
if (inflight) {
|
|
1317
|
+
await inflight.catch(() => void 0);
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
const owner = this.system && !this.detached ? this : getOwningWatchSession(this.key);
|
|
1321
|
+
if (owner === null && !this.detached && getManifestJson(this.key) !== null) {
|
|
1322
|
+
await this.runFullPipeline();
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
if (owner === null || !owner.startObservedAttempt()) {
|
|
1326
|
+
queue.revisions.delete(revision);
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
878
1329
|
}
|
|
879
1330
|
}
|
|
1331
|
+
/** Starts one attempt over every untaken observation of the build. */
|
|
1332
|
+
startObservedAttempt() {
|
|
1333
|
+
if (!this.system || this.detached) return false;
|
|
1334
|
+
const { taken, rescan, sources } = this.takeObservations();
|
|
1335
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
1336
|
+
const removedFiles = /* @__PURE__ */ new Set();
|
|
1337
|
+
for (const [path$14, state] of taken) (state === REMOVED_OBSERVATION ? removedFiles : modifiedFiles).add(path$14);
|
|
1338
|
+
this.settleTaken(taken, this.runWatchAttempt({
|
|
1339
|
+
modifiedFiles,
|
|
1340
|
+
removedFiles,
|
|
1341
|
+
sources
|
|
1342
|
+
}, rescan));
|
|
1343
|
+
return true;
|
|
1344
|
+
}
|
|
1345
|
+
/** Every untaken observation of the build, and whether a full rescan is
|
|
1346
|
+
* requested, which the caller's attempt now covers. */
|
|
1347
|
+
takeObservations() {
|
|
1348
|
+
const queue = observationQueue(this.key);
|
|
1349
|
+
const taken = new Map(queue.untaken);
|
|
1350
|
+
const rescan = queue.rescan;
|
|
1351
|
+
const sources = new Map(queue.suppliedSources);
|
|
1352
|
+
queue.untaken.clear();
|
|
1353
|
+
queue.suppliedSources.clear();
|
|
1354
|
+
queue.rescan = false;
|
|
1355
|
+
return {
|
|
1356
|
+
taken,
|
|
1357
|
+
rescan,
|
|
1358
|
+
sources
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
/** `transaction` settles every waiting revision, and is the active
|
|
1362
|
+
* attempt equivalent observations join until it settles. */
|
|
1363
|
+
settleTaken(taken, transaction) {
|
|
1364
|
+
const queue = observationQueue(this.key);
|
|
1365
|
+
for (const [revision, attempt] of queue.revisions) if (attempt === null) queue.revisions.set(revision, transaction);
|
|
1366
|
+
queue.active = {
|
|
1367
|
+
taken,
|
|
1368
|
+
transaction
|
|
1369
|
+
};
|
|
1370
|
+
const settle = () => {
|
|
1371
|
+
if (queue.active?.transaction === transaction) queue.active = null;
|
|
1372
|
+
};
|
|
1373
|
+
transaction.then(settle, settle);
|
|
1374
|
+
}
|
|
1375
|
+
/** Registers one attempt in the build's single-flight slot before it
|
|
1376
|
+
* processes, so a nested full system reload inherits the gate rather
|
|
1377
|
+
* than clear it, and clears the slot when it settles either way. */
|
|
1378
|
+
runWatchAttempt(changes, rescan = false) {
|
|
1379
|
+
const transaction = Promise.resolve().then(() => rescan ? this.runFullPipeline([], changes.sources) : this.processWatchUpdate(changes));
|
|
1380
|
+
setWatchTransaction(this.key, transaction);
|
|
1381
|
+
const settle = () => {
|
|
1382
|
+
if (getWatchTransaction(this.key) === transaction) setWatchTransaction(this.key, null);
|
|
1383
|
+
};
|
|
1384
|
+
transaction.then(settle, settle);
|
|
1385
|
+
return transaction;
|
|
1386
|
+
}
|
|
880
1387
|
async processWatchUpdate(changes) {
|
|
881
1388
|
const rootDir = this.rootDir;
|
|
882
1389
|
try {
|
|
@@ -887,31 +1394,25 @@ var ExtractionSession = class {
|
|
|
887
1394
|
const changed = [...changes.modifiedFiles ?? [], ...changes.removedFiles ?? []];
|
|
888
1395
|
let assetChanged = false;
|
|
889
1396
|
let systemHit;
|
|
1397
|
+
let configHit;
|
|
890
1398
|
try {
|
|
891
|
-
if (
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
} catch {}
|
|
896
|
-
if (current !== hash) {
|
|
897
|
-
systemHit = dep;
|
|
898
|
-
break;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
else systemHit = changed.find((path$2) => require_pipeline.toWatchKeys(path$2).some((key) => this.systemDependencyKeys.has(key)));
|
|
1399
|
+
if (changed.length === 0) {
|
|
1400
|
+
systemHit = firstChangedFile(this.systemDependencyHashes);
|
|
1401
|
+
if (systemHit === void 0) configHit = firstChangedFile(this.configInputHashes);
|
|
1402
|
+
} else systemHit = changed.find((path$15) => this.isSystemDependency(path$15));
|
|
902
1403
|
} catch (err) {
|
|
903
1404
|
this.warn(`HMR system-reload check failed: ${String(err)}`);
|
|
904
1405
|
}
|
|
905
|
-
|
|
906
|
-
|
|
1406
|
+
configHit ??= changed.find((path$16) => require_pipeline.toWatchKeys(path$16).some((key) => this.configInputKeys.has(key)));
|
|
1407
|
+
const debtRetry = this.systemReloadOwed && this.heldRejection === null;
|
|
1408
|
+
if (systemHit || configHit || debtRetry) {
|
|
1409
|
+
this.log(systemHit ? `system reload: system dependency changed (${(0, path.relative)(rootDir, systemHit)})` : configHit ? `system reload: configuration changed (${(0, path.relative)(rootDir, configHit)})` : "system reload: retrying the reload a failure left owed");
|
|
907
1410
|
this.resetForHmr();
|
|
908
1411
|
try {
|
|
909
|
-
const promise = this.runFullPipeline(this.pendingFromBatch(changes));
|
|
910
|
-
setAnalysisStartedPromise(promise);
|
|
1412
|
+
const promise = this.runFullPipeline(this.pendingFromBatch(changes), changes.sources);
|
|
1413
|
+
setAnalysisStartedPromise(this.key, promise);
|
|
911
1414
|
await promise;
|
|
912
|
-
this.systemReloadOwed = false;
|
|
913
1415
|
} catch (err) {
|
|
914
|
-
if (err instanceof AbortedParseRejection) this.systemReloadOwed = true;
|
|
915
1416
|
try {
|
|
916
1417
|
this.beginStatusAttempt();
|
|
917
1418
|
this.writeAnalysisStatus("failed", this.pendingFromBatch(changes), String(err));
|
|
@@ -922,11 +1423,11 @@ var ExtractionSession = class {
|
|
|
922
1423
|
}
|
|
923
1424
|
return;
|
|
924
1425
|
}
|
|
925
|
-
if (changed.some((path$
|
|
1426
|
+
if (changed.some((path$17) => require_pipeline.toWatchKeys(path$17).some((key) => this.assetDependencyKeys.has(key)))) {
|
|
926
1427
|
this.assetCopyCache.clear();
|
|
927
1428
|
assetChanged = true;
|
|
928
1429
|
}
|
|
929
|
-
const { excludeMatcher, extensionsSet } = this.
|
|
1430
|
+
const { excludeMatcher, extensionsSet } = this.scanConfig;
|
|
930
1431
|
const priorCacheEntries = /* @__PURE__ */ new Map();
|
|
931
1432
|
const priorExternalFileOwners = { ...this.externalFileOwners };
|
|
932
1433
|
const recordPrior = (key) => {
|
|
@@ -934,6 +1435,7 @@ var ExtractionSession = class {
|
|
|
934
1435
|
};
|
|
935
1436
|
const inventoryUpdates = [];
|
|
936
1437
|
const ownerRemovals = [];
|
|
1438
|
+
const removedProjectFiles = /* @__PURE__ */ new Map();
|
|
937
1439
|
let removedAny = false;
|
|
938
1440
|
if (changes.removedFiles) for (const removedPath of changes.removedFiles) {
|
|
939
1441
|
let key;
|
|
@@ -947,8 +1449,10 @@ var ExtractionSession = class {
|
|
|
947
1449
|
}
|
|
948
1450
|
recordPrior(key);
|
|
949
1451
|
this.clearIngestionFailure(key);
|
|
1452
|
+
this.hostSuppliedKeys.delete(key);
|
|
950
1453
|
if (this.fileCache.delete(key)) {
|
|
951
1454
|
removedAny = true;
|
|
1455
|
+
if (!owningRoot) removedProjectFiles.set(key, removedPath);
|
|
952
1456
|
if (owningRoot) {
|
|
953
1457
|
inventoryUpdates.push({
|
|
954
1458
|
root: owningRoot,
|
|
@@ -979,15 +1483,29 @@ var ExtractionSession = class {
|
|
|
979
1483
|
owningRoot: null
|
|
980
1484
|
}));
|
|
981
1485
|
const changedPaths = [];
|
|
1486
|
+
const inOutput = require_pipeline.outputSourceGuard(rootDir, this.structuralExclude);
|
|
1487
|
+
let readFailuresChanged = false;
|
|
982
1488
|
for (const target of targets) {
|
|
983
1489
|
const relPath = target.key;
|
|
1490
|
+
const outputDir = inOutput(target.abs);
|
|
1491
|
+
if (outputDir !== null) {
|
|
1492
|
+
this.warn(require_pipeline.outputSourceMessage(relPath, outputDir));
|
|
1493
|
+
continue;
|
|
1494
|
+
}
|
|
984
1495
|
let source;
|
|
985
1496
|
try {
|
|
986
|
-
|
|
987
|
-
|
|
1497
|
+
const supplied = changes.sources?.get(target.abs);
|
|
1498
|
+
source = supplied ?? (0, fs.readFileSync)(target.abs, "utf-8");
|
|
1499
|
+
if (supplied === void 0) this.hostSuppliedKeys.delete(relPath);
|
|
1500
|
+
else this.hostSuppliedKeys.add(relPath);
|
|
1501
|
+
} catch (err) {
|
|
1502
|
+
if (require_pipeline.isDeletedSource(err)) continue;
|
|
1503
|
+
this.clearIngestionFailure(relPath);
|
|
1504
|
+
this.ingestionFailureDiagnostics = [...this.ingestionFailureDiagnostics, require_pipeline.unreadableSourceDiagnostic(relPath, err)];
|
|
1505
|
+
readFailuresChanged = true;
|
|
988
1506
|
continue;
|
|
989
1507
|
}
|
|
990
|
-
this.clearIngestionFailure(relPath);
|
|
1508
|
+
if (this.clearIngestionFailure(relPath)) readFailuresChanged = true;
|
|
991
1509
|
const rawHash = require_pipeline.contentHash(source);
|
|
992
1510
|
const cached = this.fileCache.get(relPath);
|
|
993
1511
|
const hash = rawHash;
|
|
@@ -1010,7 +1528,7 @@ var ExtractionSession = class {
|
|
|
1010
1528
|
}
|
|
1011
1529
|
}
|
|
1012
1530
|
}
|
|
1013
|
-
if (changedPaths.length > 0 || removedAny || assetChanged) {
|
|
1531
|
+
if (changedPaths.length > 0 || removedAny || readFailuresChanged || assetChanged) {
|
|
1014
1532
|
const pending = changedPaths.map((rel) => [rel, this.fileCache.get(rel).hash]);
|
|
1015
1533
|
const commitObservedBatch = () => {
|
|
1016
1534
|
for (const update of inventoryUpdates) {
|
|
@@ -1036,23 +1554,30 @@ var ExtractionSession = class {
|
|
|
1036
1554
|
});
|
|
1037
1555
|
this.throwOnAbortedParse(ingested);
|
|
1038
1556
|
this.skippedSources = ingested.skippedOriginals ?? [];
|
|
1557
|
+
this.ingestionOpenings = ingested.openings ?? [];
|
|
1558
|
+
this.ingestionAdapted = require_pipeline.adaptedOriginals(ingested.ownership);
|
|
1039
1559
|
if (this.systemReloadOwed) {
|
|
1040
1560
|
this.log("system reload: publishing the reload a parse hold deferred");
|
|
1561
|
+
const stale = this.staleHostSuppliedSources(changes.sources);
|
|
1562
|
+
if (stale.length > 0) throw new DeferredReloadRefusal(`[${this.driverLabel}] system reload not published: its full read would take ${stale.join(", ")} from disk, which does not hold the bytes the host supplied; the last generation stays served, and the reload publishes with the next change once the disk holds them`);
|
|
1041
1563
|
this.resetForHmr();
|
|
1042
|
-
await this.runFullPipeline(pending);
|
|
1043
|
-
this.systemReloadOwed = false;
|
|
1564
|
+
await this.runFullPipeline(pending, changes.sources);
|
|
1044
1565
|
return;
|
|
1045
1566
|
}
|
|
1046
1567
|
this.externalFileOwners = require_pipeline.projectExternalFileOwners(ingested, this.externalFileOwners);
|
|
1047
|
-
resetAnalysisStartedPromise();
|
|
1568
|
+
resetAnalysisStartedPromise(this.key);
|
|
1048
1569
|
const promise = this.runIncrementalPipeline(ingested.analysisEntries, pending);
|
|
1049
|
-
setAnalysisStartedPromise(promise);
|
|
1570
|
+
setAnalysisStartedPromise(this.key, promise);
|
|
1050
1571
|
await promise;
|
|
1051
1572
|
} catch (err) {
|
|
1052
|
-
if (err instanceof AbortedParseRejection) commitObservedBatch();
|
|
1573
|
+
if (err instanceof AbortedParseRejection || err instanceof DeferredReloadRefusal) commitObservedBatch();
|
|
1053
1574
|
else {
|
|
1054
|
-
for (const [key, prior] of priorCacheEntries)
|
|
1055
|
-
|
|
1575
|
+
for (const [key, prior] of priorCacheEntries) {
|
|
1576
|
+
const removed = removedProjectFiles.get(key);
|
|
1577
|
+
if (removed !== void 0 && !(0, fs.existsSync)(removed)) continue;
|
|
1578
|
+
if (prior === null) this.fileCache.delete(key);
|
|
1579
|
+
else this.fileCache.set(key, prior);
|
|
1580
|
+
}
|
|
1056
1581
|
this.externalFileOwners = priorExternalFileOwners;
|
|
1057
1582
|
}
|
|
1058
1583
|
if (this.statusAttemptOpen) {
|
|
@@ -1061,7 +1586,7 @@ var ExtractionSession = class {
|
|
|
1061
1586
|
}
|
|
1062
1587
|
throw err;
|
|
1063
1588
|
}
|
|
1064
|
-
this.publishSourceIngestion(ingested);
|
|
1589
|
+
this.publishSourceIngestion(ingested, "incremental", (path$18) => this.fileCache.get(path$18));
|
|
1065
1590
|
commitObservedBatch();
|
|
1066
1591
|
} else if (this.statusAttemptOpen) {
|
|
1067
1592
|
this.debouncePending.clear();
|
|
@@ -1076,88 +1601,233 @@ var ExtractionSession = class {
|
|
|
1076
1601
|
const rejection = this.corpus.rejection(prepared);
|
|
1077
1602
|
if (!rejection) return;
|
|
1078
1603
|
this.heldRejection = rejection.message;
|
|
1604
|
+
this.onOutcome?.({
|
|
1605
|
+
kind: "held",
|
|
1606
|
+
abortedOriginals: rejection.originals
|
|
1607
|
+
});
|
|
1079
1608
|
throw new AbortedParseRejection(rejection.message);
|
|
1080
1609
|
}
|
|
1081
|
-
|
|
1610
|
+
/** Held sources whose bytes the host supplied and which the disk no
|
|
1611
|
+
* longer matches: a full read not carrying them (`carried`) would
|
|
1612
|
+
* publish the disk's bytes in their place. */
|
|
1613
|
+
staleHostSuppliedSources(carried) {
|
|
1614
|
+
const stale = [];
|
|
1615
|
+
for (const key of this.hostSuppliedKeys) {
|
|
1616
|
+
const held = this.fileCache.get(key);
|
|
1617
|
+
const abs = (0, path.resolve)(this.rootDir, key);
|
|
1618
|
+
if (held === void 0 || carried?.has(abs)) continue;
|
|
1619
|
+
let onDisk;
|
|
1620
|
+
try {
|
|
1621
|
+
onDisk = (0, fs.readFileSync)(abs, "utf-8");
|
|
1622
|
+
} catch {
|
|
1623
|
+
onDisk = null;
|
|
1624
|
+
}
|
|
1625
|
+
if (onDisk === null || require_pipeline.contentHash(onDisk) !== held.hash) stale.push(key);
|
|
1626
|
+
}
|
|
1627
|
+
return stale.sort();
|
|
1628
|
+
}
|
|
1629
|
+
/** `raw` is the attempt's input by original path, from which a
|
|
1630
|
+
* quarantined original stays held: every later pass re-ingests it, so it
|
|
1631
|
+
* keeps counting as skipped (nothing it renders is pruned) and its fix
|
|
1632
|
+
* edit compares against its bytes. */
|
|
1633
|
+
publishSourceIngestion(result, pass, raw) {
|
|
1634
|
+
const endedHold = this.heldRejection !== null;
|
|
1635
|
+
const previousOwnership = this.corpus.published.ownership;
|
|
1082
1636
|
this.heldRejection = null;
|
|
1083
|
-
|
|
1637
|
+
const held = new Map(result.originalEntries.map((entry) => [entry.path, {
|
|
1084
1638
|
hash: entry.hash,
|
|
1085
1639
|
source: entry.source
|
|
1086
1640
|
}]));
|
|
1641
|
+
for (const path$19 of result.skippedOriginals ?? []) {
|
|
1642
|
+
const entry = raw(path$19);
|
|
1643
|
+
if (entry) held.set(path$19, {
|
|
1644
|
+
hash: entry.hash ?? require_pipeline.contentHash(entry.source),
|
|
1645
|
+
source: entry.source
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
this.fileCache = held;
|
|
1087
1649
|
this.corpus.publish(result);
|
|
1650
|
+
const staged = this.stagedPublication;
|
|
1651
|
+
if (staged === null) return;
|
|
1652
|
+
this.stagedPublication = null;
|
|
1653
|
+
const previous = this.publication;
|
|
1654
|
+
const publication = {
|
|
1655
|
+
...staged,
|
|
1656
|
+
ownership: result.ownership
|
|
1657
|
+
};
|
|
1658
|
+
this.publication = publication;
|
|
1659
|
+
this.onOutcome?.({
|
|
1660
|
+
kind: "published",
|
|
1661
|
+
publication,
|
|
1662
|
+
pass,
|
|
1663
|
+
planChanged: require_pipeline.diffFilePlans(require_pipeline.snapshotFilePlans(previous?.manifest ?? null), require_pipeline.snapshotFilePlans(publication.manifest)),
|
|
1664
|
+
endedHold,
|
|
1665
|
+
redeliver: endedHold ? Object.values(result.ownership).filter((owner) => previousOwnership[owner.originalPath]?.originalHash !== owner.originalHash).map((owner) => owner.originalPath) : []
|
|
1666
|
+
});
|
|
1088
1667
|
}
|
|
1089
1668
|
/** Full analysis + publication, registered in the one in-flight
|
|
1090
|
-
* transaction slot; a nested reload leaves the enclosing registration.
|
|
1091
|
-
|
|
1669
|
+
* transaction slot; a nested reload leaves the enclosing registration.
|
|
1670
|
+
* `sources` are bytes a host already read (see WatchChanges.sources):
|
|
1671
|
+
* the full read uses them, and those of the observations it takes, in
|
|
1672
|
+
* place of the disk. */
|
|
1673
|
+
async runFullPipeline(pending = [], sources = /* @__PURE__ */ new Map()) {
|
|
1092
1674
|
if (this.releasePublicationClaim === null) {
|
|
1093
|
-
const releaseOwner = claimExclusiveSessionOwner(`${this.driverLabel}:${this.sessionDir}`);
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
};
|
|
1675
|
+
const releaseOwner = claimExclusiveSessionOwner(this.key, `${this.driverLabel}:${this.sessionDir}`);
|
|
1676
|
+
if (this.persistArtifacts) {
|
|
1677
|
+
(0, fs.mkdirSync)(this.sessionDir, { recursive: true });
|
|
1678
|
+
holdBuildClaimRecord(this.key, () => holdDirectoryClaim(this.sessionDir, { build: this.sessionId }));
|
|
1679
|
+
}
|
|
1680
|
+
this.releasePublicationClaim = releaseOwner;
|
|
1100
1681
|
}
|
|
1101
1682
|
const priorSystem = this.system;
|
|
1102
|
-
const
|
|
1103
|
-
|
|
1104
|
-
|
|
1683
|
+
const priorRegistration = this.snapshotWatchRegistration();
|
|
1684
|
+
this.attemptRecordedSystemDependencies = false;
|
|
1685
|
+
const nested = getWatchTransaction(this.key) !== null;
|
|
1686
|
+
const observations = nested ? null : this.takeObservations();
|
|
1687
|
+
const taken = observations?.taken ?? null;
|
|
1688
|
+
const transaction = this.runPipelineTransaction(pending, new Map([...observations?.sources ?? [], ...sources]));
|
|
1689
|
+
if (taken !== null) {
|
|
1690
|
+
setWatchTransaction(this.key, transaction);
|
|
1691
|
+
this.settleTaken(taken, transaction);
|
|
1692
|
+
}
|
|
1105
1693
|
try {
|
|
1106
1694
|
await transaction;
|
|
1107
1695
|
} catch (error) {
|
|
1108
1696
|
this.system = priorSystem;
|
|
1697
|
+
this.restoreWatchRegistration(priorRegistration);
|
|
1698
|
+
if (priorSystem !== null && (this.development || error instanceof AbortedParseRejection)) this.systemReloadOwed = true;
|
|
1109
1699
|
throw error;
|
|
1110
1700
|
} finally {
|
|
1111
|
-
if (!nested) setWatchTransaction(null);
|
|
1701
|
+
if (!nested) setWatchTransaction(this.key, null);
|
|
1702
|
+
}
|
|
1703
|
+
this.systemReloadOwed = false;
|
|
1704
|
+
this.lastGoodSystemDependencies = {
|
|
1705
|
+
keys: this.systemDependencyKeys,
|
|
1706
|
+
paths: this.systemDependencyPaths,
|
|
1707
|
+
missing: this.systemMissingPaths,
|
|
1708
|
+
hashes: this.systemDependencyHashes
|
|
1709
|
+
};
|
|
1710
|
+
this.commitSystemAuthority();
|
|
1711
|
+
}
|
|
1712
|
+
/** This attempt's system dependencies: the files its load read, and the
|
|
1713
|
+
* paths it found absent, whose creation is a system change. */
|
|
1714
|
+
recordSystemDependencies(systemPath, dependencies, missing) {
|
|
1715
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1716
|
+
for (const path$20 of [
|
|
1717
|
+
systemPath,
|
|
1718
|
+
...dependencies,
|
|
1719
|
+
...missing
|
|
1720
|
+
]) for (const key of require_pipeline.toWatchKeys(path$20)) keys.add(key);
|
|
1721
|
+
const hashes = /* @__PURE__ */ new Map();
|
|
1722
|
+
for (const dep of dependencies.length > 0 ? dependencies : [systemPath]) try {
|
|
1723
|
+
hashes.set(dep, require_pipeline.contentHash((0, fs.readFileSync)(dep, "utf-8")));
|
|
1724
|
+
} catch {
|
|
1725
|
+
hashes.set(dep, "");
|
|
1726
|
+
}
|
|
1727
|
+
for (const path$21 of missing) hashes.set(path$21, "");
|
|
1728
|
+
this.systemDependencyKeys = keys;
|
|
1729
|
+
this.systemDependencyPaths = dependencies;
|
|
1730
|
+
this.systemMissingPaths = missing;
|
|
1731
|
+
this.systemDependencyHashes = hashes;
|
|
1732
|
+
this.attemptRecordedSystemDependencies = true;
|
|
1733
|
+
}
|
|
1734
|
+
/** Records the configuration inputs and, for a tsconfig-aliased host,
|
|
1735
|
+
* re-reads its aliases. */
|
|
1736
|
+
readConfigInputs(rootDir) {
|
|
1737
|
+
this.configInputPaths = [(0, path.join)(rootDir, "package.json"), ...require_pipeline.tsconfigChainPaths(rootDir)];
|
|
1738
|
+
this.configInputKeys = new Set(this.configInputPaths.flatMap((path$22) => require_pipeline.toWatchKeys(path$22)));
|
|
1739
|
+
this.configInputHashes = new Map(this.configInputPaths.map((path$23) => [path$23, fileHashOrEmpty(path$23)]));
|
|
1740
|
+
if (this.aliasesFromTsconfig) this.pathAliasesJson = require_pipeline.buildPathAliasesJson(require_pipeline.readTsconfigAliasPairs(rootDir), rootDir)?.json ?? null;
|
|
1741
|
+
}
|
|
1742
|
+
/** What hosts watch for assets, and what the last-good generation
|
|
1743
|
+
* reported about its kits and packages, which a failed full pass must
|
|
1744
|
+
* leave in place. */
|
|
1745
|
+
snapshotWatchRegistration() {
|
|
1746
|
+
return {
|
|
1747
|
+
assetDependencyPaths: new Set(this.assetDependencyPaths),
|
|
1748
|
+
assetDependencyKeys: new Set(this.assetDependencyKeys),
|
|
1749
|
+
kitDescriptors: this.kitDescriptors,
|
|
1750
|
+
lastExternalOutcomes: this.lastExternalOutcomes
|
|
1751
|
+
};
|
|
1752
|
+
}
|
|
1753
|
+
/** Restores the last-good registration. The system dependencies are the
|
|
1754
|
+
* last committed pass's and, when its load got that far, this failed
|
|
1755
|
+
* attempt's (spec §2.3): a fix to any of them must reach the owed reload,
|
|
1756
|
+
* and an earlier failure's are replaced, never accumulated. */
|
|
1757
|
+
restoreWatchRegistration(prior) {
|
|
1758
|
+
Object.assign(this, prior);
|
|
1759
|
+
const lastGood = this.lastGoodSystemDependencies;
|
|
1760
|
+
if (!this.attemptRecordedSystemDependencies) {
|
|
1761
|
+
this.systemDependencyKeys = lastGood.keys;
|
|
1762
|
+
this.systemDependencyPaths = lastGood.paths;
|
|
1763
|
+
this.systemMissingPaths = lastGood.missing;
|
|
1764
|
+
this.systemDependencyHashes = lastGood.hashes;
|
|
1765
|
+
return;
|
|
1112
1766
|
}
|
|
1767
|
+
this.systemMissingPaths = [.../* @__PURE__ */ new Set([...lastGood.missing, ...this.systemMissingPaths])];
|
|
1768
|
+
this.systemDependencyKeys = /* @__PURE__ */ new Set([...lastGood.keys, ...this.systemDependencyKeys]);
|
|
1769
|
+
this.systemDependencyPaths = [.../* @__PURE__ */ new Set([...lastGood.paths, ...this.systemDependencyPaths])];
|
|
1770
|
+
this.systemDependencyHashes = new Map([...this.systemDependencyHashes, ...lastGood.hashes]);
|
|
1771
|
+
}
|
|
1772
|
+
/** The build's authority becomes the system this session committed, with
|
|
1773
|
+
* it and never before: a failed reload leaves the committed one, and the
|
|
1774
|
+
* context proofs made against it. A committed reload discards every
|
|
1775
|
+
* other context's proof, even for an unchanged system: the reload may
|
|
1776
|
+
* have changed what another context resolves, so each loads again. */
|
|
1777
|
+
commitSystemAuthority() {
|
|
1778
|
+
setSystemAuthority(this.key, {
|
|
1779
|
+
...systemLoad(this.system),
|
|
1780
|
+
context: this.resolutionContextText(),
|
|
1781
|
+
label: this.resolutionContextLabel(),
|
|
1782
|
+
others: /* @__PURE__ */ new Map()
|
|
1783
|
+
});
|
|
1113
1784
|
}
|
|
1114
|
-
async runPipelineTransaction(pending) {
|
|
1785
|
+
async runPipelineTransaction(pending, hostSources) {
|
|
1115
1786
|
const pipelineStart = this.now();
|
|
1116
1787
|
const bt = {};
|
|
1117
1788
|
this.runSessionStartHygiene();
|
|
1118
|
-
setSessionArtifactDir(this.sessionDir);
|
|
1789
|
+
setSessionArtifactDir(this.key, this.sessionDir);
|
|
1119
1790
|
const rootDir = this.rootDir;
|
|
1120
1791
|
const priorSourceState = this.snapshotSourceState();
|
|
1121
1792
|
const resolvedSystemPath = (0, path.resolve)(rootDir, this.options.system);
|
|
1793
|
+
this.readConfigInputs(rootDir);
|
|
1794
|
+
const context = {
|
|
1795
|
+
conditions: this.conditions,
|
|
1796
|
+
workspaces: require_pipeline.declaredWorkspacePackages(rootDir),
|
|
1797
|
+
extensions: this.extensionPolicy
|
|
1798
|
+
};
|
|
1799
|
+
this.log(`extension order: ${context.extensions.resolverVersion}`);
|
|
1122
1800
|
let t = this.now();
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1801
|
+
try {
|
|
1802
|
+
this.system = this.loadSystemUnder(context);
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
const reads = require_pipeline.systemLoadReads(error);
|
|
1805
|
+
this.recordSystemDependencies(resolvedSystemPath, reads.dependencies, reads.missing);
|
|
1806
|
+
throw error;
|
|
1807
|
+
}
|
|
1130
1808
|
this.assetCopyCache.clear();
|
|
1131
1809
|
this.assetDependencyPaths.clear();
|
|
1132
1810
|
this.assetDependencyKeys.clear();
|
|
1133
|
-
|
|
1134
|
-
const deps = this.system.dependencies ?? [];
|
|
1135
|
-
const keys = /* @__PURE__ */ new Set();
|
|
1136
|
-
for (const key of require_pipeline.toWatchKeys(resolvedSystemPath)) keys.add(key);
|
|
1137
|
-
for (const dep of deps) for (const key of require_pipeline.toWatchKeys(dep)) keys.add(key);
|
|
1138
|
-
this.systemDependencyKeys = keys;
|
|
1139
|
-
this.systemDependencyPaths = deps;
|
|
1140
|
-
const hashes = /* @__PURE__ */ new Map();
|
|
1141
|
-
for (const dep of deps.length > 0 ? deps : [resolvedSystemPath]) try {
|
|
1142
|
-
hashes.set(dep, require_pipeline.contentHash((0, fs.readFileSync)(dep, "utf-8")));
|
|
1143
|
-
} catch {
|
|
1144
|
-
hashes.set(dep, "");
|
|
1145
|
-
}
|
|
1146
|
-
this.systemDependencyHashes = hashes;
|
|
1147
|
-
}
|
|
1811
|
+
this.recordSystemDependencies(resolvedSystemPath, this.system.dependencies ?? [], this.system.missing ?? []);
|
|
1148
1812
|
bt.systemLoad = this.elapsed(t);
|
|
1149
1813
|
t = this.now();
|
|
1150
|
-
const { excludeMatcher, extensionsSet } = this.
|
|
1814
|
+
const { excludeMatcher, extensionsSet } = this.scanConfig;
|
|
1151
1815
|
const files = require_pipeline.discoverFiles(rootDir, rootDir, excludeMatcher, extensionsSet);
|
|
1816
|
+
if (claimUnmatchedExcludeReport(this.key)) for (const line of require_pipeline.unmatchedExcludeLines(excludeMatcher)) this.logger.error(line);
|
|
1152
1817
|
bt.fileDiscovery = this.elapsed(t);
|
|
1153
1818
|
t = this.now();
|
|
1154
1819
|
const rawEntries = [];
|
|
1155
1820
|
const ingestionFailures = [];
|
|
1821
|
+
const readPaths = /* @__PURE__ */ new Set();
|
|
1156
1822
|
for (const filePath of files) {
|
|
1157
1823
|
const relPath = (0, path.relative)(rootDir, filePath);
|
|
1824
|
+
readPaths.add(filePath);
|
|
1158
1825
|
let source;
|
|
1159
1826
|
try {
|
|
1160
|
-
|
|
1827
|
+
const supplied = hostSources.get(filePath);
|
|
1828
|
+
source = supplied ?? (0, fs.readFileSync)(filePath, "utf-8");
|
|
1829
|
+
if (supplied === void 0) this.hostSuppliedKeys.delete(relPath);
|
|
1830
|
+
else this.hostSuppliedKeys.add(relPath);
|
|
1161
1831
|
} catch (err) {
|
|
1162
1832
|
if (!require_pipeline.isDeletedSource(err)) ingestionFailures.push(require_pipeline.unreadableSourceDiagnostic(relPath, err));
|
|
1163
1833
|
continue;
|
|
@@ -1169,21 +1839,36 @@ var ExtractionSession = class {
|
|
|
1169
1839
|
});
|
|
1170
1840
|
}
|
|
1171
1841
|
bt.fileRead = this.elapsed(t);
|
|
1842
|
+
const rawPaths = new Set(rawEntries.map((entry) => entry.path));
|
|
1172
1843
|
t = this.now();
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1844
|
+
const resolver = require_pipeline.discoveryResolver(this.hostResolve, this.engine(), {
|
|
1845
|
+
rootDir,
|
|
1846
|
+
conditions: context.conditions,
|
|
1847
|
+
workspaces: context.workspaces,
|
|
1848
|
+
extensions: context.extensions
|
|
1177
1849
|
});
|
|
1178
|
-
discoveryDiagnostics
|
|
1179
|
-
const
|
|
1850
|
+
const discoveryDiagnostics = [];
|
|
1851
|
+
const systemPackageRoot = require_pipeline.findPackageRoot(resolvedSystemPath);
|
|
1852
|
+
const kitMembers = /* @__PURE__ */ new Map();
|
|
1853
|
+
const packageNames = await require_pipeline.extractSystemFilePackages(resolvedSystemPath, { systemMembership: this.engine().systemMembership ?? requireEngine().systemMembership }, (diagnostic) => discoveryDiagnostics.push(diagnostic), (specifier, importer) => require_pipeline.findPackageRoot(importer) === systemPackageRoot ? resolver.resolveSpecifier(specifier) : resolver.loaderResolve(specifier, (0, path.dirname)(importer)), context.extensions, kitMembers);
|
|
1854
|
+
discoveryDiagnostics.push(...require_pipeline.importedKitDiagnostics(rawEntries, this.engine(), rootDir, packageNames, (entries) => this.corpus.facts(entries)));
|
|
1180
1855
|
const rawExternalFiles = /* @__PURE__ */ new Map();
|
|
1181
1856
|
const collected = await require_pipeline.collectExternalPackageSources({
|
|
1857
|
+
extensions: context.extensions,
|
|
1858
|
+
kitSourceRoot: require_pipeline.nativeKitSourceRoot(this.engine(), rootDir, context.conditions, context.workspaces, require_pipeline.extensionPolicyJson(context.extensions)),
|
|
1859
|
+
locatePackage: require_pipeline.nativePackageLocator(this.engine(), context.workspaces),
|
|
1182
1860
|
specifiers: packageNames,
|
|
1183
|
-
resolveSpecifier: (
|
|
1861
|
+
resolveSpecifier: async (specifier) => kitMembers.get(specifier) ?? await resolver.resolveSpecifier(specifier),
|
|
1862
|
+
locateFrom: (specifier) => {
|
|
1863
|
+
const file = kitMembers.get(specifier);
|
|
1864
|
+
return file === void 0 ? rootDir : (0, path.dirname)(file);
|
|
1865
|
+
},
|
|
1866
|
+
unresolvedReason: resolver.unresolvedReason,
|
|
1867
|
+
requireSourceEntry: resolver.requireSourceEntry,
|
|
1184
1868
|
rootDir,
|
|
1185
1869
|
extensionsSet,
|
|
1186
|
-
hasEntry: (relPath) =>
|
|
1870
|
+
hasEntry: (relPath) => rawPaths.has(relPath),
|
|
1871
|
+
hostOutputDirs: this.structuralExclude.map((dir) => (0, path.resolve)(rootDir, dir)),
|
|
1187
1872
|
onSourceRead: (source, _relPath, absPath) => {
|
|
1188
1873
|
rawExternalFiles.set(absPath, require_pipeline.contentHash(source));
|
|
1189
1874
|
},
|
|
@@ -1202,21 +1887,22 @@ var ExtractionSession = class {
|
|
|
1202
1887
|
});
|
|
1203
1888
|
this.lastExternalOutcomes = collected.outcomes;
|
|
1204
1889
|
this.ingestionFailureDiagnostics = ingestionFailures;
|
|
1890
|
+
if (resolver.hostFirst) discoveryDiagnostics.push(...systemInstanceMismatches({
|
|
1891
|
+
rootDir,
|
|
1892
|
+
specifiers: packageNames,
|
|
1893
|
+
instances: collected.packageInstances,
|
|
1894
|
+
evaluated: this.systemDependencyPaths,
|
|
1895
|
+
loaderResolve: (specifier) => resolver.loaderResolve(specifier, (0, path.dirname)(resolvedSystemPath))
|
|
1896
|
+
}));
|
|
1205
1897
|
require_pipeline.surfaceManifestDiagnostics({ diagnostics: [...discoveryDiagnostics, ...collected.diagnostics] }, (message) => this.warn(message), {
|
|
1206
1898
|
levels: this.options.diagnostics,
|
|
1207
1899
|
strict: this.options.strict,
|
|
1208
1900
|
reportErrors: this.reportErrors()
|
|
1209
1901
|
});
|
|
1210
|
-
const unresolvableMessage = require_pipeline.unresolvableIncludesMessage(collected.outcomes);
|
|
1211
|
-
if (unresolvableMessage !== null)
|
|
1212
|
-
if (this.options.strict) this.strictFailure(unresolvableMessage);
|
|
1213
|
-
else this.warn(unresolvableMessage);
|
|
1214
|
-
}
|
|
1902
|
+
const unresolvableMessage = require_pipeline.unresolvableIncludesMessage(collected.outcomes, (0, path.relative)(rootDir, resolvedSystemPath));
|
|
1903
|
+
if (unresolvableMessage !== null) this.strictOrWarn(unresolvableMessage);
|
|
1215
1904
|
const staleDistMessage = require_pipeline.staleDistIncludesMessage(collected.outcomes);
|
|
1216
|
-
if (staleDistMessage !== null)
|
|
1217
|
-
if (this.options.strict) this.strictFailure(staleDistMessage);
|
|
1218
|
-
else this.warn(staleDistMessage);
|
|
1219
|
-
}
|
|
1905
|
+
if (staleDistMessage !== null) this.strictOrWarn(staleDistMessage);
|
|
1220
1906
|
const rejectedSpecifiers = /* @__PURE__ */ new Set();
|
|
1221
1907
|
const crossVolumeDetails = [];
|
|
1222
1908
|
for (const [dir, specifiers] of Object.entries(collected.dirOwnerSets)) {
|
|
@@ -1233,6 +1919,7 @@ var ExtractionSession = class {
|
|
|
1233
1919
|
for (const message of this.stickyDiagnostics.values()) this.warn(message);
|
|
1234
1920
|
const admitted = require_pipeline.excludeCollectedPackages(collected, rejectedSpecifiers, rootDir);
|
|
1235
1921
|
this.kitDescriptors = admitted.kitDescriptors;
|
|
1922
|
+
this.packageInstances = admitted.packageInstances;
|
|
1236
1923
|
try {
|
|
1237
1924
|
const identity = require_pipeline.createSourceIdentity(rootDir);
|
|
1238
1925
|
this.externalRootOwners = /* @__PURE__ */ new Map();
|
|
@@ -1268,23 +1955,29 @@ var ExtractionSession = class {
|
|
|
1268
1955
|
this.externalSourceEntries = admitted.sourceEntries;
|
|
1269
1956
|
this.externalSourceSideEffects = admitted.sourceEntrySideEffects;
|
|
1270
1957
|
for (const entry of admitted.entries) {
|
|
1271
|
-
const
|
|
1958
|
+
const entryPath = (0, path.resolve)(rootDir, entry.path);
|
|
1959
|
+
readPaths.add(entryPath);
|
|
1960
|
+
const source = hostSources.get(entryPath) ?? entry.source;
|
|
1272
1961
|
rawEntries.push({
|
|
1273
1962
|
path: entry.path,
|
|
1274
|
-
source
|
|
1275
|
-
hash
|
|
1963
|
+
source,
|
|
1964
|
+
hash: require_pipeline.contentHash(source)
|
|
1276
1965
|
});
|
|
1277
1966
|
}
|
|
1278
1967
|
this.externalPackageDirs = admitted.packageDirs;
|
|
1279
1968
|
this.externalLinkedDirs = admitted.linkedDirs;
|
|
1280
1969
|
this.externalOutputDirs = admitted.outputDirs;
|
|
1281
1970
|
bt.packageResolve = this.elapsed(t);
|
|
1971
|
+
const misspelled = misspelledHostSources(hostSources, readPaths).map(({ path: path$24, read }) => `${(0, path.relative)(rootDir, path$24)} (read as ${read.map((readPath) => (0, path.relative)(rootDir, readPath)).join(", ")})`);
|
|
1972
|
+
if (misspelled.length > 0) throw new DeferredReloadRefusal(`[${this.driverLabel}] reload not published: the host supplied bytes for ${misspelled.join(", ")}, which the full read reaches only under another spelling; the last generation stays served`);
|
|
1282
1973
|
this.beginStatusAttempt();
|
|
1283
1974
|
let accepted;
|
|
1284
1975
|
try {
|
|
1285
|
-
accepted = await this.corpus.prepare(rawEntries);
|
|
1976
|
+
accepted = await this.corpus.prepare(this.withoutOutputSources(rawEntries));
|
|
1286
1977
|
this.throwOnAbortedParse(accepted);
|
|
1287
1978
|
this.skippedSources = accepted.skippedOriginals ?? [];
|
|
1979
|
+
this.ingestionOpenings = accepted.openings ?? [];
|
|
1980
|
+
this.ingestionAdapted = require_pipeline.adaptedOriginals(accepted.ownership);
|
|
1288
1981
|
} catch (err) {
|
|
1289
1982
|
this.debouncePending.clear();
|
|
1290
1983
|
this.writeAnalysisStatus("failed", pending, String(err));
|
|
@@ -1292,19 +1985,30 @@ var ExtractionSession = class {
|
|
|
1292
1985
|
}
|
|
1293
1986
|
this.externalFileOwners = require_pipeline.projectExternalFileOwners(accepted, this.externalFileOwners);
|
|
1294
1987
|
bt.fileCount = accepted.analysisEntries.length;
|
|
1295
|
-
require_pipeline.clearEngineCache(
|
|
1988
|
+
require_pipeline.clearEngineCache(() => this.engine());
|
|
1296
1989
|
await this.analyzeAndEmit(accepted.analysisEntries, packageMap, false, bt, pipelineStart, pending);
|
|
1297
|
-
this.publishSourceIngestion(accepted);
|
|
1298
|
-
setSharedExternalDirs(admitted.packageDirs);
|
|
1299
|
-
setSharedExternalEntries(admitted.sourceEntries);
|
|
1300
|
-
setSharedExternalSideEffects(admitted.sourceEntrySideEffects);
|
|
1990
|
+
this.publishSourceIngestion(accepted, "full", (path$25) => rawEntries.find((entry) => entry.path === path$25));
|
|
1991
|
+
setSharedExternalDirs(this.key, admitted.packageDirs);
|
|
1992
|
+
setSharedExternalEntries(this.key, admitted.sourceEntries);
|
|
1993
|
+
setSharedExternalSideEffects(this.key, admitted.sourceEntrySideEffects);
|
|
1301
1994
|
this.onExternalRootsCommitted?.(this.externalWatchRoots);
|
|
1302
|
-
setOwningWatchSession(this);
|
|
1995
|
+
setOwningWatchSession(this.key, this);
|
|
1303
1996
|
} catch (err) {
|
|
1304
1997
|
this.restoreSourceState(priorSourceState);
|
|
1305
1998
|
throw err;
|
|
1306
1999
|
}
|
|
1307
2000
|
}
|
|
2001
|
+
/** The full pass's analysis inputs, less any file whose real path lies in
|
|
2002
|
+
* an output directory, each reported. */
|
|
2003
|
+
withoutOutputSources(entries) {
|
|
2004
|
+
const inOutput = require_pipeline.outputSourceGuard(this.rootDir, this.structuralExclude);
|
|
2005
|
+
return entries.filter((entry) => {
|
|
2006
|
+
const outputDir = inOutput((0, path.resolve)(this.rootDir, entry.path));
|
|
2007
|
+
if (outputDir === null) return true;
|
|
2008
|
+
this.warn(require_pipeline.outputSourceMessage(entry.path, outputDir));
|
|
2009
|
+
return false;
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
1308
2012
|
/** Per-generation source state captured as ONE object so the restore
|
|
1309
2013
|
* cannot omit a member; deep-copied fields are mutated in place. */
|
|
1310
2014
|
snapshotSourceState() {
|
|
@@ -1326,16 +2030,92 @@ var ExtractionSession = class {
|
|
|
1326
2030
|
restoreSourceState(snapshot) {
|
|
1327
2031
|
Object.assign(this, snapshot);
|
|
1328
2032
|
}
|
|
1329
|
-
/**
|
|
1330
|
-
*
|
|
2033
|
+
/** The system under `context`: the one load path for the build's
|
|
2034
|
+
* authority and for a participant checking its own context. */
|
|
2035
|
+
loadSystemUnder(context) {
|
|
2036
|
+
return require_pipeline.loadSystemConfig(() => this.engine(), {
|
|
2037
|
+
systemPath: (0, path.resolve)(this.rootDir, this.options.system),
|
|
2038
|
+
rootDir: this.rootDir,
|
|
2039
|
+
prefix: this.options.prefix,
|
|
2040
|
+
prefixContextualVars: this.options.prefixContextualVars,
|
|
2041
|
+
conditions: context.conditions,
|
|
2042
|
+
workspaces: context.workspaces,
|
|
2043
|
+
extensions: context.extensions
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
/** What the system loader reads of this participant besides the root, as
|
|
2047
|
+
* stable text: its conditions and extension order. */
|
|
2048
|
+
resolutionContextText() {
|
|
2049
|
+
return JSON.stringify([this.conditions, require_pipeline.extensionPolicyJson(this.extensionPolicy)]);
|
|
2050
|
+
}
|
|
2051
|
+
resolutionContextLabel() {
|
|
2052
|
+
return `${this.driverLabel} with conditions [${this.conditions.join(", ")}] and extension order ${this.extensionPolicy.resolverVersion}`;
|
|
2053
|
+
}
|
|
2054
|
+
/** One agreed system authority (SA5): a participant whose resolution
|
|
2055
|
+
* context differs from the one the build's system was loaded under loads
|
|
2056
|
+
* it once under its own, and proceeds only on the same system. Otherwise
|
|
2057
|
+
* it throws, naming both contexts, whichever participant came first. */
|
|
2058
|
+
agreeWithSystemAuthority() {
|
|
2059
|
+
const authority = getSystemAuthority(this.key);
|
|
2060
|
+
if (authority === null) return;
|
|
2061
|
+
const context = this.resolutionContextText();
|
|
2062
|
+
if (context === authority.context) return;
|
|
2063
|
+
let own = authority.others.get(context);
|
|
2064
|
+
if (own === void 0) {
|
|
2065
|
+
own = systemLoad(this.loadSystemUnder({
|
|
2066
|
+
conditions: this.conditions,
|
|
2067
|
+
workspaces: require_pipeline.declaredWorkspacePackages(this.rootDir),
|
|
2068
|
+
extensions: this.extensionPolicy
|
|
2069
|
+
}));
|
|
2070
|
+
authority.others.set(context, own);
|
|
2071
|
+
}
|
|
2072
|
+
if (own.identity === authority.identity) return;
|
|
2073
|
+
const shared = new Set(authority.dependencies);
|
|
2074
|
+
const mine = new Set(own.dependencies);
|
|
2075
|
+
const differing = [...authority.dependencies.filter((path$26) => !mine.has(path$26)), ...own.dependencies.filter((path$27) => !shared.has(path$27))].map((path$28) => (0, path.relative)(this.rootDir, path$28));
|
|
2076
|
+
throw new SessionConfigurationConflict(this.key, authority.label, this.resolutionContextLabel(), differing.length > 0 ? [`system modules ${differing.join(", ")}`] : ["the evaluated system"], "Every participant of one build must evaluate the same system: give each compiler conditions that select the same system modules.");
|
|
2077
|
+
}
|
|
2078
|
+
/** Release this session's publication claim so a successor may publish,
|
|
2079
|
+
* and leave the build, which retires with its last participant.
|
|
2080
|
+
* Idempotent; `reattach` joins the build again. */
|
|
1331
2081
|
close() {
|
|
1332
2082
|
this.releasePublicationClaim?.();
|
|
1333
2083
|
this.releasePublicationClaim = null;
|
|
2084
|
+
if (this.joinedKey === null || this.participant === null) return;
|
|
2085
|
+
if (getOwningWatchSession(this.joinedKey) === this) setOwningWatchSession(this.joinedKey, null);
|
|
2086
|
+
leaveSession(this.joinedKey, this.participant);
|
|
2087
|
+
this.participant = null;
|
|
2088
|
+
this.detached = true;
|
|
2089
|
+
}
|
|
2090
|
+
/** A closed participant attaching again: its private analysis is stale,
|
|
2091
|
+
* so it is discarded, and it joins its build again. Idempotent. */
|
|
2092
|
+
reattach() {
|
|
2093
|
+
if (!this.detached) return;
|
|
2094
|
+
this.detached = false;
|
|
2095
|
+
this.system = null;
|
|
2096
|
+
this.joinedKey = null;
|
|
2097
|
+
}
|
|
2098
|
+
/** Brings a reattached participant up to date with a full rescan of a
|
|
2099
|
+
* live build, which reconciles deletions too; true then. A retired build
|
|
2100
|
+
* is false: the host starts it as at first. */
|
|
2101
|
+
async catchUp() {
|
|
2102
|
+
if (getManifestJson(this.key) === null) return false;
|
|
2103
|
+
await this.awaitObservation(this.recordRescan());
|
|
2104
|
+
return true;
|
|
2105
|
+
}
|
|
2106
|
+
/** Queues a full rescan under a new revision. It is never equivalent to
|
|
2107
|
+
* an active attempt: only a full read covers it. */
|
|
2108
|
+
recordRescan() {
|
|
2109
|
+
const queue = observationQueue(this.key);
|
|
2110
|
+
queue.rescan = true;
|
|
2111
|
+
const revision = ++queue.revision;
|
|
2112
|
+
queue.revisions.set(revision, null);
|
|
2113
|
+
return revision;
|
|
1334
2114
|
}
|
|
1335
2115
|
/** Reset analysis state for a system reload: write guards go back to null
|
|
1336
2116
|
* and reseed from the disk envelopes on the next publication. */
|
|
1337
2117
|
resetForHmr() {
|
|
1338
|
-
resetAnalysisStartedPromise();
|
|
2118
|
+
resetAnalysisStartedPromise(this.key);
|
|
1339
2119
|
this.artifactRecords = {
|
|
1340
2120
|
manifest: null,
|
|
1341
2121
|
inputs: null,
|
|
@@ -1346,16 +2126,16 @@ var ExtractionSession = class {
|
|
|
1346
2126
|
/** The (sourceKey, observed hash) pairs of a watch batch — the status
|
|
1347
2127
|
* file's pending set; loaders wait only on observed inputs. */
|
|
1348
2128
|
pendingFromBatch(changes) {
|
|
1349
|
-
const { excludeMatcher, extensionsSet } = this.
|
|
2129
|
+
const { excludeMatcher, extensionsSet } = this.scanConfig;
|
|
1350
2130
|
const pending = [];
|
|
1351
|
-
for (const path$
|
|
1352
|
-
const classified = this.classifyWatchPath(path$
|
|
2131
|
+
for (const path$29 of changes.modifiedFiles ?? []) {
|
|
2132
|
+
const classified = this.classifyWatchPath(path$29, {
|
|
1353
2133
|
excludeMatcher,
|
|
1354
2134
|
extensionsSet
|
|
1355
2135
|
});
|
|
1356
2136
|
if (!classified) continue;
|
|
1357
2137
|
try {
|
|
1358
|
-
pending.push([classified.key, require_pipeline.contentHash((0, fs.readFileSync)(path$
|
|
2138
|
+
pending.push([classified.key, require_pipeline.contentHash(changes.sources?.get(path$29) ?? (0, fs.readFileSync)(path$29, "utf-8"))]);
|
|
1359
2139
|
} catch {}
|
|
1360
2140
|
}
|
|
1361
2141
|
return pending;
|
|
@@ -1367,28 +2147,28 @@ var ExtractionSession = class {
|
|
|
1367
2147
|
if (!identity) return changes;
|
|
1368
2148
|
if (!changes.modifiedFiles && !changes.removedFiles) return changes;
|
|
1369
2149
|
if (this.externalWatchRoots.length === 0) return changes;
|
|
1370
|
-
const { extensionsSet } = this.
|
|
2150
|
+
const { extensionsSet } = this.scanConfig;
|
|
1371
2151
|
const dirtyRoots = /* @__PURE__ */ new Set();
|
|
1372
2152
|
const modified = /* @__PURE__ */ new Set();
|
|
1373
2153
|
const removed = /* @__PURE__ */ new Set();
|
|
1374
|
-
const rootHitFor = (path$
|
|
1375
|
-
const containing = identity.containingExternalRoot(path$
|
|
2154
|
+
const rootHitFor = (path$30) => {
|
|
2155
|
+
const containing = identity.containingExternalRoot(path$30);
|
|
1376
2156
|
if (!containing) return null;
|
|
1377
2157
|
try {
|
|
1378
|
-
return (0, fs.statSync)(path$
|
|
2158
|
+
return (0, fs.statSync)(path$30).isDirectory() ? containing : null;
|
|
1379
2159
|
} catch {
|
|
1380
|
-
return identity.resolveDeletedSourceId(path$
|
|
2160
|
+
return identity.resolveDeletedSourceId(path$30) ? null : containing;
|
|
1381
2161
|
}
|
|
1382
2162
|
};
|
|
1383
|
-
for (const path$
|
|
1384
|
-
const root = rootHitFor(path$
|
|
2163
|
+
for (const path$31 of changes.modifiedFiles ?? []) {
|
|
2164
|
+
const root = rootHitFor(path$31);
|
|
1385
2165
|
if (root) dirtyRoots.add(root);
|
|
1386
|
-
else modified.add(path$
|
|
2166
|
+
else modified.add(path$31);
|
|
1387
2167
|
}
|
|
1388
|
-
for (const path$
|
|
1389
|
-
const root = rootHitFor(path$
|
|
2168
|
+
for (const path$32 of changes.removedFiles ?? []) {
|
|
2169
|
+
const root = rootHitFor(path$32);
|
|
1390
2170
|
if (root) dirtyRoots.add(root);
|
|
1391
|
-
else removed.add(path$
|
|
2171
|
+
else removed.add(path$32);
|
|
1392
2172
|
}
|
|
1393
2173
|
for (const root of dirtyRoots) {
|
|
1394
2174
|
const previous = this.externalInventory.get(root) ?? /* @__PURE__ */ new Map();
|
|
@@ -1405,13 +2185,14 @@ var ExtractionSession = class {
|
|
|
1405
2185
|
} catch {
|
|
1406
2186
|
continue;
|
|
1407
2187
|
}
|
|
1408
|
-
if (previous.get(resolved.sourceKey)?.hash !== rawHash) modified.add(abs);
|
|
2188
|
+
if (previous.get(resolved.sourceKey)?.hash !== rawHash || this.ingestionFailureDiagnostics.some((diagnostic) => diagnostic.file === resolved.sourceKey)) modified.add(abs);
|
|
1409
2189
|
}
|
|
1410
2190
|
for (const [key, entry] of previous) if (!seen.has(key)) removed.add(entry.abs);
|
|
1411
2191
|
}
|
|
1412
2192
|
return {
|
|
1413
2193
|
modifiedFiles: modified,
|
|
1414
|
-
removedFiles: removed
|
|
2194
|
+
removedFiles: removed,
|
|
2195
|
+
sources: changes.sources
|
|
1415
2196
|
};
|
|
1416
2197
|
}
|
|
1417
2198
|
/** Route one watcher path: a project-root member (consumer excludes apply)
|
|
@@ -1478,33 +2259,40 @@ var ExtractionSession = class {
|
|
|
1478
2259
|
packageMap,
|
|
1479
2260
|
system,
|
|
1480
2261
|
emitter: {
|
|
1481
|
-
runtimeImport:
|
|
1482
|
-
cssModuleId:
|
|
2262
|
+
runtimeImport: this.runtimeImport,
|
|
2263
|
+
cssModuleId: this.cssModuleId,
|
|
1483
2264
|
systemPropsModuleId: this.systemPropsModuleId ?? systemPropsPath(this.sessionDir)
|
|
1484
2265
|
},
|
|
1485
2266
|
pathAliasesJson: this.pathAliasesJson,
|
|
1486
2267
|
staticCssJson: this.staticCssJson,
|
|
1487
2268
|
externalDirs: this.externalPackageDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
2269
|
+
rootDir: this.rootDir,
|
|
2270
|
+
assets: this.assetClassification,
|
|
2271
|
+
adapted: this.ingestionAdapted,
|
|
2272
|
+
loadExpander: this.loadExpander,
|
|
1488
2273
|
devMode: this.engineDevMode(devMode),
|
|
1489
2274
|
analysisContext: {
|
|
1490
2275
|
skippedSources: [...this.skippedSources, ...this.ingestionFailureDiagnostics.map((d) => d.file)].map((file) => (0, path.isAbsolute)(file) ? (0, path.relative)(this.rootDir, file) : file),
|
|
1491
2276
|
unbundledComputedImports: this.host.unbundledComputedImports,
|
|
2277
|
+
openings: [...this.ingestionOpenings],
|
|
1492
2278
|
packageDirs: this.externalPackageDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
1493
2279
|
linkedDirs: this.externalLinkedDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
1494
|
-
outputDirs: this.externalOutputDirs.map((dir) => (0, path.relative)(this.rootDir, dir))
|
|
2280
|
+
outputDirs: this.externalOutputDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
2281
|
+
extensions: this.extensionPolicy
|
|
1495
2282
|
}
|
|
1496
2283
|
};
|
|
1497
2284
|
this.writeAnalysisStatus("analyzing", pending);
|
|
1498
|
-
const result = require_pipeline.runProjectAnalysis(
|
|
2285
|
+
const result = require_pipeline.runProjectAnalysis(() => this.engine(), {
|
|
1499
2286
|
...analysisOptions,
|
|
1500
2287
|
warn: (message) => this.warn(message),
|
|
1501
2288
|
info: (message) => this.log(message),
|
|
1502
2289
|
strict: this.options.strict,
|
|
1503
2290
|
diagnostics: this.options.diagnostics,
|
|
1504
2291
|
reportErrors: this.reportErrors(),
|
|
1505
|
-
extraDiagnostics: this.ingestionFailureDiagnostics,
|
|
2292
|
+
extraDiagnostics: [...this.ingestionFailureDiagnostics, ...await this.compareAliasesWithHost(fileEntries)],
|
|
1506
2293
|
kitDescriptors: this.kitDescriptors
|
|
1507
2294
|
});
|
|
2295
|
+
if (result.manifest) require_pipeline.assertRuntimeImportSuppliesTerminals(this.runtimeImport, result.manifest);
|
|
1508
2296
|
require_pipeline.enforceExternalTokenContracts({
|
|
1509
2297
|
diagnostics: result.manifest?.diagnostics,
|
|
1510
2298
|
fileOwners: this.externalFileOwners,
|
|
@@ -1539,12 +2327,12 @@ var ExtractionSession = class {
|
|
|
1539
2327
|
declaration,
|
|
1540
2328
|
variables,
|
|
1541
2329
|
require_pipeline.postProcessCss(body, {
|
|
1542
|
-
minify: this.options.minify ??
|
|
2330
|
+
minify: this.options.minify ?? this.startupMode === "production",
|
|
1543
2331
|
targets: this.lcssTargets,
|
|
1544
2332
|
warnFn: (msg) => this.warn(msg)
|
|
1545
2333
|
})
|
|
1546
2334
|
].filter(Boolean).join("\n");
|
|
1547
|
-
require_pipeline.reportSurvivingAssetPlaceholders(fullCss, {
|
|
2335
|
+
if (!this.hostSubstitutesAssets) require_pipeline.reportSurvivingAssetPlaceholders(fullCss, {
|
|
1548
2336
|
strict: this.options.strict,
|
|
1549
2337
|
warn: (message) => this.warn(message),
|
|
1550
2338
|
reportErrors: this.reportErrors(),
|
|
@@ -1557,7 +2345,7 @@ var ExtractionSession = class {
|
|
|
1557
2345
|
prefix: `[${this.driverLabel}]`,
|
|
1558
2346
|
surface: "generated runtime modules"
|
|
1559
2347
|
});
|
|
1560
|
-
setSharedCss(fullCss);
|
|
2348
|
+
setSharedCss(this.key, fullCss);
|
|
1561
2349
|
const systemPropsContent = require_pipeline.buildSystemPropsModule({
|
|
1562
2350
|
systemPropMapJson: JSON.stringify(manifest?.system_prop_map ?? {}),
|
|
1563
2351
|
groupRegistryJson: system.groupRegistryJson,
|
|
@@ -1565,9 +2353,20 @@ var ExtractionSession = class {
|
|
|
1565
2353
|
admittedTransforms: manifest?.admitted_transforms ?? {},
|
|
1566
2354
|
typedSystemProps: manifest?.typed_system_props ?? []
|
|
1567
2355
|
});
|
|
1568
|
-
setSharedSystemProps(systemPropsContent);
|
|
1569
|
-
setManifestJson(result.manifestJson);
|
|
1570
|
-
|
|
2356
|
+
setSharedSystemProps(this.key, systemPropsContent);
|
|
2357
|
+
setManifestJson(this.key, result.manifestJson);
|
|
2358
|
+
if (manifest) this.stagedPublication = {
|
|
2359
|
+
manifest,
|
|
2360
|
+
manifestJson: result.manifestJson,
|
|
2361
|
+
analysisGlobalCss: sheets.globalCss,
|
|
2362
|
+
analysisComponentCss: sheets.componentCss,
|
|
2363
|
+
styles: fullCss,
|
|
2364
|
+
systemPropsModule: systemPropsContent,
|
|
2365
|
+
system,
|
|
2366
|
+
packageMap
|
|
2367
|
+
};
|
|
2368
|
+
setAnalyzedHashes(this.key, new Map(fileEntries.map((entry) => [entry.path, entry.hash])));
|
|
2369
|
+
if (this.packageReadRecord !== null) setSharedPackageReadRecord(this.key, this.packageReadRecord);
|
|
1571
2370
|
this.writeAnalysisStatus("committing", pending);
|
|
1572
2371
|
const epoch = require_pipeline.hashReplacementPlans(require_pipeline.snapshotFilePlans(manifest), systemPropsContent);
|
|
1573
2372
|
if (this.lastCommit === null) this.lastCommit = this.seedCommitFromDisk();
|
|
@@ -1600,6 +2399,86 @@ var ExtractionSession = class {
|
|
|
1600
2399
|
}
|
|
1601
2400
|
/** Write the session's analysis-status artifact. Terminal states
|
|
1602
2401
|
* (idle/failed) close the attempt. */
|
|
2402
|
+
/** Where the host resolves a path alias the analysis reads to another
|
|
2403
|
+
* file (spec §2.2 A7, report only), and how the two were compared. */
|
|
2404
|
+
async compareAliasesWithHost(fileEntries) {
|
|
2405
|
+
const record = require_pipeline.packageReadRecord({
|
|
2406
|
+
rootDir: this.rootDir,
|
|
2407
|
+
roots: this.packageInstances,
|
|
2408
|
+
redirected: new Set(this.externalSourceEntries.keys()),
|
|
2409
|
+
analysed: fileEntries.map((entry) => entry.path),
|
|
2410
|
+
rendersBuilder: (file) => this.rendersBuilder(file)
|
|
2411
|
+
});
|
|
2412
|
+
this.packageReadRecord = record;
|
|
2413
|
+
const { moduleSpecifiers } = this.engine();
|
|
2414
|
+
if (this.host.noBundler) return [];
|
|
2415
|
+
if (this.hostResolve === null || moduleSpecifiers === void 0) {
|
|
2416
|
+
this.log("path aliases: no host resolver, comparison unavailable");
|
|
2417
|
+
return [];
|
|
2418
|
+
}
|
|
2419
|
+
try {
|
|
2420
|
+
const { findings, unavailable } = await require_pipeline.compareAliases({
|
|
2421
|
+
rootDir: this.rootDir,
|
|
2422
|
+
fileEntries,
|
|
2423
|
+
pathAliasesJson: this.pathAliasesJson,
|
|
2424
|
+
order: this.extensionPolicy,
|
|
2425
|
+
readings: {
|
|
2426
|
+
isAsset: this.assetClassification.isAsset,
|
|
2427
|
+
adapted: this.ingestionAdapted
|
|
2428
|
+
},
|
|
2429
|
+
moduleSpecifiers,
|
|
2430
|
+
resolve: this.hostResolve,
|
|
2431
|
+
cache: this.aliasCache,
|
|
2432
|
+
packages: record
|
|
2433
|
+
});
|
|
2434
|
+
for (const finding of findings) record.reported.add(`${finding.file}\0${finding.component}`);
|
|
2435
|
+
if (unavailable > 0) this.log(`path aliases: ${unavailable} aliased import(s) the host's resolver failed on are unavailable, not agreeing`);
|
|
2436
|
+
return findings;
|
|
2437
|
+
} catch (error) {
|
|
2438
|
+
this.log(`path aliases: comparison unavailable (${String(error)})`);
|
|
2439
|
+
return [];
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
/** Whether the file at a real path declares builder chains, by the
|
|
2443
|
+
* engine's own parse: a module that does, served unread, renders with
|
|
2444
|
+
* empty classes. One the engine cannot read counts as one that does. */
|
|
2445
|
+
rendersBuilder(file) {
|
|
2446
|
+
if (!require_pipeline.isEngineTransformExtension(file)) return false;
|
|
2447
|
+
const { extractFacts } = this.engine();
|
|
2448
|
+
if (extractFacts === void 0) return true;
|
|
2449
|
+
const path$33 = (0, path.relative)(this.rootDir, file);
|
|
2450
|
+
try {
|
|
2451
|
+
const facts = require_pipeline.parseInternalWire(extractFacts(JSON.stringify([{
|
|
2452
|
+
path: path$33,
|
|
2453
|
+
source: (0, fs.readFileSync)(file, "utf-8")
|
|
2454
|
+
}])), "extractFacts").files[path$33];
|
|
2455
|
+
return facts === void 0 || facts.parsePanicked || facts.chains.length > 0;
|
|
2456
|
+
} catch {
|
|
2457
|
+
return true;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
/**
|
|
2461
|
+
* An import the host resolved while bundling (spec §2.2 A4), reported as
|
|
2462
|
+
* the comparison reports it: webpack resolves each module's requests with
|
|
2463
|
+
* the resolve options of the rules that module matches, which its
|
|
2464
|
+
* resolver, asked outside a compilation, does not apply. `importer` and
|
|
2465
|
+
* `resolved` are absolute. A finding the comparison already reported is
|
|
2466
|
+
* not repeated; an error-level one throws in a build.
|
|
2467
|
+
*/
|
|
2468
|
+
reportHostResolution(importer, request, resolved) {
|
|
2469
|
+
const record = getSharedPackageReadRecord(this.key);
|
|
2470
|
+
if (record === null) return;
|
|
2471
|
+
const finding = require_pipeline.packageInstanceFinding(record, (0, path.relative)(record.rootDir, importer).split(path.sep).join(path.posix.sep), request, resolved);
|
|
2472
|
+
if (finding === null) return;
|
|
2473
|
+
const key = `${finding.file}\0${finding.component}`;
|
|
2474
|
+
if (record.reported.has(key)) return;
|
|
2475
|
+
record.reported.add(key);
|
|
2476
|
+
require_pipeline.surfaceManifestDiagnostics({ diagnostics: [finding] }, (message) => this.warn(message), {
|
|
2477
|
+
levels: this.options.diagnostics,
|
|
2478
|
+
strict: this.options.strict,
|
|
2479
|
+
reportErrors: this.reportErrors()
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
1603
2482
|
writeAnalysisStatus(state, pending, diagnostic) {
|
|
1604
2483
|
const status = {
|
|
1605
2484
|
schema: 2,
|
|
@@ -1709,6 +2588,11 @@ var ExtractionSession = class {
|
|
|
1709
2588
|
/** Maintain the session's epoch witness, rewritten ONLY when the value
|
|
1710
2589
|
* changes, then expose it through the singleton. */
|
|
1711
2590
|
publishReplacementEpoch(epoch) {
|
|
2591
|
+
if (!this.persistArtifacts) {
|
|
2592
|
+
this.lastEpochValue = epoch;
|
|
2593
|
+
setReplacementEpoch(this.key, epoch);
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
1712
2596
|
const onDisk = this.diskEpochValue();
|
|
1713
2597
|
if (this.lastEpochValue === null) this.lastEpochValue = onDisk;
|
|
1714
2598
|
if (epoch !== onDisk) this.writeSessionArtifact(REPLACEMENT_EPOCH_ARTIFACT, JSON.stringify({
|
|
@@ -1720,7 +2604,7 @@ var ExtractionSession = class {
|
|
|
1720
2604
|
this.lastEpochValue = epoch;
|
|
1721
2605
|
this.reconcileSiblingEpochs(epoch);
|
|
1722
2606
|
}
|
|
1723
|
-
setReplacementEpoch(epoch);
|
|
2607
|
+
setReplacementEpoch(this.key, epoch);
|
|
1724
2608
|
}
|
|
1725
2609
|
/** sessions-root listing memo, keyed by the root dir's mtime — a new or
|
|
1726
2610
|
* pruned sibling DIRECTORY moves it; agreeing siblings stay listed. */
|
|
@@ -1765,10 +2649,14 @@ var ExtractionSession = class {
|
|
|
1765
2649
|
}
|
|
1766
2650
|
}
|
|
1767
2651
|
/** The pid of `siblingDir`'s live owner, or null when nothing proves one.
|
|
1768
|
-
*
|
|
1769
|
-
*
|
|
1770
|
-
*
|
|
2652
|
+
* A tree whose build is registered in this process is ours and live,
|
|
2653
|
+
* claim or no claim: a publisher's close() releases the claim while the
|
|
2654
|
+
* build's other participants still serve from it. Otherwise a missing,
|
|
2655
|
+
* undecodable or unreadable claim, or one naming our own pid, means no
|
|
2656
|
+
* owner: the opposite default from the CLI lock check on purpose, so do
|
|
2657
|
+
* not unify the two. */
|
|
1771
2658
|
liveSiblingOwnerPid(siblingDir) {
|
|
2659
|
+
if (findSession(sessionKey((0, path.basename)(siblingDir), this.rootDir)) !== void 0) return process.pid;
|
|
1772
2660
|
let claim;
|
|
1773
2661
|
try {
|
|
1774
2662
|
claim = readCliLockRecord(siblingDir);
|
|
@@ -1799,7 +2687,7 @@ var ExtractionSession = class {
|
|
|
1799
2687
|
/** Delete legacy flat artifacts and prune sibling session directories past
|
|
1800
2688
|
* the retention window — never this session's own dir. */
|
|
1801
2689
|
runSessionStartHygiene() {
|
|
1802
|
-
if (this.sessionStartHygieneDone) return;
|
|
2690
|
+
if (this.sessionStartHygieneDone || !this.persistArtifacts) return;
|
|
1803
2691
|
this.sessionStartHygieneDone = true;
|
|
1804
2692
|
const animusDir = (0, path.join)(this.rootDir, ANIMUS_ARTIFACT_DIR);
|
|
1805
2693
|
const isCliPublishedSet = this.cliWriterHoldsLock(animusDir) || verifyCommitRecord(animusDir).length === 0;
|
|
@@ -1840,13 +2728,29 @@ var ExtractionSession = class {
|
|
|
1840
2728
|
/** Write one artifact write-then-rename, so a cross-process reader never
|
|
1841
2729
|
* sees a torn file; the tmp name carries the pid to keep writers apart. */
|
|
1842
2730
|
writeSessionArtifact(name, content) {
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
2731
|
+
if (this.persistArtifacts) {
|
|
2732
|
+
const dir = this.sessionDir;
|
|
2733
|
+
(0, fs.mkdirSync)(dir, { recursive: true });
|
|
2734
|
+
const tmpPath = (0, path.join)(dir, `.${name}.${process.pid}.tmp`);
|
|
2735
|
+
(0, fs.writeFileSync)(tmpPath, content);
|
|
2736
|
+
(0, fs.renameSync)(tmpPath, (0, path.join)(dir, name));
|
|
2737
|
+
}
|
|
1848
2738
|
this.onArtifactWrite?.(name, content);
|
|
1849
2739
|
}
|
|
2740
|
+
/** How an asset's bytes are fingerprinted, of a file or of bytes read:
|
|
2741
|
+
* the engine's fast fingerprint where it has both, the content hash
|
|
2742
|
+
* otherwise, so the two always agree. */
|
|
2743
|
+
assetFingerprints() {
|
|
2744
|
+
const { fingerprintFile, fingerprintBytes } = this.engine();
|
|
2745
|
+
if (fingerprintFile && fingerprintBytes) return {
|
|
2746
|
+
ofFile: (path$34) => fingerprintFile(path$34) ?? require_pipeline.contentHash((0, fs.readFileSync)(path$34)),
|
|
2747
|
+
ofBytes: fingerprintBytes
|
|
2748
|
+
};
|
|
2749
|
+
return {
|
|
2750
|
+
ofFile: (path$35) => require_pipeline.contentHash((0, fs.readFileSync)(path$35)),
|
|
2751
|
+
ofBytes: require_pipeline.contentHash
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
1850
2754
|
/** Copy each asset() specifier's bytes, from every sheet, into `assets/`
|
|
1851
2755
|
* under a content-hashed name and substitute a url relative to
|
|
1852
2756
|
* styles.css. */
|
|
@@ -1856,12 +2760,12 @@ var ExtractionSession = class {
|
|
|
1856
2760
|
const expected = /* @__PURE__ */ new Set();
|
|
1857
2761
|
this.assetDependencyPaths.clear();
|
|
1858
2762
|
this.assetDependencyKeys.clear();
|
|
2763
|
+
if (this.hostSubstitutesAssets) return sheets;
|
|
1859
2764
|
const urlBySpecifier = /* @__PURE__ */ new Map();
|
|
1860
2765
|
for (const specifier of specifiers) {
|
|
1861
2766
|
const cached = this.assetCopyCache.get(specifier);
|
|
1862
2767
|
if (cached && (0, fs.existsSync)((0, path.join)(assetsDir, cached.fileName))) try {
|
|
1863
|
-
|
|
1864
|
-
if (current.mtimeMs === cached.mtimeMs && current.size === cached.size) {
|
|
2768
|
+
if (this.assetFingerprints().ofFile(cached.sourcePath) === cached.fingerprint) {
|
|
1865
2769
|
this.trackAssetDependency(cached.sourcePath);
|
|
1866
2770
|
expected.add(cached.fileName);
|
|
1867
2771
|
urlBySpecifier.set(specifier, cached.url);
|
|
@@ -1870,37 +2774,32 @@ var ExtractionSession = class {
|
|
|
1870
2774
|
} catch {}
|
|
1871
2775
|
const resolvedPath = this.resolveAssetSpecifier(specifier);
|
|
1872
2776
|
if (!resolvedPath) {
|
|
1873
|
-
|
|
1874
|
-
if (this.options.strict) this.strictFailure(`[${this.driverLabel}] ${message}`);
|
|
1875
|
-
else this.warn(message);
|
|
2777
|
+
this.reportUnresolvableAsset(specifier);
|
|
1876
2778
|
urlBySpecifier.set(specifier, specifier);
|
|
1877
2779
|
continue;
|
|
1878
2780
|
}
|
|
1879
|
-
const bytes = (
|
|
1880
|
-
const sourceStat = (0, fs.statSync)(resolvedPath);
|
|
2781
|
+
const { fileName, bytes } = require_pipeline.writeAssetCopy(resolvedPath, assetsDir);
|
|
1881
2782
|
this.trackAssetDependency(resolvedPath);
|
|
1882
|
-
const ext = (0, path.extname)(resolvedPath);
|
|
1883
|
-
const fileName = `${(0, path.basename)(resolvedPath, ext)}.${require_pipeline.contentHash(bytes).slice(0, 8)}${ext}`;
|
|
1884
2783
|
expected.add(fileName);
|
|
1885
|
-
if (!(0, fs.existsSync)(assetsDir)) (0, fs.mkdirSync)(assetsDir, { recursive: true });
|
|
1886
|
-
const assetPath = (0, path.join)(assetsDir, fileName);
|
|
1887
|
-
if (!(0, fs.existsSync)(assetPath)) (0, fs.writeFileSync)(assetPath, bytes);
|
|
1888
2784
|
const url = `./${SESSION_ASSETS_DIR}/${fileName}`;
|
|
1889
2785
|
urlBySpecifier.set(specifier, url);
|
|
1890
2786
|
this.assetCopyCache.set(specifier, {
|
|
1891
2787
|
sourcePath: resolvedPath,
|
|
1892
|
-
|
|
1893
|
-
size: sourceStat.size,
|
|
2788
|
+
fingerprint: this.assetFingerprints().ofBytes(bytes),
|
|
1894
2789
|
fileName,
|
|
1895
2790
|
url
|
|
1896
2791
|
});
|
|
1897
2792
|
}
|
|
1898
|
-
if (pruneSuperseded) pruneStaleAssets(assetsDir, expected);
|
|
2793
|
+
if (pruneSuperseded) require_pipeline.pruneStaleAssets(assetsDir, expected);
|
|
1899
2794
|
return require_pipeline.substituteSheetAssets(sheets, urlBySpecifier);
|
|
1900
2795
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2796
|
+
reportUnresolvableAsset(specifier) {
|
|
2797
|
+
const message = `unresolvable asset() specifier: ${specifier}`;
|
|
2798
|
+
this.strictOrWarn(message, `[${this.driverLabel}] ${message}`);
|
|
2799
|
+
}
|
|
2800
|
+
trackAssetDependency(path$36) {
|
|
2801
|
+
this.assetDependencyPaths.add(path$36);
|
|
2802
|
+
for (const key of require_pipeline.toWatchKeys(path$36)) this.assetDependencyKeys.add(key);
|
|
1904
2803
|
}
|
|
1905
2804
|
/** Resolve an asset specifier through the shared resolver, with one
|
|
1906
2805
|
* last resort: a discovered source entry's package root. */
|
|
@@ -1919,23 +2818,6 @@ var ExtractionSession = class {
|
|
|
1919
2818
|
return null;
|
|
1920
2819
|
}
|
|
1921
2820
|
};
|
|
1922
|
-
/** Sync `assets/` to the current build's file set; per-entry failures are
|
|
1923
|
-
* tolerated — a concurrent session may have removed the same file. */
|
|
1924
|
-
function pruneStaleAssets(assetsDir, expected) {
|
|
1925
|
-
if (!(0, fs.existsSync)(assetsDir)) return;
|
|
1926
|
-
let entries;
|
|
1927
|
-
try {
|
|
1928
|
-
entries = (0, fs.readdirSync)(assetsDir);
|
|
1929
|
-
} catch {
|
|
1930
|
-
return;
|
|
1931
|
-
}
|
|
1932
|
-
for (const entry of entries) {
|
|
1933
|
-
if (expected.has(entry)) continue;
|
|
1934
|
-
try {
|
|
1935
|
-
(0, fs.unlinkSync)((0, path.join)(assetsDir, entry));
|
|
1936
|
-
} catch {}
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
2821
|
//#endregion
|
|
1940
2822
|
//#region session/turbopack-orchestrator.ts
|
|
1941
2823
|
async function runSessionPipeline(session) {
|
|
@@ -1943,6 +2825,7 @@ async function runSessionPipeline(session) {
|
|
|
1943
2825
|
session.persistAnalysisInputs = true;
|
|
1944
2826
|
await session.runFullPipeline();
|
|
1945
2827
|
}
|
|
2828
|
+
/** Each build's watched root: another build of the root watches it too. */
|
|
1946
2829
|
const activeWatcherRoots = /* @__PURE__ */ new Set();
|
|
1947
2830
|
const IGNORED_SEGMENTS = /* @__PURE__ */ new Set([
|
|
1948
2831
|
ANIMUS_ARTIFACT_DIR,
|
|
@@ -1969,17 +2852,18 @@ function isIntrinsicString(value) {
|
|
|
1969
2852
|
}
|
|
1970
2853
|
function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
1971
2854
|
const { debounceMs = 75, watchFn = fs.watch, holdEvents = false } = options;
|
|
1972
|
-
|
|
1973
|
-
activeWatcherRoots.
|
|
2855
|
+
const watched = JSON.stringify([session.sessionId, rootDir]);
|
|
2856
|
+
if (activeWatcherRoots.has(watched)) return { kind: "already-watched" };
|
|
2857
|
+
activeWatcherRoots.add(watched);
|
|
1974
2858
|
session.debounceCeilingMs = debounceMs;
|
|
1975
2859
|
const pendingPaths = /* @__PURE__ */ new Set();
|
|
1976
2860
|
const watchers = /* @__PURE__ */ new Map();
|
|
1977
|
-
let timer = null;
|
|
1978
2861
|
let updateChain = Promise.resolve();
|
|
1979
2862
|
let closed = false;
|
|
1980
2863
|
let died = false;
|
|
1981
2864
|
let holdingEvents = holdEvents;
|
|
1982
2865
|
const externalWatchers = /* @__PURE__ */ new Map();
|
|
2866
|
+
const dependencyWatchers = /* @__PURE__ */ new Map();
|
|
1983
2867
|
let pendingOpened = /* @__PURE__ */ new Map();
|
|
1984
2868
|
let capturedDuringSnapshot = [];
|
|
1985
2869
|
const externalDiagnosticKey = (root) => `external-watch:${root}`;
|
|
@@ -1998,24 +2882,28 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
1998
2882
|
session.stickyDiagnostics?.set(externalDiagnosticKey(root), message);
|
|
1999
2883
|
console.warn(`[animus-extract] ${message}`);
|
|
2000
2884
|
};
|
|
2885
|
+
let cyclesInFlight = 0;
|
|
2001
2886
|
const flush = () => {
|
|
2002
|
-
timer = null;
|
|
2003
2887
|
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
2004
2888
|
const removedFiles = /* @__PURE__ */ new Set();
|
|
2005
2889
|
for (const path$1 of pendingPaths) ((0, fs.existsSync)(path$1) ? modifiedFiles : removedFiles).add(path$1);
|
|
2006
2890
|
pendingPaths.clear();
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2891
|
+
if (closed) return;
|
|
2892
|
+
cyclesInFlight++;
|
|
2893
|
+
const cycle = session.handleWatchUpdate({
|
|
2894
|
+
modifiedFiles,
|
|
2895
|
+
removedFiles
|
|
2896
|
+
}).catch((err) => {
|
|
2897
|
+
console.warn(`[animus-extract] watch update failed: ${String(err)}`);
|
|
2898
|
+
}).finally(() => {
|
|
2899
|
+
cyclesInFlight--;
|
|
2900
|
+
if (cyclesInFlight > 0) return;
|
|
2901
|
+
if (pendingOpened.size > 0 || capturedDuringSnapshot.length > 0) rollbackPendingExternal();
|
|
2902
|
+
syncDependencyWatchers();
|
|
2017
2903
|
});
|
|
2904
|
+
updateChain = updateChain.then(() => cycle);
|
|
2018
2905
|
};
|
|
2906
|
+
const coalescing = new CoalescingWindow(flush, debounceMs);
|
|
2019
2907
|
const rollbackPendingExternal = () => {
|
|
2020
2908
|
for (const watcher of pendingOpened.values()) watcher.close();
|
|
2021
2909
|
pendingOpened = /* @__PURE__ */ new Map();
|
|
@@ -2024,16 +2912,17 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
2024
2912
|
const closeAll = () => {
|
|
2025
2913
|
if (closed) return;
|
|
2026
2914
|
closed = true;
|
|
2027
|
-
|
|
2028
|
-
timer = null;
|
|
2915
|
+
coalescing.cancel();
|
|
2029
2916
|
for (const watcher of watchers.values()) watcher.close();
|
|
2030
2917
|
watchers.clear();
|
|
2918
|
+
for (const { watcher } of dependencyWatchers.values()) watcher.close();
|
|
2919
|
+
dependencyWatchers.clear();
|
|
2031
2920
|
for (const watcher of externalWatchers.values()) watcher.close();
|
|
2032
2921
|
externalWatchers.clear();
|
|
2033
2922
|
rollbackPendingExternal();
|
|
2034
2923
|
session.onExternalRootResolved = null;
|
|
2035
2924
|
session.onExternalRootsCommitted = null;
|
|
2036
|
-
activeWatcherRoots.delete(
|
|
2925
|
+
activeWatcherRoots.delete(watched);
|
|
2037
2926
|
};
|
|
2038
2927
|
const onWatcherError = (err) => {
|
|
2039
2928
|
died = true;
|
|
@@ -2049,10 +2938,7 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
2049
2938
|
} catch (err) {
|
|
2050
2939
|
console.warn(`[animus-extract] Turbopack debounce status update failed: ${String(err)}`);
|
|
2051
2940
|
}
|
|
2052
|
-
|
|
2053
|
-
timer = setTimeout(flush, debounceMs);
|
|
2054
|
-
timer.unref?.();
|
|
2055
|
-
}
|
|
2941
|
+
coalescing.note();
|
|
2056
2942
|
};
|
|
2057
2943
|
const onEvent = (baseDir, filename) => {
|
|
2058
2944
|
if (closed || !filename) return;
|
|
@@ -2096,6 +2982,59 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
2096
2982
|
degradeExternalRoot(root, err);
|
|
2097
2983
|
}
|
|
2098
2984
|
};
|
|
2985
|
+
/** Whether the root or an external root watcher already reports `path`. */
|
|
2986
|
+
const watchedByRoot = (path$2) => {
|
|
2987
|
+
if (require_pipeline.isPathWithinRoot(rootDir, path$2) && !(0, path.relative)(rootDir, path$2).split(/[\\/]/).some((segment) => IGNORED_SEGMENTS.has(segment))) return true;
|
|
2988
|
+
return [...externalWatchers.keys()].some((root) => require_pipeline.isPathWithinRoot(root, path$2));
|
|
2989
|
+
};
|
|
2990
|
+
/** Watches the system, asset and configuration dependencies no root
|
|
2991
|
+
* watcher reports, by their directories, for those files only. */
|
|
2992
|
+
const syncDependencyWatchers = () => {
|
|
2993
|
+
if (closed) return;
|
|
2994
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
2995
|
+
for (const path$3 of [
|
|
2996
|
+
...session.systemDependencyPaths,
|
|
2997
|
+
...session.assetDependencyPaths,
|
|
2998
|
+
...session.configInputPaths
|
|
2999
|
+
]) {
|
|
3000
|
+
if (watchedByRoot(path$3)) continue;
|
|
3001
|
+
const files = wanted.get((0, path.dirname)(path$3)) ?? /* @__PURE__ */ new Set();
|
|
3002
|
+
files.add(path$3);
|
|
3003
|
+
wanted.set((0, path.dirname)(path$3), files);
|
|
3004
|
+
}
|
|
3005
|
+
for (const [dir, { watcher }] of [...dependencyWatchers]) {
|
|
3006
|
+
if (wanted.has(dir)) continue;
|
|
3007
|
+
watcher.close();
|
|
3008
|
+
dependencyWatchers.delete(dir);
|
|
3009
|
+
}
|
|
3010
|
+
for (const [dir, files] of wanted) {
|
|
3011
|
+
const existing = dependencyWatchers.get(dir);
|
|
3012
|
+
if (existing) {
|
|
3013
|
+
existing.files = files;
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
try {
|
|
3017
|
+
const watcher = watchFn(dir, { recursive: false }, (_event, name) => {
|
|
3018
|
+
if (closed || !name) return;
|
|
3019
|
+
const abs = (0, path.join)(dir, name.toString());
|
|
3020
|
+
if (dependencyWatchers.get(dir)?.files.has(abs)) enqueuePath(abs);
|
|
3021
|
+
});
|
|
3022
|
+
watcher.on("error", (err) => {
|
|
3023
|
+
watcher.close();
|
|
3024
|
+
dependencyWatchers.delete(dir);
|
|
3025
|
+
console.warn(`[animus-extract] dependency watch on ${dir} failed (${String(err)}); its changes require a dev-server restart`);
|
|
3026
|
+
});
|
|
3027
|
+
watcher.unref?.();
|
|
3028
|
+
dependencyWatchers.set(dir, {
|
|
3029
|
+
watcher,
|
|
3030
|
+
files
|
|
3031
|
+
});
|
|
3032
|
+
} catch (err) {
|
|
3033
|
+
if (errnoCode(err) === "ENOENT") continue;
|
|
3034
|
+
console.warn(`[animus-extract] dependency watch on ${dir} unavailable (${String(err)}); its changes require a dev-server restart`);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
};
|
|
2099
3038
|
const addWatcher = (dir, recursive) => {
|
|
2100
3039
|
const watcher = watchFn(dir, { recursive }, (_event, filename) => onEvent(dir, filename));
|
|
2101
3040
|
watcher.on("error", onWatcherError);
|
|
@@ -2114,6 +3053,7 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
2114
3053
|
return { kind: "unavailable" };
|
|
2115
3054
|
}
|
|
2116
3055
|
for (const root of session.externalWatchRoots ?? []) openExternalWatcher(root, externalWatchers);
|
|
3056
|
+
syncDependencyWatchers();
|
|
2117
3057
|
session.onExternalRootResolved = (root) => {
|
|
2118
3058
|
if (closed) return;
|
|
2119
3059
|
if (externalWatchers.has(root) || pendingOpened.has(root)) return;
|
|
@@ -2138,7 +3078,8 @@ function startTurbopackWatcher(session, rootDir, options = {}) {
|
|
|
2138
3078
|
onDied: null,
|
|
2139
3079
|
deliverHeldEvents: () => {
|
|
2140
3080
|
holdingEvents = false;
|
|
2141
|
-
|
|
3081
|
+
syncDependencyWatchers();
|
|
3082
|
+
if (pendingPaths.size > 0) coalescing.flush();
|
|
2142
3083
|
return updateChain;
|
|
2143
3084
|
},
|
|
2144
3085
|
settle: () => updateChain
|
|
@@ -2155,19 +3096,27 @@ exports.CLI_COMMIT_ARTIFACT = CLI_COMMIT_ARTIFACT;
|
|
|
2155
3096
|
exports.CLI_LOCK_ARTIFACT = CLI_LOCK_ARTIFACT;
|
|
2156
3097
|
exports.CLI_LOCK_HEARTBEAT_INTERVAL_MS = CLI_LOCK_HEARTBEAT_INTERVAL_MS;
|
|
2157
3098
|
exports.CLI_LOCK_STALE_AFTER_MS = CLI_LOCK_STALE_AFTER_MS;
|
|
3099
|
+
exports.COALESCING_WINDOW_MS = COALESCING_WINDOW_MS;
|
|
3100
|
+
exports.CoalescingWindow = CoalescingWindow;
|
|
3101
|
+
exports.EnvironmentFailure = EnvironmentFailure;
|
|
2158
3102
|
exports.ExtractionSession = ExtractionSession;
|
|
2159
3103
|
exports.MANIFEST_ARTIFACT = MANIFEST_ARTIFACT;
|
|
2160
3104
|
exports.SESSION_ASSETS_DIR = SESSION_ASSETS_DIR;
|
|
2161
3105
|
exports.STYLES_ARTIFACT = STYLES_ARTIFACT;
|
|
2162
3106
|
exports.SYSTEM_PROPS_ARTIFACT = SYSTEM_PROPS_ARTIFACT;
|
|
3107
|
+
exports.SessionConfigurationConflict = SessionConfigurationConflict;
|
|
2163
3108
|
exports.TURBOPACK_SYSTEM_PROPS_ID = TURBOPACK_SYSTEM_PROPS_ID;
|
|
2164
3109
|
exports.analysisCommitPath = analysisCommitPath;
|
|
2165
3110
|
exports.analysisInputsPath = analysisInputsPath;
|
|
2166
3111
|
exports.analysisStatusPath = analysisStatusPath;
|
|
2167
3112
|
exports.checkLockLiveness = checkLockLiveness;
|
|
2168
3113
|
exports.collectSessionAssets = collectSessionAssets;
|
|
3114
|
+
exports.createBuildId = createBuildId;
|
|
2169
3115
|
exports.decodeCommitRecord = decodeCommitRecord;
|
|
2170
3116
|
exports.engineApi = engineApi;
|
|
3117
|
+
exports.engineFactory = engineFactory;
|
|
3118
|
+
exports.engineHandle = engineHandle;
|
|
3119
|
+
exports.findSession = findSession;
|
|
2171
3120
|
exports.getAnalysisStartedPromise = getAnalysisStartedPromise;
|
|
2172
3121
|
exports.getAnalyzedHashes = getAnalyzedHashes;
|
|
2173
3122
|
exports.getManifestJson = getManifestJson;
|
|
@@ -2180,17 +3129,20 @@ exports.getSharedExternalSideEffects = getSharedExternalSideEffects;
|
|
|
2180
3129
|
exports.getSharedSystemProps = getSharedSystemProps;
|
|
2181
3130
|
exports.holdDirectoryClaim = holdDirectoryClaim;
|
|
2182
3131
|
exports.isLockHolderAlive = isLockHolderAlive;
|
|
3132
|
+
exports.joinSession = joinSession;
|
|
2183
3133
|
exports.lockRecordBytes = lockRecordBytes;
|
|
2184
3134
|
exports.manifestPath = manifestPath;
|
|
2185
|
-
exports.pruneStaleAssets = pruneStaleAssets;
|
|
3135
|
+
exports.pruneStaleAssets = require_pipeline.pruneStaleAssets;
|
|
2186
3136
|
exports.readCliLockRecord = readCliLockRecord;
|
|
2187
3137
|
exports.readJsonEnvelope = readJsonEnvelope;
|
|
3138
|
+
exports.releaseEngineHandle = releaseEngineHandle;
|
|
2188
3139
|
exports.replacementEpochPath = replacementEpochPath;
|
|
3140
|
+
exports.requireEngine = requireEngine;
|
|
2189
3141
|
exports.resetAnalysisStartedPromise = resetAnalysisStartedPromise;
|
|
2190
3142
|
exports.runSessionPipeline = runSessionPipeline;
|
|
2191
3143
|
exports.sessionArtifactDir = sessionArtifactDir;
|
|
3144
|
+
exports.sessionKey = sessionKey;
|
|
2192
3145
|
exports.setAnalysisStartedPromise = setAnalysisStartedPromise;
|
|
2193
|
-
exports.setSharedEngine = setSharedEngine;
|
|
2194
3146
|
exports.startTurbopackWatcher = startTurbopackWatcher;
|
|
2195
3147
|
exports.stylesPath = stylesPath;
|
|
2196
3148
|
exports.systemPropsPath = systemPropsPath;
|