@animus-ui/vite-plugin 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/dist/asset-copy-dirs.d.ts +10 -0
- package/dist/asset-copy-dirs.d.ts.map +1 -0
- package/dist/build-start.d.ts +2 -1
- package/dist/build-start.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/context.d.ts +136 -141
- package/dist/context.d.ts.map +1 -1
- package/dist/hmr.d.ts +8 -2
- package/dist/hmr.d.ts.map +1 -1
- package/dist/index.cjs +733 -903
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/rediscovery.d.ts +11 -15
- package/dist/rediscovery.d.ts.map +1 -1
- package/dist/session-outcomes.d.ts +23 -0
- package/dist/session-outcomes.d.ts.map +1 -0
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/virtual-modules.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/reset-coalescer.d.ts +0 -21
- package/dist/reset-coalescer.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -5,45 +5,119 @@ Object.defineProperties(exports, {
|
|
|
5
5
|
let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
|
|
6
6
|
let path = require("path");
|
|
7
7
|
let fs = require("fs");
|
|
8
|
+
let _animus_ui_extract_session = require("@animus-ui/extract/session");
|
|
9
|
+
let child_process = require("child_process");
|
|
10
|
+
let vite = require("vite");
|
|
8
11
|
let crypto = require("crypto");
|
|
12
|
+
//#region src/asset-copy-dirs.ts
|
|
13
|
+
/**
|
|
14
|
+
* Each process's dev asset copies live in their own directory, named by its
|
|
15
|
+
* pid and start time, so a sweep can tell an abandoned directory from a
|
|
16
|
+
* live server's even when the pid has been reused.
|
|
17
|
+
*/
|
|
18
|
+
/** A directory left this long is swept unless its owner proves live. */
|
|
19
|
+
const ABANDONED_AFTER_MS = 864e5;
|
|
20
|
+
/** `ps` reports start times to the second. */
|
|
21
|
+
const START_TIME_TOLERANCE_MS = 2e3;
|
|
22
|
+
const processStartMs = Math.round(Date.now() - process.uptime() * 1e3);
|
|
23
|
+
/** This process's copy directory name: `<pid>-<start ms>`. */
|
|
24
|
+
function processCopyDir() {
|
|
25
|
+
return `${process.pid}-${processStartMs}`;
|
|
26
|
+
}
|
|
27
|
+
/** When `pid` started, as `ps` reports it: null where `ps` ran and could
|
|
28
|
+
* not say, `'unavailable'` where there is no `ps` (Windows). */
|
|
29
|
+
function startTimeOf(pid) {
|
|
30
|
+
if (process.platform === "win32") return "unavailable";
|
|
31
|
+
try {
|
|
32
|
+
const started = Date.parse((0, child_process.execFileSync)("ps", [
|
|
33
|
+
"-o",
|
|
34
|
+
"lstart=",
|
|
35
|
+
"-p",
|
|
36
|
+
String(pid)
|
|
37
|
+
], {
|
|
38
|
+
encoding: "utf-8",
|
|
39
|
+
stdio: [
|
|
40
|
+
"ignore",
|
|
41
|
+
"pipe",
|
|
42
|
+
"ignore"
|
|
43
|
+
]
|
|
44
|
+
}).trim());
|
|
45
|
+
return Number.isNaN(started) ? null : started;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error?.code === "ENOENT") return "unavailable";
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Whether the process that named `entry` is still running. A young
|
|
52
|
+
* directory needs only a live pid; one past ABANDONED_AFTER_MS needs its
|
|
53
|
+
* recorded start time to match the running process too. */
|
|
54
|
+
function ownerLive(copyRoot, entry) {
|
|
55
|
+
const match = /^(\d+)-(\d+)$/.exec(entry);
|
|
56
|
+
if (match === null) return false;
|
|
57
|
+
const pid = Number(match[1]);
|
|
58
|
+
const startMs = Number(match[2]);
|
|
59
|
+
if (!(0, _animus_ui_extract_session.isLockHolderAlive)(pid)) return false;
|
|
60
|
+
let ageMs;
|
|
61
|
+
try {
|
|
62
|
+
ageMs = Date.now() - (0, fs.statSync)((0, path.join)(copyRoot, entry)).mtimeMs;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (ageMs < ABANDONED_AFTER_MS) return true;
|
|
67
|
+
const started = startTimeOf(pid);
|
|
68
|
+
if (started === "unavailable") return true;
|
|
69
|
+
return started !== null && Math.abs(started - startMs) <= START_TIME_TOLERANCE_MS;
|
|
70
|
+
}
|
|
71
|
+
/** The copy roots this process has swept: once per process. */
|
|
72
|
+
const sweptCopyRoots = /* @__PURE__ */ new Set();
|
|
73
|
+
/** Removes every other process's copy directory whose owner is not live. */
|
|
74
|
+
function sweepAbandonedCopyDirs(copyRoot) {
|
|
75
|
+
if (sweptCopyRoots.has(copyRoot)) return;
|
|
76
|
+
sweptCopyRoots.add(copyRoot);
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
entries = (0, fs.readdirSync)(copyRoot);
|
|
80
|
+
} catch {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
if (entry === processCopyDir() || ownerLive(copyRoot, entry)) continue;
|
|
85
|
+
(0, fs.rmSync)((0, path.join)(copyRoot, entry), {
|
|
86
|
+
recursive: true,
|
|
87
|
+
force: true
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
9
92
|
//#region src/build-start.ts
|
|
10
93
|
/**
|
|
11
94
|
* `resolveSpecifier` maps a specifier to an absolute id. `emitAsset` is
|
|
12
95
|
* present in build only, where `asset()` files become Rollup assets.
|
|
13
96
|
*/
|
|
14
97
|
async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
|
|
15
|
-
(0, _animus_ui_extract_pipeline.clearEngineCache)(ctx.engineApi);
|
|
16
98
|
ctx.assetPassComplete = false;
|
|
17
99
|
ctx.assetUrlBySpecifier.clear();
|
|
18
100
|
ctx.assetResolutionFailures.clear();
|
|
19
|
-
|
|
20
|
-
ctx.loadSystem();
|
|
101
|
+
const t0 = performance.now();
|
|
21
102
|
if (ctx.options.layers) {
|
|
22
103
|
(0, _animus_ui_extract_pipeline.validateLayerOrder)(ctx.options.layers);
|
|
23
104
|
ctx.log(`Custom layers: [${ctx.options.layers.join(", ")}]`);
|
|
24
105
|
}
|
|
106
|
+
const session = ctx.session ?? ctx.createSession();
|
|
107
|
+
ctx.excludeMatcher = session.scanConfig.excludeMatcher;
|
|
108
|
+
ctx.extensionsSet = session.scanConfig.extensionsSet;
|
|
109
|
+
session.reattach();
|
|
110
|
+
session.hostResolve = resolveSpecifier;
|
|
111
|
+
if (ctx.isProd) await session.runFullPipeline();
|
|
112
|
+
else await ctx.runAttempt((started) => started.runFullPipeline());
|
|
25
113
|
if (ctx.verbose) {
|
|
26
114
|
const propCount = Object.keys(JSON.parse(ctx.system.propConfigJson)).length;
|
|
27
115
|
const groupCount = Object.keys(JSON.parse(ctx.system.groupRegistryJson)).length;
|
|
28
116
|
ctx.log(`System loaded: ${propCount} props, ${groupCount} groups (${Math.round(performance.now() - t0)}ms)`);
|
|
29
117
|
}
|
|
30
|
-
const discovered = await discoverSources(ctx, resolveSpecifier);
|
|
31
|
-
if (!ctx.isProd) ctx.rediscoverSources = async () => {
|
|
32
|
-
const rediscovered = await discoverSources(ctx, resolveSpecifier);
|
|
33
|
-
return {
|
|
34
|
-
...rediscovered,
|
|
35
|
-
rawEntries: rediscovered.rawEntries.map((entry) => {
|
|
36
|
-
const cached = ctx.fileCache.get(entry.path);
|
|
37
|
-
return cached ? {
|
|
38
|
-
path: entry.path,
|
|
39
|
-
...cached
|
|
40
|
-
} : entry;
|
|
41
|
-
})
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
t0 = performance.now();
|
|
45
|
-
await ctx.analyzeIngested(discovered);
|
|
46
118
|
const assetSpecifiers = ctx.sheetAssetSpecifiers();
|
|
119
|
+
ctx.assetSourceBySpecifier.clear();
|
|
120
|
+
if (ctx.assetCopyDir !== null) sweepAbandonedCopyDirs((0, path.dirname)(ctx.assetCopyDir));
|
|
47
121
|
for (const specifier of assetSpecifiers) {
|
|
48
122
|
const resolvedPath = await resolveSpecifier(specifier);
|
|
49
123
|
if (!resolvedPath) {
|
|
@@ -56,9 +130,12 @@ async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
|
|
|
56
130
|
} catch (err) {
|
|
57
131
|
ctx.assetFallback(specifier, `[animus-extract] failed to emit asset() specifier ${specifier}: ${String(err)}`, err);
|
|
58
132
|
}
|
|
59
|
-
else
|
|
133
|
+
else try {
|
|
134
|
+
ctx.assetUrlBySpecifier.set(specifier, ctx.devAssetUrl(specifier, resolvedPath));
|
|
135
|
+
} catch (err) {
|
|
136
|
+
ctx.assetFallback(specifier, `[animus-extract] failed to copy asset() specifier ${specifier}: ${String(err)}`, err);
|
|
137
|
+
}
|
|
60
138
|
}
|
|
61
|
-
ctx.substituteSheets();
|
|
62
139
|
ctx.assetPassComplete = true;
|
|
63
140
|
if (ctx.storedManifest) {
|
|
64
141
|
const report = ctx.storedManifest.report;
|
|
@@ -69,7 +146,7 @@ async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
|
|
|
69
146
|
else if (d.kind === "variant") ctx.trace(`${d.component} variant '${d.name}' pruned: ${d.reason}`);
|
|
70
147
|
else if (d.kind === "state") ctx.trace(`${d.component} state '${d.name}' pruned: ${d.reason}`);
|
|
71
148
|
ctx.log(`CSS: ${ctx.resolvedComponentCss.length} bytes (${Object.keys(ctx.storedManifest.components).length} components)`);
|
|
72
|
-
if (!ctx.isProd
|
|
149
|
+
if (!ctx.isProd) {
|
|
73
150
|
const staticSize = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
74
151
|
layers: ctx.options.layers,
|
|
75
152
|
variableCss: ctx.variableCss,
|
|
@@ -88,88 +165,6 @@ async function runBuildStart(ctx, resolveSpecifier, emitAsset) {
|
|
|
88
165
|
ctx.layerDeclaration = declaration;
|
|
89
166
|
if (ctx.options.verify) ctx.runSelfVerify();
|
|
90
167
|
}
|
|
91
|
-
/** Local and external discovery, publishing the package state on `ctx`, and
|
|
92
|
-
* the analysis input it yields. */
|
|
93
|
-
async function discoverSources(ctx, resolveSpecifier) {
|
|
94
|
-
const t0 = performance.now();
|
|
95
|
-
ctx.excludeMatcher = (0, _animus_ui_extract_pipeline.createExcludeMatcher)(ctx.options.exclude);
|
|
96
|
-
const excludePatterns = ctx.excludeMatcher;
|
|
97
|
-
ctx.extensionsSet = new Set(ctx.options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
98
|
-
const filePaths = (0, _animus_ui_extract_pipeline.discoverFiles)(ctx.rootDir, ctx.rootDir, excludePatterns, ctx.extensionsSet);
|
|
99
|
-
const rawEntries = [];
|
|
100
|
-
const ingestionFailures = [];
|
|
101
|
-
for (const filePath of filePaths) {
|
|
102
|
-
const relPath = (0, path.relative)(ctx.rootDir, filePath);
|
|
103
|
-
let source;
|
|
104
|
-
try {
|
|
105
|
-
source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
106
|
-
} catch (err) {
|
|
107
|
-
if (!(0, _animus_ui_extract_pipeline.isDeletedSource)(err)) ingestionFailures.push((0, _animus_ui_extract_pipeline.unreadableSourceDiagnostic)(relPath, err));
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
const hash = !ctx.isProd ? (0, _animus_ui_extract_pipeline.contentHash)(source) : void 0;
|
|
111
|
-
rawEntries.push({
|
|
112
|
-
path: relPath,
|
|
113
|
-
source,
|
|
114
|
-
hash
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
const localFileCount = rawEntries.length;
|
|
118
|
-
const discoveryDiagnostics = [];
|
|
119
|
-
const packageSpecifiers = await (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(ctx.resolvedSystemPath, (0, _animus_ui_extract_pipeline.engineModuleParser)(ctx.engineApi()), (diagnostic) => discoveryDiagnostics.push(diagnostic), resolveSpecifier);
|
|
120
|
-
discoveryDiagnostics.push(...(0, _animus_ui_extract_pipeline.importedKitDiagnostics)(rawEntries, ctx.engineApi(), ctx.rootDir, packageSpecifiers));
|
|
121
|
-
ctx.externalSourceEntries.clear();
|
|
122
|
-
const collected = await (0, _animus_ui_extract_pipeline.collectExternalPackageSources)({
|
|
123
|
-
specifiers: packageSpecifiers,
|
|
124
|
-
resolveSpecifier,
|
|
125
|
-
rootDir: ctx.rootDir,
|
|
126
|
-
extensionsSet: ctx.extensionsSet,
|
|
127
|
-
hasEntry: (relPath) => rawEntries.some((entry) => entry.path === relPath),
|
|
128
|
-
onUnreadable: (relPath, err) => {
|
|
129
|
-
if (!(0, _animus_ui_extract_pipeline.isDeletedSource)(err)) ingestionFailures.push((0, _animus_ui_extract_pipeline.unreadableSourceDiagnostic)(relPath, err));
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
ctx.ingestionFailureDiagnostics = ingestionFailures;
|
|
133
|
-
(0, _animus_ui_extract_pipeline.surfaceManifestDiagnostics)({ diagnostics: [...discoveryDiagnostics, ...collected.diagnostics] }, (message) => ctx.warn(message), {
|
|
134
|
-
levels: ctx.options.diagnostics,
|
|
135
|
-
strict: ctx.options.strict,
|
|
136
|
-
reportErrors: ctx.reportErrors()
|
|
137
|
-
});
|
|
138
|
-
ctx.packageMap = collected.packageMap;
|
|
139
|
-
ctx.externalPackageOutcomes = collected.outcomes;
|
|
140
|
-
ctx.kitDescriptors = collected.kitDescriptors;
|
|
141
|
-
ctx.externalDirOwners = (0, _animus_ui_extract_pipeline.firstOwners)(collected.dirOwnerSets);
|
|
142
|
-
ctx.externalFileOwners = collected.fileOwners;
|
|
143
|
-
ctx.enforceIncludeResolution();
|
|
144
|
-
for (const [specifier, srcEntry] of collected.sourceEntries) ctx.externalSourceEntries.set(specifier, srcEntry);
|
|
145
|
-
ctx.externalSourceSideEffects = collected.sourceEntrySideEffects;
|
|
146
|
-
for (const entry of collected.entries) {
|
|
147
|
-
const hash = !ctx.isProd ? (0, _animus_ui_extract_pipeline.contentHash)(entry.source) : void 0;
|
|
148
|
-
rawEntries.push({
|
|
149
|
-
path: entry.path,
|
|
150
|
-
source: entry.source,
|
|
151
|
-
hash
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
ctx.externalPackageDirs = collected.packageDirs;
|
|
155
|
-
ctx.externalLinkedDirs = collected.linkedDirs;
|
|
156
|
-
ctx.externalOutputDirs = collected.outputDirs;
|
|
157
|
-
ctx.registerSystemWatchPaths();
|
|
158
|
-
const packageFileCount = rawEntries.length - localFileCount;
|
|
159
|
-
ctx.log(`Discovered ${rawEntries.length} files (${packageFileCount} from packages) (${Math.round(performance.now() - t0)}ms)`);
|
|
160
|
-
return {
|
|
161
|
-
rawEntries,
|
|
162
|
-
afterIngestion: (accepted) => {
|
|
163
|
-
if (!ctx.isProd) ctx.mutateFileCache((cache) => {
|
|
164
|
-
cache.clear();
|
|
165
|
-
for (const entry of accepted.originalEntries) cache.set(entry.path, {
|
|
166
|
-
hash: entry.hash,
|
|
167
|
-
source: entry.source
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
168
|
//#endregion
|
|
174
169
|
//#region src/config.ts
|
|
175
170
|
function applyResolvedConfig(ctx, config) {
|
|
@@ -177,7 +172,19 @@ function applyResolvedConfig(ctx, config) {
|
|
|
177
172
|
ctx.development = !ctx.isProd || Boolean(config.build.watch);
|
|
178
173
|
ctx.emissionProd = (0, _animus_ui_extract_pipeline.resolveMode)(ctx.options.mode, () => config.command === "build" ? "production" : "development").mode === "production";
|
|
179
174
|
ctx.rootDir = config.root;
|
|
175
|
+
if (!ctx.isProd) {
|
|
176
|
+
const copyDir = (0, path.resolve)(config.cacheDir, "animus-assets", processCopyDir());
|
|
177
|
+
const served = (0, vite.isFileLoadingAllowed)(config, (0, path.join)(copyDir, "asset"));
|
|
178
|
+
ctx.assetCopyDir = served ? copyDir : null;
|
|
179
|
+
ctx.servesFile = (path$8) => (0, vite.isFileLoadingAllowed)(config, path$8);
|
|
180
|
+
if (!served) ctx.warn(`[animus-extract] asset() copies in ${copyDir} are not served under server.fs; serving the live files instead, so an edited asset keeps its URL`);
|
|
181
|
+
}
|
|
182
|
+
ctx.outputExcludes = (0, _animus_ui_extract_pipeline.outputExclusions)(config.root, (0, _animus_ui_extract_pipeline.viteOutputTargets)(config));
|
|
180
183
|
ctx.resolveConditions = config.resolve.conditions.map((condition) => condition === "development|production" ? config.isProduction ? "production" : "development" : condition);
|
|
184
|
+
ctx.extensionPolicy = (0, _animus_ui_extract_pipeline.viteExtensionPolicy)(config.resolve.extensions);
|
|
185
|
+
ctx.assetClassification = (0, _animus_ui_extract_pipeline.viteAssetClassification)(config.assetsInclude);
|
|
186
|
+
ctx.loadExpander = (0, _animus_ui_extract_pipeline.viteHostExpander)(config);
|
|
187
|
+
ctx.log(`extension order: ${ctx.extensionPolicy.resolverVersion}`);
|
|
181
188
|
ctx.logger = config.logger;
|
|
182
189
|
ctx.base = config.base ?? "/";
|
|
183
190
|
ctx.lcssTargets = (0, _animus_ui_extract_pipeline.resolveLightningTargets)(ctx.options.targets, ctx.rootDir);
|
|
@@ -196,6 +203,7 @@ function applyResolvedConfig(ctx, config) {
|
|
|
196
203
|
ctx.pathAliasesJson = built.json;
|
|
197
204
|
ctx.log(`Path aliases forwarded: ${built.count} entries`);
|
|
198
205
|
}
|
|
206
|
+
ctx.createSession();
|
|
199
207
|
}
|
|
200
208
|
//#endregion
|
|
201
209
|
//#region src/constants.ts
|
|
@@ -277,80 +285,6 @@ function eventKey(file, timestamp) {
|
|
|
277
285
|
return `${file}\u0000${timestamp}`;
|
|
278
286
|
}
|
|
279
287
|
//#endregion
|
|
280
|
-
//#region src/module-invalidation.ts
|
|
281
|
-
/**
|
|
282
|
-
* Completeness is load-bearing: the file-plan diff and the sources re-delivered
|
|
283
|
-
* when a held publication ends are the only invalidation candidate sources, so
|
|
284
|
-
* a node shape missed here stays stale for the session.
|
|
285
|
-
*/
|
|
286
|
-
function invalidateFileModules(ctx, relPaths) {
|
|
287
|
-
const server = ctx.devServer;
|
|
288
|
-
if (!server || relPaths.length === 0) return 0;
|
|
289
|
-
const graphs = server.environments ? Object.values(server.environments).map((env) => env?.moduleGraph).filter(Boolean) : [server.moduleGraph].filter(Boolean);
|
|
290
|
-
let total = 0;
|
|
291
|
-
for (const relPath of relPaths) {
|
|
292
|
-
const absPath = (0, path.resolve)(ctx.rootDir, relPath);
|
|
293
|
-
let fileCount = 0;
|
|
294
|
-
for (const graph of graphs) {
|
|
295
|
-
const nodes = /* @__PURE__ */ new Set();
|
|
296
|
-
const byFile = graph.getModulesByFile?.(absPath);
|
|
297
|
-
if (byFile) for (const mod of byFile) nodes.add(mod);
|
|
298
|
-
const byId = graph.getModuleById?.(absPath);
|
|
299
|
-
if (byId) nodes.add(byId);
|
|
300
|
-
for (const mod of nodes) {
|
|
301
|
-
graph.invalidateModule(mod);
|
|
302
|
-
fileCount++;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (fileCount > 0) ctx.trace(`HMR invalidate: ${relPath} (plan changed, ${fileCount} nodes)`);
|
|
306
|
-
total += fileCount;
|
|
307
|
-
}
|
|
308
|
-
return total;
|
|
309
|
-
}
|
|
310
|
-
//#endregion
|
|
311
|
-
//#region src/reset-coalescer.ts
|
|
312
|
-
var ResetCoalescer = class {
|
|
313
|
-
run;
|
|
314
|
-
onError;
|
|
315
|
-
quietMs;
|
|
316
|
-
schedule;
|
|
317
|
-
cancel;
|
|
318
|
-
timer = null;
|
|
319
|
-
running = false;
|
|
320
|
-
dirty = false;
|
|
321
|
-
constructor(run, onError, quietMs = 60, schedule = (fn, ms) => setTimeout(fn, ms), cancel = (timer) => clearTimeout(timer)) {
|
|
322
|
-
this.run = run;
|
|
323
|
-
this.onError = onError;
|
|
324
|
-
this.quietMs = quietMs;
|
|
325
|
-
this.schedule = schedule;
|
|
326
|
-
this.cancel = cancel;
|
|
327
|
-
}
|
|
328
|
-
/** Ask for a reset; bursts within the quiescence window collapse. */
|
|
329
|
-
request() {
|
|
330
|
-
if (this.running) {
|
|
331
|
-
this.dirty = true;
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
if (this.timer !== null) this.cancel(this.timer);
|
|
335
|
-
this.timer = this.schedule(async () => {
|
|
336
|
-
this.timer = null;
|
|
337
|
-
this.running = true;
|
|
338
|
-
try {
|
|
339
|
-
const result = this.run();
|
|
340
|
-
if (result instanceof Promise) await result;
|
|
341
|
-
} catch (err) {
|
|
342
|
-
this.onError(err);
|
|
343
|
-
} finally {
|
|
344
|
-
this.running = false;
|
|
345
|
-
if (this.dirty) {
|
|
346
|
-
this.dirty = false;
|
|
347
|
-
this.request();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}, this.quietMs);
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
//#endregion
|
|
354
288
|
//#region src/context.ts
|
|
355
289
|
function emptySystemConfig() {
|
|
356
290
|
return {
|
|
@@ -365,55 +299,14 @@ function emptySystemConfig() {
|
|
|
365
299
|
keyframesJson: null
|
|
366
300
|
};
|
|
367
301
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
}
|
|
376
|
-
function generateSystemPropsModule(ctx) {
|
|
377
|
-
return (0, _animus_ui_extract_pipeline.buildSystemPropsModule)({
|
|
378
|
-
systemPropMapJson: ctx.storedSystemPropMapJson,
|
|
379
|
-
groupRegistryJson: ctx.system.groupRegistryJson,
|
|
380
|
-
dynamicProps: JSON.parse(ctx.storedDynamicPropsJson),
|
|
381
|
-
admittedTransforms: JSON.parse(ctx.storedAdmittedTransformsJson),
|
|
382
|
-
typedSystemProps: JSON.parse(ctx.storedTypedSystemPropsJson)
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
/** Memoized on the inputs themselves, so no writer owes the memo a refresh
|
|
386
|
-
* and a stale memo cannot serve a module from a generation that is gone. */
|
|
387
|
-
const systemPropsModules = /* @__PURE__ */ new WeakMap();
|
|
388
|
-
/** NUL cannot appear in JSON text, so concatenation is injective. */
|
|
389
|
-
function systemPropsModuleKey(ctx) {
|
|
390
|
-
return [
|
|
391
|
-
ctx.storedSystemPropMapJson,
|
|
392
|
-
ctx.system.groupRegistryJson,
|
|
393
|
-
ctx.storedDynamicPropsJson,
|
|
394
|
-
ctx.storedAdmittedTransformsJson,
|
|
395
|
-
ctx.storedTypedSystemPropsJson
|
|
396
|
-
].join("\0");
|
|
397
|
-
}
|
|
398
|
-
/** The exact source `virtual:animus/system-props` serves now — one definition,
|
|
399
|
-
* so the served bytes and the HMR change decision cannot diverge. */
|
|
400
|
-
function systemPropsModuleSource(ctx) {
|
|
401
|
-
const key = systemPropsModuleKey(ctx);
|
|
402
|
-
const memo = systemPropsModules.get(ctx);
|
|
403
|
-
if (memo && memo.key === key) return memo.source;
|
|
404
|
-
const source = generateSystemPropsModule(ctx);
|
|
405
|
-
systemPropsModules.set(ctx, {
|
|
406
|
-
key,
|
|
407
|
-
source
|
|
408
|
-
});
|
|
409
|
-
return source;
|
|
410
|
-
}
|
|
411
|
-
/** Drop a deleted or renamed-away raw original from the dev file cache.
|
|
412
|
-
* Generated MDX/Svelte children live only in the published corpus. */
|
|
413
|
-
function pruneFileCache(cache, rootDir, absPath) {
|
|
414
|
-
const rel = (0, path.relative)(rootDir, (0, path.resolve)(absPath));
|
|
415
|
-
return cache.delete(rel);
|
|
416
|
-
}
|
|
302
|
+
/** The system-props module before a first publication: no props. */
|
|
303
|
+
const EMPTY_SYSTEM_PROPS_MODULE = (0, _animus_ui_extract_pipeline.buildSystemPropsModule)({
|
|
304
|
+
systemPropMapJson: "{}",
|
|
305
|
+
groupRegistryJson: emptySystemConfig().groupRegistryJson,
|
|
306
|
+
dynamicProps: {},
|
|
307
|
+
admittedTransforms: {},
|
|
308
|
+
typedSystemProps: []
|
|
309
|
+
});
|
|
417
310
|
const NO_SHEETS = {
|
|
418
311
|
variableCss: "",
|
|
419
312
|
globalCss: "",
|
|
@@ -424,7 +317,6 @@ var PluginContext = class {
|
|
|
424
317
|
verbose;
|
|
425
318
|
/** The higher tier: `verbose: 'trace'` or `ANIMUS_DEBUG=trace`. */
|
|
426
319
|
tracing;
|
|
427
|
-
staticCssJson;
|
|
428
320
|
isProd = false;
|
|
429
321
|
/** Serving or watching: an error-level diagnostic reports, and only a
|
|
430
322
|
* one-shot build fails on one. */
|
|
@@ -435,145 +327,298 @@ var PluginContext = class {
|
|
|
435
327
|
rootDir = "";
|
|
436
328
|
logger = null;
|
|
437
329
|
system = emptySystemConfig();
|
|
438
|
-
/** Whether a system has loaded, so a failed reload has one to keep. */
|
|
439
|
-
systemLoaded = false;
|
|
440
330
|
lcssTargets = {};
|
|
441
331
|
pathAliasesJson = null;
|
|
442
332
|
/** Vite's export conditions, so the loader reads the files Vite serves. */
|
|
443
333
|
resolveConditions = [];
|
|
444
|
-
/**
|
|
445
|
-
|
|
334
|
+
/** Vite's resolved extension order; the built-in policy until
|
|
335
|
+
* `configResolved`. */
|
|
336
|
+
extensionPolicy = _animus_ui_extract_pipeline.BUILT_IN_EXTENSION_POLICY;
|
|
337
|
+
/** Vite's asset classification, captured at `configResolved`. */
|
|
338
|
+
assetClassification = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("noHost");
|
|
339
|
+
/** How Vite's build expands computed loads, captured at
|
|
340
|
+
* `configResolved`; none where it cannot be reproduced exactly. */
|
|
341
|
+
loadExpander;
|
|
446
342
|
extensionsSet;
|
|
343
|
+
/** Built once per buildStart, before any discovery, from the user's
|
|
344
|
+
* exclude list and the output directory. */
|
|
447
345
|
excludeMatcher;
|
|
346
|
+
/** Where Vite writes, every environment's output directories and files,
|
|
347
|
+
* as structural exclusions; none outside the root. Set in
|
|
348
|
+
* `configResolved`. */
|
|
349
|
+
outputExcludes = [];
|
|
448
350
|
storedManifest = null;
|
|
449
351
|
storedManifestJson = "";
|
|
450
|
-
|
|
451
|
-
resolvedComponentCss
|
|
452
|
-
|
|
352
|
+
/** The analysis sheets with their asset() placeholders. They are read
|
|
353
|
+
* through `globalCss` and `resolvedComponentCss`, which substitute the
|
|
354
|
+
* current asset URLs, so an edited asset changes its URL without
|
|
355
|
+
* re-analysis. */
|
|
356
|
+
analysisGlobalCss = "";
|
|
357
|
+
analysisComponentCss = "";
|
|
358
|
+
get globalCss() {
|
|
359
|
+
return this.substitutedSheets().globalCss;
|
|
360
|
+
}
|
|
361
|
+
set globalCss(css) {
|
|
362
|
+
this.analysisGlobalCss = css;
|
|
363
|
+
}
|
|
364
|
+
get resolvedComponentCss() {
|
|
365
|
+
return this.substitutedSheets().componentCss;
|
|
366
|
+
}
|
|
367
|
+
set resolvedComponentCss(css) {
|
|
368
|
+
this.analysisComponentCss = css;
|
|
369
|
+
}
|
|
370
|
+
substitutedSheets() {
|
|
371
|
+
return (0, _animus_ui_extract_pipeline.substituteSheetAssets)({
|
|
372
|
+
...NO_SHEETS,
|
|
373
|
+
globalCss: this.analysisGlobalCss,
|
|
374
|
+
componentCss: this.analysisComponentCss
|
|
375
|
+
}, this.assetUrlBySpecifier);
|
|
376
|
+
}
|
|
453
377
|
layerDeclaration = "";
|
|
454
378
|
transformOutputHashes = /* @__PURE__ */ new Map();
|
|
455
379
|
recordTransformOutput(relativePath, code) {
|
|
456
380
|
this.transformOutputHashes.set(relativePath, (0, _animus_ui_extract_pipeline.contentHash)(code));
|
|
457
381
|
}
|
|
458
382
|
reverseProvenance = {};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
fileCache = /* @__PURE__ */ new Map();
|
|
464
|
-
/** Mutation attempts against `fileCache`, for memos that must not key on
|
|
465
|
-
* `size` — a delete plus an unrelated create leaves it unchanged. */
|
|
466
|
-
fileCacheGeneration = 0;
|
|
467
|
-
/** The one mutator of the raw-source cache: every add, replacement and
|
|
468
|
-
* deletion goes through here so no write escapes the counter above. */
|
|
469
|
-
mutateFileCache(mutate) {
|
|
470
|
-
const cache = this.fileCache;
|
|
471
|
-
const result = mutate(cache);
|
|
472
|
-
this.fileCacheGeneration += 1;
|
|
473
|
-
return result;
|
|
474
|
-
}
|
|
475
|
-
/** Generated MDX/Svelte paths never enter `fileCache` — they live only in
|
|
476
|
-
* this corpus's published projection. */
|
|
477
|
-
corpus = (0, _animus_ui_extract_pipeline.createSourceCorpus)({
|
|
478
|
-
engineApi: () => this.engineApi(),
|
|
479
|
-
prefix: "[animus-extract]",
|
|
480
|
-
strict: () => !!this.options.strict,
|
|
481
|
-
warn: (message) => this.warn(message),
|
|
482
|
-
reportErrors: () => this.reportErrors()
|
|
483
|
-
});
|
|
484
|
-
rawExtensionFallbacks = /* @__PURE__ */ new Set();
|
|
383
|
+
/** The exact source `virtual:animus/system-props` serves: the last
|
|
384
|
+
* publication's, so the served bytes and the HMR change decision cannot
|
|
385
|
+
* diverge. */
|
|
386
|
+
systemPropsModule = EMPTY_SYSTEM_PROPS_MODULE;
|
|
485
387
|
/** Original path → hash of bytes whose parse aborted in a rejected attempt;
|
|
486
388
|
* non-empty while that rejection holds publication. Those bytes are served
|
|
487
389
|
* raw so the host reports its own syntax error instead of receiving the
|
|
488
390
|
* previous module from the retained engine. */
|
|
489
391
|
abortedParseHashes = /* @__PURE__ */ new Map();
|
|
490
|
-
/** A system reload is pending: the next analysis attempt loads the latest
|
|
491
|
-
* system. A parse hold keeps it pending and the published system served;
|
|
492
|
-
* new dependency watch keys stay registered meanwhile. */
|
|
493
|
-
systemReloadOwed = false;
|
|
494
|
-
/** The one mutator of a file's fallback state — every raw-serve exit of the
|
|
495
|
-
* transform hook reports here, except aborted bytes, which never execute.
|
|
496
|
-
* Production keeps the set empty. The
|
|
497
|
-
* barrier's one-shot withhold release must not route through here: it
|
|
498
|
-
* retires other files' records, no claim about their current serve. */
|
|
499
|
-
recordFallbackState(relativePath, isFallback) {
|
|
500
|
-
if (this.isProd) return;
|
|
501
|
-
if (isFallback) this.rawExtensionFallbacks.add(relativePath);
|
|
502
|
-
else this.rawExtensionFallbacks.delete(relativePath);
|
|
503
|
-
}
|
|
504
392
|
hotUpdateEvents = new HotUpdateEvents();
|
|
505
|
-
|
|
506
|
-
|
|
393
|
+
/** Coalesces a burst of invalidations into one full reload. */
|
|
394
|
+
reloadWindow = new _animus_ui_extract_session.CoalescingWindow(() => {
|
|
395
|
+
this.devServer?.hot?.send({ type: "full-reload" });
|
|
396
|
+
});
|
|
507
397
|
base = "/";
|
|
508
398
|
assetUrlBySpecifier = /* @__PURE__ */ new Map();
|
|
399
|
+
/** Where dev asset() copies live, immutable and content-named; null in a
|
|
400
|
+
* build, or when the dev server does not serve the directory, which
|
|
401
|
+
* serves the live files instead. Set in `configResolved`. */
|
|
402
|
+
assetCopyDir = null;
|
|
403
|
+
/** specifier → the source file its dev copy was made from. */
|
|
404
|
+
assetSourceBySpecifier = /* @__PURE__ */ new Map();
|
|
405
|
+
/** source → its copies, oldest first: the current and the previous. */
|
|
406
|
+
assetCopyHistory = /* @__PURE__ */ new Map();
|
|
407
|
+
/** Specifiers whose copy the dev server does not serve, warned once. */
|
|
408
|
+
unservedAssetCopies = /* @__PURE__ */ new Set();
|
|
409
|
+
/** Whether the dev server serves `path`, by Vite's `server.fs` rule;
|
|
410
|
+
* set in `configResolved`. */
|
|
411
|
+
servesFile = () => true;
|
|
509
412
|
assetResolutionFailures = /* @__PURE__ */ new Set();
|
|
510
413
|
assetPassComplete = false;
|
|
511
414
|
externalPackageDirs = [];
|
|
512
|
-
/** Those whose package is linked rather than installed. */
|
|
513
|
-
externalLinkedDirs = [];
|
|
514
|
-
/** Those read from their package's compiled output. */
|
|
515
|
-
externalOutputDirs = [];
|
|
516
|
-
externalDirOwners = {};
|
|
517
415
|
/** specifier → whether its redirected source entry has side effects, from
|
|
518
416
|
* the owning package's `sideEffects`; absent leaves Vite's default. */
|
|
519
417
|
externalSourceSideEffects = /* @__PURE__ */ new Map();
|
|
520
|
-
externalFileOwners = {};
|
|
521
418
|
externalSourceEntries = /* @__PURE__ */ new Map();
|
|
522
419
|
externalPackageOutcomes = [];
|
|
523
|
-
/** The analysed kits' descriptors, from the last discovery. */
|
|
524
|
-
kitDescriptors = [];
|
|
525
|
-
/** Configured files buildStart or a late rediscovery could not read.
|
|
526
|
-
* Later analyses read no files themselves, so they replay these until the
|
|
527
|
-
* file is read into the cache or deleted. */
|
|
528
|
-
ingestionFailureDiagnostics = [];
|
|
529
420
|
devServer;
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
v2Engine = null;
|
|
534
|
-
v2SentSources = null;
|
|
535
|
-
v2DriftWarned = false;
|
|
421
|
+
/** The Session's path lists already registered with Vite's watcher; the
|
|
422
|
+
* Session replaces a list when it changes. */
|
|
423
|
+
watchedPathLists = /* @__PURE__ */ new WeakSet();
|
|
536
424
|
/** Every native extraction call goes through this one accessor. */
|
|
537
425
|
engineApi;
|
|
426
|
+
/** The native module's stateless lookups, before the root resolves: the
|
|
427
|
+
* shared factory, which registers no handle. */
|
|
428
|
+
kitEngine = _animus_ui_extract_session.engineFactory;
|
|
429
|
+
engineKey = null;
|
|
430
|
+
/** This context's build: one Vite config instance, one build. */
|
|
431
|
+
build = (0, _animus_ui_extract_session.createBuildId)();
|
|
432
|
+
/** The build's Session, created once the Vite root resolves. */
|
|
433
|
+
session = null;
|
|
434
|
+
/** The outcomes each in-flight `submitChanges` collects. */
|
|
435
|
+
outcomeCollectors = /* @__PURE__ */ new Set();
|
|
538
436
|
constructor(options, engineApiOverride) {
|
|
539
437
|
this.options = options;
|
|
540
|
-
this.staticCssJson = (0, _animus_ui_extract_pipeline.serializeStaticCss)(options.staticCss);
|
|
541
438
|
const verbosity = (0, _animus_ui_extract_pipeline.resolveVerbosity)(options.verbose, process.env.ANIMUS_DEBUG);
|
|
542
439
|
this.verbose = verbosity !== "quiet";
|
|
543
440
|
this.tracing = verbosity === "trace";
|
|
544
441
|
this.extensionsSet = new Set(options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
545
|
-
this.excludeMatcher = (0, _animus_ui_extract_pipeline.createExcludeMatcher)(options.exclude);
|
|
546
442
|
if (engineApiOverride) {
|
|
547
443
|
this.engineApi = engineApiOverride;
|
|
444
|
+
this.kitEngine = engineApiOverride;
|
|
548
445
|
return;
|
|
549
446
|
}
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
447
|
+
const build = this.build;
|
|
448
|
+
const rehydrateFilesJson = (filesJsonRaw) => {
|
|
449
|
+
if (!filesJsonRaw.includes("\"source\":\"\"")) return filesJsonRaw;
|
|
450
|
+
const entries = (0, _animus_ui_extract_pipeline.parseFilesJson)(filesJsonRaw, "animus-extract");
|
|
451
|
+
const analysisEntries = this.session?.corpus.published.analysisEntries ?? /* @__PURE__ */ new Map();
|
|
452
|
+
for (const entry of entries) if (entry.source === "") entry.source = analysisEntries.get(entry.path)?.source ?? "";
|
|
453
|
+
return JSON.stringify(entries);
|
|
454
|
+
};
|
|
455
|
+
this.engineApi = () => {
|
|
456
|
+
if (!this.rootDir) throw new Error("[animus-extract] the engine was used before the Vite root resolved");
|
|
457
|
+
this.engineKey ??= (0, _animus_ui_extract_session.sessionKey)(build, this.rootDir);
|
|
458
|
+
return (0, _animus_ui_extract_session.engineHandle)(this.engineKey, {
|
|
459
|
+
label: "animus-extract",
|
|
460
|
+
rehydrateFilesJson
|
|
461
|
+
})();
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/** Creates the build's Session from the resolved config: Vite's engine,
|
|
465
|
+
* logger and core options, kept in memory. */
|
|
466
|
+
createSession() {
|
|
467
|
+
this.session?.close();
|
|
468
|
+
const options = this.options;
|
|
469
|
+
const session = new _animus_ui_extract_session.ExtractionSession({
|
|
470
|
+
system: options.system,
|
|
471
|
+
exclude: options.exclude,
|
|
472
|
+
extensions: options.extensions,
|
|
473
|
+
strict: options.strict,
|
|
474
|
+
diagnostics: options.diagnostics,
|
|
475
|
+
verbose: options.verbose,
|
|
476
|
+
prefix: options.prefix,
|
|
477
|
+
prefixContextualVars: options.prefixContextualVars,
|
|
478
|
+
targets: options.targets,
|
|
479
|
+
minify: options.minify,
|
|
480
|
+
staticCss: options.staticCss,
|
|
481
|
+
layers: options.layers,
|
|
482
|
+
engine: options.engine,
|
|
483
|
+
mode: this.emissionProd ? "production" : "development"
|
|
484
|
+
}, {
|
|
485
|
+
build: this.build,
|
|
486
|
+
unbundledComputedImports: true,
|
|
487
|
+
engine: () => this.engineApi(),
|
|
488
|
+
logger: {
|
|
489
|
+
info: (line) => (this.logger ?? console).info(line),
|
|
490
|
+
warn: (line) => (this.logger ?? console).warn(line),
|
|
491
|
+
error: (line) => (this.logger ?? console).error(line)
|
|
575
492
|
}
|
|
576
493
|
});
|
|
494
|
+
session.persistArtifacts = false;
|
|
495
|
+
session.hostSubstitutesAssets = true;
|
|
496
|
+
session.driverLabel = "animus-extract";
|
|
497
|
+
session.onOutcome = (outcome) => this.observeOutcome(session, outcome);
|
|
498
|
+
session.development = this.development;
|
|
499
|
+
session.rootDir = this.rootDir;
|
|
500
|
+
session.conditions = this.resolveConditions;
|
|
501
|
+
session.extensionPolicy = this.extensionPolicy;
|
|
502
|
+
session.assetClassification = this.assetClassification;
|
|
503
|
+
session.loadExpander = this.loadExpander;
|
|
504
|
+
session.pathAliasesJson = this.pathAliasesJson;
|
|
505
|
+
session.runtimeImport = options.runtimeImport ?? _animus_ui_extract_pipeline.DEFAULT_RUNTIME_IMPORT;
|
|
506
|
+
session.cssModuleId = VIRTUAL_CSS_ID;
|
|
507
|
+
session.systemPropsModuleId = VIRTUAL_SYSTEM_PROPS_ID;
|
|
508
|
+
session.structuralExclude = this.outputExcludes;
|
|
509
|
+
this.session = session;
|
|
510
|
+
return session;
|
|
511
|
+
}
|
|
512
|
+
/** Serves each publication as the Session makes it, so every reader in
|
|
513
|
+
* between sees one generation. */
|
|
514
|
+
observeOutcome(session, outcome) {
|
|
515
|
+
let systemPropsChanged = false;
|
|
516
|
+
if (outcome.kind === "held") for (const [path$6, hash] of outcome.abortedOriginals) this.abortedParseHashes.set(path$6, hash);
|
|
517
|
+
else {
|
|
518
|
+
this.abortedParseHashes.clear();
|
|
519
|
+
const before = this.systemPropsModule;
|
|
520
|
+
this.adoptPublication(session);
|
|
521
|
+
systemPropsChanged = this.systemPropsModule !== before;
|
|
522
|
+
}
|
|
523
|
+
for (const collected of this.outcomeCollectors) collected.push({
|
|
524
|
+
outcome,
|
|
525
|
+
systemPropsChanged
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
/** Submits a dev change set to the Session. Until a first publication
|
|
529
|
+
* there is no generation to update: the full read covers the change. */
|
|
530
|
+
submitChanges(changes) {
|
|
531
|
+
return this.runAttempt((session) => session.publication === null ? session.runFullPipeline([], changes.sources) : session.handleWatchUpdate(changes));
|
|
532
|
+
}
|
|
533
|
+
/** Runs one Session attempt and returns the outcomes that settle it. A
|
|
534
|
+
* hold warns and resolves with its outcome (Vite reports the syntax
|
|
535
|
+
* error itself); any other failure rejects. Either way the watcher
|
|
536
|
+
* learns the paths the attempt's system load named. */
|
|
537
|
+
async runAttempt(attempt) {
|
|
538
|
+
const session = this.session ?? this.createSession();
|
|
539
|
+
const collected = [];
|
|
540
|
+
this.outcomeCollectors.add(collected);
|
|
541
|
+
try {
|
|
542
|
+
await attempt(session);
|
|
543
|
+
} catch (error) {
|
|
544
|
+
if (!collected.some(({ outcome }) => outcome.kind === "held")) throw error;
|
|
545
|
+
this.warn(error instanceof Error ? error.message : String(error));
|
|
546
|
+
} finally {
|
|
547
|
+
this.outcomeCollectors.delete(collected);
|
|
548
|
+
this.registerSystemWatchPaths();
|
|
549
|
+
}
|
|
550
|
+
return collected;
|
|
551
|
+
}
|
|
552
|
+
/** Serves the Session's last publication: the manifest, the analysis
|
|
553
|
+
* sheets (asset placeholders kept; Vite substitutes them), the system,
|
|
554
|
+
* the package map and the external-package state. */
|
|
555
|
+
adoptPublication(session) {
|
|
556
|
+
const publication = session.publication;
|
|
557
|
+
if (publication === null) return;
|
|
558
|
+
const manifest = publication.manifest;
|
|
559
|
+
this.system = publication.system;
|
|
560
|
+
this.storedManifest = manifest;
|
|
561
|
+
this.storedManifestJson = publication.manifestJson;
|
|
562
|
+
this.globalCss = publication.analysisGlobalCss;
|
|
563
|
+
this.resolvedComponentCss = publication.analysisComponentCss;
|
|
564
|
+
this.reverseProvenance = manifest.reverse_provenance;
|
|
565
|
+
this.systemPropsModule = publication.systemPropsModule;
|
|
566
|
+
this.externalPackageDirs = session.externalPackageDirs;
|
|
567
|
+
this.externalSourceEntries = session.externalSourceEntries;
|
|
568
|
+
this.externalSourceSideEffects = session.externalSourceSideEffects;
|
|
569
|
+
this.externalPackageOutcomes = session.lastExternalOutcomes;
|
|
570
|
+
this.applyAssetSubstitutions();
|
|
571
|
+
}
|
|
572
|
+
/** The environment a host without environments builds as. */
|
|
573
|
+
singleEnvironment = { name: "build" };
|
|
574
|
+
/** Each environment build that started and has not ended, by name. A
|
|
575
|
+
* custom `buildApp` builds a shared plugin's environments in parallel, so
|
|
576
|
+
* the context's build retires with the last of them, not the first. */
|
|
577
|
+
openBuilds = /* @__PURE__ */ new Map();
|
|
578
|
+
/** Opens `environment`'s build, after any open build under its name has
|
|
579
|
+
* ended: a builder reused while an earlier build still runs, or a second
|
|
580
|
+
* environment under one name, builds after it rather than beside it.
|
|
581
|
+
* Each build then ends its own generation once, in `buildEnd`. */
|
|
582
|
+
async openBuild(environment) {
|
|
583
|
+
for (;;) {
|
|
584
|
+
const open = this.openBuilds.get(environment.name);
|
|
585
|
+
if (open === void 0) break;
|
|
586
|
+
await open.ended;
|
|
587
|
+
}
|
|
588
|
+
let end = () => {};
|
|
589
|
+
const ended = new Promise((settle) => {
|
|
590
|
+
end = settle;
|
|
591
|
+
});
|
|
592
|
+
this.openBuilds.set(environment.name, {
|
|
593
|
+
environment,
|
|
594
|
+
ended,
|
|
595
|
+
end
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
/** Ends `environment`'s open build; whether that left none open. Vite
|
|
599
|
+
* ends each build once, in `buildEnd`, and a dev server ends only the
|
|
600
|
+
* environments that started, so only the build that opened an entry
|
|
601
|
+
* ends it. */
|
|
602
|
+
endBuild(environment) {
|
|
603
|
+
const open = this.openBuilds.get(environment.name);
|
|
604
|
+
if (open === void 0 || open.environment !== environment) return false;
|
|
605
|
+
this.openBuilds.delete(environment.name);
|
|
606
|
+
open.end();
|
|
607
|
+
return this.openBuilds.size === 0;
|
|
608
|
+
}
|
|
609
|
+
/** Retires this context's build when Vite closes it: the Session leaves
|
|
610
|
+
* the registry (with its manifest and watch state) and the engine handle
|
|
611
|
+
* is released, injected engine or not. The next build start rejoins. */
|
|
612
|
+
close() {
|
|
613
|
+
this.session?.close();
|
|
614
|
+
this.releaseEngine();
|
|
615
|
+
}
|
|
616
|
+
/** Releases this context's engine handle: a standalone one, which no
|
|
617
|
+
* joined build retires. The next use makes a fresh one. */
|
|
618
|
+
releaseEngine() {
|
|
619
|
+
if (this.engineKey === null) return;
|
|
620
|
+
(0, _animus_ui_extract_session.releaseEngineHandle)(this.engineKey);
|
|
621
|
+
this.engineKey = null;
|
|
577
622
|
}
|
|
578
623
|
log(msg) {
|
|
579
624
|
if (this.verbose) (this.logger ?? console).info(`[animus] ${msg}`);
|
|
@@ -604,214 +649,57 @@ var PluginContext = class {
|
|
|
604
649
|
if (!report) throw new Error(message, cause === void 0 ? void 0 : { cause });
|
|
605
650
|
report(message);
|
|
606
651
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
});
|
|
621
|
-
const deps = this.system.dependencies ?? [];
|
|
622
|
-
const keys = /* @__PURE__ */ new Set();
|
|
623
|
-
for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(this.resolvedSystemPath)) keys.add(key);
|
|
624
|
-
for (const dep of deps) for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(dep)) keys.add(key);
|
|
625
|
-
this.systemDependencyKeys = keys;
|
|
626
|
-
this.systemDependencyPaths = deps;
|
|
627
|
-
this.registerSystemWatchPaths();
|
|
628
|
-
this.systemLoaded = true;
|
|
629
|
-
} catch (e) {
|
|
630
|
-
if (this.options.strict) {
|
|
631
|
-
const message = `[animus-extract] Failed to load system from ${this.resolvedSystemPath}: ${e}`;
|
|
632
|
-
if (!this.systemLoaded) throw new Error(message, { cause: e });
|
|
633
|
-
this.strictFailure(message, e);
|
|
634
|
-
return;
|
|
652
|
+
devFsUrl(absPath) {
|
|
653
|
+
return `${this.base.endsWith("/") ? this.base.slice(0, -1) : this.base}/@fs${absPath}`;
|
|
654
|
+
}
|
|
655
|
+
/** A dev asset() URL: an immutable content-named copy of `absPath`, so an
|
|
656
|
+
* edit gets a new URL, or the live file where copies are not served. */
|
|
657
|
+
devAssetUrl(specifier, absPath) {
|
|
658
|
+
if (this.assetCopyDir === null) return this.devFsUrl(absPath);
|
|
659
|
+
const { fileName } = (0, _animus_ui_extract_pipeline.writeAssetCopy)(absPath, this.assetCopyDir);
|
|
660
|
+
const copyPath = (0, path.join)(this.assetCopyDir, fileName);
|
|
661
|
+
if (!this.servesFile(copyPath)) {
|
|
662
|
+
if (!this.unservedAssetCopies.has(specifier)) {
|
|
663
|
+
this.unservedAssetCopies.add(specifier);
|
|
664
|
+
this.warn(`[animus-extract] the copy of asset() ${specifier} is not served under server.fs; serving the live file instead, so an edit keeps its URL`);
|
|
635
665
|
}
|
|
636
|
-
|
|
666
|
+
return this.devFsUrl(absPath);
|
|
667
|
+
}
|
|
668
|
+
if (this.assetSourceBySpecifier.get(specifier) !== absPath) this.devServer?.watcher?.add(absPath);
|
|
669
|
+
this.assetSourceBySpecifier.set(specifier, absPath);
|
|
670
|
+
this.retainAssetCopy(absPath, fileName);
|
|
671
|
+
return this.devFsUrl(copyPath);
|
|
672
|
+
}
|
|
673
|
+
/** Keeps each source's current copy and the one before it; an asset
|
|
674
|
+
* edit reloads the page, so an older URL is never fetched again. A copy
|
|
675
|
+
* another source still keeps is never removed. */
|
|
676
|
+
retainAssetCopy(source, fileName) {
|
|
677
|
+
const history = this.assetCopyHistory.get(source) ?? [];
|
|
678
|
+
if (history[history.length - 1] !== fileName) history.push(fileName);
|
|
679
|
+
this.assetCopyHistory.set(source, history);
|
|
680
|
+
while (history.length > 2) {
|
|
681
|
+
const oldest = history.shift();
|
|
682
|
+
if (![...this.assetCopyHistory.values()].some((copies) => copies.includes(oldest)) && this.assetCopyDir !== null) (0, fs.rmSync)((0, path.join)(this.assetCopyDir, oldest), { force: true });
|
|
637
683
|
}
|
|
638
684
|
}
|
|
639
|
-
/**
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
685
|
+
/** Whether `absFile` is the source of a dev asset copy. */
|
|
686
|
+
isAssetSource(absFile) {
|
|
687
|
+
const keys = new Set((0, _animus_ui_extract_pipeline.toWatchKeys)(absFile));
|
|
688
|
+
return [...this.assetSourceBySpecifier.values()].some((source) => (0, _animus_ui_extract_pipeline.toWatchKeys)(source).some((key) => keys.has(key)));
|
|
689
|
+
}
|
|
690
|
+
/** Copies an edited asset under its new content name and redelivers the
|
|
691
|
+
* stylesheet that names it. */
|
|
692
|
+
refreshAssetCopies(absFile) {
|
|
693
|
+
const keys = new Set((0, _animus_ui_extract_pipeline.toWatchKeys)(absFile));
|
|
694
|
+
for (const [specifier, source] of this.assetSourceBySpecifier) {
|
|
695
|
+
if (!(0, _animus_ui_extract_pipeline.toWatchKeys)(source).some((key) => keys.has(key))) continue;
|
|
645
696
|
try {
|
|
646
|
-
|
|
647
|
-
return true;
|
|
697
|
+
this.assetUrlBySpecifier.set(specifier, this.devAssetUrl(specifier, source));
|
|
648
698
|
} catch (err) {
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
/** Runs project analysis and updates every manifest-derived state. Returns
|
|
654
|
-
* false when nothing published, and the caller must roll its cache back.
|
|
655
|
-
* While a source is skipped or unreadable, its renders are unseen, so
|
|
656
|
-
* nothing is pruned. */
|
|
657
|
-
runAnalysis(fileEntries, skippedOriginals = []) {
|
|
658
|
-
const ingestionFailures = this.unsettledIngestionFailures();
|
|
659
|
-
let result;
|
|
660
|
-
try {
|
|
661
|
-
result = (0, _animus_ui_extract_pipeline.runProjectAnalysis)(this.engineApi, {
|
|
662
|
-
fileEntries,
|
|
663
|
-
packageMap: this.packageMap,
|
|
664
|
-
system: this.system,
|
|
665
|
-
emitter: {
|
|
666
|
-
runtimeImport: this.options.runtimeImport ?? "@animus-ui/system",
|
|
667
|
-
cssModuleId: VIRTUAL_CSS_ID
|
|
668
|
-
},
|
|
669
|
-
pathAliasesJson: this.pathAliasesJson,
|
|
670
|
-
staticCssJson: this.staticCssJson,
|
|
671
|
-
externalDirs: this.externalPackageDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
672
|
-
devMode: !this.emissionProd,
|
|
673
|
-
analysisContext: {
|
|
674
|
-
skippedSources: [...skippedOriginals, ...ingestionFailures.map((d) => d.file)].map((file) => (0, path.isAbsolute)(file) ? (0, path.relative)(this.rootDir, file) : file),
|
|
675
|
-
unbundledComputedImports: true,
|
|
676
|
-
packageDirs: this.externalPackageDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
677
|
-
linkedDirs: this.externalLinkedDirs.map((dir) => (0, path.relative)(this.rootDir, dir)),
|
|
678
|
-
outputDirs: this.externalOutputDirs.map((dir) => (0, path.relative)(this.rootDir, dir))
|
|
679
|
-
},
|
|
680
|
-
warn: (m) => this.warn(m),
|
|
681
|
-
info: (m) => this.log(m),
|
|
682
|
-
strict: this.options.strict,
|
|
683
|
-
diagnostics: this.options.diagnostics,
|
|
684
|
-
reportErrors: this.reportErrors(),
|
|
685
|
-
extraDiagnostics: ingestionFailures,
|
|
686
|
-
kitDescriptors: this.kitDescriptors
|
|
687
|
-
});
|
|
688
|
-
} catch (e) {
|
|
689
|
-
const message = `[animus-extract] analyzeProject failed: ${e}`;
|
|
690
|
-
if (e instanceof _animus_ui_extract_pipeline.DiagnosticFailure) throw new Error(message, { cause: e });
|
|
691
|
-
if (this.options.strict) {
|
|
692
|
-
if (this.storedManifest === null) throw new Error(message, { cause: e });
|
|
693
|
-
this.strictFailure(message, e);
|
|
694
|
-
return false;
|
|
695
|
-
}
|
|
696
|
-
console.warn("[animus-extract] analyzeProject failed:", e);
|
|
697
|
-
return false;
|
|
698
|
-
}
|
|
699
|
-
this.assertRuntimeImportSuppliesTerminals(result.manifest);
|
|
700
|
-
(0, _animus_ui_extract_pipeline.reportSurvivingAssetPlaceholders)((0, _animus_ui_extract_pipeline.generatedModuleCode)(result.manifest), {
|
|
701
|
-
strict: this.options.strict,
|
|
702
|
-
warn: (message) => this.warn(message),
|
|
703
|
-
reportErrors: this.reportErrors(),
|
|
704
|
-
prefix: "[animus-extract]",
|
|
705
|
-
surface: "generated runtime modules"
|
|
706
|
-
});
|
|
707
|
-
this.storedManifest = result.manifest;
|
|
708
|
-
this.storedManifestJson = result.manifestJson;
|
|
709
|
-
this.ingestionFailureDiagnostics = ingestionFailures;
|
|
710
|
-
this.storedSystemPropMapJson = JSON.stringify(result.manifest.system_prop_map);
|
|
711
|
-
this.storedDynamicPropsJson = JSON.stringify(result.manifest.dynamic_props);
|
|
712
|
-
this.storedAdmittedTransformsJson = JSON.stringify(result.manifest.admitted_transforms);
|
|
713
|
-
this.storedTypedSystemPropsJson = JSON.stringify(result.manifest.typed_system_props);
|
|
714
|
-
this.reverseProvenance = result.manifest.reverse_provenance;
|
|
715
|
-
this.storedSheets = result.manifest.sheets;
|
|
716
|
-
this.globalCss = result.globalCss;
|
|
717
|
-
this.resolvedComponentCss = result.componentCss;
|
|
718
|
-
this.applyAssetSubstitutions();
|
|
719
|
-
return true;
|
|
720
|
-
}
|
|
721
|
-
assertRuntimeImportSuppliesTerminals(manifest) {
|
|
722
|
-
const override = this.options.runtimeImport;
|
|
723
|
-
if (!override || override === "@animus-ui/system") return;
|
|
724
|
-
const offenders = [];
|
|
725
|
-
for (const [id, descriptor] of Object.entries(manifest.components)) if (/\bcreateComponent\(|\bcreateComposedFamily\(/.test(descriptor.replacement)) offenders.push(id);
|
|
726
|
-
if (offenders.length === 0) return;
|
|
727
|
-
const shown = offenders.slice(0, 5).join(", ");
|
|
728
|
-
throw new Error(`[animus-extract] runtimeImport '${override}' is valid only when every extracted terminal is .asClass(), but ${offenders.length} component(s) need createComponent/createComposedFamily from the default '@animus-ui/system' runtime: ${shown}` + (offenders.length > 5 ? ", …" : "") + `. Remove the override or convert these terminals to .asClass().`);
|
|
729
|
-
}
|
|
730
|
-
/** The one ingest → quarantine → analyze → publish transaction. The corpus
|
|
731
|
-
* publishes only on success. `afterIngestion` runs after quarantine even
|
|
732
|
-
* when an aborted parse rejects the attempt; `beforeAnalysis` runs only
|
|
733
|
-
* once the attempt will analyze. A rejection names its aborted originals
|
|
734
|
-
* and holds publication: development callers keep their cache's latest
|
|
735
|
-
* sources, valid edits included, for the repair to publish together. In
|
|
736
|
-
* every strictness mode it fails a production build, which has no
|
|
737
|
-
* generation to keep, and only warns in development: the host reports the
|
|
738
|
-
* syntax error itself, and a throw would leave its client on the failed
|
|
739
|
-
* module. The publication that ends a hold invalidates the modules of
|
|
740
|
-
* every original whose admitted bytes changed meanwhile and returns them
|
|
741
|
-
* as `redeliver`, for a hot update to push to the client. An owed system
|
|
742
|
-
* reload loads the latest system for the attempt: a hold restores the
|
|
743
|
-
* published one and keeps the reload owed, any other outcome completes
|
|
744
|
-
* the reload's invalidation. */
|
|
745
|
-
async analyzeIngested(options) {
|
|
746
|
-
const holding = this.abortedParseHashes.size > 0;
|
|
747
|
-
const previous = this.corpus.published.ownership;
|
|
748
|
-
const reloadingSystem = this.systemReloadOwed;
|
|
749
|
-
const prevPlans = reloadingSystem ? (0, _animus_ui_extract_pipeline.snapshotFilePlans)(this.storedManifest) : null;
|
|
750
|
-
const publishedSystem = this.system;
|
|
751
|
-
let published = false;
|
|
752
|
-
try {
|
|
753
|
-
if (reloadingSystem) this.loadSystem();
|
|
754
|
-
const accepted = await this.corpus.prepare(options?.rawEntries ?? {
|
|
755
|
-
fileCache: this.fileCache,
|
|
756
|
-
externalFileOwners: this.externalFileOwners
|
|
757
|
-
});
|
|
758
|
-
options?.afterIngestion?.(accepted);
|
|
759
|
-
const rejection = this.corpus.rejection(accepted);
|
|
760
|
-
if (rejection) {
|
|
761
|
-
for (const [path$3, hash] of rejection.originals) this.abortedParseHashes.set(path$3, hash);
|
|
762
|
-
if (this.isProd) throw new Error(rejection.message);
|
|
763
|
-
this.warn(rejection.message);
|
|
764
|
-
return {
|
|
765
|
-
ok: false,
|
|
766
|
-
accepted,
|
|
767
|
-
abortedOriginals: rejection.originals,
|
|
768
|
-
endedHold: false,
|
|
769
|
-
redeliver: []
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
if (reloadingSystem) (0, _animus_ui_extract_pipeline.clearEngineCache)(this.engineApi);
|
|
773
|
-
options?.beforeAnalysis?.(accepted);
|
|
774
|
-
const ok = this.runAnalysis(accepted.analysisEntries, accepted.skippedOriginals) !== false;
|
|
775
|
-
if (!ok) return {
|
|
776
|
-
ok,
|
|
777
|
-
accepted,
|
|
778
|
-
endedHold: false,
|
|
779
|
-
redeliver: []
|
|
780
|
-
};
|
|
781
|
-
this.publishSourceIngestion(accepted);
|
|
782
|
-
published = true;
|
|
783
|
-
this.abortedParseHashes.clear();
|
|
784
|
-
if (!holding) return {
|
|
785
|
-
ok,
|
|
786
|
-
accepted,
|
|
787
|
-
endedHold: false,
|
|
788
|
-
redeliver: []
|
|
789
|
-
};
|
|
790
|
-
const redeliver = Object.values(accepted.ownership).filter((owner) => previous[owner.originalPath]?.originalHash !== owner.originalHash).map((owner) => owner.originalPath);
|
|
791
|
-
invalidateFileModules(this, redeliver);
|
|
792
|
-
return {
|
|
793
|
-
ok,
|
|
794
|
-
accepted,
|
|
795
|
-
endedHold: true,
|
|
796
|
-
redeliver
|
|
797
|
-
};
|
|
798
|
-
} finally {
|
|
799
|
-
const systemHeld = reloadingSystem && this.abortedParseHashes.size > 0;
|
|
800
|
-
if (systemHeld) this.system = publishedSystem;
|
|
801
|
-
else if (reloadingSystem) {
|
|
802
|
-
this.systemReloadOwed = false;
|
|
803
|
-
if (published && prevPlans) invalidateFileModules(this, (0, _animus_ui_extract_pipeline.diffFilePlans)(prevPlans, (0, _animus_ui_extract_pipeline.snapshotFilePlans)(this.storedManifest)));
|
|
699
|
+
this.warn(`[animus-extract] could not copy edited asset ${source}: ${String(err)}`);
|
|
804
700
|
}
|
|
805
|
-
if (reloadingSystem) this.invalidateSystemReloadModules(!systemHeld);
|
|
806
701
|
}
|
|
807
|
-
|
|
808
|
-
publishSourceIngestion(result) {
|
|
809
|
-
this.corpus.publish(result);
|
|
810
|
-
this.externalFileOwners = (0, _animus_ui_extract_pipeline.projectExternalFileOwners)(result, this.externalFileOwners);
|
|
811
|
-
this.enforceExternalTokenContracts();
|
|
812
|
-
}
|
|
813
|
-
devFsUrl(absPath) {
|
|
814
|
-
return `${this.base.endsWith("/") ? this.base.slice(0, -1) : this.base}/@fs${absPath}`;
|
|
702
|
+
this.invalidateSystemReloadModules(true);
|
|
815
703
|
}
|
|
816
704
|
assetFallback(specifier, message, cause) {
|
|
817
705
|
if (this.options.strict) this.strictFailure(message, cause);
|
|
@@ -823,8 +711,8 @@ var PluginContext = class {
|
|
|
823
711
|
sheetAssetSpecifiers() {
|
|
824
712
|
return (0, _animus_ui_extract_pipeline.findSheetAssetSpecifiers)({
|
|
825
713
|
variableCss: this.system.variableCss,
|
|
826
|
-
globalCss: this.
|
|
827
|
-
componentCss: this.
|
|
714
|
+
globalCss: this.analysisGlobalCss,
|
|
715
|
+
componentCss: this.analysisComponentCss
|
|
828
716
|
});
|
|
829
717
|
}
|
|
830
718
|
/** `system.variableCss` with its asset() placeholders substituted from
|
|
@@ -837,18 +725,6 @@ var PluginContext = class {
|
|
|
837
725
|
variableCss: this.system.variableCss
|
|
838
726
|
}, this.assetUrlBySpecifier).variableCss;
|
|
839
727
|
}
|
|
840
|
-
/** Substitutes the analysis sheets in place, through the same step as the
|
|
841
|
-
* session; the theme's variable CSS is substituted on read
|
|
842
|
-
* (`variableCss`). */
|
|
843
|
-
substituteSheets() {
|
|
844
|
-
const substituted = (0, _animus_ui_extract_pipeline.substituteSheetAssets)({
|
|
845
|
-
...NO_SHEETS,
|
|
846
|
-
globalCss: this.globalCss,
|
|
847
|
-
componentCss: this.resolvedComponentCss
|
|
848
|
-
}, this.assetUrlBySpecifier);
|
|
849
|
-
this.globalCss = substituted.globalCss;
|
|
850
|
-
this.resolvedComponentCss = substituted.componentCss;
|
|
851
|
-
}
|
|
852
728
|
/** Resolves asset specifiers a system reload introduced. Node-side only —
|
|
853
729
|
* the plugin hook context is unavailable here, so results can differ. */
|
|
854
730
|
applyAssetSubstitutions() {
|
|
@@ -856,44 +732,9 @@ var PluginContext = class {
|
|
|
856
732
|
if (this.assetResolutionFailures.delete(specifier)) this.assetUrlBySpecifier.delete(specifier);
|
|
857
733
|
if (this.assetUrlBySpecifier.has(specifier)) continue;
|
|
858
734
|
const resolved = (0, _animus_ui_extract_pipeline.resolveAssetFile)(specifier, this.rootDir, this.pathAliasesJson);
|
|
859
|
-
if (resolved) this.assetUrlBySpecifier.set(specifier, this.
|
|
735
|
+
if (resolved) this.assetUrlBySpecifier.set(specifier, this.devAssetUrl(specifier, resolved));
|
|
860
736
|
else this.assetFallback(specifier, `[animus-extract] unresolvable asset() specifier: ${specifier}`);
|
|
861
737
|
}
|
|
862
|
-
this.substituteSheets();
|
|
863
|
-
}
|
|
864
|
-
resetCoalescer = null;
|
|
865
|
-
requestSystemReload(trigger) {
|
|
866
|
-
this.log(`HMR system reload scheduled: ${trigger}`);
|
|
867
|
-
this.resetCoalescer ??= new ResetCoalescer(
|
|
868
|
-
async () => this.performSystemReload(),
|
|
869
|
-
/** The coalescer fires from a bare timer, outside Vite's error
|
|
870
|
-
* handling, so an unhandled strict throw would exit the process. */
|
|
871
|
-
(err) => {
|
|
872
|
-
const display = String(err);
|
|
873
|
-
this.warn(`[animus-extract] system reload failed: ${display}`);
|
|
874
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
875
|
-
const stack = err instanceof Error ? err.stack ?? "" : "";
|
|
876
|
-
this.devServer?.hot?.send({
|
|
877
|
-
type: "error",
|
|
878
|
-
err: {
|
|
879
|
-
message,
|
|
880
|
-
stack,
|
|
881
|
-
plugin: "animus-extract"
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
);
|
|
886
|
-
this.resetCoalescer.request();
|
|
887
|
-
}
|
|
888
|
-
async performSystemReload() {
|
|
889
|
-
return runExclusiveAnalysis(this, () => this.performSystemReloadExclusive());
|
|
890
|
-
}
|
|
891
|
-
async performSystemReloadExclusive() {
|
|
892
|
-
const resetStart = performance.now();
|
|
893
|
-
this.systemReloadOwed = true;
|
|
894
|
-
const { ok } = await this.analyzeIngested(await this.rediscoverSources?.());
|
|
895
|
-
if (!ok) return;
|
|
896
|
-
this.log(`HMR system reload complete: ${Math.round(performance.now() - resetStart)}ms`);
|
|
897
738
|
}
|
|
898
739
|
invalidateSystemReloadModules(reloadClient) {
|
|
899
740
|
const server = this.devServer;
|
|
@@ -909,15 +750,35 @@ var PluginContext = class {
|
|
|
909
750
|
if (reloadClient) server.hot?.send({ type: "full-reload" });
|
|
910
751
|
}
|
|
911
752
|
isSystemDependency(absFile) {
|
|
912
|
-
return
|
|
753
|
+
return this.session?.isSystemDependency(absFile) ?? false;
|
|
913
754
|
}
|
|
914
|
-
/** Watch every loader-reported dependency path
|
|
915
|
-
*
|
|
755
|
+
/** Watch every loader-reported dependency path, the external package
|
|
756
|
+
* roots, and the paths the load found absent that no watched directory
|
|
757
|
+
* covers (creating one reloads the system). Vite hard-ignores
|
|
758
|
+
* node_modules, so dependencies installed there produce no events. A list
|
|
759
|
+
* already registered is not added again. */
|
|
916
760
|
registerSystemWatchPaths() {
|
|
917
761
|
const watcher = this.devServer?.watcher;
|
|
918
|
-
|
|
919
|
-
if (
|
|
920
|
-
|
|
762
|
+
const session = this.session;
|
|
763
|
+
if (!watcher || !session) return;
|
|
764
|
+
for (const paths of [
|
|
765
|
+
session.systemDependencyPaths,
|
|
766
|
+
session.systemMissingPaths,
|
|
767
|
+
session.externalPackageDirs
|
|
768
|
+
]) {
|
|
769
|
+
if (paths.length === 0 || this.watchedPathLists.has(paths)) continue;
|
|
770
|
+
this.watchedPathLists.add(paths);
|
|
771
|
+
const added = paths === session.systemMissingPaths ? this.uncoveredMissingPaths(paths, session.externalPackageDirs) : paths;
|
|
772
|
+
if (added.length > 0) watcher.add(added);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
/** The absent paths no watched directory reports a creation for. Vite's
|
|
776
|
+
* root watch and a package root's watch already do, and a watch on an
|
|
777
|
+
* absent file inside a watched directory stops that directory's events
|
|
778
|
+
* under inotify (chokidar's non-fsevents handler). */
|
|
779
|
+
uncoveredMissingPaths(paths, packageDirs) {
|
|
780
|
+
const watchedDirs = this.rootDir ? [this.rootDir, ...packageDirs] : packageDirs;
|
|
781
|
+
return paths.filter((path$7) => !watchedDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, path$7)));
|
|
921
782
|
}
|
|
922
783
|
/** Invalidate the component CSS and system-props modules, then reload. The
|
|
923
784
|
* reload alone rescues nothing: Vite keeps serving its cached transform.
|
|
@@ -930,33 +791,7 @@ var PluginContext = class {
|
|
|
930
791
|
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
931
792
|
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
932
793
|
}
|
|
933
|
-
|
|
934
|
-
this.pendingReloadTimer = setTimeout(() => {
|
|
935
|
-
this.pendingReloadTimer = null;
|
|
936
|
-
this.devServer?.hot?.send({ type: "full-reload" });
|
|
937
|
-
}, 100);
|
|
938
|
-
}
|
|
939
|
-
/** An unresolvable `.includes()` specifier or a stale dist entry under an
|
|
940
|
-
* extended package warns, and fails the build under `strict` (strict
|
|
941
|
-
* development reports it). */
|
|
942
|
-
enforceIncludeResolution() {
|
|
943
|
-
for (const message of [(0, _animus_ui_extract_pipeline.unresolvableIncludesMessage)(this.externalPackageOutcomes), (0, _animus_ui_extract_pipeline.staleDistIncludesMessage)(this.externalPackageOutcomes)]) {
|
|
944
|
-
if (message === null) continue;
|
|
945
|
-
if (this.options.strict) this.strictFailure(message);
|
|
946
|
-
else this.warn(message);
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
enforceExternalTokenContracts() {
|
|
950
|
-
(0, _animus_ui_extract_pipeline.enforceExternalTokenContracts)({
|
|
951
|
-
diagnostics: this.storedManifest?.diagnostics,
|
|
952
|
-
fileOwners: this.externalFileOwners,
|
|
953
|
-
dirOwners: this.externalDirOwners,
|
|
954
|
-
sourceThemeManifestsJson: this.system.sourceThemeManifestsJson,
|
|
955
|
-
strict: this.options.strict,
|
|
956
|
-
prefix: "[animus-extract]",
|
|
957
|
-
warn: (message) => this.warn(message),
|
|
958
|
-
reportErrors: this.reportErrors()
|
|
959
|
-
});
|
|
794
|
+
this.reloadWindow.note();
|
|
960
795
|
}
|
|
961
796
|
runSelfVerify() {
|
|
962
797
|
const failures = (0, _animus_ui_extract_pipeline.runStructuralSelfCheck)({
|
|
@@ -981,131 +816,113 @@ var PluginContext = class {
|
|
|
981
816
|
}
|
|
982
817
|
};
|
|
983
818
|
//#endregion
|
|
984
|
-
//#region src/
|
|
985
|
-
/** The current manifest's unresolved-parent drops, parsed from diagnostics. */
|
|
986
|
-
function unresolvedParentDrops(ctx) {
|
|
987
|
-
const drops = [];
|
|
988
|
-
const manifest = ctx.storedManifest;
|
|
989
|
-
if (!manifest) return drops;
|
|
990
|
-
for (const d of manifest.diagnostics) {
|
|
991
|
-
const parent = (0, _animus_ui_extract_pipeline.unresolvedParentName)(d);
|
|
992
|
-
if (parent !== null) drops.push({
|
|
993
|
-
file: d.file,
|
|
994
|
-
component: d.component,
|
|
995
|
-
parent
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
return drops;
|
|
999
|
-
}
|
|
1000
|
-
const EMPTY_DROP_FILES = /* @__PURE__ */ new Set();
|
|
1001
|
-
const dropFilesByManifest = /* @__PURE__ */ new WeakMap();
|
|
1002
|
-
/** Files carrying an unresolved-parent drop in the current manifest. */
|
|
1003
|
-
function unresolvedDropFiles(ctx) {
|
|
1004
|
-
const manifest = ctx.storedManifest;
|
|
1005
|
-
if (!manifest) return EMPTY_DROP_FILES;
|
|
1006
|
-
let files = dropFilesByManifest.get(manifest);
|
|
1007
|
-
if (!files) {
|
|
1008
|
-
const derived = /* @__PURE__ */ new Set();
|
|
1009
|
-
for (const d of manifest.diagnostics) if ((0, _animus_ui_extract_pipeline.isUnresolvedParentDrop)(d)) derived.add(d.file);
|
|
1010
|
-
files = derived;
|
|
1011
|
-
dropFilesByManifest.set(manifest, files);
|
|
1012
|
-
}
|
|
1013
|
-
return files;
|
|
1014
|
-
}
|
|
819
|
+
//#region src/module-invalidation.ts
|
|
1015
820
|
/**
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
821
|
+
* Completeness is load-bearing: the file-plan diff and the sources re-delivered
|
|
822
|
+
* when a held publication ends are the only invalidation candidate sources, so
|
|
823
|
+
* a node shape missed here stays stale for the session.
|
|
1018
824
|
*/
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
cacheGeneration: ctx.fileCacheGeneration
|
|
1038
|
-
});
|
|
1039
|
-
warnInadmissibleParents(ctx, drops);
|
|
1040
|
-
return reanalyzed;
|
|
1041
|
-
}
|
|
1042
|
-
barrenWalkMemos.delete(ctx);
|
|
1043
|
-
if (folded.length > 0) ctx.log(`rediscovery: folded ${folded.length} on-disk file(s) after unresolved-parent drop`);
|
|
1044
|
-
reanalyzed = true;
|
|
1045
|
-
let published = false;
|
|
1046
|
-
let held = false;
|
|
1047
|
-
try {
|
|
1048
|
-
const analysis = await ctx.analyzeIngested();
|
|
1049
|
-
published = analysis.ok;
|
|
1050
|
-
held = analysis.abortedOriginals !== void 0;
|
|
1051
|
-
} finally {
|
|
1052
|
-
if (!published && !held) ctx.mutateFileCache((cache) => {
|
|
1053
|
-
for (const key of folded) cache.delete(key);
|
|
1054
|
-
});
|
|
825
|
+
function invalidateFileModules(ctx, relPaths) {
|
|
826
|
+
const server = ctx.devServer;
|
|
827
|
+
if (!server || relPaths.length === 0) return 0;
|
|
828
|
+
const graphs = server.environments ? Object.values(server.environments).map((env) => env?.moduleGraph).filter(Boolean) : [server.moduleGraph].filter(Boolean);
|
|
829
|
+
let total = 0;
|
|
830
|
+
for (const relPath of relPaths) {
|
|
831
|
+
const absPath = (0, path.resolve)(ctx.rootDir, relPath);
|
|
832
|
+
let fileCount = 0;
|
|
833
|
+
for (const graph of graphs) {
|
|
834
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
835
|
+
const byFile = graph.getModulesByFile?.(absPath);
|
|
836
|
+
if (byFile) for (const mod of byFile) nodes.add(mod);
|
|
837
|
+
const byId = graph.getModuleById?.(absPath);
|
|
838
|
+
if (byId) nodes.add(byId);
|
|
839
|
+
for (const mod of nodes) {
|
|
840
|
+
graph.invalidateModule(mod);
|
|
841
|
+
fileCount++;
|
|
842
|
+
}
|
|
1055
843
|
}
|
|
1056
|
-
if (
|
|
844
|
+
if (fileCount > 0) ctx.trace(`HMR invalidate: ${relPath} (plan changed, ${fileCount} nodes)`);
|
|
845
|
+
total += fileCount;
|
|
1057
846
|
}
|
|
1058
|
-
|
|
1059
|
-
return reanalyzed;
|
|
847
|
+
return total;
|
|
1060
848
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
} catch (err) {
|
|
1077
|
-
if (!(0, _animus_ui_extract_pipeline.isDeletedSource)(err) && !ctx.ingestionFailureDiagnostics.some((d) => d.file === relPath)) ctx.ingestionFailureDiagnostics.push((0, _animus_ui_extract_pipeline.unreadableSourceDiagnostic)(relPath, err));
|
|
1078
|
-
continue;
|
|
1079
|
-
}
|
|
1080
|
-
pending.push([relPath, {
|
|
1081
|
-
hash: (0, _animus_ui_extract_pipeline.contentHash)(source),
|
|
1082
|
-
source
|
|
1083
|
-
}]);
|
|
1084
|
-
folded.push(relPath);
|
|
1085
|
-
}
|
|
1086
|
-
if (pending.length > 0) ctx.mutateFileCache((cache) => {
|
|
1087
|
-
for (const [relPath, entry] of pending) cache.set(relPath, entry);
|
|
1088
|
-
});
|
|
1089
|
-
return folded;
|
|
849
|
+
//#endregion
|
|
850
|
+
//#region src/rediscovery.ts
|
|
851
|
+
const NO_DROPS = /* @__PURE__ */ new Map();
|
|
852
|
+
const dropsByManifest = /* @__PURE__ */ new WeakMap();
|
|
853
|
+
/** The current manifest's extensions dropped over their parent, by file:
|
|
854
|
+
* the engine's typed missing-input evidence (spec §12.2 row 11). */
|
|
855
|
+
function droppedExtensions(ctx) {
|
|
856
|
+
const manifest = ctx.storedManifest;
|
|
857
|
+
if (!manifest) return NO_DROPS;
|
|
858
|
+
let drops = dropsByManifest.get(manifest);
|
|
859
|
+
if (!drops) {
|
|
860
|
+
drops = (0, _animus_ui_extract_pipeline.servedFiles)(manifest).dropped;
|
|
861
|
+
dropsByManifest.set(manifest, drops);
|
|
862
|
+
}
|
|
863
|
+
return drops;
|
|
1090
864
|
}
|
|
1091
|
-
/** Per context: the drop tuple-set and the cache mutation generation at the
|
|
1092
|
-
* last walk that folded nothing. */
|
|
1093
|
-
const barrenWalkMemos = /* @__PURE__ */ new WeakMap();
|
|
1094
865
|
/** Per-context (file, parent, condition) verdicts already warned — each
|
|
1095
866
|
* condition warns once; cleared when the drops disappear. */
|
|
1096
867
|
const warnedVerdicts = /* @__PURE__ */ new WeakMap();
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
868
|
+
/** Where a parent the analysis does not hold is on disk, probed from its
|
|
869
|
+
* base under the host's extension order: one no analysed module answers
|
|
870
|
+
* (`absent`), or one the host opens but the analysis never read
|
|
871
|
+
* (`incomplete`), such as a file the watcher has not reported yet. */
|
|
872
|
+
function parentOnDisk(ctx, input) {
|
|
873
|
+
if (input.outcome !== "absent" && input.outcome !== "incomplete" || input.base === null) return { kind: "none" };
|
|
874
|
+
const probed = (0, _animus_ui_extract_pipeline.probeAbsolutePathSpecifier)((0, path.resolve)(ctx.rootDir, input.base), void 0, ctx.rootDir, ctx.extensionPolicy);
|
|
875
|
+
if (probed.kind === "absent") return { kind: "none" };
|
|
876
|
+
if (probed.kind === "case-mismatch") return {
|
|
877
|
+
kind: "inadmissible",
|
|
878
|
+
reason: `is unresolved: ${(0, _animus_ui_extract_pipeline.caseMismatchReason)(`the import '${input.specifier}'`, {
|
|
879
|
+
candidate: (0, path.relative)(ctx.rootDir, probed.candidate),
|
|
880
|
+
actual: (0, path.relative)(ctx.rootDir, probed.actual)
|
|
881
|
+
}, "spell the import as the file is spelled.")}`
|
|
882
|
+
};
|
|
883
|
+
const path$5 = probed.path;
|
|
884
|
+
const relPath = (0, path.relative)(ctx.rootDir, path$5);
|
|
885
|
+
const excludedBy = ctx.excludeMatcher.explain(path$5, relPath);
|
|
886
|
+
const reason = excludedBy ? `which is excluded by pattern '${excludedBy}'. Include that file in the extraction sources or remove this extension.` : !(0, _animus_ui_extract_pipeline.isPathWithinRoot)(ctx.rootDir, path$5) ? "outside the extraction root. Declare its package in the system includes or move it under the project root." : !ctx.extensionsSet.has((0, path.extname)(path$5)) ? `whose extension is not in the configured source extensions.` : (0, path.extname)(path$5) === ".mdx" || ctx.session?.heldSources.has(relPath) ? null : void 0;
|
|
887
|
+
if (reason === void 0) return {
|
|
888
|
+
kind: "fold",
|
|
889
|
+
path: path$5
|
|
890
|
+
};
|
|
891
|
+
if (reason === null) return { kind: "none" };
|
|
892
|
+
return {
|
|
893
|
+
kind: "inadmissible",
|
|
894
|
+
reason: `resolves to ${relPath}, ${reason}`
|
|
895
|
+
};
|
|
896
|
+
}
|
|
1103
897
|
/**
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
898
|
+
* Folds the on-disk files the manifest's dropped extensions name as missing
|
|
899
|
+
* parents into the Session, in one bounded pass, and returns the outcomes
|
|
900
|
+
* of that attempt. A parent no file answers, or one that cannot be
|
|
901
|
+
* admitted, leaves its drop standing: the transform then fails rather than
|
|
902
|
+
* serving builder code (SA1).
|
|
1106
903
|
*/
|
|
1107
|
-
function
|
|
1108
|
-
|
|
904
|
+
async function reconcileSourceCorpus(ctx) {
|
|
905
|
+
if (ctx.isProd || ctx.session === null) return [];
|
|
906
|
+
const drops = [...droppedExtensions(ctx).values()];
|
|
907
|
+
if (drops.length === 0) {
|
|
908
|
+
warnedVerdicts.get(ctx)?.clear();
|
|
909
|
+
return [];
|
|
910
|
+
}
|
|
911
|
+
const folded = /* @__PURE__ */ new Set();
|
|
912
|
+
const inadmissible = [];
|
|
913
|
+
for (const drop of drops) {
|
|
914
|
+
const onDisk = parentOnDisk(ctx, drop);
|
|
915
|
+
if (onDisk.kind === "fold") folded.add(onDisk.path);
|
|
916
|
+
else if (onDisk.kind === "inadmissible") inadmissible.push([drop, onDisk.reason]);
|
|
917
|
+
}
|
|
918
|
+
warnInadmissibleParents(ctx, drops, inadmissible);
|
|
919
|
+
if (folded.size === 0) return [];
|
|
920
|
+
ctx.log(`rediscovery: folded ${folded.size} on-disk file(s) a dropped extension names as its parent`);
|
|
921
|
+
return ctx.submitChanges({ modifiedFiles: folded });
|
|
922
|
+
}
|
|
923
|
+
/** Warns, once per condition, for parents that resolve on disk but cannot
|
|
924
|
+
* be admitted. */
|
|
925
|
+
function warnInadmissibleParents(ctx, drops, inadmissible) {
|
|
1109
926
|
let warned = warnedVerdicts.get(ctx);
|
|
1110
927
|
if (!warned) {
|
|
1111
928
|
warned = /* @__PURE__ */ new Set();
|
|
@@ -1113,31 +930,51 @@ function warnInadmissibleParents(ctx, drops) {
|
|
|
1113
930
|
}
|
|
1114
931
|
const active = new Set(drops.map((d) => `${d.file}\0${d.parent}`));
|
|
1115
932
|
for (const key of [...warned]) if (!active.has(key.slice(0, key.lastIndexOf("\0")))) warned.delete(key);
|
|
1116
|
-
for (const drop of
|
|
1117
|
-
const consumer = ctx.fileCache.get(drop.file);
|
|
1118
|
-
if (!consumer) continue;
|
|
1119
|
-
const specifier = importSpecifierFor(consumer.source, drop.parent);
|
|
1120
|
-
if (!specifier || !specifier.startsWith(".")) continue;
|
|
1121
|
-
const resolved = (0, _animus_ui_extract_pipeline.resolveAbsolutePathSpecifier)((0, path.resolve)(ctx.rootDir, (0, path.dirname)(drop.file), specifier), PARENT_PROBE_EXTENSIONS);
|
|
1122
|
-
if (!resolved) continue;
|
|
1123
|
-
const relPath = (0, path.relative)(ctx.rootDir, resolved);
|
|
1124
|
-
const excludedBy = excludeMatcher.explain(resolved, relPath);
|
|
1125
|
-
const reason = excludedBy ? `which is excluded by pattern '${excludedBy}'. Include that file in the extraction sources or remove this extension.` : !(0, _animus_ui_extract_pipeline.isPathWithinRoot)(ctx.rootDir, resolved) ? "outside the extraction root. Declare its package in the system includes or move it under the project root." : !ctx.extensionsSet.has((0, path.extname)(resolved)) ? `whose extension is not in the configured source extensions.` : null;
|
|
1126
|
-
if (reason === null) continue;
|
|
933
|
+
for (const [drop, reason] of inadmissible) {
|
|
1127
934
|
const verdictKey = `${drop.file}\0${drop.parent}\0${reason}`;
|
|
1128
935
|
if (warned.has(verdictKey)) continue;
|
|
1129
936
|
warned.add(verdictKey);
|
|
1130
|
-
ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}'
|
|
937
|
+
ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}' ${reason}`);
|
|
1131
938
|
}
|
|
1132
939
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
940
|
+
//#endregion
|
|
941
|
+
//#region src/session-outcomes.ts
|
|
942
|
+
/** The file event as a Session change set. */
|
|
943
|
+
function changesOf(type, absFile) {
|
|
944
|
+
const files = /* @__PURE__ */ new Set([absFile]);
|
|
945
|
+
return type === "delete" ? { removedFiles: files } : { modifiedFiles: files };
|
|
946
|
+
}
|
|
947
|
+
/** What the published outcomes of one submission change, merged. */
|
|
948
|
+
function mergePublished(outcomes) {
|
|
949
|
+
const merged = {
|
|
950
|
+
published: false,
|
|
951
|
+
full: false,
|
|
952
|
+
endedHold: false,
|
|
953
|
+
systemPropsChanged: false,
|
|
954
|
+
planChanged: /* @__PURE__ */ new Set(),
|
|
955
|
+
redeliver: /* @__PURE__ */ new Set()
|
|
956
|
+
};
|
|
957
|
+
for (const { outcome, systemPropsChanged } of outcomes) {
|
|
958
|
+
if (outcome.kind !== "published") continue;
|
|
959
|
+
merged.published = true;
|
|
960
|
+
merged.full ||= outcome.pass === "full";
|
|
961
|
+
merged.endedHold ||= outcome.endedHold;
|
|
962
|
+
merged.systemPropsChanged ||= systemPropsChanged;
|
|
963
|
+
for (const path of outcome.planChanged) merged.planChanged.add(path);
|
|
964
|
+
for (const path of outcome.redeliver) merged.redeliver.add(path);
|
|
965
|
+
}
|
|
966
|
+
return merged;
|
|
967
|
+
}
|
|
968
|
+
/** Acts on what every submission's publications owe the module graph: a
|
|
969
|
+
* publication that ends a hold re-delivers the originals whose bytes
|
|
970
|
+
* changed meanwhile, and a full pass (a system reload) reloads the
|
|
971
|
+
* client. */
|
|
972
|
+
function applyPublished(ctx, merged) {
|
|
973
|
+
if (merged.redeliver.size > 0) invalidateFileModules(ctx, [...merged.redeliver]);
|
|
974
|
+
if (merged.full) {
|
|
975
|
+
invalidateFileModules(ctx, [...merged.planChanged]);
|
|
976
|
+
ctx.invalidateSystemReloadModules(true);
|
|
1139
977
|
}
|
|
1140
|
-
return null;
|
|
1141
978
|
}
|
|
1142
979
|
//#endregion
|
|
1143
980
|
//#region src/transform.ts
|
|
@@ -1154,7 +991,7 @@ function applyDevBridgeImport(code) {
|
|
|
1154
991
|
function extendingFiles(ctx, analysisPaths) {
|
|
1155
992
|
const manifest = ctx.storedManifest;
|
|
1156
993
|
const files = /* @__PURE__ */ new Set();
|
|
1157
|
-
const queue = analysisPaths.flatMap((path$
|
|
994
|
+
const queue = analysisPaths.flatMap((path$3) => manifest?.files[path$3] ?? []);
|
|
1158
995
|
const seen = new Set(queue);
|
|
1159
996
|
while (queue.length > 0) {
|
|
1160
997
|
const id = queue.shift();
|
|
@@ -1168,12 +1005,11 @@ function extendingFiles(ctx, analysisPaths) {
|
|
|
1168
1005
|
}
|
|
1169
1006
|
return files;
|
|
1170
1007
|
}
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
if (id.startsWith("\0") || id.startsWith("virtual:animus/")) return null;
|
|
1008
|
+
async function transformSource(ctx, code, requestId) {
|
|
1009
|
+
if (requestId.startsWith("\0") || requestId.startsWith("virtual:animus/")) return null;
|
|
1010
|
+
const query = requestId.indexOf("?");
|
|
1011
|
+
if (query !== -1 && !/^v=[\w-]+$/.test(requestId.slice(query + 1))) return null;
|
|
1012
|
+
const id = query === -1 ? requestId : requestId.slice(0, query);
|
|
1177
1013
|
const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
|
|
1178
1014
|
const relativePath = (0, path.relative)(ctx.rootDir, id);
|
|
1179
1015
|
if (!isExternalPkg) {
|
|
@@ -1183,57 +1019,36 @@ async function transformSource(ctx, code, id) {
|
|
|
1183
1019
|
}
|
|
1184
1020
|
const abortedHash = ctx.abortedParseHashes.get(relativePath);
|
|
1185
1021
|
if (abortedHash !== void 0 && abortedHash === (0, _animus_ui_extract_pipeline.contentHash)(code)) return null;
|
|
1022
|
+
if (!ctx.storedManifest) throw new Error(`[animus-extract] no analysis is available to transform ${relativePath}: the project analysis failed; see the error above.`);
|
|
1023
|
+
let detectionFailed = false;
|
|
1186
1024
|
if (!ctx.storedManifest.files[relativePath]?.length) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
const owner = Object.entries(ctx.externalDirOwners).find(([dir]) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
|
|
1191
|
-
if (owner) ctx.externalFileOwners[relativePath] = owner[1];
|
|
1192
|
-
}
|
|
1193
|
-
const hash = (0, _animus_ui_extract_pipeline.contentHash)(code);
|
|
1194
|
-
ctx.mutateFileCache((cache) => cache.set(relativePath, {
|
|
1195
|
-
hash,
|
|
1196
|
-
source: code
|
|
1197
|
-
}));
|
|
1198
|
-
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
1199
|
-
let analysisOk = false;
|
|
1200
|
-
let held = false;
|
|
1025
|
+
const session = ctx.session;
|
|
1026
|
+
if (!ctx.isProd && session !== null && !session.heldSources.has(relativePath)) {
|
|
1027
|
+
let outcomes = [];
|
|
1201
1028
|
try {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
if (!analysisOk && !held) ctx.mutateFileCache((cache) => cache.delete(relativePath));
|
|
1029
|
+
outcomes = await ctx.submitChanges(changesOf("create", id));
|
|
1030
|
+
} catch (error) {
|
|
1031
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
1032
|
+
detectionFailed = true;
|
|
1207
1033
|
}
|
|
1208
|
-
if (
|
|
1209
|
-
await reconcileSourceCorpus(ctx);
|
|
1210
|
-
|
|
1034
|
+
if (mergePublished(outcomes).published) {
|
|
1035
|
+
const merged = mergePublished([...outcomes, ...await reconcileSourceCorpus(ctx)]);
|
|
1036
|
+
applyPublished(ctx, merged);
|
|
1037
|
+
if (!merged.full) invalidateFileModules(ctx, [...merged.planChanged].filter((path$4) => path$4 !== relativePath));
|
|
1211
1038
|
const compCount = ctx.storedManifest?.files[relativePath]?.length ?? 0;
|
|
1212
1039
|
ctx.info(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
|
|
1213
1040
|
ctx.invalidateExtractedModules();
|
|
1214
1041
|
}
|
|
1215
|
-
});
|
|
1216
|
-
if (!ctx.storedManifest.files[relativePath]?.length) {
|
|
1217
|
-
ctx.recordFallbackState(relativePath, unresolvedDropFiles(ctx).has(relativePath));
|
|
1218
|
-
return null;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
if (!ctx.isProd && ctx.rawExtensionFallbacks.size > 0) {
|
|
1222
|
-
const conflicted = rawFallbackDescendants(ctx, relativePath);
|
|
1223
|
-
if (conflicted.length > 0) {
|
|
1224
|
-
invalidateFileModules(ctx, conflicted);
|
|
1225
|
-
ctx.invalidateExtractedModules();
|
|
1226
|
-
for (const file of conflicted) ctx.rawExtensionFallbacks.delete(file);
|
|
1227
|
-
throw new Error(`ANIMUS_COMPOSITION_RECOVERING: '${relativePath}' extracted while extension consumer(s) ${conflicted.join(", ")} are still serving the runtime fallback — recovering via reload; this resolves on the next request.`);
|
|
1228
1042
|
}
|
|
1043
|
+
if (detectionFailed) throw new Error(`[animus-extract] the analysis of the new file ${relativePath} failed; see the error above.`);
|
|
1229
1044
|
}
|
|
1045
|
+
const drop = droppedExtensions(ctx).get(relativePath);
|
|
1046
|
+
if (drop !== void 0) throw new Error((0, _animus_ui_extract_pipeline.droppedExtensionMessage)(drop));
|
|
1047
|
+
if (!ctx.storedManifest.files[relativePath]?.length) return null;
|
|
1230
1048
|
try {
|
|
1231
1049
|
const { transformFile } = ctx.engineApi();
|
|
1232
1050
|
const result = transformFile(code, relativePath, ctx.storedManifestJson);
|
|
1233
|
-
if (!result.hasComponents) {
|
|
1234
|
-
ctx.recordFallbackState(relativePath, true);
|
|
1235
|
-
return null;
|
|
1236
|
-
}
|
|
1051
|
+
if (!result.hasComponents) throw new Error(`the analysis extracted components from ${relativePath}, but its transform found none`);
|
|
1237
1052
|
if (ctx.tracing) {
|
|
1238
1053
|
const compCount = ctx.storedManifest.files[relativePath]?.length ?? 0;
|
|
1239
1054
|
ctx.trace(`transform ${relativePath}: ${compCount} components`);
|
|
@@ -1243,35 +1058,30 @@ async function transformSource(ctx, code, id) {
|
|
|
1243
1058
|
outputCode = applyDevBridgeImport(result.code);
|
|
1244
1059
|
ctx.recordTransformOutput(relativePath, outputCode);
|
|
1245
1060
|
}
|
|
1246
|
-
ctx.recordFallbackState(relativePath, false);
|
|
1247
1061
|
return {
|
|
1248
1062
|
code: outputCode,
|
|
1249
1063
|
map: null
|
|
1250
1064
|
};
|
|
1251
1065
|
} catch (e) {
|
|
1252
|
-
|
|
1253
|
-
else console.warn(`[animus-extract] Failed to transform ${id}:`, e);
|
|
1254
|
-
ctx.recordFallbackState(relativePath, true);
|
|
1255
|
-
return null;
|
|
1066
|
+
throw new Error(`[animus-extract] Failed to transform ${id}: ${e}`, { cause: e });
|
|
1256
1067
|
}
|
|
1257
1068
|
}
|
|
1258
1069
|
//#endregion
|
|
1259
1070
|
//#region src/hmr.ts
|
|
1260
1071
|
/** Vite dispatches this hook once per environment for one file event: the
|
|
1261
|
-
*
|
|
1262
|
-
|
|
1072
|
+
* dispatch that claims it submits the file to the Session, and invalidation
|
|
1073
|
+
* runs in each. */
|
|
1074
|
+
async function handleHotUpdate(ctx, environment, { type, file, timestamp, modules, read }) {
|
|
1263
1075
|
if (ctx.isProd) return;
|
|
1264
|
-
return runExclusiveAnalysis(ctx, () => handleHotUpdateExclusive(ctx, environment, options));
|
|
1265
|
-
}
|
|
1266
|
-
async function handleHotUpdateExclusive(ctx, environment, { type, file, timestamp, modules, read }) {
|
|
1267
1076
|
const ownsEvent = ctx.hotUpdateEvents.claim(environment.name, file, timestamp);
|
|
1268
1077
|
const absFile = (0, path.resolve)(file);
|
|
1269
1078
|
ctx.trace(`hotUpdate ${type} ${(0, path.relative)(ctx.rootDir, absFile)} env=${environment.name} owns=${ownsEvent}`);
|
|
1270
1079
|
if (ctx.isSystemDependency(absFile)) {
|
|
1271
|
-
if (ownsEvent)
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1080
|
+
if (ownsEvent) requestSystemReload(ctx, absFile, type);
|
|
1081
|
+
return [];
|
|
1082
|
+
}
|
|
1083
|
+
if (ctx.assetCopyDir !== null && ctx.isAssetSource(absFile)) {
|
|
1084
|
+
if (ownsEvent && type !== "delete") ctx.refreshAssetCopies(absFile);
|
|
1275
1085
|
return [];
|
|
1276
1086
|
}
|
|
1277
1087
|
if (type === "delete") {
|
|
@@ -1295,31 +1105,74 @@ async function handleHotUpdateExclusive(ctx, environment, { type, file, timestam
|
|
|
1295
1105
|
}
|
|
1296
1106
|
return invalidateStaleModules(ctx, environment, modules, result);
|
|
1297
1107
|
}
|
|
1298
|
-
/**
|
|
1299
|
-
*
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
*
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1108
|
+
/** Each context's pending system-dependency events, and the window they
|
|
1109
|
+
* wait out together. */
|
|
1110
|
+
const systemReloads = /* @__PURE__ */ new WeakMap();
|
|
1111
|
+
/** A system dependency's event waits out the coalescing window (SA19)
|
|
1112
|
+
* before the Session reads it: a watcher can report a write as it starts
|
|
1113
|
+
* and drop the event for its end, so an immediate read would load a
|
|
1114
|
+
* half-written module. Events in one window reload once. */
|
|
1115
|
+
function requestSystemReload(ctx, absFile, type) {
|
|
1116
|
+
let state = systemReloads.get(ctx);
|
|
1117
|
+
if (state === void 0) {
|
|
1118
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1119
|
+
state = {
|
|
1120
|
+
pending,
|
|
1121
|
+
window: new _animus_ui_extract_session.CoalescingWindow(() => {
|
|
1122
|
+
if (pending.size === 0) return;
|
|
1123
|
+
const taken = [...pending];
|
|
1124
|
+
pending.clear();
|
|
1125
|
+
return reloadSystem(ctx, taken);
|
|
1126
|
+
})
|
|
1127
|
+
};
|
|
1128
|
+
systemReloads.set(ctx, state);
|
|
1309
1129
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1130
|
+
ctx.log(`HMR system reload scheduled: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
1131
|
+
state.pending.set(absFile, type);
|
|
1132
|
+
state.window.note();
|
|
1133
|
+
}
|
|
1134
|
+
/** Drops a pending system reload: the context is closing. */
|
|
1135
|
+
function cancelSystemReload(ctx) {
|
|
1136
|
+
const state = systemReloads.get(ctx);
|
|
1137
|
+
state?.window.cancel();
|
|
1138
|
+
state?.pending.clear();
|
|
1139
|
+
}
|
|
1140
|
+
/** Reloads the system through the Session over the window's events. It
|
|
1141
|
+
* runs from a timer, so it never throws: a failed reload keeps the last
|
|
1142
|
+
* good system served and warns; under `strict` the client overlay reports
|
|
1143
|
+
* it too. */
|
|
1144
|
+
async function reloadSystem(ctx, events) {
|
|
1145
|
+
const reloadStart = performance.now();
|
|
1146
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
1147
|
+
const removedFiles = /* @__PURE__ */ new Set();
|
|
1148
|
+
for (const [path$1, type] of events) (type === "delete" ? removedFiles : modifiedFiles).add(path$1);
|
|
1149
|
+
let outcomes;
|
|
1313
1150
|
try {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1151
|
+
outcomes = await ctx.submitChanges({
|
|
1152
|
+
modifiedFiles,
|
|
1153
|
+
removedFiles
|
|
1154
|
+
});
|
|
1155
|
+
} catch (err) {
|
|
1156
|
+
ctx.warn(`[animus-extract] system reload failed: ${String(err)}`);
|
|
1157
|
+
if (ctx.options.strict) {
|
|
1158
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1159
|
+
const stack = err instanceof Error ? err.stack ?? "" : "";
|
|
1160
|
+
ctx.devServer?.hot?.send({
|
|
1161
|
+
type: "error",
|
|
1162
|
+
err: {
|
|
1163
|
+
message,
|
|
1164
|
+
stack,
|
|
1165
|
+
plugin: "animus-extract"
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
ctx.invalidateSystemReloadModules(true);
|
|
1317
1170
|
return;
|
|
1318
1171
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
})
|
|
1172
|
+
const merged = mergePublished(outcomes);
|
|
1173
|
+
applyPublished(ctx, merged);
|
|
1174
|
+
if (!merged.published) ctx.invalidateSystemReloadModules(false);
|
|
1175
|
+
else ctx.log(`HMR system reload complete: ${Math.round(performance.now() - reloadStart)}ms`);
|
|
1323
1176
|
}
|
|
1324
1177
|
/** The once-per-event analysis half; its result is what the remaining
|
|
1325
1178
|
* environments act on. */
|
|
@@ -1327,76 +1180,47 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
|
|
|
1327
1180
|
const ext = (0, path.extname)(file);
|
|
1328
1181
|
if (!ctx.extensionsSet.has(ext)) return { kind: "ignored" };
|
|
1329
1182
|
const excludeMatcher = ctx.excludeMatcher;
|
|
1330
|
-
|
|
1331
|
-
if (!isExternalPkg && excludeMatcher.matches(file, (0, path.relative)(ctx.rootDir, file))) return { kind: "ignored" };
|
|
1183
|
+
if (!ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, file)) && excludeMatcher.matches(file, (0, path.relative)(ctx.rootDir, file))) return { kind: "ignored" };
|
|
1332
1184
|
const relPath = (0, path.relative)(ctx.rootDir, absFile);
|
|
1333
|
-
|
|
1185
|
+
const outputDir = (0, _animus_ui_extract_pipeline.outputSourceGuard)(ctx.rootDir, ctx.outputExcludes)(absFile);
|
|
1186
|
+
if (outputDir !== null) {
|
|
1187
|
+
ctx.warn((0, _animus_ui_extract_pipeline.outputSourceMessage)(relPath, outputDir));
|
|
1188
|
+
return { kind: "ignored" };
|
|
1189
|
+
}
|
|
1190
|
+
let settled;
|
|
1334
1191
|
try {
|
|
1335
|
-
|
|
1192
|
+
settled = read ? await read() : void 0;
|
|
1336
1193
|
} catch {
|
|
1337
1194
|
return { kind: "ignored" };
|
|
1338
1195
|
}
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1196
|
+
const hmrStart = performance.now();
|
|
1197
|
+
const changes = changesOf("update", absFile);
|
|
1198
|
+
const outcomes = await ctx.submitChanges(settled === void 0 ? changes : {
|
|
1199
|
+
...changes,
|
|
1200
|
+
sources: /* @__PURE__ */ new Map([[absFile, settled]])
|
|
1201
|
+
});
|
|
1202
|
+
const last = outcomes.at(-1)?.outcome;
|
|
1203
|
+
if (last === void 0) {
|
|
1342
1204
|
ctx.trace(`HMR skip: ${relPath} (unchanged)`);
|
|
1343
1205
|
return { kind: "unchanged" };
|
|
1344
1206
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
source
|
|
1353
|
-
}));
|
|
1354
|
-
const restoreEntry = () => {
|
|
1355
|
-
ctx.mutateFileCache((cache) => {
|
|
1356
|
-
if (cached) cache.set(relPath, cached);
|
|
1357
|
-
else cache.delete(relPath);
|
|
1358
|
-
});
|
|
1359
|
-
if (priorExternalOwner) ctx.externalFileOwners[relPath] = priorExternalOwner;
|
|
1360
|
-
else delete ctx.externalFileOwners[relPath];
|
|
1361
|
-
};
|
|
1362
|
-
const hmrStart = performance.now();
|
|
1363
|
-
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
1364
|
-
const analysisStart = performance.now();
|
|
1365
|
-
const systemPropsBefore = systemPropsModuleSource(ctx);
|
|
1366
|
-
let analysis;
|
|
1367
|
-
try {
|
|
1368
|
-
analysis = await ctx.analyzeIngested();
|
|
1369
|
-
} catch (e) {
|
|
1370
|
-
restoreEntry();
|
|
1371
|
-
throw e;
|
|
1372
|
-
}
|
|
1373
|
-
if (!analysis.ok) {
|
|
1374
|
-
if (analysis.abortedOriginals) return analysis.abortedOriginals.has(relPath) ? { kind: "ignored" } : { kind: "unchanged" };
|
|
1375
|
-
restoreEntry();
|
|
1376
|
-
return { kind: "ignored" };
|
|
1377
|
-
}
|
|
1378
|
-
try {
|
|
1379
|
-
await reconcileSourceCorpus(ctx);
|
|
1380
|
-
} catch (e) {
|
|
1381
|
-
restoreEntry();
|
|
1382
|
-
throw e;
|
|
1383
|
-
}
|
|
1384
|
-
const systemPropsChanged = systemPropsModuleSource(ctx) !== systemPropsBefore;
|
|
1385
|
-
const analysisMs = Math.round(performance.now() - analysisStart);
|
|
1386
|
-
const nativeEntry = ctx.corpus.published.analysisEntries.get(relPath);
|
|
1387
|
-
const presentationOnly = nativeEntry && !analysis.endedHold ? isPresentationOnlyEdit(ctx, relPath, nativeEntry.source) : false;
|
|
1388
|
-
const redelivered = presentationOnly ? [] : [relPath, ...analysis.redeliver];
|
|
1207
|
+
if (last.kind === "held") return last.abortedOriginals.has(relPath) ? { kind: "ignored" } : { kind: "unchanged" };
|
|
1208
|
+
const merged = mergePublished([...outcomes, ...await reconcileSourceCorpus(ctx)]);
|
|
1209
|
+
applyPublished(ctx, merged);
|
|
1210
|
+
const published = ctx.session.corpus.published;
|
|
1211
|
+
const nativeEntry = published.analysisEntries.get(relPath);
|
|
1212
|
+
const presentationOnly = nativeEntry && !merged.endedHold ? isPresentationOnlyEdit(ctx, relPath, nativeEntry.source) : false;
|
|
1213
|
+
const redelivered = presentationOnly ? [] : [relPath, ...merged.redeliver];
|
|
1389
1214
|
const staleDefinitionFiles = [.../* @__PURE__ */ new Set([
|
|
1390
|
-
...
|
|
1391
|
-
...
|
|
1392
|
-
...extendingFiles(ctx, redelivered.flatMap((path$
|
|
1215
|
+
...merged.planChanged,
|
|
1216
|
+
...merged.redeliver,
|
|
1217
|
+
...extendingFiles(ctx, redelivered.flatMap((path$2) => published.ownership[path$2]?.analysisPaths ?? [path$2]))
|
|
1393
1218
|
])].filter((defFile) => (0, path.resolve)(ctx.rootDir, defFile) !== absFile);
|
|
1394
|
-
|
|
1395
|
-
ctx.log(`HMR update: ${relPath} — analysis ${analysisMs}ms, total ${hmrMs}ms${presentationOnly ? " (presentation-only)" : ""}`);
|
|
1219
|
+
ctx.log(`HMR update: ${relPath} — total ${Math.round(performance.now() - hmrStart)}ms${presentationOnly ? " (presentation-only)" : ""}`);
|
|
1396
1220
|
return {
|
|
1397
1221
|
kind: "analyzed",
|
|
1398
1222
|
staleDefinitionFiles,
|
|
1399
|
-
systemPropsChanged,
|
|
1223
|
+
systemPropsChanged: merged.systemPropsChanged,
|
|
1400
1224
|
presentationOnly
|
|
1401
1225
|
};
|
|
1402
1226
|
}
|
|
@@ -1415,17 +1239,14 @@ function isPresentationOnlyEdit(ctx, scannerRelPath, source) {
|
|
|
1415
1239
|
return false;
|
|
1416
1240
|
}
|
|
1417
1241
|
}
|
|
1418
|
-
/**
|
|
1419
|
-
*
|
|
1242
|
+
/** A deleted file leaves the Session's corpus, so its CSS stops being
|
|
1243
|
+
* re-emitted. */
|
|
1420
1244
|
async function pruneDeletedFile(ctx, absFile) {
|
|
1421
|
-
const
|
|
1422
|
-
if (!
|
|
1423
|
-
|
|
1424
|
-
const prevPlans = (0, _animus_ui_extract_pipeline.snapshotFilePlans)(ctx.storedManifest);
|
|
1425
|
-
const { ok } = await ctx.analyzeIngested();
|
|
1426
|
-
if (!ok) return;
|
|
1245
|
+
const merged = mergePublished(await ctx.submitChanges(changesOf("delete", absFile)));
|
|
1246
|
+
if (!merged.published) return;
|
|
1247
|
+
applyPublished(ctx, merged);
|
|
1427
1248
|
ctx.trace(`Deleted file pruned: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
1428
|
-
|
|
1249
|
+
if (!merged.full) invalidateFileModules(ctx, [...merged.planChanged]);
|
|
1429
1250
|
ctx.invalidateExtractedModules();
|
|
1430
1251
|
}
|
|
1431
1252
|
/** The per-environment invalidation half. Static CSS is left alone: it only
|
|
@@ -1534,7 +1355,7 @@ function loadVirtualModule(ctx, id) {
|
|
|
1534
1355
|
return css;
|
|
1535
1356
|
};
|
|
1536
1357
|
if (id === RESOLVED_CSS_ID) {
|
|
1537
|
-
if (!ctx.isProd && ctx.
|
|
1358
|
+
if (!ctx.isProd && ctx.storedManifest) {
|
|
1538
1359
|
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
1539
1360
|
layers: ctx.options.layers,
|
|
1540
1361
|
variableCss: ctx.variableCss,
|
|
@@ -1604,24 +1425,26 @@ if (import.meta.hot) {
|
|
|
1604
1425
|
}
|
|
1605
1426
|
`;
|
|
1606
1427
|
}
|
|
1607
|
-
if (id === RESOLVED_SYSTEM_PROPS_ID) return
|
|
1428
|
+
if (id === RESOLVED_SYSTEM_PROPS_ID) return ctx.systemPropsModule;
|
|
1608
1429
|
return null;
|
|
1609
1430
|
}
|
|
1610
1431
|
//#endregion
|
|
1611
1432
|
//#region src/index.ts
|
|
1612
|
-
function sourceKitConfig(rootDir,
|
|
1613
|
-
const kits =
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1433
|
+
async function sourceKitConfig(rootDir, systemPath, engine) {
|
|
1434
|
+
const kits = /* @__PURE__ */ new Map();
|
|
1435
|
+
for (const kit of [...(0, _animus_ui_extract_pipeline.sourceKitDependencies)(rootDir, engine), ...await (0, _animus_ui_extract_pipeline.systemKitDependencies)(rootDir, systemPath, engine)]) if (kit.installed && !kits.has(kit.name)) kits.set(kit.name, kit);
|
|
1436
|
+
if (kits.size === 0) return void 0;
|
|
1437
|
+
const names = [...kits.keys()];
|
|
1438
|
+
return {
|
|
1439
|
+
optimizeDeps: {
|
|
1440
|
+
exclude: names,
|
|
1441
|
+
include: [...kits.values()].flatMap((kit) => kit.dependencies.map((dependency) => `${kit.name} > ${dependency}`))
|
|
1442
|
+
},
|
|
1443
|
+
ssr: { noExternal: names }
|
|
1444
|
+
};
|
|
1622
1445
|
}
|
|
1623
1446
|
function animusExtract(options) {
|
|
1624
|
-
(0, _animus_ui_extract_pipeline.
|
|
1447
|
+
(0, _animus_ui_extract_pipeline.assertSupportedEngine)(options.engine);
|
|
1625
1448
|
(0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options, ["verify", "appearanceBootstrap"], [{
|
|
1626
1449
|
key: "root",
|
|
1627
1450
|
reason: "the Vite driver derives its root from the resolved Vite config"
|
|
@@ -1633,10 +1456,11 @@ function animusExtract(options) {
|
|
|
1633
1456
|
return {
|
|
1634
1457
|
name: "animus-extract",
|
|
1635
1458
|
enforce: "pre",
|
|
1636
|
-
config(userConfig, env) {
|
|
1459
|
+
async config(userConfig, env) {
|
|
1637
1460
|
const { mode } = (0, _animus_ui_extract_pipeline.resolveMode)(options.mode, () => env.command === "build" ? "production" : "development");
|
|
1638
1461
|
const define = { __ANIMUS_DEV__: mode === "development" };
|
|
1639
|
-
const
|
|
1462
|
+
const root = (0, path.resolve)(userConfig.root ?? process.cwd());
|
|
1463
|
+
const kits = await sourceKitConfig(root, (0, path.resolve)(root, options.system), ctx.kitEngine());
|
|
1640
1464
|
return kits ? {
|
|
1641
1465
|
define,
|
|
1642
1466
|
...kits
|
|
@@ -1649,9 +1473,15 @@ function animusExtract(options) {
|
|
|
1649
1473
|
configResolved(config) {
|
|
1650
1474
|
applyResolvedConfig(ctx, config);
|
|
1651
1475
|
},
|
|
1476
|
+
buildEnd() {
|
|
1477
|
+
if (!ctx.endBuild(this.environment ?? ctx.singleEnvironment)) return;
|
|
1478
|
+
cancelSystemReload(ctx);
|
|
1479
|
+
ctx.close();
|
|
1480
|
+
},
|
|
1652
1481
|
async buildStart() {
|
|
1653
|
-
await
|
|
1654
|
-
|
|
1482
|
+
await ctx.openBuild(this.environment ?? ctx.singleEnvironment);
|
|
1483
|
+
await runBuildStart(ctx, async (specifier, importer) => {
|
|
1484
|
+
return (await this.resolve(specifier, importer))?.id ?? null;
|
|
1655
1485
|
}, ctx.isProd ? (fileName, source) => this.emitFile({
|
|
1656
1486
|
type: "asset",
|
|
1657
1487
|
name: fileName,
|