@animus-ui/vite-plugin 0.1.25 → 0.1.27
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 +126 -149
- 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 +718 -913
- 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$9) => (0, vite.isFileLoadingAllowed)(config, path$9);
|
|
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. */
|
|
@@ -434,146 +326,273 @@ var PluginContext = class {
|
|
|
434
326
|
emissionProd = false;
|
|
435
327
|
rootDir = "";
|
|
436
328
|
logger = null;
|
|
437
|
-
system = emptySystemConfig();
|
|
438
|
-
/** Whether a system has loaded, so a failed reload has one to keep. */
|
|
439
|
-
systemLoaded = false;
|
|
440
329
|
lcssTargets = {};
|
|
441
330
|
pathAliasesJson = null;
|
|
442
331
|
/** Vite's export conditions, so the loader reads the files Vite serves. */
|
|
443
332
|
resolveConditions = [];
|
|
444
|
-
/**
|
|
445
|
-
|
|
333
|
+
/** Vite's resolved extension order; the built-in policy until
|
|
334
|
+
* `configResolved`. */
|
|
335
|
+
extensionPolicy = _animus_ui_extract_pipeline.BUILT_IN_EXTENSION_POLICY;
|
|
336
|
+
/** Vite's asset classification, captured at `configResolved`. */
|
|
337
|
+
assetClassification = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("noHost");
|
|
338
|
+
/** How Vite's build expands computed loads, captured at
|
|
339
|
+
* `configResolved`; none where it cannot be reproduced exactly. */
|
|
340
|
+
loadExpander;
|
|
446
341
|
extensionsSet;
|
|
342
|
+
/** Built once per buildStart, before any discovery, from the user's
|
|
343
|
+
* exclude list and the output directory. */
|
|
447
344
|
excludeMatcher;
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
345
|
+
/** Where Vite writes, every environment's output directories and files,
|
|
346
|
+
* as structural exclusions; none outside the root. Set in
|
|
347
|
+
* `configResolved`. */
|
|
348
|
+
outputExcludes = [];
|
|
349
|
+
/** The Session publication this context serves, adopted whole as the
|
|
350
|
+
* Session commits it; null until one commits, and a failed analysis
|
|
351
|
+
* leaves the previous one served. Every view below reads it. */
|
|
352
|
+
served = null;
|
|
353
|
+
/** What a context serves before a first publication. */
|
|
354
|
+
unpublishedSystem = emptySystemConfig();
|
|
355
|
+
get storedManifest() {
|
|
356
|
+
return this.served?.manifest ?? null;
|
|
357
|
+
}
|
|
358
|
+
get storedManifestJson() {
|
|
359
|
+
return this.served?.manifestJson ?? "";
|
|
360
|
+
}
|
|
361
|
+
get system() {
|
|
362
|
+
return this.served?.system ?? this.unpublishedSystem;
|
|
363
|
+
}
|
|
364
|
+
get reverseProvenance() {
|
|
365
|
+
return this.served?.manifest.reverse_provenance ?? {};
|
|
366
|
+
}
|
|
367
|
+
/** The exact source `virtual:animus/system-props` serves: the served
|
|
368
|
+
* publication's, so the served bytes and the HMR change decision cannot
|
|
369
|
+
* diverge. */
|
|
370
|
+
get systemPropsModule() {
|
|
371
|
+
return this.served?.systemPropsModule ?? EMPTY_SYSTEM_PROPS_MODULE;
|
|
372
|
+
}
|
|
373
|
+
get externalPackageDirs() {
|
|
374
|
+
return this.served?.externalPackageDirs ?? [];
|
|
375
|
+
}
|
|
376
|
+
/** specifier → whether its redirected source entry has side effects, from
|
|
377
|
+
* the owning package's `sideEffects`; absent leaves Vite's default. */
|
|
378
|
+
get externalSourceSideEffects() {
|
|
379
|
+
return this.served?.externalSourceSideEffects ?? /* @__PURE__ */ new Map();
|
|
380
|
+
}
|
|
381
|
+
get externalSourceEntries() {
|
|
382
|
+
return this.served?.externalSourceEntries ?? /* @__PURE__ */ new Map();
|
|
383
|
+
}
|
|
384
|
+
/** The analysis sheets keep their asset() placeholders; these views
|
|
385
|
+
* substitute the current asset URLs, so an edited asset changes its URL
|
|
386
|
+
* without re-analysis. */
|
|
387
|
+
get globalCss() {
|
|
388
|
+
return this.substitutedSheets().globalCss;
|
|
389
|
+
}
|
|
390
|
+
get resolvedComponentCss() {
|
|
391
|
+
return this.substitutedSheets().componentCss;
|
|
392
|
+
}
|
|
393
|
+
analysisSheets() {
|
|
394
|
+
return {
|
|
395
|
+
...NO_SHEETS,
|
|
396
|
+
globalCss: this.served?.analysisGlobalCss ?? "",
|
|
397
|
+
componentCss: this.served?.analysisComponentCss ?? ""
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
substitutedSheets() {
|
|
401
|
+
return (0, _animus_ui_extract_pipeline.substituteSheetAssets)(this.analysisSheets(), this.assetUrlBySpecifier);
|
|
402
|
+
}
|
|
453
403
|
layerDeclaration = "";
|
|
454
404
|
transformOutputHashes = /* @__PURE__ */ new Map();
|
|
455
405
|
recordTransformOutput(relativePath, code) {
|
|
456
406
|
this.transformOutputHashes.set(relativePath, (0, _animus_ui_extract_pipeline.contentHash)(code));
|
|
457
407
|
}
|
|
458
|
-
reverseProvenance = {};
|
|
459
|
-
storedSystemPropMapJson = "{}";
|
|
460
|
-
storedDynamicPropsJson = "{}";
|
|
461
|
-
storedAdmittedTransformsJson = "{}";
|
|
462
|
-
storedTypedSystemPropsJson = "[]";
|
|
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();
|
|
485
408
|
/** Original path → hash of bytes whose parse aborted in a rejected attempt;
|
|
486
409
|
* non-empty while that rejection holds publication. Those bytes are served
|
|
487
410
|
* raw so the host reports its own syntax error instead of receiving the
|
|
488
411
|
* previous module from the retained engine. */
|
|
489
412
|
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
413
|
hotUpdateEvents = new HotUpdateEvents();
|
|
505
|
-
|
|
506
|
-
|
|
414
|
+
/** Coalesces a burst of invalidations into one full reload. */
|
|
415
|
+
reloadWindow = new _animus_ui_extract_session.CoalescingWindow(() => {
|
|
416
|
+
this.devServer?.hot?.send({ type: "full-reload" });
|
|
417
|
+
});
|
|
507
418
|
base = "/";
|
|
508
419
|
assetUrlBySpecifier = /* @__PURE__ */ new Map();
|
|
420
|
+
/** Where dev asset() copies live, immutable and content-named; null in a
|
|
421
|
+
* build, or when the dev server does not serve the directory, which
|
|
422
|
+
* serves the live files instead. Set in `configResolved`. */
|
|
423
|
+
assetCopyDir = null;
|
|
424
|
+
/** specifier → the source file its dev copy was made from. */
|
|
425
|
+
assetSourceBySpecifier = /* @__PURE__ */ new Map();
|
|
426
|
+
/** source → its copies, oldest first: the current and the previous. */
|
|
427
|
+
assetCopyHistory = /* @__PURE__ */ new Map();
|
|
428
|
+
/** Specifiers whose copy the dev server does not serve, warned once. */
|
|
429
|
+
unservedAssetCopies = /* @__PURE__ */ new Set();
|
|
430
|
+
/** Whether the dev server serves `path`, by Vite's `server.fs` rule;
|
|
431
|
+
* set in `configResolved`. */
|
|
432
|
+
servesFile = () => true;
|
|
509
433
|
assetResolutionFailures = /* @__PURE__ */ new Set();
|
|
510
434
|
assetPassComplete = false;
|
|
511
|
-
|
|
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
|
-
/** specifier → whether its redirected source entry has side effects, from
|
|
518
|
-
* the owning package's `sideEffects`; absent leaves Vite's default. */
|
|
519
|
-
externalSourceSideEffects = /* @__PURE__ */ new Map();
|
|
520
|
-
externalFileOwners = {};
|
|
521
|
-
externalSourceEntries = /* @__PURE__ */ new Map();
|
|
435
|
+
/** The Session's last external package outcomes, for the self-verify. */
|
|
522
436
|
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
437
|
devServer;
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
v2Engine = null;
|
|
534
|
-
v2SentSources = null;
|
|
535
|
-
v2DriftWarned = false;
|
|
438
|
+
/** The paths already registered, per watcher: a reused plugin instance
|
|
439
|
+
* whose dev server is replaced registers everything with the new one. */
|
|
440
|
+
watchedPaths = /* @__PURE__ */ new WeakMap();
|
|
536
441
|
/** Every native extraction call goes through this one accessor. */
|
|
537
442
|
engineApi;
|
|
443
|
+
/** The native module's stateless lookups, before the root resolves: the
|
|
444
|
+
* shared factory, which registers no handle. */
|
|
445
|
+
kitEngine = _animus_ui_extract_session.engineFactory;
|
|
446
|
+
engineKey = null;
|
|
447
|
+
/** This context's build: one Vite config instance, one build. */
|
|
448
|
+
build = (0, _animus_ui_extract_session.createBuildId)();
|
|
449
|
+
/** The build's Session, created once the Vite root resolves. */
|
|
450
|
+
session = null;
|
|
451
|
+
/** The outcomes each in-flight `submitChanges` collects. */
|
|
452
|
+
outcomeCollectors = /* @__PURE__ */ new Set();
|
|
538
453
|
constructor(options, engineApiOverride) {
|
|
539
454
|
this.options = options;
|
|
540
|
-
this.staticCssJson = (0, _animus_ui_extract_pipeline.serializeStaticCss)(options.staticCss);
|
|
541
455
|
const verbosity = (0, _animus_ui_extract_pipeline.resolveVerbosity)(options.verbose, process.env.ANIMUS_DEBUG);
|
|
542
456
|
this.verbose = verbosity !== "quiet";
|
|
543
457
|
this.tracing = verbosity === "trace";
|
|
544
458
|
this.extensionsSet = new Set(options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
545
|
-
this.excludeMatcher = (0, _animus_ui_extract_pipeline.createExcludeMatcher)(options.exclude);
|
|
546
459
|
if (engineApiOverride) {
|
|
547
460
|
this.engineApi = engineApiOverride;
|
|
461
|
+
this.kitEngine = engineApiOverride;
|
|
548
462
|
return;
|
|
549
463
|
}
|
|
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
|
-
|
|
464
|
+
const build = this.build;
|
|
465
|
+
const rehydrateFilesJson = (filesJsonRaw) => {
|
|
466
|
+
if (!filesJsonRaw.includes("\"source\":\"\"")) return filesJsonRaw;
|
|
467
|
+
const entries = (0, _animus_ui_extract_pipeline.parseFilesJson)(filesJsonRaw, "animus-extract");
|
|
468
|
+
const analysisEntries = this.session?.corpus.published.analysisEntries ?? /* @__PURE__ */ new Map();
|
|
469
|
+
for (const entry of entries) if (entry.source === "") entry.source = analysisEntries.get(entry.path)?.source ?? "";
|
|
470
|
+
return JSON.stringify(entries);
|
|
471
|
+
};
|
|
472
|
+
this.engineApi = () => {
|
|
473
|
+
if (!this.rootDir) throw new Error("[animus-extract] the engine was used before the Vite root resolved");
|
|
474
|
+
this.engineKey ??= (0, _animus_ui_extract_session.sessionKey)(build, this.rootDir);
|
|
475
|
+
return (0, _animus_ui_extract_session.engineHandle)(this.engineKey, {
|
|
476
|
+
label: "animus-extract",
|
|
477
|
+
rehydrateFilesJson
|
|
478
|
+
})();
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
/** Creates the build's Session from the resolved config: Vite's engine,
|
|
482
|
+
* logger and core options, kept in memory. */
|
|
483
|
+
createSession() {
|
|
484
|
+
this.session?.close();
|
|
485
|
+
const options = this.options;
|
|
486
|
+
const session = new _animus_ui_extract_session.ExtractionSession({
|
|
487
|
+
system: options.system,
|
|
488
|
+
exclude: options.exclude,
|
|
489
|
+
extensions: options.extensions,
|
|
490
|
+
strict: options.strict,
|
|
491
|
+
diagnostics: options.diagnostics,
|
|
492
|
+
verbose: options.verbose,
|
|
493
|
+
prefix: options.prefix,
|
|
494
|
+
prefixContextualVars: options.prefixContextualVars,
|
|
495
|
+
targets: options.targets,
|
|
496
|
+
minify: options.minify,
|
|
497
|
+
staticCss: options.staticCss,
|
|
498
|
+
layers: options.layers,
|
|
499
|
+
engine: options.engine,
|
|
500
|
+
runtimeImport: options.runtimeImport,
|
|
501
|
+
mode: this.emissionProd ? "production" : "development"
|
|
502
|
+
}, {
|
|
503
|
+
build: this.build,
|
|
504
|
+
unbundledComputedImports: true,
|
|
505
|
+
engine: () => this.engineApi(),
|
|
506
|
+
logger: {
|
|
507
|
+
info: (line) => (this.logger ?? console).info(line),
|
|
508
|
+
warn: (line) => (this.logger ?? console).warn(line),
|
|
509
|
+
error: (line) => (this.logger ?? console).error(line)
|
|
575
510
|
}
|
|
576
511
|
});
|
|
512
|
+
session.persistArtifacts = false;
|
|
513
|
+
session.hostSubstitutesAssets = true;
|
|
514
|
+
session.driverLabel = "animus-extract";
|
|
515
|
+
session.onOutcome = (outcome) => this.observeOutcome(session, outcome);
|
|
516
|
+
session.development = this.development;
|
|
517
|
+
session.rootDir = this.rootDir;
|
|
518
|
+
session.conditions = this.resolveConditions;
|
|
519
|
+
session.extensionPolicy = this.extensionPolicy;
|
|
520
|
+
session.assetClassification = this.assetClassification;
|
|
521
|
+
session.loadExpander = this.loadExpander;
|
|
522
|
+
session.pathAliasesJson = this.pathAliasesJson;
|
|
523
|
+
session.delivery = {
|
|
524
|
+
cssModuleId: VIRTUAL_CSS_ID,
|
|
525
|
+
systemPropsModuleId: VIRTUAL_SYSTEM_PROPS_ID
|
|
526
|
+
};
|
|
527
|
+
session.structuralExclude = this.outputExcludes;
|
|
528
|
+
this.session = session;
|
|
529
|
+
return session;
|
|
530
|
+
}
|
|
531
|
+
/** Serves each publication as the Session makes it, so every reader in
|
|
532
|
+
* between sees one generation. */
|
|
533
|
+
observeOutcome(session, outcome) {
|
|
534
|
+
let systemPropsChanged = false;
|
|
535
|
+
if (outcome.kind === "held") for (const [path$6, hash] of outcome.abortedOriginals) this.abortedParseHashes.set(path$6, hash);
|
|
536
|
+
else {
|
|
537
|
+
this.abortedParseHashes.clear();
|
|
538
|
+
const before = this.systemPropsModule;
|
|
539
|
+
this.adoptPublication(session);
|
|
540
|
+
systemPropsChanged = this.systemPropsModule !== before;
|
|
541
|
+
}
|
|
542
|
+
for (const collected of this.outcomeCollectors) collected.push({
|
|
543
|
+
outcome,
|
|
544
|
+
systemPropsChanged
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/** Submits a dev change set to the Session. Until a first publication
|
|
548
|
+
* there is no generation to update: the full read covers the change. */
|
|
549
|
+
submitChanges(changes) {
|
|
550
|
+
return this.runAttempt((session) => session.publication === null ? session.runFullPipeline([], changes.sources) : session.handleWatchUpdate(changes));
|
|
551
|
+
}
|
|
552
|
+
/** Runs one Session attempt and returns the outcomes that settle it. A
|
|
553
|
+
* hold warns and resolves with its outcome (Vite reports the syntax
|
|
554
|
+
* error itself); any other failure rejects. Either way the watcher
|
|
555
|
+
* learns the paths the attempt's system load named. */
|
|
556
|
+
async runAttempt(attempt) {
|
|
557
|
+
const session = this.session ?? this.createSession();
|
|
558
|
+
const collected = [];
|
|
559
|
+
this.outcomeCollectors.add(collected);
|
|
560
|
+
try {
|
|
561
|
+
await attempt(session);
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (!collected.some(({ outcome }) => outcome.kind === "held")) throw error;
|
|
564
|
+
this.warn(error instanceof Error ? error.message : String(error));
|
|
565
|
+
} finally {
|
|
566
|
+
this.outcomeCollectors.delete(collected);
|
|
567
|
+
this.registerSystemWatchPaths();
|
|
568
|
+
}
|
|
569
|
+
return collected;
|
|
570
|
+
}
|
|
571
|
+
/** Serves the Session's last publication whole (asset placeholders
|
|
572
|
+
* kept; Vite substitutes them). */
|
|
573
|
+
adoptPublication(session) {
|
|
574
|
+
if (session.publication === null) return;
|
|
575
|
+
this.served = session.publication;
|
|
576
|
+
this.externalPackageOutcomes = session.lastExternalOutcomes;
|
|
577
|
+
this.applyAssetSubstitutions();
|
|
578
|
+
}
|
|
579
|
+
/** The context's environment builds: a custom `buildApp` builds a shared
|
|
580
|
+
* plugin's environments in parallel, so the context's build retires with
|
|
581
|
+
* the last of them, not the first. */
|
|
582
|
+
builds = new _animus_ui_extract_session.EnvironmentBuilds();
|
|
583
|
+
/** Retires this context's build when Vite closes it: the Session leaves
|
|
584
|
+
* the registry (with its manifest and watch state) and the engine handle
|
|
585
|
+
* is released, injected engine or not. The next build start rejoins. */
|
|
586
|
+
close() {
|
|
587
|
+
this.session?.close();
|
|
588
|
+
this.releaseEngine();
|
|
589
|
+
}
|
|
590
|
+
/** Releases this context's engine handle: a standalone one, which no
|
|
591
|
+
* joined build retires. The next use makes a fresh one. */
|
|
592
|
+
releaseEngine() {
|
|
593
|
+
if (this.engineKey === null) return;
|
|
594
|
+
(0, _animus_ui_extract_session.releaseEngineHandle)(this.engineKey);
|
|
595
|
+
this.engineKey = null;
|
|
577
596
|
}
|
|
578
597
|
log(msg) {
|
|
579
598
|
if (this.verbose) (this.logger ?? console).info(`[animus] ${msg}`);
|
|
@@ -604,214 +623,57 @@ var PluginContext = class {
|
|
|
604
623
|
if (!report) throw new Error(message, cause === void 0 ? void 0 : { cause });
|
|
605
624
|
report(message);
|
|
606
625
|
}
|
|
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;
|
|
626
|
+
devFsUrl(absPath) {
|
|
627
|
+
return `${this.base.endsWith("/") ? this.base.slice(0, -1) : this.base}/@fs${absPath}`;
|
|
628
|
+
}
|
|
629
|
+
/** A dev asset() URL: an immutable content-named copy of `absPath`, so an
|
|
630
|
+
* edit gets a new URL, or the live file where copies are not served. */
|
|
631
|
+
devAssetUrl(specifier, absPath) {
|
|
632
|
+
if (this.assetCopyDir === null) return this.devFsUrl(absPath);
|
|
633
|
+
const { fileName } = (0, _animus_ui_extract_pipeline.writeAssetCopy)(absPath, this.assetCopyDir);
|
|
634
|
+
const copyPath = (0, path.join)(this.assetCopyDir, fileName);
|
|
635
|
+
if (!this.servesFile(copyPath)) {
|
|
636
|
+
if (!this.unservedAssetCopies.has(specifier)) {
|
|
637
|
+
this.unservedAssetCopies.add(specifier);
|
|
638
|
+
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
639
|
}
|
|
636
|
-
|
|
640
|
+
return this.devFsUrl(absPath);
|
|
641
|
+
}
|
|
642
|
+
if (this.assetSourceBySpecifier.get(specifier) !== absPath) this.devServer?.watcher?.add(absPath);
|
|
643
|
+
this.assetSourceBySpecifier.set(specifier, absPath);
|
|
644
|
+
this.retainAssetCopy(absPath, fileName);
|
|
645
|
+
return this.devFsUrl(copyPath);
|
|
646
|
+
}
|
|
647
|
+
/** Keeps each source's current copy and the one before it; an asset
|
|
648
|
+
* edit reloads the page, so an older URL is never fetched again. A copy
|
|
649
|
+
* another source still keeps is never removed. */
|
|
650
|
+
retainAssetCopy(source, fileName) {
|
|
651
|
+
const history = this.assetCopyHistory.get(source) ?? [];
|
|
652
|
+
if (history[history.length - 1] !== fileName) history.push(fileName);
|
|
653
|
+
this.assetCopyHistory.set(source, history);
|
|
654
|
+
while (history.length > 2) {
|
|
655
|
+
const oldest = history.shift();
|
|
656
|
+
if (![...this.assetCopyHistory.values()].some((copies) => copies.includes(oldest)) && this.assetCopyDir !== null) (0, fs.rmSync)((0, path.join)(this.assetCopyDir, oldest), { force: true });
|
|
637
657
|
}
|
|
638
658
|
}
|
|
639
|
-
/**
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
659
|
+
/** Whether `absFile` is the source of a dev asset copy. */
|
|
660
|
+
isAssetSource(absFile) {
|
|
661
|
+
const keys = new Set((0, _animus_ui_extract_pipeline.toWatchKeys)(absFile));
|
|
662
|
+
return [...this.assetSourceBySpecifier.values()].some((source) => (0, _animus_ui_extract_pipeline.toWatchKeys)(source).some((key) => keys.has(key)));
|
|
663
|
+
}
|
|
664
|
+
/** Copies an edited asset under its new content name and redelivers the
|
|
665
|
+
* stylesheet that names it. */
|
|
666
|
+
refreshAssetCopies(absFile) {
|
|
667
|
+
const keys = new Set((0, _animus_ui_extract_pipeline.toWatchKeys)(absFile));
|
|
668
|
+
for (const [specifier, source] of this.assetSourceBySpecifier) {
|
|
669
|
+
if (!(0, _animus_ui_extract_pipeline.toWatchKeys)(source).some((key) => keys.has(key))) continue;
|
|
645
670
|
try {
|
|
646
|
-
|
|
647
|
-
return true;
|
|
671
|
+
this.assetUrlBySpecifier.set(specifier, this.devAssetUrl(specifier, source));
|
|
648
672
|
} catch (err) {
|
|
649
|
-
|
|
673
|
+
this.warn(`[animus-extract] could not copy edited asset ${source}: ${String(err)}`);
|
|
650
674
|
}
|
|
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
675
|
}
|
|
699
|
-
this.
|
|
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)));
|
|
804
|
-
}
|
|
805
|
-
if (reloadingSystem) this.invalidateSystemReloadModules(!systemHeld);
|
|
806
|
-
}
|
|
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}`;
|
|
676
|
+
this.invalidateSystemReloadModules(true);
|
|
815
677
|
}
|
|
816
678
|
assetFallback(specifier, message, cause) {
|
|
817
679
|
if (this.options.strict) this.strictFailure(message, cause);
|
|
@@ -823,8 +685,8 @@ var PluginContext = class {
|
|
|
823
685
|
sheetAssetSpecifiers() {
|
|
824
686
|
return (0, _animus_ui_extract_pipeline.findSheetAssetSpecifiers)({
|
|
825
687
|
variableCss: this.system.variableCss,
|
|
826
|
-
globalCss: this.globalCss,
|
|
827
|
-
componentCss: this.
|
|
688
|
+
globalCss: this.analysisSheets().globalCss,
|
|
689
|
+
componentCss: this.analysisSheets().componentCss
|
|
828
690
|
});
|
|
829
691
|
}
|
|
830
692
|
/** `system.variableCss` with its asset() placeholders substituted from
|
|
@@ -837,18 +699,6 @@ var PluginContext = class {
|
|
|
837
699
|
variableCss: this.system.variableCss
|
|
838
700
|
}, this.assetUrlBySpecifier).variableCss;
|
|
839
701
|
}
|
|
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
702
|
/** Resolves asset specifiers a system reload introduced. Node-side only —
|
|
853
703
|
* the plugin hook context is unavailable here, so results can differ. */
|
|
854
704
|
applyAssetSubstitutions() {
|
|
@@ -856,44 +706,9 @@ var PluginContext = class {
|
|
|
856
706
|
if (this.assetResolutionFailures.delete(specifier)) this.assetUrlBySpecifier.delete(specifier);
|
|
857
707
|
if (this.assetUrlBySpecifier.has(specifier)) continue;
|
|
858
708
|
const resolved = (0, _animus_ui_extract_pipeline.resolveAssetFile)(specifier, this.rootDir, this.pathAliasesJson);
|
|
859
|
-
if (resolved) this.assetUrlBySpecifier.set(specifier, this.
|
|
709
|
+
if (resolved) this.assetUrlBySpecifier.set(specifier, this.devAssetUrl(specifier, resolved));
|
|
860
710
|
else this.assetFallback(specifier, `[animus-extract] unresolvable asset() specifier: ${specifier}`);
|
|
861
711
|
}
|
|
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
712
|
}
|
|
898
713
|
invalidateSystemReloadModules(reloadClient) {
|
|
899
714
|
const server = this.devServer;
|
|
@@ -909,15 +724,36 @@ var PluginContext = class {
|
|
|
909
724
|
if (reloadClient) server.hot?.send({ type: "full-reload" });
|
|
910
725
|
}
|
|
911
726
|
isSystemDependency(absFile) {
|
|
912
|
-
return
|
|
913
|
-
}
|
|
914
|
-
/**
|
|
915
|
-
*
|
|
727
|
+
return this.session?.isSystemDependency(absFile) ?? false;
|
|
728
|
+
}
|
|
729
|
+
/** Registers the Session's watch plan with Vite's watcher, each path
|
|
730
|
+
* once per watcher. Vite also watches its packages' own directories beside the
|
|
731
|
+
* plan's canonical roots, and an absent path either covers (or the root
|
|
732
|
+
* does) is left to their watch: a watch on an absent file inside a
|
|
733
|
+
* watched directory stops that directory's events under inotify
|
|
734
|
+
* (chokidar's non-fsevents handler). Vite hard-ignores node_modules, so
|
|
735
|
+
* dependencies installed there produce no events. */
|
|
916
736
|
registerSystemWatchPaths() {
|
|
917
737
|
const watcher = this.devServer?.watcher;
|
|
918
|
-
|
|
919
|
-
if (
|
|
920
|
-
|
|
738
|
+
const session = this.session;
|
|
739
|
+
if (!watcher || !session) return;
|
|
740
|
+
const directories = [.../* @__PURE__ */ new Set([...session.watchPlan().directories, ...session.externalPackageDirs])];
|
|
741
|
+
const covered = this.rootDir ? [this.rootDir, ...directories] : directories;
|
|
742
|
+
const plan = session.watchPlan(covered);
|
|
743
|
+
let registered = this.watchedPaths.get(watcher);
|
|
744
|
+
if (registered === void 0) {
|
|
745
|
+
registered = /* @__PURE__ */ new Set();
|
|
746
|
+
this.watchedPaths.set(watcher, registered);
|
|
747
|
+
}
|
|
748
|
+
for (const paths of [
|
|
749
|
+
plan.files,
|
|
750
|
+
plan.missing,
|
|
751
|
+
directories
|
|
752
|
+
]) {
|
|
753
|
+
const added = paths.filter((path$7) => !registered.has(path$7));
|
|
754
|
+
for (const path$8 of added) registered.add(path$8);
|
|
755
|
+
if (added.length > 0) watcher.add(added);
|
|
756
|
+
}
|
|
921
757
|
}
|
|
922
758
|
/** Invalidate the component CSS and system-props modules, then reload. The
|
|
923
759
|
* reload alone rescues nothing: Vite keeps serving its cached transform.
|
|
@@ -930,33 +766,7 @@ var PluginContext = class {
|
|
|
930
766
|
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
931
767
|
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
932
768
|
}
|
|
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
|
-
});
|
|
769
|
+
this.reloadWindow.note();
|
|
960
770
|
}
|
|
961
771
|
runSelfVerify() {
|
|
962
772
|
const failures = (0, _animus_ui_extract_pipeline.runStructuralSelfCheck)({
|
|
@@ -981,131 +791,113 @@ var PluginContext = class {
|
|
|
981
791
|
}
|
|
982
792
|
};
|
|
983
793
|
//#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
|
-
}
|
|
794
|
+
//#region src/module-invalidation.ts
|
|
1015
795
|
/**
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
796
|
+
* Completeness is load-bearing: the file-plan diff and the sources re-delivered
|
|
797
|
+
* when a held publication ends are the only invalidation candidate sources, so
|
|
798
|
+
* a node shape missed here stays stale for the session.
|
|
1018
799
|
*/
|
|
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
|
-
});
|
|
800
|
+
function invalidateFileModules(ctx, relPaths) {
|
|
801
|
+
const server = ctx.devServer;
|
|
802
|
+
if (!server || relPaths.length === 0) return 0;
|
|
803
|
+
const graphs = server.environments ? Object.values(server.environments).map((env) => env?.moduleGraph).filter(Boolean) : [server.moduleGraph].filter(Boolean);
|
|
804
|
+
let total = 0;
|
|
805
|
+
for (const relPath of relPaths) {
|
|
806
|
+
const absPath = (0, path.resolve)(ctx.rootDir, relPath);
|
|
807
|
+
let fileCount = 0;
|
|
808
|
+
for (const graph of graphs) {
|
|
809
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
810
|
+
const byFile = graph.getModulesByFile?.(absPath);
|
|
811
|
+
if (byFile) for (const mod of byFile) nodes.add(mod);
|
|
812
|
+
const byId = graph.getModuleById?.(absPath);
|
|
813
|
+
if (byId) nodes.add(byId);
|
|
814
|
+
for (const mod of nodes) {
|
|
815
|
+
graph.invalidateModule(mod);
|
|
816
|
+
fileCount++;
|
|
817
|
+
}
|
|
1055
818
|
}
|
|
1056
|
-
if (
|
|
819
|
+
if (fileCount > 0) ctx.trace(`HMR invalidate: ${relPath} (plan changed, ${fileCount} nodes)`);
|
|
820
|
+
total += fileCount;
|
|
1057
821
|
}
|
|
1058
|
-
|
|
1059
|
-
return reanalyzed;
|
|
822
|
+
return total;
|
|
1060
823
|
}
|
|
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;
|
|
824
|
+
//#endregion
|
|
825
|
+
//#region src/rediscovery.ts
|
|
826
|
+
const NO_DROPS = /* @__PURE__ */ new Map();
|
|
827
|
+
const dropsByManifest = /* @__PURE__ */ new WeakMap();
|
|
828
|
+
/** The current manifest's extensions dropped over their parent, by file:
|
|
829
|
+
* the engine's typed missing-input evidence (spec §12.2 row 11). */
|
|
830
|
+
function droppedExtensions(ctx) {
|
|
831
|
+
const manifest = ctx.storedManifest;
|
|
832
|
+
if (!manifest) return NO_DROPS;
|
|
833
|
+
let drops = dropsByManifest.get(manifest);
|
|
834
|
+
if (!drops) {
|
|
835
|
+
drops = (0, _animus_ui_extract_pipeline.servedFiles)(manifest).dropped;
|
|
836
|
+
dropsByManifest.set(manifest, drops);
|
|
837
|
+
}
|
|
838
|
+
return drops;
|
|
1090
839
|
}
|
|
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
840
|
/** Per-context (file, parent, condition) verdicts already warned — each
|
|
1095
841
|
* condition warns once; cleared when the drops disappear. */
|
|
1096
842
|
const warnedVerdicts = /* @__PURE__ */ new WeakMap();
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
843
|
+
/** Where a parent the analysis does not hold is on disk, probed from its
|
|
844
|
+
* base under the host's extension order: one no analysed module answers
|
|
845
|
+
* (`absent`), or one the host opens but the analysis never read
|
|
846
|
+
* (`incomplete`), such as a file the watcher has not reported yet. */
|
|
847
|
+
function parentOnDisk(ctx, input) {
|
|
848
|
+
if (input.outcome !== "absent" && input.outcome !== "incomplete" || input.base === null) return { kind: "none" };
|
|
849
|
+
const probed = (0, _animus_ui_extract_pipeline.probeAbsolutePathSpecifier)((0, path.resolve)(ctx.rootDir, input.base), void 0, ctx.rootDir, ctx.extensionPolicy);
|
|
850
|
+
if (probed.kind === "absent") return { kind: "none" };
|
|
851
|
+
if (probed.kind === "case-mismatch") return {
|
|
852
|
+
kind: "inadmissible",
|
|
853
|
+
reason: `is unresolved: ${(0, _animus_ui_extract_pipeline.caseMismatchReason)(`the import '${input.specifier}'`, {
|
|
854
|
+
candidate: (0, path.relative)(ctx.rootDir, probed.candidate),
|
|
855
|
+
actual: (0, path.relative)(ctx.rootDir, probed.actual)
|
|
856
|
+
}, "spell the import as the file is spelled.")}`
|
|
857
|
+
};
|
|
858
|
+
const path$5 = probed.path;
|
|
859
|
+
const relPath = (0, path.relative)(ctx.rootDir, path$5);
|
|
860
|
+
const excludedBy = ctx.excludeMatcher.explain(path$5, relPath);
|
|
861
|
+
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;
|
|
862
|
+
if (reason === void 0) return {
|
|
863
|
+
kind: "fold",
|
|
864
|
+
path: path$5
|
|
865
|
+
};
|
|
866
|
+
if (reason === null) return { kind: "none" };
|
|
867
|
+
return {
|
|
868
|
+
kind: "inadmissible",
|
|
869
|
+
reason: `resolves to ${relPath}, ${reason}`
|
|
870
|
+
};
|
|
871
|
+
}
|
|
1103
872
|
/**
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
873
|
+
* Folds the on-disk files the manifest's dropped extensions name as missing
|
|
874
|
+
* parents into the Session, in one bounded pass, and returns the outcomes
|
|
875
|
+
* of that attempt. A parent no file answers, or one that cannot be
|
|
876
|
+
* admitted, leaves its drop standing: the transform then fails rather than
|
|
877
|
+
* serving builder code (SA1).
|
|
1106
878
|
*/
|
|
1107
|
-
function
|
|
1108
|
-
|
|
879
|
+
async function reconcileSourceCorpus(ctx) {
|
|
880
|
+
if (ctx.isProd || ctx.session === null) return [];
|
|
881
|
+
const drops = [...droppedExtensions(ctx).values()];
|
|
882
|
+
if (drops.length === 0) {
|
|
883
|
+
warnedVerdicts.get(ctx)?.clear();
|
|
884
|
+
return [];
|
|
885
|
+
}
|
|
886
|
+
const folded = /* @__PURE__ */ new Set();
|
|
887
|
+
const inadmissible = [];
|
|
888
|
+
for (const drop of drops) {
|
|
889
|
+
const onDisk = parentOnDisk(ctx, drop);
|
|
890
|
+
if (onDisk.kind === "fold") folded.add(onDisk.path);
|
|
891
|
+
else if (onDisk.kind === "inadmissible") inadmissible.push([drop, onDisk.reason]);
|
|
892
|
+
}
|
|
893
|
+
warnInadmissibleParents(ctx, drops, inadmissible);
|
|
894
|
+
if (folded.size === 0) return [];
|
|
895
|
+
ctx.log(`rediscovery: folded ${folded.size} on-disk file(s) a dropped extension names as its parent`);
|
|
896
|
+
return ctx.submitChanges({ modifiedFiles: folded });
|
|
897
|
+
}
|
|
898
|
+
/** Warns, once per condition, for parents that resolve on disk but cannot
|
|
899
|
+
* be admitted. */
|
|
900
|
+
function warnInadmissibleParents(ctx, drops, inadmissible) {
|
|
1109
901
|
let warned = warnedVerdicts.get(ctx);
|
|
1110
902
|
if (!warned) {
|
|
1111
903
|
warned = /* @__PURE__ */ new Set();
|
|
@@ -1113,31 +905,51 @@ function warnInadmissibleParents(ctx, drops) {
|
|
|
1113
905
|
}
|
|
1114
906
|
const active = new Set(drops.map((d) => `${d.file}\0${d.parent}`));
|
|
1115
907
|
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;
|
|
908
|
+
for (const [drop, reason] of inadmissible) {
|
|
1127
909
|
const verdictKey = `${drop.file}\0${drop.parent}\0${reason}`;
|
|
1128
910
|
if (warned.has(verdictKey)) continue;
|
|
1129
911
|
warned.add(verdictKey);
|
|
1130
|
-
ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}'
|
|
912
|
+
ctx.warn(`cannot extract '${drop.component}': parent '${drop.parent}' ${reason}`);
|
|
1131
913
|
}
|
|
1132
914
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
915
|
+
//#endregion
|
|
916
|
+
//#region src/session-outcomes.ts
|
|
917
|
+
/** The file event as a Session change set. */
|
|
918
|
+
function changesOf(type, absFile) {
|
|
919
|
+
const files = /* @__PURE__ */ new Set([absFile]);
|
|
920
|
+
return type === "delete" ? { removedFiles: files } : { modifiedFiles: files };
|
|
921
|
+
}
|
|
922
|
+
/** What the published outcomes of one submission change, merged. */
|
|
923
|
+
function mergePublished(outcomes) {
|
|
924
|
+
const merged = {
|
|
925
|
+
published: false,
|
|
926
|
+
full: false,
|
|
927
|
+
endedHold: false,
|
|
928
|
+
systemPropsChanged: false,
|
|
929
|
+
planChanged: /* @__PURE__ */ new Set(),
|
|
930
|
+
redeliver: /* @__PURE__ */ new Set()
|
|
931
|
+
};
|
|
932
|
+
for (const { outcome, systemPropsChanged } of outcomes) {
|
|
933
|
+
if (outcome.kind !== "published") continue;
|
|
934
|
+
merged.published = true;
|
|
935
|
+
merged.full ||= outcome.pass === "full";
|
|
936
|
+
merged.endedHold ||= outcome.endedHold;
|
|
937
|
+
merged.systemPropsChanged ||= systemPropsChanged;
|
|
938
|
+
for (const path of outcome.planChanged) merged.planChanged.add(path);
|
|
939
|
+
for (const path of outcome.redeliver) merged.redeliver.add(path);
|
|
940
|
+
}
|
|
941
|
+
return merged;
|
|
942
|
+
}
|
|
943
|
+
/** Acts on what every submission's publications owe the module graph: a
|
|
944
|
+
* publication that ends a hold re-delivers the originals whose bytes
|
|
945
|
+
* changed meanwhile, and a full pass (a system reload) reloads the
|
|
946
|
+
* client. */
|
|
947
|
+
function applyPublished(ctx, merged) {
|
|
948
|
+
if (merged.redeliver.size > 0) invalidateFileModules(ctx, [...merged.redeliver]);
|
|
949
|
+
if (merged.full) {
|
|
950
|
+
invalidateFileModules(ctx, [...merged.planChanged]);
|
|
951
|
+
ctx.invalidateSystemReloadModules(true);
|
|
1139
952
|
}
|
|
1140
|
-
return null;
|
|
1141
953
|
}
|
|
1142
954
|
//#endregion
|
|
1143
955
|
//#region src/transform.ts
|
|
@@ -1154,7 +966,7 @@ function applyDevBridgeImport(code) {
|
|
|
1154
966
|
function extendingFiles(ctx, analysisPaths) {
|
|
1155
967
|
const manifest = ctx.storedManifest;
|
|
1156
968
|
const files = /* @__PURE__ */ new Set();
|
|
1157
|
-
const queue = analysisPaths.flatMap((path$
|
|
969
|
+
const queue = analysisPaths.flatMap((path$3) => manifest?.files[path$3] ?? []);
|
|
1158
970
|
const seen = new Set(queue);
|
|
1159
971
|
while (queue.length > 0) {
|
|
1160
972
|
const id = queue.shift();
|
|
@@ -1168,12 +980,11 @@ function extendingFiles(ctx, analysisPaths) {
|
|
|
1168
980
|
}
|
|
1169
981
|
return files;
|
|
1170
982
|
}
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
if (id.startsWith("\0") || id.startsWith("virtual:animus/")) return null;
|
|
983
|
+
async function transformSource(ctx, code, requestId) {
|
|
984
|
+
if (requestId.startsWith("\0") || requestId.startsWith("virtual:animus/")) return null;
|
|
985
|
+
const query = requestId.indexOf("?");
|
|
986
|
+
if (query !== -1 && !/^v=[\w-]+$/.test(requestId.slice(query + 1))) return null;
|
|
987
|
+
const id = query === -1 ? requestId : requestId.slice(0, query);
|
|
1177
988
|
const isExternalPkg = ctx.externalPackageDirs.some((dir) => (0, _animus_ui_extract_pipeline.isPathWithinRoot)(dir, id));
|
|
1178
989
|
const relativePath = (0, path.relative)(ctx.rootDir, id);
|
|
1179
990
|
if (!isExternalPkg) {
|
|
@@ -1183,57 +994,36 @@ async function transformSource(ctx, code, id) {
|
|
|
1183
994
|
}
|
|
1184
995
|
const abortedHash = ctx.abortedParseHashes.get(relativePath);
|
|
1185
996
|
if (abortedHash !== void 0 && abortedHash === (0, _animus_ui_extract_pipeline.contentHash)(code)) return null;
|
|
997
|
+
if (!ctx.storedManifest) throw new Error(`[animus-extract] no analysis is available to transform ${relativePath}: the project analysis failed; see the error above.`);
|
|
998
|
+
let detectionFailed = false;
|
|
1186
999
|
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;
|
|
1000
|
+
const session = ctx.session;
|
|
1001
|
+
if (!ctx.isProd && session !== null && !session.heldSources.has(relativePath)) {
|
|
1002
|
+
let outcomes = [];
|
|
1201
1003
|
try {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
if (!analysisOk && !held) ctx.mutateFileCache((cache) => cache.delete(relativePath));
|
|
1004
|
+
outcomes = await ctx.submitChanges(changesOf("create", id));
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
1007
|
+
detectionFailed = true;
|
|
1207
1008
|
}
|
|
1208
|
-
if (
|
|
1209
|
-
await reconcileSourceCorpus(ctx);
|
|
1210
|
-
|
|
1009
|
+
if (mergePublished(outcomes).published) {
|
|
1010
|
+
const merged = mergePublished([...outcomes, ...await reconcileSourceCorpus(ctx)]);
|
|
1011
|
+
applyPublished(ctx, merged);
|
|
1012
|
+
if (!merged.full) invalidateFileModules(ctx, [...merged.planChanged].filter((path$4) => path$4 !== relativePath));
|
|
1211
1013
|
const compCount = ctx.storedManifest?.files[relativePath]?.length ?? 0;
|
|
1212
1014
|
ctx.info(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
|
|
1213
1015
|
ctx.invalidateExtractedModules();
|
|
1214
1016
|
}
|
|
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
1017
|
}
|
|
1018
|
+
if (detectionFailed) throw new Error(`[animus-extract] the analysis of the new file ${relativePath} failed; see the error above.`);
|
|
1229
1019
|
}
|
|
1020
|
+
const drop = droppedExtensions(ctx).get(relativePath);
|
|
1021
|
+
if (drop !== void 0) throw new Error((0, _animus_ui_extract_pipeline.droppedExtensionMessage)(drop));
|
|
1022
|
+
if (!ctx.storedManifest.files[relativePath]?.length) return null;
|
|
1230
1023
|
try {
|
|
1231
1024
|
const { transformFile } = ctx.engineApi();
|
|
1232
1025
|
const result = transformFile(code, relativePath, ctx.storedManifestJson);
|
|
1233
|
-
if (!result.hasComponents) {
|
|
1234
|
-
ctx.recordFallbackState(relativePath, true);
|
|
1235
|
-
return null;
|
|
1236
|
-
}
|
|
1026
|
+
if (!result.hasComponents) throw new Error(`the analysis extracted components from ${relativePath}, but its transform found none`);
|
|
1237
1027
|
if (ctx.tracing) {
|
|
1238
1028
|
const compCount = ctx.storedManifest.files[relativePath]?.length ?? 0;
|
|
1239
1029
|
ctx.trace(`transform ${relativePath}: ${compCount} components`);
|
|
@@ -1243,35 +1033,30 @@ async function transformSource(ctx, code, id) {
|
|
|
1243
1033
|
outputCode = applyDevBridgeImport(result.code);
|
|
1244
1034
|
ctx.recordTransformOutput(relativePath, outputCode);
|
|
1245
1035
|
}
|
|
1246
|
-
ctx.recordFallbackState(relativePath, false);
|
|
1247
1036
|
return {
|
|
1248
1037
|
code: outputCode,
|
|
1249
1038
|
map: null
|
|
1250
1039
|
};
|
|
1251
1040
|
} catch (e) {
|
|
1252
|
-
|
|
1253
|
-
else console.warn(`[animus-extract] Failed to transform ${id}:`, e);
|
|
1254
|
-
ctx.recordFallbackState(relativePath, true);
|
|
1255
|
-
return null;
|
|
1041
|
+
throw new Error(`[animus-extract] Failed to transform ${id}: ${e}`, { cause: e });
|
|
1256
1042
|
}
|
|
1257
1043
|
}
|
|
1258
1044
|
//#endregion
|
|
1259
1045
|
//#region src/hmr.ts
|
|
1260
1046
|
/** Vite dispatches this hook once per environment for one file event: the
|
|
1261
|
-
*
|
|
1262
|
-
|
|
1047
|
+
* dispatch that claims it submits the file to the Session, and invalidation
|
|
1048
|
+
* runs in each. */
|
|
1049
|
+
async function handleHotUpdate(ctx, environment, { type, file, timestamp, modules, read }) {
|
|
1263
1050
|
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
1051
|
const ownsEvent = ctx.hotUpdateEvents.claim(environment.name, file, timestamp);
|
|
1268
1052
|
const absFile = (0, path.resolve)(file);
|
|
1269
1053
|
ctx.trace(`hotUpdate ${type} ${(0, path.relative)(ctx.rootDir, absFile)} env=${environment.name} owns=${ownsEvent}`);
|
|
1270
1054
|
if (ctx.isSystemDependency(absFile)) {
|
|
1271
|
-
if (ownsEvent)
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1055
|
+
if (ownsEvent) requestSystemReload(ctx, absFile, type);
|
|
1056
|
+
return [];
|
|
1057
|
+
}
|
|
1058
|
+
if (ctx.assetCopyDir !== null && ctx.isAssetSource(absFile)) {
|
|
1059
|
+
if (ownsEvent && type !== "delete") ctx.refreshAssetCopies(absFile);
|
|
1275
1060
|
return [];
|
|
1276
1061
|
}
|
|
1277
1062
|
if (type === "delete") {
|
|
@@ -1295,31 +1080,74 @@ async function handleHotUpdateExclusive(ctx, environment, { type, file, timestam
|
|
|
1295
1080
|
}
|
|
1296
1081
|
return invalidateStaleModules(ctx, environment, modules, result);
|
|
1297
1082
|
}
|
|
1298
|
-
/**
|
|
1299
|
-
*
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
*
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1083
|
+
/** Each context's pending system-dependency events, and the window they
|
|
1084
|
+
* wait out together. */
|
|
1085
|
+
const systemReloads = /* @__PURE__ */ new WeakMap();
|
|
1086
|
+
/** A system dependency's event waits out the coalescing window (SA19)
|
|
1087
|
+
* before the Session reads it: a watcher can report a write as it starts
|
|
1088
|
+
* and drop the event for its end, so an immediate read would load a
|
|
1089
|
+
* half-written module. Events in one window reload once. */
|
|
1090
|
+
function requestSystemReload(ctx, absFile, type) {
|
|
1091
|
+
let state = systemReloads.get(ctx);
|
|
1092
|
+
if (state === void 0) {
|
|
1093
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1094
|
+
state = {
|
|
1095
|
+
pending,
|
|
1096
|
+
window: new _animus_ui_extract_session.CoalescingWindow(() => {
|
|
1097
|
+
if (pending.size === 0) return;
|
|
1098
|
+
const taken = [...pending];
|
|
1099
|
+
pending.clear();
|
|
1100
|
+
return reloadSystem(ctx, taken);
|
|
1101
|
+
})
|
|
1102
|
+
};
|
|
1103
|
+
systemReloads.set(ctx, state);
|
|
1309
1104
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1105
|
+
ctx.log(`HMR system reload scheduled: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
1106
|
+
state.pending.set(absFile, type);
|
|
1107
|
+
state.window.note();
|
|
1108
|
+
}
|
|
1109
|
+
/** Drops a pending system reload: the context is closing. */
|
|
1110
|
+
function cancelSystemReload(ctx) {
|
|
1111
|
+
const state = systemReloads.get(ctx);
|
|
1112
|
+
state?.window.cancel();
|
|
1113
|
+
state?.pending.clear();
|
|
1114
|
+
}
|
|
1115
|
+
/** Reloads the system through the Session over the window's events. It
|
|
1116
|
+
* runs from a timer, so it never throws: a failed reload keeps the last
|
|
1117
|
+
* good system served and warns; under `strict` the client overlay reports
|
|
1118
|
+
* it too. */
|
|
1119
|
+
async function reloadSystem(ctx, events) {
|
|
1120
|
+
const reloadStart = performance.now();
|
|
1121
|
+
const modifiedFiles = /* @__PURE__ */ new Set();
|
|
1122
|
+
const removedFiles = /* @__PURE__ */ new Set();
|
|
1123
|
+
for (const [path$1, type] of events) (type === "delete" ? removedFiles : modifiedFiles).add(path$1);
|
|
1124
|
+
let outcomes;
|
|
1313
1125
|
try {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1126
|
+
outcomes = await ctx.submitChanges({
|
|
1127
|
+
modifiedFiles,
|
|
1128
|
+
removedFiles
|
|
1129
|
+
});
|
|
1130
|
+
} catch (err) {
|
|
1131
|
+
ctx.warn(`[animus-extract] system reload failed: ${String(err)}`);
|
|
1132
|
+
if (ctx.options.strict) {
|
|
1133
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1134
|
+
const stack = err instanceof Error ? err.stack ?? "" : "";
|
|
1135
|
+
ctx.devServer?.hot?.send({
|
|
1136
|
+
type: "error",
|
|
1137
|
+
err: {
|
|
1138
|
+
message,
|
|
1139
|
+
stack,
|
|
1140
|
+
plugin: "animus-extract"
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
ctx.invalidateSystemReloadModules(true);
|
|
1317
1145
|
return;
|
|
1318
1146
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
})
|
|
1147
|
+
const merged = mergePublished(outcomes);
|
|
1148
|
+
applyPublished(ctx, merged);
|
|
1149
|
+
if (!merged.published) ctx.invalidateSystemReloadModules(false);
|
|
1150
|
+
else ctx.log(`HMR system reload complete: ${Math.round(performance.now() - reloadStart)}ms`);
|
|
1323
1151
|
}
|
|
1324
1152
|
/** The once-per-event analysis half; its result is what the remaining
|
|
1325
1153
|
* environments act on. */
|
|
@@ -1327,76 +1155,47 @@ async function analyzeChangedFile(ctx, file, absFile, read) {
|
|
|
1327
1155
|
const ext = (0, path.extname)(file);
|
|
1328
1156
|
if (!ctx.extensionsSet.has(ext)) return { kind: "ignored" };
|
|
1329
1157
|
const excludeMatcher = ctx.excludeMatcher;
|
|
1330
|
-
|
|
1331
|
-
if (!isExternalPkg && excludeMatcher.matches(file, (0, path.relative)(ctx.rootDir, file))) return { kind: "ignored" };
|
|
1158
|
+
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
1159
|
const relPath = (0, path.relative)(ctx.rootDir, absFile);
|
|
1333
|
-
|
|
1160
|
+
const outputDir = (0, _animus_ui_extract_pipeline.outputSourceGuard)(ctx.rootDir, ctx.outputExcludes)(absFile);
|
|
1161
|
+
if (outputDir !== null) {
|
|
1162
|
+
ctx.warn((0, _animus_ui_extract_pipeline.outputSourceMessage)(relPath, outputDir));
|
|
1163
|
+
return { kind: "ignored" };
|
|
1164
|
+
}
|
|
1165
|
+
let settled;
|
|
1334
1166
|
try {
|
|
1335
|
-
|
|
1167
|
+
settled = read ? await read() : void 0;
|
|
1336
1168
|
} catch {
|
|
1337
1169
|
return { kind: "ignored" };
|
|
1338
1170
|
}
|
|
1339
|
-
const
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1171
|
+
const hmrStart = performance.now();
|
|
1172
|
+
const changes = changesOf("update", absFile);
|
|
1173
|
+
const outcomes = await ctx.submitChanges(settled === void 0 ? changes : {
|
|
1174
|
+
...changes,
|
|
1175
|
+
sources: /* @__PURE__ */ new Map([[absFile, settled]])
|
|
1176
|
+
});
|
|
1177
|
+
const last = outcomes.at(-1)?.outcome;
|
|
1178
|
+
if (last === void 0) {
|
|
1342
1179
|
ctx.trace(`HMR skip: ${relPath} (unchanged)`);
|
|
1343
1180
|
return { kind: "unchanged" };
|
|
1344
1181
|
}
|
|
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];
|
|
1182
|
+
if (last.kind === "held") return last.abortedOriginals.has(relPath) ? { kind: "ignored" } : { kind: "unchanged" };
|
|
1183
|
+
const merged = mergePublished([...outcomes, ...await reconcileSourceCorpus(ctx)]);
|
|
1184
|
+
applyPublished(ctx, merged);
|
|
1185
|
+
const published = ctx.session.corpus.published;
|
|
1186
|
+
const nativeEntry = published.analysisEntries.get(relPath);
|
|
1187
|
+
const presentationOnly = nativeEntry && !merged.endedHold ? isPresentationOnlyEdit(ctx, relPath, nativeEntry.source) : false;
|
|
1188
|
+
const redelivered = presentationOnly ? [] : [relPath, ...merged.redeliver];
|
|
1389
1189
|
const staleDefinitionFiles = [.../* @__PURE__ */ new Set([
|
|
1390
|
-
...
|
|
1391
|
-
...
|
|
1392
|
-
...extendingFiles(ctx, redelivered.flatMap((path$
|
|
1190
|
+
...merged.planChanged,
|
|
1191
|
+
...merged.redeliver,
|
|
1192
|
+
...extendingFiles(ctx, redelivered.flatMap((path$2) => published.ownership[path$2]?.analysisPaths ?? [path$2]))
|
|
1393
1193
|
])].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)" : ""}`);
|
|
1194
|
+
ctx.log(`HMR update: ${relPath} — total ${Math.round(performance.now() - hmrStart)}ms${presentationOnly ? " (presentation-only)" : ""}`);
|
|
1396
1195
|
return {
|
|
1397
1196
|
kind: "analyzed",
|
|
1398
1197
|
staleDefinitionFiles,
|
|
1399
|
-
systemPropsChanged,
|
|
1198
|
+
systemPropsChanged: merged.systemPropsChanged,
|
|
1400
1199
|
presentationOnly
|
|
1401
1200
|
};
|
|
1402
1201
|
}
|
|
@@ -1415,17 +1214,14 @@ function isPresentationOnlyEdit(ctx, scannerRelPath, source) {
|
|
|
1415
1214
|
return false;
|
|
1416
1215
|
}
|
|
1417
1216
|
}
|
|
1418
|
-
/**
|
|
1419
|
-
*
|
|
1217
|
+
/** A deleted file leaves the Session's corpus, so its CSS stops being
|
|
1218
|
+
* re-emitted. */
|
|
1420
1219
|
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;
|
|
1220
|
+
const merged = mergePublished(await ctx.submitChanges(changesOf("delete", absFile)));
|
|
1221
|
+
if (!merged.published) return;
|
|
1222
|
+
applyPublished(ctx, merged);
|
|
1427
1223
|
ctx.trace(`Deleted file pruned: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
1428
|
-
|
|
1224
|
+
if (!merged.full) invalidateFileModules(ctx, [...merged.planChanged]);
|
|
1429
1225
|
ctx.invalidateExtractedModules();
|
|
1430
1226
|
}
|
|
1431
1227
|
/** The per-environment invalidation half. Static CSS is left alone: it only
|
|
@@ -1534,7 +1330,7 @@ function loadVirtualModule(ctx, id) {
|
|
|
1534
1330
|
return css;
|
|
1535
1331
|
};
|
|
1536
1332
|
if (id === RESOLVED_CSS_ID) {
|
|
1537
|
-
if (!ctx.isProd && ctx.
|
|
1333
|
+
if (!ctx.isProd && ctx.storedManifest) {
|
|
1538
1334
|
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
1539
1335
|
layers: ctx.options.layers,
|
|
1540
1336
|
variableCss: ctx.variableCss,
|
|
@@ -1604,24 +1400,26 @@ if (import.meta.hot) {
|
|
|
1604
1400
|
}
|
|
1605
1401
|
`;
|
|
1606
1402
|
}
|
|
1607
|
-
if (id === RESOLVED_SYSTEM_PROPS_ID) return
|
|
1403
|
+
if (id === RESOLVED_SYSTEM_PROPS_ID) return ctx.systemPropsModule;
|
|
1608
1404
|
return null;
|
|
1609
1405
|
}
|
|
1610
1406
|
//#endregion
|
|
1611
1407
|
//#region src/index.ts
|
|
1612
|
-
function sourceKitConfig(rootDir,
|
|
1613
|
-
const kits =
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
const
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1408
|
+
async function sourceKitConfig(rootDir, systemPath, engine) {
|
|
1409
|
+
const kits = /* @__PURE__ */ new Map();
|
|
1410
|
+
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);
|
|
1411
|
+
if (kits.size === 0) return void 0;
|
|
1412
|
+
const names = [...kits.keys()];
|
|
1413
|
+
return {
|
|
1414
|
+
optimizeDeps: {
|
|
1415
|
+
exclude: names,
|
|
1416
|
+
include: [...kits.values()].flatMap((kit) => kit.dependencies.map((dependency) => `${kit.name} > ${dependency}`))
|
|
1417
|
+
},
|
|
1418
|
+
ssr: { noExternal: names }
|
|
1419
|
+
};
|
|
1622
1420
|
}
|
|
1623
1421
|
function animusExtract(options) {
|
|
1624
|
-
(0, _animus_ui_extract_pipeline.
|
|
1422
|
+
(0, _animus_ui_extract_pipeline.assertSupportedEngine)(options.engine);
|
|
1625
1423
|
(0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options, ["verify", "appearanceBootstrap"], [{
|
|
1626
1424
|
key: "root",
|
|
1627
1425
|
reason: "the Vite driver derives its root from the resolved Vite config"
|
|
@@ -1633,10 +1431,11 @@ function animusExtract(options) {
|
|
|
1633
1431
|
return {
|
|
1634
1432
|
name: "animus-extract",
|
|
1635
1433
|
enforce: "pre",
|
|
1636
|
-
config(userConfig, env) {
|
|
1434
|
+
async config(userConfig, env) {
|
|
1637
1435
|
const { mode } = (0, _animus_ui_extract_pipeline.resolveMode)(options.mode, () => env.command === "build" ? "production" : "development");
|
|
1638
1436
|
const define = { __ANIMUS_DEV__: mode === "development" };
|
|
1639
|
-
const
|
|
1437
|
+
const root = (0, path.resolve)(userConfig.root ?? process.cwd());
|
|
1438
|
+
const kits = await sourceKitConfig(root, (0, path.resolve)(root, options.system), ctx.kitEngine());
|
|
1640
1439
|
return kits ? {
|
|
1641
1440
|
define,
|
|
1642
1441
|
...kits
|
|
@@ -1649,9 +1448,15 @@ function animusExtract(options) {
|
|
|
1649
1448
|
configResolved(config) {
|
|
1650
1449
|
applyResolvedConfig(ctx, config);
|
|
1651
1450
|
},
|
|
1451
|
+
buildEnd() {
|
|
1452
|
+
if (!ctx.builds.end(this.environment ?? ctx.builds.single)) return;
|
|
1453
|
+
cancelSystemReload(ctx);
|
|
1454
|
+
ctx.close();
|
|
1455
|
+
},
|
|
1652
1456
|
async buildStart() {
|
|
1653
|
-
await
|
|
1654
|
-
|
|
1457
|
+
await ctx.builds.open(this.environment ?? ctx.builds.single);
|
|
1458
|
+
await runBuildStart(ctx, async (specifier, importer) => {
|
|
1459
|
+
return (await this.resolve(specifier, importer))?.id ?? null;
|
|
1655
1460
|
}, ctx.isProd ? (fileName, source) => this.emitFile({
|
|
1656
1461
|
type: "asset",
|
|
1657
1462
|
name: fileName,
|