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