@animus-ui/vite-plugin 0.1.1-beta.2 → 0.1.2
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/README.md +66 -0
- package/dist/build-start.d.ts +9 -0
- package/dist/build-start.d.ts.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/context.d.ts +171 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/css.d.ts +9 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/hmr.d.ts +23 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hot-update-events.d.ts +54 -0
- package/dist/hot-update-events.d.ts.map +1 -0
- package/dist/index-html.d.ts +27 -0
- package/dist/index-html.d.ts.map +1 -0
- package/dist/index.cjs +878 -742
- package/dist/index.d.ts +55 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/reset-coalescer.d.ts +22 -0
- package/dist/reset-coalescer.d.ts.map +1 -0
- package/dist/transform.d.ts +11 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/virtual-modules.d.ts +7 -0
- package/dist/virtual-modules.d.ts.map +1 -0
- package/package.json +4 -4
- package/dist/analyze-project-args.d.ts +0 -2
- package/dist/analyze-project-args.d.ts.map +0 -1
- package/dist/manifest-diagnostics.d.ts +0 -10
- package/dist/manifest-diagnostics.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2,71 +2,17 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
//#region \0rolldown/runtime.js
|
|
6
|
-
var __create = Object.create;
|
|
7
|
-
var __defProp = Object.defineProperty;
|
|
8
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
14
|
-
key = keys[i];
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
16
|
-
get: ((k) => from[k]).bind(null, key),
|
|
17
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
return to;
|
|
21
|
-
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
-
value: mod,
|
|
24
|
-
enumerable: true
|
|
25
|
-
}) : target, mod));
|
|
26
|
-
//#endregion
|
|
27
5
|
let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
|
|
28
|
-
let browserslist = require("browserslist");
|
|
29
|
-
browserslist = __toESM(browserslist);
|
|
30
|
-
let crypto = require("crypto");
|
|
31
6
|
let fs = require("fs");
|
|
32
|
-
let lightningcss = require("lightningcss");
|
|
33
7
|
let path = require("path");
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
function buildAnalyzeProjectArgs(inputs) {
|
|
37
|
-
return [
|
|
38
|
-
inputs.filesJson,
|
|
39
|
-
inputs.scalesJson,
|
|
40
|
-
inputs.variableMapJson,
|
|
41
|
-
inputs.contextualVarsJson,
|
|
42
|
-
inputs.propConfigJson,
|
|
43
|
-
inputs.groupRegistryJson,
|
|
44
|
-
inputs.packageResolutionJson,
|
|
45
|
-
inputs.devMode,
|
|
46
|
-
inputs.emitterConfigJson,
|
|
47
|
-
inputs.selectorAliasesJson,
|
|
48
|
-
null,
|
|
49
|
-
inputs.globalStyleBlocksJson,
|
|
50
|
-
inputs.pathAliasesJson,
|
|
51
|
-
inputs.keyframesJson
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
//#endregion
|
|
55
|
-
//#region src/manifest-diagnostics.ts
|
|
56
|
-
function surfaceManifestDiagnostics(manifest, warn) {
|
|
57
|
-
for (const diagnostic of manifest.diagnostics ?? []) if (diagnostic.kind === "bail") warn(`⚠ ${diagnostic.component} not extracted: ${diagnostic.message}`);
|
|
58
|
-
else if (diagnostic.kind === "skip") warn(`⚠ ${diagnostic.component}: skipped ${diagnostic.message}`);
|
|
59
|
-
else if (diagnostic.kind === "warn") warn(`⚠ ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`);
|
|
60
|
-
}
|
|
61
|
-
//#endregion
|
|
62
|
-
//#region src/index.ts
|
|
8
|
+
let crypto = require("crypto");
|
|
9
|
+
//#region src/constants.ts
|
|
63
10
|
const VIRTUAL_CSS_ID = "virtual:animus/styles.css";
|
|
64
11
|
const RESOLVED_CSS_ID = "\0virtual:animus/styles.css";
|
|
65
12
|
const VIRTUAL_COMPONENTS_ID = "virtual:animus/components.js";
|
|
66
13
|
const RESOLVED_COMPONENTS_ID = "\0virtual:animus/components.js";
|
|
67
14
|
const VIRTUAL_BRIDGE_ID = "virtual:animus/hmr-bridge.js";
|
|
68
15
|
const RESOLVED_BRIDGE_ID = "\0virtual:animus/hmr-bridge.js";
|
|
69
|
-
const VIRTUAL_SYSTEM_PROPS_ID = "virtual:animus/system-props";
|
|
70
16
|
const RESOLVED_SYSTEM_PROPS_ID = "\0virtual:animus/system-props";
|
|
71
17
|
const DEFAULT_EXCLUDE = [
|
|
72
18
|
"node_modules",
|
|
@@ -74,70 +20,295 @@ const DEFAULT_EXCLUDE = [
|
|
|
74
20
|
".test.",
|
|
75
21
|
".spec."
|
|
76
22
|
];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/build-start.ts
|
|
25
|
+
/**
|
|
26
|
+
* buildStart: load the system, discover and ingest sources (local +
|
|
27
|
+
* external packages), run whole-project analysis, and log the report.
|
|
28
|
+
* `resolveSpecifier` is the bundler seam — Vite's `this.resolve` mapped to
|
|
29
|
+
* an absolute id.
|
|
30
|
+
*/
|
|
31
|
+
async function runBuildStart(ctx, resolveSpecifier) {
|
|
32
|
+
(0, _animus_ui_extract_pipeline.clearEngineCache)(ctx.engineApi);
|
|
33
|
+
let t0 = performance.now();
|
|
34
|
+
ctx.loadSystem();
|
|
35
|
+
if (ctx.options.layers) {
|
|
36
|
+
(0, _animus_ui_extract_pipeline.validateLayerOrder)(ctx.options.layers);
|
|
37
|
+
ctx.log(`Custom layers: [${ctx.options.layers.join(", ")}]`);
|
|
84
38
|
}
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
39
|
+
if (ctx.verbose) {
|
|
40
|
+
const propCount = Object.keys(JSON.parse(ctx.system.propConfigJson)).length;
|
|
41
|
+
const groupCount = Object.keys(JSON.parse(ctx.system.groupRegistryJson)).length;
|
|
42
|
+
ctx.log(`System loaded: ${propCount} props, ${groupCount} groups (${Math.round(performance.now() - t0)}ms)`);
|
|
43
|
+
}
|
|
44
|
+
t0 = performance.now();
|
|
45
|
+
const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
|
|
46
|
+
ctx.extensionsSet = new Set(ctx.options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
47
|
+
const shouldHandleMdx = ctx.extensionsSet.has(".mdx");
|
|
48
|
+
let mdxMissingDepWarned = false;
|
|
49
|
+
const preprocessMdxEntry = async (source, relPath) => {
|
|
50
|
+
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(source, relPath);
|
|
51
|
+
if (result.kind === "missing-dep") {
|
|
52
|
+
if (!mdxMissingDepWarned) {
|
|
53
|
+
ctx.warn("⚠ .mdx in extensions but @mdx-js/mdx not installed; MDX files skipped");
|
|
54
|
+
mdxMissingDepWarned = true;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (result.kind === "error") {
|
|
59
|
+
ctx.warn(`⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
source: result.source,
|
|
64
|
+
relPath: relPath + ".tsx"
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const filePaths = (0, _animus_ui_extract_pipeline.discoverFiles)(ctx.rootDir, ctx.rootDir, excludePatterns, ctx.extensionsSet);
|
|
68
|
+
const fileEntries = [];
|
|
69
|
+
for (const filePath of filePaths) try {
|
|
70
|
+
let source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
71
|
+
let relPath = (0, path.relative)(ctx.rootDir, filePath);
|
|
72
|
+
if (shouldHandleMdx && (0, path.extname)(filePath) === ".mdx") {
|
|
73
|
+
const processed = await preprocessMdxEntry(source, relPath);
|
|
74
|
+
if (!processed) continue;
|
|
75
|
+
source = processed.source;
|
|
76
|
+
relPath = processed.relPath;
|
|
77
|
+
}
|
|
78
|
+
const hash = !ctx.isProd ? (0, _animus_ui_extract_pipeline.contentHash)(source) : void 0;
|
|
79
|
+
fileEntries.push({
|
|
80
|
+
path: relPath,
|
|
81
|
+
source,
|
|
82
|
+
hash
|
|
83
|
+
});
|
|
84
|
+
if (!ctx.isProd && hash) ctx.fileCache.set(relPath, {
|
|
85
|
+
hash,
|
|
86
|
+
source
|
|
87
|
+
});
|
|
88
|
+
} catch {}
|
|
89
|
+
const localFileCount = fileEntries.length;
|
|
90
|
+
const packageSpecifiers = (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(ctx.resolvedSystemPath);
|
|
91
|
+
ctx.externalSourceEntries.clear();
|
|
92
|
+
const collected = await (0, _animus_ui_extract_pipeline.collectExternalPackageSources)({
|
|
93
|
+
specifiers: packageSpecifiers,
|
|
94
|
+
resolveSpecifier,
|
|
95
|
+
rootDir: ctx.rootDir,
|
|
96
|
+
extensionsSet: ctx.extensionsSet,
|
|
97
|
+
hasEntry: (relPath) => fileEntries.some((e) => e.path === relPath),
|
|
98
|
+
preprocessFile: async (source, relPath, absPath) => {
|
|
99
|
+
if (shouldHandleMdx && (0, path.extname)(absPath) === ".mdx") return preprocessMdxEntry(source, relPath);
|
|
100
|
+
return {
|
|
101
|
+
source,
|
|
102
|
+
relPath
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
onUnreadable: (relPath, err) => ctx.warn(`skipped unreadable package file ${relPath}: ${String(err)}`)
|
|
106
|
+
});
|
|
107
|
+
ctx.packageMap = collected.packageMap;
|
|
108
|
+
ctx.externalPackageOutcomes = collected.outcomes;
|
|
109
|
+
for (const [specifier, srcEntry] of collected.sourceEntries) ctx.externalSourceEntries.set(specifier, srcEntry);
|
|
110
|
+
for (const entry of collected.entries) {
|
|
111
|
+
const hash = !ctx.isProd ? (0, _animus_ui_extract_pipeline.contentHash)(entry.source) : void 0;
|
|
112
|
+
fileEntries.push({
|
|
113
|
+
path: entry.path,
|
|
114
|
+
source: entry.source,
|
|
115
|
+
hash
|
|
116
|
+
});
|
|
117
|
+
if (!ctx.isProd && hash) ctx.fileCache.set(entry.path, {
|
|
118
|
+
hash,
|
|
119
|
+
source: entry.source
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
ctx.externalPackageDirs = collected.packageDirs;
|
|
123
|
+
const packageFileCount = fileEntries.length - localFileCount;
|
|
124
|
+
ctx.log(`Discovered ${fileEntries.length} files (${packageFileCount} from packages) (${Math.round(performance.now() - t0)}ms)`);
|
|
125
|
+
t0 = performance.now();
|
|
126
|
+
ctx.runAnalysis(fileEntries);
|
|
127
|
+
if (ctx.storedManifest) {
|
|
128
|
+
const report = ctx.storedManifest.report;
|
|
129
|
+
if (report) {
|
|
130
|
+
ctx.log(`Extracted ${report.components_extracted}/${report.components_total} components (${Math.round(performance.now() - t0)}ms)`);
|
|
131
|
+
ctx.logTimingWaterfall(ctx.storedManifest.timing ?? {});
|
|
132
|
+
ctx.log(`Reconciliation: ${report.components_extracted} kept, ${report.variants_eliminated} variants pruned, ${report.states_eliminated} states pruned`);
|
|
133
|
+
const details = report.eliminated_details || [];
|
|
134
|
+
for (const d of details) if (d.kind === "component") ctx.warn(`⚠ ${d.component} eliminated: ${d.reason}`);
|
|
135
|
+
else if (d.kind === "prospective_component") ctx.warn(`⚠ ${d.component} would be eliminated in production: ${d.reason}`);
|
|
136
|
+
else if (d.kind === "variant") ctx.warn(`⚠ ${d.component} variant '${d.name}' pruned: ${d.reason}`);
|
|
137
|
+
else if (d.kind === "state") ctx.warn(`⚠ ${d.component} state '${d.name}' pruned: ${d.reason}`);
|
|
94
138
|
}
|
|
95
|
-
|
|
96
|
-
|
|
139
|
+
ctx.log(`CSS: ${ctx.resolvedComponentCss.length} bytes (${Object.keys(ctx.storedManifest.components || {}).length} components)`);
|
|
140
|
+
if (!ctx.isProd && ctx.storedSheets) {
|
|
141
|
+
const staticSize = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
142
|
+
layers: ctx.options.layers,
|
|
143
|
+
variableCss: ctx.system.variableCss,
|
|
144
|
+
globalCss: ctx.globalCss
|
|
145
|
+
}).length;
|
|
146
|
+
const componentSize = ctx.resolvedComponentCss.length;
|
|
147
|
+
ctx.log(`Delivery: split mode — static ${staticSize} bytes, components ${componentSize} bytes (adopted stylesheet)`);
|
|
148
|
+
} else ctx.log("Delivery: single file mode (production)");
|
|
97
149
|
}
|
|
98
|
-
|
|
150
|
+
const { declaration } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
151
|
+
layers: ctx.options.layers,
|
|
152
|
+
variableCss: "",
|
|
153
|
+
globalCss: "",
|
|
154
|
+
split: true
|
|
155
|
+
});
|
|
156
|
+
ctx.layerDeclaration = declaration;
|
|
157
|
+
if (ctx.options.verify) ctx.runSelfVerify();
|
|
99
158
|
}
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/config.ts
|
|
100
161
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
162
|
+
* configResolved: capture build mode, root, and logger; resolve Lightning
|
|
163
|
+
* CSS targets; harvest resolve.alias into the Rust `pathAliasesJson`
|
|
164
|
+
* contract via the shared encoder.
|
|
103
165
|
*/
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
166
|
+
function applyResolvedConfig(ctx, config) {
|
|
167
|
+
ctx.isProd = config.command === "build";
|
|
168
|
+
ctx.rootDir = config.root;
|
|
169
|
+
ctx.logger = config.logger;
|
|
170
|
+
ctx.lcssTargets = (0, _animus_ui_extract_pipeline.resolveLightningTargets)(ctx.options.targets, ctx.rootDir);
|
|
171
|
+
ctx.log(`Lightning CSS targets resolved (${Object.keys(ctx.lcssTargets).length} browsers)`);
|
|
172
|
+
const rawAlias = config.resolve?.alias;
|
|
173
|
+
if (rawAlias) {
|
|
174
|
+
const pairs = [];
|
|
175
|
+
if (Array.isArray(rawAlias)) {
|
|
176
|
+
for (const entry of rawAlias) if (typeof entry.find === "string" && typeof entry.replacement === "string") pairs.push({
|
|
177
|
+
pattern: entry.find,
|
|
178
|
+
target: entry.replacement,
|
|
179
|
+
kind: "prefix"
|
|
180
|
+
});
|
|
181
|
+
} else if (typeof rawAlias === "object" && rawAlias !== null) {
|
|
182
|
+
for (const [key, value] of Object.entries(rawAlias)) if (typeof value === "string") pairs.push({
|
|
183
|
+
pattern: key,
|
|
184
|
+
target: value
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const built = (0, _animus_ui_extract_pipeline.buildPathAliasesJson)(pairs, ctx.rootDir);
|
|
188
|
+
if (built) {
|
|
189
|
+
ctx.pathAliasesJson = built.json;
|
|
190
|
+
ctx.log(`Path aliases forwarded: ${built.count} entries`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
112
193
|
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/hot-update-events.ts
|
|
113
196
|
/**
|
|
114
|
-
*
|
|
115
|
-
*
|
|
197
|
+
* Once-per-file-event coordination across the `hotUpdate` dispatches.
|
|
198
|
+
*
|
|
199
|
+
* Vite 8 calls `hotUpdate` once per environment for a single file event — the
|
|
200
|
+
* client environment first, then every non-client environment (see
|
|
201
|
+
* `handleHMRUpdate` in vite/dist/node/chunks/node.js). Analysis work (cache
|
|
202
|
+
* mutation, engine re-analysis, geological reset scheduling) must happen
|
|
203
|
+
* exactly once per event; module invalidation must happen per environment
|
|
204
|
+
* against that environment's own graph.
|
|
205
|
+
*
|
|
206
|
+
* The client dispatch always claims — it is dispatched first, so nothing can
|
|
207
|
+
* have claimed the event before it, and claiming unconditionally keeps a
|
|
208
|
+
* same-millisecond `(file, timestamp)` collision from ever costing a client
|
|
209
|
+
* analysis. A non-client dispatch claims only when the event carries no
|
|
210
|
+
* recorded claim at all, which happens when the plugin is filtered out of the
|
|
211
|
+
* client environment.
|
|
212
|
+
*
|
|
213
|
+
* Keys are retained for a bounded number of recent events so that interleaved
|
|
214
|
+
* file events (Vite does not serialize watcher handlers) still find their own
|
|
215
|
+
* result.
|
|
116
216
|
*/
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
filename: "animus-extracted.css",
|
|
122
|
-
code: Buffer.from(css),
|
|
123
|
-
minify: opts.minify,
|
|
124
|
-
targets: opts.targets
|
|
125
|
-
}).code.toString();
|
|
126
|
-
} catch (e) {
|
|
127
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
128
|
-
(opts.warnFn ?? console.warn)(`[animus] Lightning CSS post-processing failed: ${msg}`);
|
|
129
|
-
return css;
|
|
217
|
+
var HotUpdateEvents = class {
|
|
218
|
+
results = /* @__PURE__ */ new Map();
|
|
219
|
+
constructor(historyLimit = 16) {
|
|
220
|
+
this.historyLimit = historyLimit;
|
|
130
221
|
}
|
|
222
|
+
/** True when this environment dispatch owns the event's analysis work. */
|
|
223
|
+
claim(environmentName, file, timestamp) {
|
|
224
|
+
const key = eventKey(file, timestamp);
|
|
225
|
+
if (environmentName !== "client" && this.results.has(key)) return false;
|
|
226
|
+
this.results.set(key, { kind: "ignored" });
|
|
227
|
+
while (this.results.size > this.historyLimit) {
|
|
228
|
+
const oldest = this.results.keys().next().value;
|
|
229
|
+
if (oldest === void 0) break;
|
|
230
|
+
this.results.delete(oldest);
|
|
231
|
+
}
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
/** Publish the owning dispatch's decision to the other environments. */
|
|
235
|
+
record(file, timestamp, result) {
|
|
236
|
+
this.results.set(eventKey(file, timestamp), result);
|
|
237
|
+
}
|
|
238
|
+
/** The decision for this event — `ignored` when the event was evicted. */
|
|
239
|
+
resultOf(file, timestamp) {
|
|
240
|
+
return this.results.get(eventKey(file, timestamp)) ?? { kind: "ignored" };
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
/** A path can hold any character but NUL, so NUL is the safe separator. */
|
|
244
|
+
function eventKey(file, timestamp) {
|
|
245
|
+
return `${file}\u0000${timestamp}`;
|
|
131
246
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/reset-coalescer.ts
|
|
249
|
+
/**
|
|
250
|
+
* Coalesces bursts of system-dependency events into single geological
|
|
251
|
+
* resets: a quiescence window before starting, at most one reset in
|
|
252
|
+
* flight, and exactly one follow-up when requests arrive mid-reset. A
|
|
253
|
+
* package build regenerating its dist fires one reset, not one per file.
|
|
254
|
+
*
|
|
255
|
+
* Timer functions are injected seams — `vi.mock` on node builtins silently
|
|
256
|
+
* no-ops under vite-plus-test, so tests drive a manual scheduler instead.
|
|
257
|
+
*/
|
|
258
|
+
var ResetCoalescer = class {
|
|
259
|
+
timer = null;
|
|
260
|
+
running = false;
|
|
261
|
+
dirty = false;
|
|
262
|
+
constructor(run, quietMs = 60, schedule = (fn, ms) => setTimeout(fn, ms), cancel = (timer) => clearTimeout(timer)) {
|
|
263
|
+
this.run = run;
|
|
264
|
+
this.quietMs = quietMs;
|
|
265
|
+
this.schedule = schedule;
|
|
266
|
+
this.cancel = cancel;
|
|
267
|
+
}
|
|
268
|
+
/** Ask for a reset; bursts within the quiescence window collapse. */
|
|
269
|
+
request() {
|
|
270
|
+
if (this.running) {
|
|
271
|
+
this.dirty = true;
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (this.timer !== null) this.cancel(this.timer);
|
|
275
|
+
this.timer = this.schedule(() => {
|
|
276
|
+
this.timer = null;
|
|
277
|
+
this.running = true;
|
|
278
|
+
try {
|
|
279
|
+
this.run();
|
|
280
|
+
} finally {
|
|
281
|
+
this.running = false;
|
|
282
|
+
if (this.dirty) {
|
|
283
|
+
this.dirty = false;
|
|
284
|
+
this.request();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}, this.quietMs);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/context.ts
|
|
292
|
+
/** Pre-load / failed-load defaults — the plugin's historical initial state. */
|
|
293
|
+
function emptySystemConfig() {
|
|
294
|
+
return {
|
|
295
|
+
propConfigJson: "{}",
|
|
296
|
+
groupRegistryJson: "{}",
|
|
297
|
+
scalesJson: "{}",
|
|
298
|
+
variableMapJson: "{}",
|
|
299
|
+
variableCss: "",
|
|
300
|
+
contextualVarsJson: "{}",
|
|
301
|
+
selectorAliasesJson: null,
|
|
302
|
+
globalStyleBlocksJson: null,
|
|
303
|
+
keyframesJson: null
|
|
304
|
+
};
|
|
135
305
|
}
|
|
136
306
|
/**
|
|
137
307
|
* Reconstruct file entries from cache, including content hashes.
|
|
138
308
|
* For unchanged files (hash matches changedPath), sends empty source
|
|
139
309
|
* to avoid serializing full source text across the NAPI boundary.
|
|
140
|
-
*
|
|
310
|
+
* The engine adapter's `rehydrateFilesJson` refills empty sources from
|
|
311
|
+
* this same cache before analyze.
|
|
141
312
|
*/
|
|
142
313
|
function buildFileEntriesFromCache(cache, changedPath) {
|
|
143
314
|
const entries = [];
|
|
@@ -148,223 +319,276 @@ function buildFileEntriesFromCache(cache, changedPath) {
|
|
|
148
319
|
});
|
|
149
320
|
return entries;
|
|
150
321
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Drop a deleted (or renamed-away) file from the dev file cache so its
|
|
324
|
+
* last-known source stops riding along as a ghost entry on every later
|
|
325
|
+
* re-analysis. Both key forms are tried: the plain rootDir-relative path, and
|
|
326
|
+
* the `.tsx` suffix MDX sources carry after preprocessing. External package
|
|
327
|
+
* entries are rootDir-relative too (with leading `..` segments) and prune the
|
|
328
|
+
* same way. Returns whether an entry was actually removed.
|
|
329
|
+
*/
|
|
330
|
+
function pruneFileCache(cache, rootDir, absPath) {
|
|
331
|
+
const rel = (0, path.relative)(rootDir, (0, path.resolve)(absPath));
|
|
332
|
+
return cache.delete(rel) || cache.delete(rel + ".tsx");
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Per-plugin-instance state and the pipeline operations over it. Hook
|
|
336
|
+
* bodies live in their own modules (build-start, virtual-modules,
|
|
337
|
+
* transform, hmr) and receive this context — the plugin factory in
|
|
338
|
+
* index.ts only wires Vite hooks to those functions.
|
|
339
|
+
*
|
|
340
|
+
* A class rather than closure variables so each hook module names exactly
|
|
341
|
+
* the state it touches, and the engine store (DEF-1: per-instance, never
|
|
342
|
+
* module-level) is explicit.
|
|
343
|
+
*/
|
|
344
|
+
var PluginContext = class {
|
|
345
|
+
options;
|
|
346
|
+
verbose;
|
|
347
|
+
/** Serialized staticCss forced-emission declarations (stable key order). */
|
|
348
|
+
staticCssJson;
|
|
349
|
+
isProd = false;
|
|
350
|
+
rootDir = "";
|
|
351
|
+
logger = null;
|
|
352
|
+
/** System-derived config (shared SystemConfig shape). */
|
|
353
|
+
system = emptySystemConfig();
|
|
354
|
+
lcssTargets = {};
|
|
355
|
+
pathAliasesJson = null;
|
|
356
|
+
extensionsSet;
|
|
357
|
+
storedManifest = null;
|
|
358
|
+
storedManifestJson = "";
|
|
359
|
+
globalCss = "";
|
|
360
|
+
resolvedComponentCss = "";
|
|
361
|
+
storedSheets = null;
|
|
362
|
+
layerDeclaration = "";
|
|
363
|
+
fragmentCache = /* @__PURE__ */ new Map();
|
|
364
|
+
reverseProvenance = {};
|
|
365
|
+
storedSystemPropMapJson = "{}";
|
|
366
|
+
storedDynamicPropsJson = "{}";
|
|
367
|
+
storedTransformsSource = "{}";
|
|
368
|
+
fileCache = /* @__PURE__ */ new Map();
|
|
369
|
+
hotUpdateEvents = new HotUpdateEvents();
|
|
370
|
+
packageMap = {};
|
|
371
|
+
externalPackageDirs = [];
|
|
372
|
+
externalSourceEntries = /* @__PURE__ */ new Map();
|
|
373
|
+
externalPackageOutcomes = [];
|
|
374
|
+
devServer;
|
|
375
|
+
bridgeInjected = false;
|
|
376
|
+
resolvedSystemPath = null;
|
|
377
|
+
systemDependencyKeys = /* @__PURE__ */ new Set();
|
|
378
|
+
systemDependencyPaths = [];
|
|
379
|
+
v2Engine = null;
|
|
380
|
+
v2SentSources = null;
|
|
381
|
+
v2DriftWarned = false;
|
|
382
|
+
/** Single engine choke-point for every native extraction call. */
|
|
383
|
+
engineApi;
|
|
384
|
+
constructor(options) {
|
|
385
|
+
this.options = options;
|
|
386
|
+
this.staticCssJson = (0, _animus_ui_extract_pipeline.serializeStaticCss)(options.staticCss);
|
|
387
|
+
this.verbose = options.verbose || process.env.ANIMUS_DEBUG === "1" || process.env.ANIMUS_DEBUG === "true";
|
|
388
|
+
this.extensionsSet = new Set(options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
389
|
+
const engineModuleId = "@animus-ui/extract";
|
|
390
|
+
this.engineApi = (0, _animus_ui_extract_pipeline.createV2EngineApi)({
|
|
391
|
+
label: "animus-extract",
|
|
392
|
+
isV2: () => true,
|
|
393
|
+
loadNativeEngine: () => require(engineModuleId),
|
|
394
|
+
rehydrateFilesJson: (filesJsonRaw) => {
|
|
395
|
+
if (!filesJsonRaw.includes("\"source\":\"\"")) return filesJsonRaw;
|
|
396
|
+
const entries = JSON.parse(filesJsonRaw);
|
|
397
|
+
for (const entry of entries) if (entry.source === "") entry.source = this.fileCache.get(entry.path)?.source ?? "";
|
|
398
|
+
return JSON.stringify(entries);
|
|
192
399
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
400
|
+
store: {
|
|
401
|
+
getEngine: () => this.v2Engine,
|
|
402
|
+
setEngine: (engine) => {
|
|
403
|
+
this.v2Engine = engine;
|
|
404
|
+
},
|
|
405
|
+
getSentSources: () => this.v2SentSources,
|
|
406
|
+
setSentSources: (sources) => {
|
|
407
|
+
this.v2SentSources = sources;
|
|
408
|
+
},
|
|
409
|
+
getDriftWarned: () => this.v2DriftWarned,
|
|
410
|
+
setDriftWarned: (value) => {
|
|
411
|
+
this.v2DriftWarned = value;
|
|
199
412
|
}
|
|
200
|
-
return JSON.parse(v2Engine.transformFile(path$2));
|
|
201
|
-
},
|
|
202
|
-
clearAnalysisCache: () => {
|
|
203
|
-
if (v2Engine) v2Engine.clearCache();
|
|
204
|
-
v2Engine = null;
|
|
205
413
|
}
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
function warn(msg) {
|
|
222
|
-
(logger ?? console).warn(`[animus] ${msg}`);
|
|
223
|
-
}
|
|
224
|
-
function logTimingWaterfall(timing) {
|
|
225
|
-
if (!verbose) return;
|
|
226
|
-
for (const [key, label] of [
|
|
227
|
-
["parseAndWalk", "parse+walk"],
|
|
228
|
-
["importResolution", "imports"],
|
|
229
|
-
["extensionProvenance", "provenance"],
|
|
230
|
-
["topologicalSort", "topo-sort"],
|
|
231
|
-
["chainEvaluation", "chains"],
|
|
232
|
-
["jsxScanning", "jsx-scan"],
|
|
233
|
-
["systemPropAggregation", "sys-props"],
|
|
234
|
-
["usageLedger", "usage"],
|
|
235
|
-
["reconciliation", "reconcile"],
|
|
236
|
-
["cssGeneration", "css-gen"],
|
|
237
|
-
["manifestSerialization", "serialize"]
|
|
238
|
-
]) {
|
|
239
|
-
const ms = timing[key] ?? 0;
|
|
240
|
-
const pad = " ".repeat(Math.max(0, 15 - label.length));
|
|
241
|
-
const extra = key === "parseAndWalk" ? ` (${timing.fileCount ?? 0} files, ${timing.cacheHits ?? 0} cached)` : "";
|
|
242
|
-
log(` ${label}${pad}${String(ms).padStart(5)}ms${extra}`);
|
|
243
|
-
}
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
log(msg) {
|
|
417
|
+
if (this.verbose) (this.logger ?? console).info(`[animus] ${msg}`);
|
|
418
|
+
}
|
|
419
|
+
warn(msg) {
|
|
420
|
+
(this.logger ?? console).warn(`[animus] ${msg}`);
|
|
421
|
+
}
|
|
422
|
+
logTimingWaterfall(timing) {
|
|
423
|
+
if (!this.verbose) return;
|
|
424
|
+
for (const line of (0, _animus_ui_extract_pipeline.formatRustTimingWaterfall)(timing, {
|
|
425
|
+
indent: " ",
|
|
426
|
+
labelWidth: 15
|
|
427
|
+
})) this.log(line);
|
|
244
428
|
}
|
|
245
|
-
let lcssTargets = {};
|
|
246
|
-
let variableCss = "";
|
|
247
|
-
let globalCss = "";
|
|
248
|
-
let globalStyleBlocksJson = null;
|
|
249
|
-
let keyframesBlocksJson = null;
|
|
250
|
-
let pathAliasesJson = null;
|
|
251
|
-
let extensionsSet = new Set(options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
252
|
-
let storedManifest = null;
|
|
253
|
-
let storedManifestJson = "";
|
|
254
|
-
let resolvedComponentCss = "";
|
|
255
|
-
let storedSheets = null;
|
|
256
|
-
let layerDeclaration = "";
|
|
257
|
-
let fragmentCache = /* @__PURE__ */ new Map();
|
|
258
|
-
let reverseProvenance = {};
|
|
259
|
-
let storedSystemPropMapJson = "{}";
|
|
260
|
-
let storedDynamicPropsJson = "{}";
|
|
261
|
-
let storedTransformsSource = "{}";
|
|
262
|
-
const fileCache = /* @__PURE__ */ new Map();
|
|
263
|
-
let packageMap = {};
|
|
264
|
-
let externalPackageDirs = [];
|
|
265
|
-
const externalSourceEntries = /* @__PURE__ */ new Map();
|
|
266
|
-
let devServer;
|
|
267
|
-
let bridgeInjected = false;
|
|
268
|
-
let resolvedSystemPath = null;
|
|
269
429
|
/**
|
|
270
|
-
* Load a SystemInstance via Rust NAPI (rquickjs bundled eval)
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* rquickjs, and returns serialized config directly — no subprocess needed.
|
|
430
|
+
* Load a SystemInstance via Rust NAPI (rquickjs bundled eval) into
|
|
431
|
+
* `this.system`. On failure the previous config is kept (strict mode
|
|
432
|
+
* throws instead).
|
|
274
433
|
*/
|
|
275
|
-
|
|
276
|
-
resolvedSystemPath = (0, path.resolve)(rootDir, options.system);
|
|
434
|
+
loadSystem() {
|
|
435
|
+
this.resolvedSystemPath = (0, path.resolve)(this.rootDir, this.options.system);
|
|
436
|
+
for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(this.resolvedSystemPath)) this.systemDependencyKeys.add(key);
|
|
277
437
|
try {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
438
|
+
this.system = (0, _animus_ui_extract_pipeline.loadSystemConfig)(this.engineApi, {
|
|
439
|
+
systemPath: this.resolvedSystemPath,
|
|
440
|
+
rootDir: this.rootDir,
|
|
441
|
+
prefix: this.options.prefix
|
|
442
|
+
});
|
|
443
|
+
const deps = this.system.dependencies ?? [];
|
|
444
|
+
const keys = /* @__PURE__ */ new Set();
|
|
445
|
+
for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(this.resolvedSystemPath)) keys.add(key);
|
|
446
|
+
for (const dep of deps) for (const key of (0, _animus_ui_extract_pipeline.toWatchKeys)(dep)) keys.add(key);
|
|
447
|
+
this.systemDependencyKeys = keys;
|
|
448
|
+
this.systemDependencyPaths = deps;
|
|
449
|
+
this.registerSystemWatchPaths();
|
|
289
450
|
} catch (e) {
|
|
290
|
-
if (options.strict) throw new Error(`[animus-extract] Failed to load system from ${resolvedSystemPath}: ${e}`, { cause: e });
|
|
291
|
-
console.warn(`[animus-extract] Failed to load system from ${resolvedSystemPath}:`, e);
|
|
451
|
+
if (this.options.strict) throw new Error(`[animus-extract] Failed to load system from ${this.resolvedSystemPath}: ${e}`, { cause: e });
|
|
452
|
+
console.warn(`[animus-extract] Failed to load system from ${this.resolvedSystemPath}:`, e);
|
|
292
453
|
}
|
|
293
454
|
}
|
|
294
455
|
/**
|
|
295
|
-
* Run project analysis and update
|
|
296
|
-
*
|
|
297
|
-
* Pipeline: NAPI analyzeProject → unit fallback.
|
|
298
|
-
* Transforms are resolved in-process via boa_engine during Rust extraction.
|
|
299
|
-
* No subprocess needed.
|
|
456
|
+
* Run project analysis via the shared `runProjectAnalysis` and update
|
|
457
|
+
* all manifest-derived state.
|
|
300
458
|
*/
|
|
301
|
-
|
|
459
|
+
runAnalysis(fileEntries) {
|
|
302
460
|
try {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
461
|
+
const result = (0, _animus_ui_extract_pipeline.runProjectAnalysis)(this.engineApi, {
|
|
462
|
+
fileEntries,
|
|
463
|
+
packageMap: this.packageMap,
|
|
464
|
+
system: this.system,
|
|
465
|
+
emitter: {
|
|
466
|
+
runtimeImport: "@animus-ui/system",
|
|
467
|
+
cssModuleId: VIRTUAL_CSS_ID
|
|
468
|
+
},
|
|
469
|
+
pathAliasesJson: this.pathAliasesJson,
|
|
470
|
+
staticCssJson: this.staticCssJson,
|
|
471
|
+
devMode: !this.isProd,
|
|
472
|
+
warn: (m) => this.warn(m)
|
|
307
473
|
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
groupRegistryJson,
|
|
315
|
-
packageResolutionJson: JSON.stringify(packageMap),
|
|
316
|
-
devMode: !isProd,
|
|
317
|
-
emitterConfigJson: emitterConfig,
|
|
318
|
-
selectorAliasesJson,
|
|
319
|
-
globalStyleBlocksJson,
|
|
320
|
-
pathAliasesJson,
|
|
321
|
-
keyframesJson: keyframesBlocksJson
|
|
322
|
-
}));
|
|
323
|
-
storedManifest = JSON.parse(manifestJson);
|
|
324
|
-
storedManifestJson = manifestJson;
|
|
325
|
-
surfaceManifestDiagnostics(storedManifest, warn);
|
|
326
|
-
storedSystemPropMapJson = JSON.stringify(storedManifest?.system_prop_map ?? {});
|
|
327
|
-
const dynamicProps = storedManifest?.dynamic_props ?? {};
|
|
328
|
-
const newDynamicPropsJson = JSON.stringify(dynamicProps);
|
|
329
|
-
if (newDynamicPropsJson !== storedDynamicPropsJson) {
|
|
330
|
-
storedDynamicPropsJson = newDynamicPropsJson;
|
|
331
|
-
const usedTransformNames = /* @__PURE__ */ new Set();
|
|
332
|
-
for (const meta of Object.values(dynamicProps)) if (meta.transform_name) usedTransformNames.add(meta.transform_name);
|
|
333
|
-
if (storedManifest?.components) {
|
|
334
|
-
for (const comp of Object.values(storedManifest.components)) if (comp.replacement) {
|
|
335
|
-
const matches = comp.replacement.matchAll(/transforms\.(\w+)/g);
|
|
336
|
-
for (const match of matches) usedTransformNames.add(match[1]);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
storedTransformsSource = "{}";
|
|
340
|
-
}
|
|
341
|
-
bridgeInjected = false;
|
|
342
|
-
const newFragments = storedManifest?.component_fragments;
|
|
474
|
+
this.storedManifest = result.manifest;
|
|
475
|
+
this.storedManifestJson = result.manifestJson;
|
|
476
|
+
this.storedSystemPropMapJson = JSON.stringify(result.manifest?.system_prop_map ?? {});
|
|
477
|
+
this.storedDynamicPropsJson = JSON.stringify(result.manifest?.dynamic_props ?? {});
|
|
478
|
+
this.bridgeInjected = false;
|
|
479
|
+
const newFragments = result.manifest?.component_fragments;
|
|
343
480
|
if (newFragments && typeof newFragments === "object") {
|
|
344
|
-
fragmentCache.clear();
|
|
345
|
-
for (const [id, sheets] of Object.entries(newFragments)) fragmentCache.set(id, sheets);
|
|
481
|
+
this.fragmentCache.clear();
|
|
482
|
+
for (const [id, sheets] of Object.entries(newFragments)) this.fragmentCache.set(id, sheets);
|
|
346
483
|
}
|
|
347
|
-
reverseProvenance =
|
|
348
|
-
storedSheets =
|
|
349
|
-
globalCss =
|
|
350
|
-
resolvedComponentCss =
|
|
351
|
-
resolvedComponentCss = (0, _animus_ui_extract_pipeline.applyUnitFallback)(resolvedComponentCss);
|
|
484
|
+
this.reverseProvenance = result.manifest?.reverse_provenance ?? {};
|
|
485
|
+
this.storedSheets = result.manifest?.sheets ?? null;
|
|
486
|
+
this.globalCss = result.globalCss;
|
|
487
|
+
this.resolvedComponentCss = result.componentCss;
|
|
352
488
|
} catch (e) {
|
|
353
|
-
if (options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`, { cause: e });
|
|
489
|
+
if (this.options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`, { cause: e });
|
|
354
490
|
console.warn("[animus-extract] analyzeProject failed:", e);
|
|
355
491
|
}
|
|
356
492
|
}
|
|
357
|
-
|
|
493
|
+
resetCoalescer = null;
|
|
494
|
+
/**
|
|
495
|
+
* Schedule a geological reset through the burst coalescer. N dependency
|
|
496
|
+
* events within the quiescence window produce one reset; events during a
|
|
497
|
+
* running reset produce exactly one follow-up.
|
|
498
|
+
*/
|
|
499
|
+
requestGeologicalReset(trigger) {
|
|
500
|
+
this.log(`HMR geological reset scheduled: ${trigger}`);
|
|
501
|
+
this.resetCoalescer ??= new ResetCoalescer(() => this.performGeologicalReset());
|
|
502
|
+
this.resetCoalescer.request();
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* The geological reset: reload the system (refreshing the dependency
|
|
506
|
+
* set), clear the Rust per-file cache, re-analyze everything with full
|
|
507
|
+
* sources, then invalidate the static/component/system-prop virtual
|
|
508
|
+
* modules and reload the client.
|
|
509
|
+
*/
|
|
510
|
+
performGeologicalReset() {
|
|
511
|
+
const resetStart = performance.now();
|
|
512
|
+
this.loadSystem();
|
|
513
|
+
(0, _animus_ui_extract_pipeline.clearEngineCache)(this.engineApi);
|
|
514
|
+
const fileEntries = [];
|
|
515
|
+
for (const [path$2, { hash, source }] of this.fileCache) fileEntries.push({
|
|
516
|
+
path: path$2,
|
|
517
|
+
source,
|
|
518
|
+
hash
|
|
519
|
+
});
|
|
520
|
+
this.runAnalysis(fileEntries);
|
|
521
|
+
this.log(`HMR geological reset complete: ${Math.round(performance.now() - resetStart)}ms`);
|
|
522
|
+
const server = this.devServer;
|
|
523
|
+
if (!server) return;
|
|
524
|
+
for (const moduleId of [
|
|
525
|
+
RESOLVED_CSS_ID,
|
|
526
|
+
RESOLVED_COMPONENTS_ID,
|
|
527
|
+
RESOLVED_SYSTEM_PROPS_ID
|
|
528
|
+
]) {
|
|
529
|
+
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
530
|
+
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
531
|
+
}
|
|
532
|
+
server.hot?.send({ type: "full-reload" });
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* True when the file is a member of the system's evaluated module-file
|
|
536
|
+
* set. Tested BEFORE component-scan exclude filters: a system dependency
|
|
537
|
+
* edit invalidates the compiler registry regardless of what the user
|
|
538
|
+
* excluded from component scanning.
|
|
539
|
+
*/
|
|
540
|
+
isSystemDependency(absFile) {
|
|
541
|
+
return (0, _animus_ui_extract_pipeline.toWatchKeys)(absFile).some((key) => this.systemDependencyKeys.has(key));
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Ask the dev watcher to watch every loader-reported dependency path.
|
|
545
|
+
* Covers system modules unreachable from app imports (workspace package
|
|
546
|
+
* config files). Vite's watcher hard-ignores `node_modules`, so
|
|
547
|
+
* node_modules-installed system dependencies produce no events — a
|
|
548
|
+
* documented limitation; workspace deps resolve to real paths outside
|
|
549
|
+
* node_modules and are watchable. No-ops without a dev server.
|
|
550
|
+
*/
|
|
551
|
+
registerSystemWatchPaths() {
|
|
552
|
+
const watcher = this.devServer?.watcher;
|
|
553
|
+
if (!watcher || this.systemDependencyPaths.length === 0) return;
|
|
554
|
+
watcher.add(this.systemDependencyPaths);
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Invalidate the component CSS and system-props virtual modules, then
|
|
558
|
+
* reload the client. Shared by the two out-of-band re-analysis paths — a
|
|
559
|
+
* file created after buildStart (transform) and a file deleted during dev
|
|
560
|
+
* (the `hotUpdate` delete event) — which both mutate the cache, re-run
|
|
561
|
+
* analysis, and then need the client to pick up the regenerated CSS.
|
|
562
|
+
* No-ops outside dev.
|
|
563
|
+
*
|
|
564
|
+
* `devServer.moduleGraph` is Vite's back-compat mixed graph: its
|
|
565
|
+
* `getModuleById` searches the client AND ssr environment graphs and its
|
|
566
|
+
* `invalidateModule` invalidates both instances behind the returned node,
|
|
567
|
+
* which is exactly the reach this path wants. It stays the seam here.
|
|
568
|
+
*/
|
|
569
|
+
invalidateExtractedModules() {
|
|
570
|
+
const server = this.devServer;
|
|
571
|
+
if (!server) return;
|
|
572
|
+
for (const moduleId of [RESOLVED_COMPONENTS_ID, RESOLVED_SYSTEM_PROPS_ID]) {
|
|
573
|
+
const mod = server.moduleGraph.getModuleById(moduleId);
|
|
574
|
+
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
575
|
+
}
|
|
576
|
+
setTimeout(() => {
|
|
577
|
+
this.devServer?.hot?.send({ type: "full-reload" });
|
|
578
|
+
}, 100);
|
|
579
|
+
}
|
|
580
|
+
runSelfVerify() {
|
|
358
581
|
const failures = [];
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
if (
|
|
362
|
-
if (
|
|
582
|
+
if (Object.keys(this.storedManifest?.components ?? {}).length === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
583
|
+
for (const { specifier, outcome } of this.externalPackageOutcomes) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources`);
|
|
584
|
+
if (!this.system.variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
|
|
585
|
+
if (`${this.system.variableCss}\n${this.globalCss}\n${this.resolvedComponentCss}`.includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
586
|
+
if (this.storedManifest && this.resolvedComponentCss.length > 0) {
|
|
363
587
|
const assembled = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
364
|
-
layers: options.layers,
|
|
365
|
-
variableCss,
|
|
366
|
-
globalCss,
|
|
367
|
-
componentCss: resolvedComponentCss
|
|
588
|
+
layers: this.options.layers,
|
|
589
|
+
variableCss: this.system.variableCss,
|
|
590
|
+
globalCss: this.globalCss,
|
|
591
|
+
componentCss: this.resolvedComponentCss
|
|
368
592
|
});
|
|
369
593
|
const baseIdx = assembled.search(/@layer\s+anm-base\s*\{/);
|
|
370
594
|
const variantsIdx = assembled.search(/@layer\s+anm-variants\s*\{/);
|
|
@@ -372,265 +596,323 @@ function animusExtract(options) {
|
|
|
372
596
|
}
|
|
373
597
|
for (const message of failures) {
|
|
374
598
|
const line = `[animus:verify] ${message}`;
|
|
375
|
-
if (options.strict) throw new Error(line);
|
|
376
|
-
if (logger) logger.warn(line, { timestamp: true });
|
|
599
|
+
if (this.options.strict) throw new Error(line);
|
|
600
|
+
if (this.logger) this.logger.warn(line, { timestamp: true });
|
|
377
601
|
else console.warn(line);
|
|
378
602
|
}
|
|
379
|
-
if (failures.length === 0) log("[animus:verify] structural self-check passed");
|
|
603
|
+
if (failures.length === 0) this.log("[animus:verify] structural self-check passed");
|
|
380
604
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
source
|
|
478
|
-
});
|
|
479
|
-
} catch {}
|
|
480
|
-
const localFileCount = fileEntries.length;
|
|
481
|
-
const packageSpecifiers = (0, _animus_ui_extract_pipeline.extractSystemFilePackages)(resolvedSystemPath);
|
|
482
|
-
packageMap = {};
|
|
483
|
-
externalSourceEntries.clear();
|
|
484
|
-
const collectedPkgDirs = [];
|
|
485
|
-
for (const specifier of packageSpecifiers) try {
|
|
486
|
-
const resolved = await this.resolve(specifier);
|
|
487
|
-
if (resolved && resolved.id) {
|
|
488
|
-
const absPath = resolved.id;
|
|
489
|
-
let pkgRoot = (0, path.dirname)(absPath);
|
|
490
|
-
while (pkgRoot !== (0, path.dirname)(pkgRoot) && !(0, fs.existsSync)((0, path.join)(pkgRoot, "package.json"))) pkgRoot = (0, path.dirname)(pkgRoot);
|
|
491
|
-
const srcDir = (0, path.join)(pkgRoot, "src");
|
|
492
|
-
if ((0, fs.existsSync)(srcDir)) {
|
|
493
|
-
collectedPkgDirs.push(srcDir);
|
|
494
|
-
const pkgFiles = discoverFiles(srcDir, rootDir, excludePatterns, extensionsSet);
|
|
495
|
-
for (const pkgFile of pkgFiles) {
|
|
496
|
-
let pkgRelPath = (0, path.relative)(rootDir, pkgFile);
|
|
497
|
-
if (!fileEntries.some((e) => e.path === pkgRelPath)) {
|
|
498
|
-
let pkgSource = (0, fs.readFileSync)(pkgFile, "utf-8");
|
|
499
|
-
if (shouldHandleMdx && (0, path.extname)(pkgFile) === ".mdx") {
|
|
500
|
-
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(pkgSource, pkgRelPath);
|
|
501
|
-
if (result.kind === "missing-dep") {
|
|
502
|
-
if (!mdxMissingDepWarned) {
|
|
503
|
-
warn("⚠ .mdx in extensions but @mdx-js/mdx not installed; MDX files skipped");
|
|
504
|
-
mdxMissingDepWarned = true;
|
|
505
|
-
}
|
|
506
|
-
continue;
|
|
507
|
-
}
|
|
508
|
-
if (result.kind === "error") {
|
|
509
|
-
warn(`⚠ MDX preprocessing failed for ${pkgRelPath}: ${result.error}`);
|
|
510
|
-
continue;
|
|
511
|
-
}
|
|
512
|
-
pkgSource = result.source;
|
|
513
|
-
pkgRelPath = pkgRelPath + ".tsx";
|
|
514
|
-
}
|
|
515
|
-
const pkgHash = !isProd ? contentHash(pkgSource) : void 0;
|
|
516
|
-
fileEntries.push({
|
|
517
|
-
path: pkgRelPath,
|
|
518
|
-
source: pkgSource,
|
|
519
|
-
hash: pkgHash
|
|
520
|
-
});
|
|
521
|
-
if (!isProd && pkgHash) fileCache.set(pkgRelPath, {
|
|
522
|
-
hash: pkgHash,
|
|
523
|
-
source: pkgSource
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
const srcEntry = (0, path.join)(srcDir, "index.ts");
|
|
528
|
-
if ((0, fs.existsSync)(srcEntry)) {
|
|
529
|
-
packageMap[specifier] = (0, path.relative)(rootDir, srcEntry);
|
|
530
|
-
externalSourceEntries.set(specifier, srcEntry);
|
|
531
|
-
} else packageMap[specifier] = (0, path.relative)(rootDir, absPath);
|
|
532
|
-
} else {
|
|
533
|
-
const relPath = (0, path.relative)(rootDir, absPath);
|
|
534
|
-
if (!fileEntries.some((e) => e.path === relPath)) {
|
|
535
|
-
const entrySource = (0, fs.readFileSync)(absPath, "utf-8");
|
|
536
|
-
const entryHash = !isProd ? contentHash(entrySource) : void 0;
|
|
537
|
-
fileEntries.push({
|
|
538
|
-
path: relPath,
|
|
539
|
-
source: entrySource,
|
|
540
|
-
hash: entryHash
|
|
541
|
-
});
|
|
542
|
-
if (!isProd && entryHash) fileCache.set(relPath, {
|
|
543
|
-
hash: entryHash,
|
|
544
|
-
source: entrySource
|
|
545
|
-
});
|
|
546
|
-
}
|
|
547
|
-
collectedPkgDirs.push((0, path.dirname)(absPath));
|
|
548
|
-
packageMap[specifier] = relPath;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
} catch {}
|
|
552
|
-
externalPackageDirs = collectedPkgDirs;
|
|
553
|
-
const packageFileCount = fileEntries.length - localFileCount;
|
|
554
|
-
log(`Discovered ${fileEntries.length} files (${packageFileCount} from packages) (${Math.round(performance.now() - t0)}ms)`);
|
|
555
|
-
t0 = performance.now();
|
|
556
|
-
runAnalysis(fileEntries);
|
|
557
|
-
if (storedManifest) {
|
|
558
|
-
const report = storedManifest.report;
|
|
559
|
-
if (report) {
|
|
560
|
-
log(`Extracted ${report.components_extracted}/${report.components_total} components (${Math.round(performance.now() - t0)}ms)`);
|
|
561
|
-
logTimingWaterfall(storedManifest.timing ?? {});
|
|
562
|
-
log(`Reconciliation: ${report.components_extracted} kept, ${report.variants_eliminated} variants pruned, ${report.states_eliminated} states pruned`);
|
|
563
|
-
const details = report.eliminated_details || [];
|
|
564
|
-
for (const d of details) if (d.kind === "component") warn(`⚠ ${d.component} eliminated: ${d.reason}`);
|
|
565
|
-
else if (d.kind === "prospective_component") warn(`⚠ ${d.component} would be eliminated in production: ${d.reason}`);
|
|
566
|
-
else if (d.kind === "variant") warn(`⚠ ${d.component} variant '${d.name}' pruned: ${d.reason}`);
|
|
567
|
-
else if (d.kind === "state") warn(`⚠ ${d.component} state '${d.name}' pruned: ${d.reason}`);
|
|
568
|
-
}
|
|
569
|
-
log(`CSS: ${resolvedComponentCss.length} bytes (${Object.keys(storedManifest.components || {}).length} components)`);
|
|
570
|
-
if (!isProd && storedSheets) {
|
|
571
|
-
const staticSize = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
572
|
-
layers: options.layers,
|
|
573
|
-
variableCss,
|
|
574
|
-
globalCss
|
|
575
|
-
}).length;
|
|
576
|
-
const componentSize = resolvedComponentCss.length;
|
|
577
|
-
log(`Delivery: split mode — static ${staticSize} bytes, components ${componentSize} bytes (adopted stylesheet)`);
|
|
578
|
-
} else log("Delivery: single file mode (production)");
|
|
605
|
+
};
|
|
606
|
+
//#endregion
|
|
607
|
+
//#region src/hmr.ts
|
|
608
|
+
/**
|
|
609
|
+
* hotUpdate: the single dev file-event hook. Handles system-dependency
|
|
610
|
+
* membership (geological reset), content-hash diffing with incremental
|
|
611
|
+
* re-analysis, deleted-file cache pruning, and targeted module invalidation
|
|
612
|
+
* (component CSS, system props, and definition files whose replacement
|
|
613
|
+
* changed).
|
|
614
|
+
*
|
|
615
|
+
* Vite 8 dispatches this hook once per environment for ONE file event — the
|
|
616
|
+
* client environment first, then every non-client environment (see
|
|
617
|
+
* `handleHMRUpdate` in vite/dist/node/chunks/node.js). The analysis half is
|
|
618
|
+
* therefore claimed by exactly one dispatch (`ctx.hotUpdateEvents`), while the
|
|
619
|
+
* invalidation half runs in every environment against its own module graph —
|
|
620
|
+
* what the mixed-graph `handleHotUpdate` used to achieve implicitly by
|
|
621
|
+
* invalidating the client and SSR instance behind one module node.
|
|
622
|
+
*
|
|
623
|
+
* The hook fires for every watched file whether or not it has modules in any
|
|
624
|
+
* graph, so system dependencies registered through `watcher.add` outside the
|
|
625
|
+
* root still reach the reset branch, with an empty `modules` list.
|
|
626
|
+
*/
|
|
627
|
+
async function handleHotUpdate(ctx, environment, { type, file, timestamp, modules }) {
|
|
628
|
+
if (ctx.isProd) return;
|
|
629
|
+
const ownsEvent = ctx.hotUpdateEvents.claim(environment.name, file, timestamp);
|
|
630
|
+
const absFile = (0, path.resolve)(file);
|
|
631
|
+
ctx.log(`hotUpdate ${type} ${(0, path.relative)(ctx.rootDir, absFile)} env=${environment.name} owns=${ownsEvent}`);
|
|
632
|
+
if (ctx.isSystemDependency(absFile)) {
|
|
633
|
+
if (ownsEvent) ctx.requestGeologicalReset((0, path.relative)(ctx.rootDir, absFile));
|
|
634
|
+
return [];
|
|
635
|
+
}
|
|
636
|
+
if (type === "create") return;
|
|
637
|
+
if (type === "delete") {
|
|
638
|
+
if (ownsEvent) pruneDeletedFile(ctx, absFile);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (ownsEvent) ctx.hotUpdateEvents.record(file, timestamp, await analyzeChangedFile(ctx, file, absFile));
|
|
642
|
+
const result = ctx.hotUpdateEvents.resultOf(file, timestamp);
|
|
643
|
+
if (result.kind === "ignored") return;
|
|
644
|
+
if (result.kind === "unchanged") return [];
|
|
645
|
+
return invalidateStaleModules(ctx, environment, modules, result.staleDefinitionFiles);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* The once-per-event analysis half: gate the file, diff its content hash,
|
|
649
|
+
* refresh the cache, and re-run project analysis. The returned result is what
|
|
650
|
+
* the remaining environments act on.
|
|
651
|
+
*/
|
|
652
|
+
async function analyzeChangedFile(ctx, file, absFile) {
|
|
653
|
+
const ext = (0, path.extname)(file);
|
|
654
|
+
if (!ctx.extensionsSet.has(ext)) return { kind: "ignored" };
|
|
655
|
+
const excludePatterns = ctx.options.exclude ?? DEFAULT_EXCLUDE;
|
|
656
|
+
if (!ctx.externalPackageDirs.some((dir) => file.startsWith(dir + path.sep) || file === dir) && excludePatterns.some((pattern) => file.includes(pattern) || (0, path.relative)(ctx.rootDir, file).includes(pattern))) return { kind: "ignored" };
|
|
657
|
+
const relPath = (0, path.relative)(ctx.rootDir, absFile);
|
|
658
|
+
let source;
|
|
659
|
+
try {
|
|
660
|
+
source = (0, fs.readFileSync)(absFile, "utf-8");
|
|
661
|
+
} catch {
|
|
662
|
+
return { kind: "ignored" };
|
|
663
|
+
}
|
|
664
|
+
let scannerRelPath = relPath;
|
|
665
|
+
if (ext === ".mdx") {
|
|
666
|
+
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(source, relPath);
|
|
667
|
+
if (result.kind === "missing-dep") {
|
|
668
|
+
ctx.warn("⚠ .mdx HMR skipped: @mdx-js/mdx not installed; restart dev server after installing");
|
|
669
|
+
return { kind: "ignored" };
|
|
670
|
+
}
|
|
671
|
+
if (result.kind === "error") {
|
|
672
|
+
ctx.warn(`⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
673
|
+
return { kind: "ignored" };
|
|
674
|
+
}
|
|
675
|
+
source = result.source;
|
|
676
|
+
scannerRelPath = relPath + ".tsx";
|
|
677
|
+
}
|
|
678
|
+
const hash = (0, _animus_ui_extract_pipeline.contentHash)(source);
|
|
679
|
+
const cached = ctx.fileCache.get(scannerRelPath);
|
|
680
|
+
if (cached && cached.hash === hash) {
|
|
681
|
+
ctx.log(`HMR skip: ${scannerRelPath} (unchanged)`);
|
|
682
|
+
return { kind: "unchanged" };
|
|
683
|
+
}
|
|
684
|
+
ctx.fileCache.set(scannerRelPath, {
|
|
685
|
+
hash,
|
|
686
|
+
source
|
|
687
|
+
});
|
|
688
|
+
const hmrStart = performance.now();
|
|
689
|
+
const prevReplacements = /* @__PURE__ */ new Map();
|
|
690
|
+
if (ctx.storedManifest?.components) for (const [id, desc] of Object.entries(ctx.storedManifest.components)) prevReplacements.set(id, desc.replacement ?? "");
|
|
691
|
+
const directComponentIds = ctx.storedManifest?.files?.[scannerRelPath] ?? [];
|
|
692
|
+
const invalidatedIds = new Set(directComponentIds);
|
|
693
|
+
const queue = [...directComponentIds];
|
|
694
|
+
while (queue.length > 0) {
|
|
695
|
+
const parentId = queue.shift();
|
|
696
|
+
const children = ctx.reverseProvenance[parentId];
|
|
697
|
+
if (children) {
|
|
698
|
+
for (const childId of children) if (!invalidatedIds.has(childId)) {
|
|
699
|
+
invalidatedIds.add(childId);
|
|
700
|
+
queue.push(childId);
|
|
579
701
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (ctx.verbose && invalidatedIds.size > directComponentIds.length) ctx.log(`HMR: ${directComponentIds.length} direct + ${invalidatedIds.size - directComponentIds.length} transitive components invalidated`);
|
|
705
|
+
const analysisStart = performance.now();
|
|
706
|
+
const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relPath);
|
|
707
|
+
ctx.runAnalysis(fileEntries);
|
|
708
|
+
const analysisMs = Math.round(performance.now() - analysisStart);
|
|
709
|
+
const staleDefinitionFiles = [];
|
|
710
|
+
if (ctx.storedManifest?.components) {
|
|
711
|
+
const staleFiles = /* @__PURE__ */ new Set();
|
|
712
|
+
for (const [id, desc] of Object.entries(ctx.storedManifest.components)) if ((desc.replacement ?? "") !== (prevReplacements.get(id) ?? "")) staleFiles.add(desc.file);
|
|
713
|
+
for (const defFile of staleFiles) {
|
|
714
|
+
if ((0, path.resolve)(ctx.rootDir, defFile) === absFile) continue;
|
|
715
|
+
staleDefinitionFiles.push(defFile);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const hmrMs = Math.round(performance.now() - hmrStart);
|
|
719
|
+
ctx.log(`HMR update: ${relPath} — analysis ${analysisMs}ms, total ${hmrMs}ms`);
|
|
720
|
+
ctx.logTimingWaterfall(ctx.storedManifest?.timing ?? {});
|
|
721
|
+
return {
|
|
722
|
+
kind: "analyzed",
|
|
723
|
+
staleDefinitionFiles
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Reconcile a deleted file in dev.
|
|
728
|
+
*
|
|
729
|
+
* Without this the removed file's last-known source stays in `ctx.fileCache`,
|
|
730
|
+
* and `buildFileEntriesFromCache` re-feeds that ghost entry to the engine on
|
|
731
|
+
* every later re-analysis — the deleted component's CSS survives for the life
|
|
732
|
+
* of the process.
|
|
733
|
+
*/
|
|
734
|
+
function pruneDeletedFile(ctx, absFile) {
|
|
735
|
+
if (!pruneFileCache(ctx.fileCache, ctx.rootDir, absFile)) return;
|
|
736
|
+
ctx.runAnalysis(buildFileEntriesFromCache(ctx.fileCache));
|
|
737
|
+
ctx.log(`Deleted file pruned: ${(0, path.relative)(ctx.rootDir, absFile)}`);
|
|
738
|
+
ctx.invalidateExtractedModules();
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* The per-environment invalidation half: invalidate the modules this
|
|
742
|
+
* environment serves and widen its update set with them. Static CSS
|
|
743
|
+
* (virtual:animus/styles.css) is NOT invalidated here — it only changes on a
|
|
744
|
+
* geological reset (vars/globals are stable during dev).
|
|
745
|
+
*/
|
|
746
|
+
function invalidateStaleModules(ctx, environment, modules, staleDefinitionFiles) {
|
|
747
|
+
const graph = environment.moduleGraph;
|
|
748
|
+
const modulesToUpdate = [...modules];
|
|
749
|
+
for (const moduleId of [RESOLVED_COMPONENTS_ID, RESOLVED_SYSTEM_PROPS_ID]) {
|
|
750
|
+
const mod = graph.getModuleById(moduleId);
|
|
751
|
+
if (mod) {
|
|
752
|
+
graph.invalidateModule(mod);
|
|
753
|
+
modulesToUpdate.push(mod);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
for (const defFile of staleDefinitionFiles) {
|
|
757
|
+
const absDefPath = (0, path.resolve)(ctx.rootDir, defFile);
|
|
758
|
+
const defModule = graph.getModuleById(absDefPath) ?? graph.getModulesByFile(absDefPath)?.values().next().value;
|
|
759
|
+
if (defModule) {
|
|
760
|
+
ctx.log(`HMR invalidate: ${defFile} (replacement changed)`);
|
|
761
|
+
graph.invalidateModule(defModule);
|
|
762
|
+
modulesToUpdate.push(defModule);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
const invalidated = modulesToUpdate.length - modules.length;
|
|
766
|
+
if (invalidated > 0) {
|
|
767
|
+
ctx.log(`HMR (${environment.name}): ${invalidated} modules invalidated`);
|
|
768
|
+
return modulesToUpdate;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
//#endregion
|
|
772
|
+
//#region src/index-html.ts
|
|
773
|
+
/**
|
|
774
|
+
* transformIndexHtml: build the head-prepend tag set.
|
|
775
|
+
*
|
|
776
|
+
* Two independent tags, both delivery-only:
|
|
777
|
+
* - the appearance bootstrap script, when the host build passed a
|
|
778
|
+
* pre-generated artifact via `appearanceBootstrap` (absent otherwise);
|
|
779
|
+
* - the `@layer` declaration style, when `buildStart` computed one.
|
|
780
|
+
*
|
|
781
|
+
* Both branches are guarded on non-empty CONTENT, not on mere presence: an
|
|
782
|
+
* artifact whose `code` is empty is a caller defect, and emitting
|
|
783
|
+
* `<script data-animus-bootstrap><\/script>` for it would break the
|
|
784
|
+
* unconfigured-parity contract in spirit (a tag that does nothing) while
|
|
785
|
+
* hiding the defect. Symmetric with `layerDeclaration`.
|
|
786
|
+
*
|
|
787
|
+
* Ordering: every descriptor rides the same `head-prepend` bucket, which Vite
|
|
788
|
+
* serializes in array order (`serializeTags`) and splices in immediately after
|
|
789
|
+
* `<head>`. The bootstrap script is therefore FIRST in the array so it runs
|
|
790
|
+
* before the layer declaration and — since built stylesheet links are appended
|
|
791
|
+
* at `</head>` — before any stylesheet reference.
|
|
792
|
+
*
|
|
793
|
+
* When neither input is set the array is empty, so an unconfigured build emits
|
|
794
|
+
* no tag, attribute, or whitespace of its own.
|
|
795
|
+
*/
|
|
796
|
+
function buildIndexHtmlTags(ctx) {
|
|
797
|
+
const tags = [];
|
|
798
|
+
const bootstrap = ctx.options.appearanceBootstrap;
|
|
799
|
+
if (bootstrap?.code) tags.push({
|
|
800
|
+
tag: "script",
|
|
801
|
+
attrs: { "data-animus-bootstrap": "" },
|
|
802
|
+
children: bootstrap.code,
|
|
803
|
+
injectTo: "head-prepend"
|
|
804
|
+
});
|
|
805
|
+
if (ctx.layerDeclaration) tags.push({
|
|
806
|
+
tag: "style",
|
|
807
|
+
attrs: { "data-animus-layers": "" },
|
|
808
|
+
children: ctx.layerDeclaration,
|
|
809
|
+
injectTo: "head-prepend"
|
|
810
|
+
});
|
|
811
|
+
return tags;
|
|
812
|
+
}
|
|
813
|
+
//#endregion
|
|
814
|
+
//#region src/transform.ts
|
|
815
|
+
/**
|
|
816
|
+
* transform: replace builder chains with `createComponent()` calls using
|
|
817
|
+
* the pre-built manifest; inject the HMR bridge import once in dev; detect
|
|
818
|
+
* files created after buildStart and fold them into the analysis.
|
|
819
|
+
*/
|
|
820
|
+
function transformSource(ctx, code, id) {
|
|
821
|
+
if (!ctx.storedManifest) return null;
|
|
822
|
+
if (!ctx.externalPackageDirs.some((dir) => id.startsWith(dir + path.sep) || id === dir)) {
|
|
823
|
+
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
824
|
+
if (id.includes("node_modules")) return null;
|
|
825
|
+
}
|
|
826
|
+
const relativePath = (0, path.relative)(ctx.rootDir, id);
|
|
827
|
+
if (!ctx.storedManifest.files?.[relativePath]?.length) {
|
|
828
|
+
if (!ctx.isProd && !ctx.fileCache.has(relativePath)) {
|
|
829
|
+
const hash = (0, _animus_ui_extract_pipeline.contentHash)(code);
|
|
830
|
+
ctx.fileCache.set(relativePath, {
|
|
831
|
+
hash,
|
|
832
|
+
source: code
|
|
833
|
+
});
|
|
834
|
+
const fileEntries = buildFileEntriesFromCache(ctx.fileCache, relativePath);
|
|
835
|
+
ctx.runAnalysis(fileEntries);
|
|
836
|
+
const compCount = ctx.storedManifest.files?.[relativePath]?.length ?? 0;
|
|
837
|
+
ctx.log(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
|
|
838
|
+
if (compCount) ctx.invalidateExtractedModules();
|
|
839
|
+
}
|
|
840
|
+
if (!ctx.storedManifest.files?.[relativePath]?.length) return null;
|
|
841
|
+
}
|
|
842
|
+
try {
|
|
843
|
+
const { transformFile } = ctx.engineApi();
|
|
844
|
+
const result = transformFile(code, relativePath, ctx.storedManifestJson);
|
|
845
|
+
if (!result.hasComponents) return null;
|
|
846
|
+
let transformedCode = result.code;
|
|
847
|
+
if (!ctx.isProd && !ctx.bridgeInjected && ctx.storedSheets) {
|
|
848
|
+
transformedCode = `import '${VIRTUAL_BRIDGE_ID}';\n${transformedCode}`;
|
|
849
|
+
ctx.bridgeInjected = true;
|
|
850
|
+
ctx.log("HMR bridge injected via transform");
|
|
851
|
+
}
|
|
852
|
+
if (ctx.verbose) {
|
|
853
|
+
const compCount = ctx.storedManifest.files?.[relativePath]?.length ?? 0;
|
|
854
|
+
ctx.log(`transform ${relativePath}: ${compCount} components`);
|
|
855
|
+
}
|
|
856
|
+
return {
|
|
857
|
+
code: transformedCode,
|
|
858
|
+
map: null
|
|
859
|
+
};
|
|
860
|
+
} catch (e) {
|
|
861
|
+
if (ctx.options.strict) throw new Error(`[animus-extract] Failed to transform ${id}: ${e}`, { cause: e });
|
|
862
|
+
console.warn(`[animus-extract] Failed to transform ${id}:`, e);
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
//#endregion
|
|
867
|
+
//#region src/virtual-modules.ts
|
|
868
|
+
/** resolveId: map virtual ids and redirect external DS package imports. */
|
|
869
|
+
function resolveVirtualId(ctx, id) {
|
|
870
|
+
if (id === "virtual:animus/styles.css") return RESOLVED_CSS_ID;
|
|
871
|
+
if (id === "virtual:animus/components.js") return RESOLVED_COMPONENTS_ID;
|
|
872
|
+
if (id === "virtual:animus/hmr-bridge.js") return RESOLVED_BRIDGE_ID;
|
|
873
|
+
if (id === "virtual:animus/system-props") return RESOLVED_SYSTEM_PROPS_ID;
|
|
874
|
+
const srcEntry = ctx.externalSourceEntries.get(id);
|
|
875
|
+
if (srcEntry) return srcEntry;
|
|
876
|
+
return null;
|
|
877
|
+
}
|
|
878
|
+
/** load: serve the virtual stylesheet, components CSS, HMR bridge, and
|
|
879
|
+
* system-props modules from manifest-derived state. */
|
|
880
|
+
function loadVirtualModule(ctx, id) {
|
|
881
|
+
const lcssOpts = {
|
|
882
|
+
minify: ctx.options.minify ?? ctx.isProd,
|
|
883
|
+
targets: ctx.lcssTargets,
|
|
884
|
+
warnFn: (msg) => ctx.warn(msg)
|
|
885
|
+
};
|
|
886
|
+
if (id === "\0virtual:animus/styles.css") {
|
|
887
|
+
if (!ctx.isProd && ctx.storedSheets) {
|
|
888
|
+
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
889
|
+
layers: ctx.options.layers,
|
|
890
|
+
variableCss: ctx.system.variableCss,
|
|
891
|
+
globalCss: ctx.globalCss,
|
|
584
892
|
split: true
|
|
585
893
|
});
|
|
586
|
-
|
|
587
|
-
if (options.verify) runSelfVerify();
|
|
588
|
-
},
|
|
589
|
-
resolveId(id) {
|
|
590
|
-
if (id === VIRTUAL_CSS_ID) return RESOLVED_CSS_ID;
|
|
591
|
-
if (id === VIRTUAL_COMPONENTS_ID) return RESOLVED_COMPONENTS_ID;
|
|
592
|
-
if (id === VIRTUAL_BRIDGE_ID) return RESOLVED_BRIDGE_ID;
|
|
593
|
-
if (id === VIRTUAL_SYSTEM_PROPS_ID) return RESOLVED_SYSTEM_PROPS_ID;
|
|
594
|
-
const srcEntry = externalSourceEntries.get(id);
|
|
595
|
-
if (srcEntry) return srcEntry;
|
|
596
|
-
return null;
|
|
597
|
-
},
|
|
598
|
-
load(id) {
|
|
599
|
-
const lcssOpts = {
|
|
600
|
-
minify: options.minify ?? isProd,
|
|
601
|
-
targets: lcssTargets,
|
|
602
|
-
warnFn: warn
|
|
603
|
-
};
|
|
604
|
-
if (id === RESOLVED_CSS_ID) {
|
|
605
|
-
if (!isProd && storedSheets) {
|
|
606
|
-
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
607
|
-
layers: options.layers,
|
|
608
|
-
variableCss,
|
|
609
|
-
globalCss,
|
|
610
|
-
split: true
|
|
611
|
-
});
|
|
612
|
-
return [variables, postProcessCss(body, {
|
|
613
|
-
...lcssOpts,
|
|
614
|
-
minify: false
|
|
615
|
-
})].filter(Boolean).join("\n");
|
|
616
|
-
}
|
|
617
|
-
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
618
|
-
layers: options.layers,
|
|
619
|
-
variableCss,
|
|
620
|
-
globalCss,
|
|
621
|
-
componentCss: resolvedComponentCss,
|
|
622
|
-
split: true
|
|
623
|
-
});
|
|
624
|
-
return [variables, postProcessCss(body, lcssOpts)].filter(Boolean).join("\n");
|
|
625
|
-
}
|
|
626
|
-
if (id === RESOLVED_COMPONENTS_ID) return `export default \`${postProcessCss((0, _animus_ui_extract_pipeline.stripLeadingLayerDeclaration)(resolvedComponentCss || ""), {
|
|
894
|
+
return [variables, (0, _animus_ui_extract_pipeline.postProcessCss)(body, {
|
|
627
895
|
...lcssOpts,
|
|
628
896
|
minify: false
|
|
629
|
-
}).
|
|
630
|
-
|
|
897
|
+
})].filter(Boolean).join("\n");
|
|
898
|
+
}
|
|
899
|
+
const { variables, body } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
900
|
+
layers: ctx.options.layers,
|
|
901
|
+
variableCss: ctx.system.variableCss,
|
|
902
|
+
globalCss: ctx.globalCss,
|
|
903
|
+
componentCss: ctx.resolvedComponentCss,
|
|
904
|
+
split: true
|
|
905
|
+
});
|
|
906
|
+
return [variables, (0, _animus_ui_extract_pipeline.postProcessCss)(body, lcssOpts)].filter(Boolean).join("\n");
|
|
907
|
+
}
|
|
908
|
+
if (id === "\0virtual:animus/components.js") return `export default \`${(0, _animus_ui_extract_pipeline.postProcessCss)((0, _animus_ui_extract_pipeline.stripLeadingLayerDeclaration)(ctx.resolvedComponentCss || ""), {
|
|
909
|
+
...lcssOpts,
|
|
910
|
+
minify: false
|
|
911
|
+
}).replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$")}\`;`;
|
|
912
|
+
if (id === "\0virtual:animus/hmr-bridge.js") return `
|
|
631
913
|
import css from '${VIRTUAL_COMPONENTS_ID}';
|
|
632
914
|
|
|
633
|
-
const GLOBAL_KEY = '__animus_sheet_${(0, crypto.createHash)("md5").update(options.system).digest("hex").slice(0, 8)}__';
|
|
915
|
+
const GLOBAL_KEY = '__animus_sheet_${(0, crypto.createHash)("md5").update(ctx.options.system).digest("hex").slice(0, 8)}__';
|
|
634
916
|
let sheet = globalThis[GLOBAL_KEY] || null;
|
|
635
917
|
|
|
636
918
|
if (typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in document) {
|
|
@@ -662,211 +944,65 @@ if (import.meta.hot) {
|
|
|
662
944
|
});
|
|
663
945
|
}
|
|
664
946
|
`;
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
947
|
+
if (id === "\0virtual:animus/system-props") return (0, _animus_ui_extract_pipeline.buildSystemPropsModule)({
|
|
948
|
+
systemPropMapJson: ctx.storedSystemPropMapJson,
|
|
949
|
+
groupRegistryJson: ctx.system.groupRegistryJson,
|
|
950
|
+
dynamicProps: JSON.parse(ctx.storedDynamicPropsJson),
|
|
951
|
+
transformsSource: ctx.storedTransformsSource
|
|
952
|
+
});
|
|
953
|
+
return null;
|
|
954
|
+
}
|
|
955
|
+
//#endregion
|
|
956
|
+
//#region src/index.ts
|
|
957
|
+
/**
|
|
958
|
+
* Vite adapter for the extraction pipeline. State and pipeline operations
|
|
959
|
+
* live in PluginContext; hook bodies live in their own modules — this
|
|
960
|
+
* factory only validates options and wires Vite hooks to those functions.
|
|
961
|
+
*/
|
|
962
|
+
function animusExtract(options) {
|
|
963
|
+
(0, _animus_ui_extract_pipeline.assertNoRetiredEngineSelection)(options.engine);
|
|
964
|
+
const ctx = new PluginContext(options);
|
|
965
|
+
return {
|
|
966
|
+
name: "animus-extract",
|
|
967
|
+
enforce: "pre",
|
|
968
|
+
configureServer(server) {
|
|
969
|
+
ctx.devServer = server;
|
|
970
|
+
ctx.registerSystemWatchPaths();
|
|
971
|
+
},
|
|
972
|
+
configResolved(config) {
|
|
973
|
+
applyResolvedConfig(ctx, config);
|
|
974
|
+
},
|
|
975
|
+
async buildStart() {
|
|
976
|
+
await runBuildStart(ctx, async (specifier) => {
|
|
977
|
+
return (await this.resolve(specifier))?.id ?? null;
|
|
978
|
+
});
|
|
979
|
+
},
|
|
980
|
+
resolveId(id) {
|
|
981
|
+
return resolveVirtualId(ctx, id);
|
|
982
|
+
},
|
|
983
|
+
load(id) {
|
|
984
|
+
return loadVirtualModule(ctx, id);
|
|
682
985
|
},
|
|
683
986
|
transform(code, id) {
|
|
684
|
-
|
|
685
|
-
if (!externalPackageDirs.some((dir) => id.startsWith(dir))) {
|
|
686
|
-
if (!/\.[jt]sx?$/.test(id)) return null;
|
|
687
|
-
if (id.includes("node_modules")) return null;
|
|
688
|
-
}
|
|
689
|
-
const relativePath = (0, path.relative)(rootDir, id);
|
|
690
|
-
if (!storedManifest.files?.[relativePath]?.length) {
|
|
691
|
-
if (!isProd && !fileCache.has(relativePath)) {
|
|
692
|
-
const hash = contentHash(code);
|
|
693
|
-
fileCache.set(relativePath, {
|
|
694
|
-
hash,
|
|
695
|
-
source: code
|
|
696
|
-
});
|
|
697
|
-
runAnalysis(buildFileEntriesFromCache(fileCache, relativePath));
|
|
698
|
-
const compCount = storedManifest.files?.[relativePath]?.length ?? 0;
|
|
699
|
-
log(`New file detected: ${relativePath} — ${compCount ? `${compCount} components extracted` : "no components"}`);
|
|
700
|
-
if (compCount && devServer) {
|
|
701
|
-
const compModule = devServer.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID);
|
|
702
|
-
if (compModule) devServer.moduleGraph.invalidateModule(compModule);
|
|
703
|
-
const sysPropModule = devServer.moduleGraph.getModuleById(RESOLVED_SYSTEM_PROPS_ID);
|
|
704
|
-
if (sysPropModule) devServer.moduleGraph.invalidateModule(sysPropModule);
|
|
705
|
-
setTimeout(() => {
|
|
706
|
-
devServer.hot.send({ type: "full-reload" });
|
|
707
|
-
}, 100);
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
if (!storedManifest.files?.[relativePath]?.length) return null;
|
|
711
|
-
}
|
|
712
|
-
try {
|
|
713
|
-
const { transformFile } = engineApi();
|
|
714
|
-
const result = transformFile(code, relativePath, storedManifestJson);
|
|
715
|
-
if (!result.hasComponents) return null;
|
|
716
|
-
let transformedCode = result.code;
|
|
717
|
-
if (!isProd && !bridgeInjected && storedSheets) {
|
|
718
|
-
transformedCode = `import '${VIRTUAL_BRIDGE_ID}';\n${transformedCode}`;
|
|
719
|
-
bridgeInjected = true;
|
|
720
|
-
log("HMR bridge injected via transform");
|
|
721
|
-
}
|
|
722
|
-
if (verbose) log(`transform ${relativePath}: ${storedManifest.files?.[relativePath]?.length ?? 0} components`);
|
|
723
|
-
return {
|
|
724
|
-
code: transformedCode,
|
|
725
|
-
map: null
|
|
726
|
-
};
|
|
727
|
-
} catch (e) {
|
|
728
|
-
if (options.strict) throw new Error(`[animus-extract] Failed to transform ${id}: ${e}`, { cause: e });
|
|
729
|
-
console.warn(`[animus-extract] Failed to transform ${id}:`, e);
|
|
730
|
-
return null;
|
|
731
|
-
}
|
|
987
|
+
return transformSource(ctx, code, id);
|
|
732
988
|
},
|
|
733
989
|
transformIndexHtml: {
|
|
734
990
|
order: "pre",
|
|
735
991
|
handler() {
|
|
736
|
-
|
|
737
|
-
return [{
|
|
738
|
-
tag: "style",
|
|
739
|
-
attrs: { "data-animus-layers": "" },
|
|
740
|
-
children: layerDeclaration,
|
|
741
|
-
injectTo: "head-prepend"
|
|
742
|
-
}];
|
|
992
|
+
return buildIndexHtmlTags(ctx);
|
|
743
993
|
}
|
|
744
994
|
},
|
|
745
|
-
async
|
|
746
|
-
|
|
747
|
-
const ext = (0, path.extname)(file);
|
|
748
|
-
if (!extensionsSet.has(ext)) return;
|
|
749
|
-
const excludePatterns = options.exclude ?? DEFAULT_EXCLUDE;
|
|
750
|
-
if (!externalPackageDirs.some((dir) => file.startsWith(dir)) && excludePatterns.some((pattern) => file.includes(pattern) || (0, path.relative)(rootDir, file).includes(pattern))) return;
|
|
751
|
-
const absFile = (0, path.resolve)(file);
|
|
752
|
-
const relPath = (0, path.relative)(rootDir, absFile);
|
|
753
|
-
if (resolvedSystemPath && absFile === (0, path.resolve)(resolvedSystemPath)) {
|
|
754
|
-
const resetStart = performance.now();
|
|
755
|
-
log(`HMR geological reset: ${relPath}`);
|
|
756
|
-
loadSystem();
|
|
757
|
-
try {
|
|
758
|
-
const { clearAnalysisCache } = engineApi();
|
|
759
|
-
clearAnalysisCache();
|
|
760
|
-
} catch {}
|
|
761
|
-
const fileEntries = [];
|
|
762
|
-
for (const [path$3, { hash, source }] of fileCache) fileEntries.push({
|
|
763
|
-
path: path$3,
|
|
764
|
-
source,
|
|
765
|
-
hash
|
|
766
|
-
});
|
|
767
|
-
runAnalysis(fileEntries);
|
|
768
|
-
log(`HMR geological reset complete: ${Math.round(performance.now() - resetStart)}ms`);
|
|
769
|
-
const geologicalModules = [...modules];
|
|
770
|
-
const cssModule = hmrServer.moduleGraph.getModuleById(RESOLVED_CSS_ID);
|
|
771
|
-
if (cssModule) {
|
|
772
|
-
hmrServer.moduleGraph.invalidateModule(cssModule);
|
|
773
|
-
geologicalModules.push(cssModule);
|
|
774
|
-
}
|
|
775
|
-
const compModule = hmrServer.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID);
|
|
776
|
-
if (compModule) {
|
|
777
|
-
hmrServer.moduleGraph.invalidateModule(compModule);
|
|
778
|
-
geologicalModules.push(compModule);
|
|
779
|
-
}
|
|
780
|
-
const sysPropModule = hmrServer.moduleGraph.getModuleById(RESOLVED_SYSTEM_PROPS_ID);
|
|
781
|
-
if (sysPropModule) {
|
|
782
|
-
hmrServer.moduleGraph.invalidateModule(sysPropModule);
|
|
783
|
-
geologicalModules.push(sysPropModule);
|
|
784
|
-
}
|
|
785
|
-
return geologicalModules;
|
|
786
|
-
}
|
|
787
|
-
let source;
|
|
788
|
-
try {
|
|
789
|
-
source = (0, fs.readFileSync)(absFile, "utf-8");
|
|
790
|
-
} catch {
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
let scannerRelPath = relPath;
|
|
794
|
-
if (ext === ".mdx") {
|
|
795
|
-
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(source, relPath);
|
|
796
|
-
if (result.kind === "missing-dep") {
|
|
797
|
-
warn("⚠ .mdx HMR skipped: @mdx-js/mdx not installed; restart dev server after installing");
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
if (result.kind === "error") {
|
|
801
|
-
warn(`⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
802
|
-
return;
|
|
803
|
-
}
|
|
804
|
-
source = result.source;
|
|
805
|
-
scannerRelPath = relPath + ".tsx";
|
|
806
|
-
}
|
|
807
|
-
const hash = contentHash(source);
|
|
808
|
-
const cached = fileCache.get(scannerRelPath);
|
|
809
|
-
if (cached && cached.hash === hash) {
|
|
810
|
-
log(`HMR skip: ${scannerRelPath} (unchanged)`);
|
|
811
|
-
return [];
|
|
812
|
-
}
|
|
813
|
-
fileCache.set(scannerRelPath, {
|
|
814
|
-
hash,
|
|
815
|
-
source
|
|
816
|
-
});
|
|
817
|
-
const hmrStart = performance.now();
|
|
818
|
-
const prevReplacements = /* @__PURE__ */ new Map();
|
|
819
|
-
if (storedManifest?.components) for (const [id, desc] of Object.entries(storedManifest.components)) prevReplacements.set(id, desc.replacement ?? "");
|
|
820
|
-
const directComponentIds = storedManifest?.files?.[scannerRelPath] ?? [];
|
|
821
|
-
const invalidatedIds = new Set(directComponentIds);
|
|
822
|
-
const queue = [...directComponentIds];
|
|
823
|
-
while (queue.length > 0) {
|
|
824
|
-
const parentId = queue.shift();
|
|
825
|
-
const children = reverseProvenance[parentId];
|
|
826
|
-
if (children) {
|
|
827
|
-
for (const childId of children) if (!invalidatedIds.has(childId)) {
|
|
828
|
-
invalidatedIds.add(childId);
|
|
829
|
-
queue.push(childId);
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
if (verbose && invalidatedIds.size > directComponentIds.length) log(`HMR: ${directComponentIds.length} direct + ${invalidatedIds.size - directComponentIds.length} transitive components invalidated`);
|
|
834
|
-
const analysisStart = performance.now();
|
|
835
|
-
runAnalysis(buildFileEntriesFromCache(fileCache, relPath));
|
|
836
|
-
const analysisMs = Math.round(performance.now() - analysisStart);
|
|
837
|
-
const modulesToUpdate = [...modules];
|
|
838
|
-
const compModule = hmrServer.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID);
|
|
839
|
-
if (compModule) {
|
|
840
|
-
hmrServer.moduleGraph.invalidateModule(compModule);
|
|
841
|
-
modulesToUpdate.push(compModule);
|
|
842
|
-
}
|
|
843
|
-
const sysPropModule = hmrServer.moduleGraph.getModuleById(RESOLVED_SYSTEM_PROPS_ID);
|
|
844
|
-
if (sysPropModule) {
|
|
845
|
-
hmrServer.moduleGraph.invalidateModule(sysPropModule);
|
|
846
|
-
modulesToUpdate.push(sysPropModule);
|
|
847
|
-
}
|
|
848
|
-
if (storedManifest?.components) {
|
|
849
|
-
const staleFiles = /* @__PURE__ */ new Set();
|
|
850
|
-
for (const [id, desc] of Object.entries(storedManifest.components)) if ((desc.replacement ?? "") !== (prevReplacements.get(id) ?? "")) staleFiles.add(desc.file);
|
|
851
|
-
for (const defFile of staleFiles) {
|
|
852
|
-
const absDefPath = (0, path.resolve)(rootDir, defFile);
|
|
853
|
-
if (absDefPath === absFile) continue;
|
|
854
|
-
const defModule = hmrServer.moduleGraph.getModuleById(absDefPath) ?? hmrServer.moduleGraph.getModulesByFile(absDefPath)?.values().next().value;
|
|
855
|
-
if (defModule) {
|
|
856
|
-
log(`HMR invalidate: ${defFile} (replacement changed)`);
|
|
857
|
-
hmrServer.moduleGraph.invalidateModule(defModule);
|
|
858
|
-
modulesToUpdate.push(defModule);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
const hmrMs = Math.round(performance.now() - hmrStart);
|
|
863
|
-
log(`HMR update: ${relPath} — analysis ${analysisMs}ms, ${modulesToUpdate.length - modules.length} modules invalidated, total ${hmrMs}ms`);
|
|
864
|
-
logTimingWaterfall(storedManifest?.timing ?? {});
|
|
865
|
-
if (modulesToUpdate.length > modules.length) return modulesToUpdate;
|
|
995
|
+
async hotUpdate(hmr) {
|
|
996
|
+
return handleHotUpdate(ctx, this.environment, hmr);
|
|
866
997
|
}
|
|
867
998
|
};
|
|
868
999
|
}
|
|
869
1000
|
//#endregion
|
|
870
1001
|
exports.animusExtract = animusExtract;
|
|
871
1002
|
exports.default = animusExtract;
|
|
872
|
-
exports
|
|
1003
|
+
Object.defineProperty(exports, "discoverFiles", {
|
|
1004
|
+
enumerable: true,
|
|
1005
|
+
get: function() {
|
|
1006
|
+
return _animus_ui_extract_pipeline.discoverFiles;
|
|
1007
|
+
}
|
|
1008
|
+
});
|