@animus-ui/vite-plugin 0.1.0-next.v7 → 0.1.1-beta.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 +41 -0
- package/dist/analyze-project-args.d.ts +2 -0
- package/dist/analyze-project-args.d.ts.map +1 -0
- package/dist/index.cjs +872 -0
- package/dist/index.d.ts +53 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/manifest-diagnostics.d.ts +10 -0
- package/dist/manifest-diagnostics.d.ts.map +1 -0
- package/package.json +39 -15
- package/dist/config-serializer.d.ts +0 -25
- package/dist/config-serializer.d.ts.map +0 -1
- package/dist/index.mjs +0 -818
- package/dist/resolve-global-styles.d.ts +0 -13
- package/dist/resolve-global-styles.d.ts.map +0 -1
- package/dist/resolve-transforms.d.ts +0 -14
- package/dist/resolve-transforms.d.ts.map +0 -1
- package/dist/theme-evaluator.d.ts +0 -32
- package/dist/theme-evaluator.d.ts.map +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
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
|
+
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
|
+
let fs = require("fs");
|
|
32
|
+
let lightningcss = require("lightningcss");
|
|
33
|
+
let path = require("path");
|
|
34
|
+
//#region src/analyze-project-args.ts
|
|
35
|
+
/** @internal */
|
|
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
|
|
63
|
+
const VIRTUAL_CSS_ID = "virtual:animus/styles.css";
|
|
64
|
+
const RESOLVED_CSS_ID = "\0virtual:animus/styles.css";
|
|
65
|
+
const VIRTUAL_COMPONENTS_ID = "virtual:animus/components.js";
|
|
66
|
+
const RESOLVED_COMPONENTS_ID = "\0virtual:animus/components.js";
|
|
67
|
+
const VIRTUAL_BRIDGE_ID = "virtual:animus/hmr-bridge.js";
|
|
68
|
+
const RESOLVED_BRIDGE_ID = "\0virtual:animus/hmr-bridge.js";
|
|
69
|
+
const VIRTUAL_SYSTEM_PROPS_ID = "virtual:animus/system-props";
|
|
70
|
+
const RESOLVED_SYSTEM_PROPS_ID = "\0virtual:animus/system-props";
|
|
71
|
+
const DEFAULT_EXCLUDE = [
|
|
72
|
+
"node_modules",
|
|
73
|
+
"dist",
|
|
74
|
+
".test.",
|
|
75
|
+
".spec."
|
|
76
|
+
];
|
|
77
|
+
function discoverFiles(dir, rootDir, excludePatterns, extensionsSet) {
|
|
78
|
+
const results = [];
|
|
79
|
+
let entries;
|
|
80
|
+
try {
|
|
81
|
+
entries = (0, fs.readdirSync)(dir, { encoding: "utf8" });
|
|
82
|
+
} catch {
|
|
83
|
+
return results;
|
|
84
|
+
}
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const fullPath = (0, path.join)(dir, entry);
|
|
87
|
+
const relativePath = (0, path.relative)(rootDir, fullPath);
|
|
88
|
+
if (excludePatterns.some((pattern) => fullPath.includes(pattern) || relativePath.includes(pattern))) continue;
|
|
89
|
+
let stat;
|
|
90
|
+
try {
|
|
91
|
+
stat = (0, fs.statSync)(fullPath);
|
|
92
|
+
} catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (stat.isDirectory()) results.push(...discoverFiles(fullPath, rootDir, excludePatterns, extensionsSet));
|
|
96
|
+
else if (extensionsSet.has((0, path.extname)(entry))) results.push(fullPath);
|
|
97
|
+
}
|
|
98
|
+
return results;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve browser targets for Lightning CSS.
|
|
102
|
+
* Priority: explicit config → project browserslist → 'defaults' fallback.
|
|
103
|
+
*/
|
|
104
|
+
function resolveLightningTargets(explicitTargets, rootDir) {
|
|
105
|
+
let queries;
|
|
106
|
+
if (explicitTargets) queries = Array.isArray(explicitTargets) ? explicitTargets : [explicitTargets];
|
|
107
|
+
else {
|
|
108
|
+
const detected = (0, browserslist.default)(void 0, { path: rootDir });
|
|
109
|
+
queries = detected.length > 0 ? detected : (0, browserslist.default)("defaults");
|
|
110
|
+
}
|
|
111
|
+
return (0, lightningcss.browserslistToTargets)(Array.isArray(queries) && typeof queries[0] === "string" && queries[0].includes(" ") ? (0, browserslist.default)(queries) : queries);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Post-process CSS with Lightning CSS: autoprefixing + optional minification.
|
|
115
|
+
* On failure, returns the original CSS and logs a warning.
|
|
116
|
+
*/
|
|
117
|
+
function postProcessCss(css, opts) {
|
|
118
|
+
if (!css) return css;
|
|
119
|
+
try {
|
|
120
|
+
return (0, lightningcss.transform)({
|
|
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;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Compute MD5 content hash for a string. */
|
|
133
|
+
function contentHash(source) {
|
|
134
|
+
return (0, crypto.createHash)("md5").update(source).digest("hex");
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Reconstruct file entries from cache, including content hashes.
|
|
138
|
+
* For unchanged files (hash matches changedPath), sends empty source
|
|
139
|
+
* to avoid serializing full source text across the NAPI boundary.
|
|
140
|
+
* Rust cache-hit path never reads file.source, so empty string is safe.
|
|
141
|
+
*/
|
|
142
|
+
function buildFileEntriesFromCache(cache, changedPath) {
|
|
143
|
+
const entries = [];
|
|
144
|
+
for (const [path$1, { hash, source }] of cache) entries.push({
|
|
145
|
+
path: path$1,
|
|
146
|
+
source: path$1 === changedPath ? source : "",
|
|
147
|
+
hash
|
|
148
|
+
});
|
|
149
|
+
return entries;
|
|
150
|
+
}
|
|
151
|
+
function animusExtract(options) {
|
|
152
|
+
const resolvedEngine = options.engine ?? "v2";
|
|
153
|
+
const engineModuleId = resolvedEngine === "v2" ? "@animus-ui/extract/engine-v2" : "@animus-ui/extract";
|
|
154
|
+
const requireEngine = () => require(engineModuleId);
|
|
155
|
+
let v2Engine = null;
|
|
156
|
+
const v2SentSources = /* @__PURE__ */ new Map();
|
|
157
|
+
let v2DriftWarned = false;
|
|
158
|
+
const engineApi = () => {
|
|
159
|
+
if (resolvedEngine !== "v2") return requireEngine();
|
|
160
|
+
const native = requireEngine();
|
|
161
|
+
return {
|
|
162
|
+
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
163
|
+
analyzeProject: (filesJsonRaw, themeJson_, variableMapJson_, contextualVarsJson_, configJson_, groupRegistryJson_, packageResolutionJson_, devMode, emitterConfigJson, selectorAliasesJson_, _selectorOrderJson, globalStyleBlocksJson_, pathAliasesJson_, keyframesJson_) => {
|
|
164
|
+
let filesJson = filesJsonRaw;
|
|
165
|
+
if (filesJsonRaw.includes("\"source\":\"\"")) {
|
|
166
|
+
const entries = JSON.parse(filesJsonRaw);
|
|
167
|
+
for (const entry of entries) if (entry.source === "") entry.source = fileCache.get(entry.path)?.source ?? "";
|
|
168
|
+
filesJson = JSON.stringify(entries);
|
|
169
|
+
}
|
|
170
|
+
v2SentSources.clear();
|
|
171
|
+
for (const entry of JSON.parse(filesJson)) v2SentSources.set(entry.path, entry.source);
|
|
172
|
+
const emitterConfig = emitterConfigJson ? JSON.parse(emitterConfigJson) : {};
|
|
173
|
+
v2Engine = null;
|
|
174
|
+
const engine = new native.ExtractEngine({
|
|
175
|
+
runtimeImport: emitterConfig.runtime_import ?? void 0,
|
|
176
|
+
cssModuleId: emitterConfig.css_module_id ?? void 0,
|
|
177
|
+
systemPropsModuleId: emitterConfig.system_props_module_id ?? void 0,
|
|
178
|
+
themeJson: themeJson_,
|
|
179
|
+
variableMapJson: variableMapJson_,
|
|
180
|
+
contextualVarsJson: contextualVarsJson_ ?? void 0,
|
|
181
|
+
configJson: configJson_,
|
|
182
|
+
groupRegistryJson: groupRegistryJson_,
|
|
183
|
+
selectorAliasesJson: selectorAliasesJson_ ?? void 0,
|
|
184
|
+
globalStyleBlocksJson: globalStyleBlocksJson_ ?? void 0,
|
|
185
|
+
keyframesJson: keyframesJson_ ?? void 0,
|
|
186
|
+
packageResolutionJson: packageResolutionJson_ ?? void 0,
|
|
187
|
+
pathAliasesJson: pathAliasesJson_ ?? void 0,
|
|
188
|
+
devMode
|
|
189
|
+
});
|
|
190
|
+
v2Engine = engine;
|
|
191
|
+
return engine.analyze(filesJson);
|
|
192
|
+
},
|
|
193
|
+
transformFile: (source, path$2, _manifest) => {
|
|
194
|
+
if (!v2Engine) throw new Error("[animus-extract] v2 transform before analyze — engine state is per-instance");
|
|
195
|
+
const sent = v2SentSources.get(path$2);
|
|
196
|
+
if (sent !== void 0 && sent !== source && !v2DriftWarned) {
|
|
197
|
+
v2DriftWarned = true;
|
|
198
|
+
console.warn(`[animus-extract] v2: transform-time source for ${path$2} differs from analyze-time source — an upstream plugin transform may be reverted (engine v2 emits from analyzed sources)`);
|
|
199
|
+
}
|
|
200
|
+
return JSON.parse(v2Engine.transformFile(path$2));
|
|
201
|
+
},
|
|
202
|
+
clearAnalysisCache: () => {
|
|
203
|
+
if (v2Engine) v2Engine.clearCache();
|
|
204
|
+
v2Engine = null;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
let themeJson = "{}";
|
|
209
|
+
let variableMapJson = "{}";
|
|
210
|
+
let contextualVarsJson = "{}";
|
|
211
|
+
let configJson = "{}";
|
|
212
|
+
let groupRegistryJson = "{}";
|
|
213
|
+
let selectorAliasesJson = null;
|
|
214
|
+
let isProd = false;
|
|
215
|
+
let rootDir = "";
|
|
216
|
+
const verbose = options.verbose || process.env.ANIMUS_DEBUG === "1" || process.env.ANIMUS_DEBUG === "true";
|
|
217
|
+
let logger = null;
|
|
218
|
+
function log(msg) {
|
|
219
|
+
if (verbose) (logger ?? console).info(`[animus] ${msg}`);
|
|
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
|
+
}
|
|
244
|
+
}
|
|
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
|
+
/**
|
|
270
|
+
* Load a SystemInstance via Rust NAPI (rquickjs bundled eval).
|
|
271
|
+
* The Rust crate reads the system file, strips TS types via OXC, resolves
|
|
272
|
+
* workspace package dependencies, bundles all modules, evaluates with
|
|
273
|
+
* rquickjs, and returns serialized config directly — no subprocess needed.
|
|
274
|
+
*/
|
|
275
|
+
function loadSystem() {
|
|
276
|
+
resolvedSystemPath = (0, path.resolve)(rootDir, options.system);
|
|
277
|
+
try {
|
|
278
|
+
const { loadSystemModule } = engineApi();
|
|
279
|
+
const config = loadSystemModule(resolvedSystemPath, rootDir);
|
|
280
|
+
configJson = config.propConfig;
|
|
281
|
+
groupRegistryJson = config.groupRegistry;
|
|
282
|
+
selectorAliasesJson = config.selectorAliases || null;
|
|
283
|
+
themeJson = config.scalesJson;
|
|
284
|
+
variableMapJson = config.variableMapJson;
|
|
285
|
+
variableCss = config.variableCss;
|
|
286
|
+
contextualVarsJson = config.contextualVarsJson;
|
|
287
|
+
globalStyleBlocksJson = config.globalStyleBlocks || null;
|
|
288
|
+
keyframesBlocksJson = config.keyframesBlocks || null;
|
|
289
|
+
} 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);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Run project analysis and update the manifest.
|
|
296
|
+
*
|
|
297
|
+
* Pipeline: NAPI analyzeProject → unit fallback.
|
|
298
|
+
* Transforms are resolved in-process via boa_engine during Rust extraction.
|
|
299
|
+
* No subprocess needed.
|
|
300
|
+
*/
|
|
301
|
+
function runAnalysis(fileEntries) {
|
|
302
|
+
try {
|
|
303
|
+
const { analyzeProject } = engineApi();
|
|
304
|
+
const emitterConfig = JSON.stringify({
|
|
305
|
+
runtime_import: "@animus-ui/system",
|
|
306
|
+
css_module_id: "virtual:animus/styles.css"
|
|
307
|
+
});
|
|
308
|
+
const manifestJson = analyzeProject(...buildAnalyzeProjectArgs({
|
|
309
|
+
filesJson: JSON.stringify(fileEntries),
|
|
310
|
+
scalesJson: themeJson,
|
|
311
|
+
variableMapJson,
|
|
312
|
+
contextualVarsJson: contextualVarsJson || null,
|
|
313
|
+
propConfigJson: configJson,
|
|
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;
|
|
343
|
+
if (newFragments && typeof newFragments === "object") {
|
|
344
|
+
fragmentCache.clear();
|
|
345
|
+
for (const [id, sheets] of Object.entries(newFragments)) fragmentCache.set(id, sheets);
|
|
346
|
+
}
|
|
347
|
+
reverseProvenance = storedManifest?.reverse_provenance ?? {};
|
|
348
|
+
storedSheets = storedManifest?.sheets ?? null;
|
|
349
|
+
globalCss = storedManifest?.sheets?.global || "";
|
|
350
|
+
resolvedComponentCss = storedManifest?.css || "";
|
|
351
|
+
resolvedComponentCss = (0, _animus_ui_extract_pipeline.applyUnitFallback)(resolvedComponentCss);
|
|
352
|
+
} catch (e) {
|
|
353
|
+
if (options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`, { cause: e });
|
|
354
|
+
console.warn("[animus-extract] analyzeProject failed:", e);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function runSelfVerify() {
|
|
358
|
+
const failures = [];
|
|
359
|
+
if (resolvedComponentCss.trim().length === 0) failures.push("No component CSS produced — check system file and include patterns");
|
|
360
|
+
if (!variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
|
|
361
|
+
if (`${variableCss}\n${globalCss}\n${resolvedComponentCss}`.includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
362
|
+
if (storedManifest && resolvedComponentCss.length > 0) {
|
|
363
|
+
const assembled = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
364
|
+
layers: options.layers,
|
|
365
|
+
variableCss,
|
|
366
|
+
globalCss,
|
|
367
|
+
componentCss: resolvedComponentCss
|
|
368
|
+
});
|
|
369
|
+
const baseIdx = assembled.search(/@layer\s+anm-base\s*\{/);
|
|
370
|
+
const variantsIdx = assembled.search(/@layer\s+anm-variants\s*\{/);
|
|
371
|
+
if (baseIdx !== -1 && variantsIdx !== -1 && baseIdx >= variantsIdx) failures.push(`CSS layer ordering violated — @layer anm-base (offset ${baseIdx}) must precede @layer anm-variants (offset ${variantsIdx})`);
|
|
372
|
+
}
|
|
373
|
+
for (const message of failures) {
|
|
374
|
+
const line = `[animus:verify] ${message}`;
|
|
375
|
+
if (options.strict) throw new Error(line);
|
|
376
|
+
if (logger) logger.warn(line, { timestamp: true });
|
|
377
|
+
else console.warn(line);
|
|
378
|
+
}
|
|
379
|
+
if (failures.length === 0) log("[animus:verify] structural self-check passed");
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
name: "animus-extract",
|
|
383
|
+
enforce: "pre",
|
|
384
|
+
configureServer(server) {
|
|
385
|
+
devServer = server;
|
|
386
|
+
},
|
|
387
|
+
configResolved(config) {
|
|
388
|
+
isProd = config.command === "build";
|
|
389
|
+
rootDir = config.root;
|
|
390
|
+
logger = config.logger;
|
|
391
|
+
lcssTargets = resolveLightningTargets(options.targets, rootDir);
|
|
392
|
+
log(`Lightning CSS targets resolved (${Object.keys(lcssTargets).length} browsers)`);
|
|
393
|
+
const rawAlias = config.resolve?.alias;
|
|
394
|
+
if (rawAlias) {
|
|
395
|
+
const entries = [];
|
|
396
|
+
if (Array.isArray(rawAlias)) {
|
|
397
|
+
for (const entry of rawAlias) if (typeof entry.find === "string" && typeof entry.replacement === "string") {
|
|
398
|
+
const replacement = entry.replacement.startsWith(rootDir) ? entry.replacement.slice(rootDir.length + 1) : entry.replacement;
|
|
399
|
+
entries.push({
|
|
400
|
+
pattern: entry.find.endsWith("/") ? entry.find : entry.find + "/",
|
|
401
|
+
replacement: replacement.endsWith("/") ? replacement : replacement + "/",
|
|
402
|
+
type: "prefix"
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
} else if (typeof rawAlias === "object" && rawAlias !== null) {
|
|
406
|
+
for (const [key, value] of Object.entries(rawAlias)) if (typeof value === "string") {
|
|
407
|
+
const replacement = value.startsWith(rootDir) ? value.slice(rootDir.length + 1) : value;
|
|
408
|
+
if (/\.\w+$/.test(replacement)) entries.push({
|
|
409
|
+
pattern: key,
|
|
410
|
+
replacement,
|
|
411
|
+
type: "exact"
|
|
412
|
+
});
|
|
413
|
+
else entries.push({
|
|
414
|
+
pattern: key.endsWith("/") ? key : key + "/",
|
|
415
|
+
replacement: replacement.endsWith("/") ? replacement : replacement + "/",
|
|
416
|
+
type: "prefix"
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
entries.sort((a, b) => b.pattern.length - a.pattern.length);
|
|
421
|
+
if (entries.length > 0) {
|
|
422
|
+
pathAliasesJson = JSON.stringify({ aliases: entries });
|
|
423
|
+
log(`Path aliases forwarded: ${entries.length} entries`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
async buildStart() {
|
|
428
|
+
try {
|
|
429
|
+
const { clearAnalysisCache } = engineApi();
|
|
430
|
+
clearAnalysisCache();
|
|
431
|
+
} catch {}
|
|
432
|
+
let t0 = performance.now();
|
|
433
|
+
loadSystem();
|
|
434
|
+
if (options.layers) {
|
|
435
|
+
(0, _animus_ui_extract_pipeline.validateLayerOrder)(options.layers);
|
|
436
|
+
log(`Custom layers: [${options.layers.join(", ")}]`);
|
|
437
|
+
}
|
|
438
|
+
if (verbose) {
|
|
439
|
+
const propCount = Object.keys(JSON.parse(configJson)).length;
|
|
440
|
+
const groupCount = Object.keys(JSON.parse(groupRegistryJson)).length;
|
|
441
|
+
log(`System loaded: ${propCount} props, ${groupCount} groups (${Math.round(performance.now() - t0)}ms)`);
|
|
442
|
+
}
|
|
443
|
+
t0 = performance.now();
|
|
444
|
+
const excludePatterns = options.exclude ?? DEFAULT_EXCLUDE;
|
|
445
|
+
extensionsSet = new Set(options.extensions ?? _animus_ui_extract_pipeline.DEFAULT_EXTENSIONS);
|
|
446
|
+
const shouldHandleMdx = extensionsSet.has(".mdx");
|
|
447
|
+
let mdxMissingDepWarned = false;
|
|
448
|
+
const filePaths = discoverFiles(rootDir, rootDir, excludePatterns, extensionsSet);
|
|
449
|
+
const fileEntries = [];
|
|
450
|
+
for (const filePath of filePaths) try {
|
|
451
|
+
let source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
452
|
+
let relPath = (0, path.relative)(rootDir, filePath);
|
|
453
|
+
if (shouldHandleMdx && (0, path.extname)(filePath) === ".mdx") {
|
|
454
|
+
const result = await (0, _animus_ui_extract_pipeline.preprocessMdx)(source, relPath);
|
|
455
|
+
if (result.kind === "missing-dep") {
|
|
456
|
+
if (!mdxMissingDepWarned) {
|
|
457
|
+
warn("⚠ .mdx in extensions but @mdx-js/mdx not installed; MDX files skipped");
|
|
458
|
+
mdxMissingDepWarned = true;
|
|
459
|
+
}
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (result.kind === "error") {
|
|
463
|
+
warn(`⚠ MDX preprocessing failed for ${relPath}: ${result.error}`);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
source = result.source;
|
|
467
|
+
relPath = relPath + ".tsx";
|
|
468
|
+
}
|
|
469
|
+
const hash = !isProd ? contentHash(source) : void 0;
|
|
470
|
+
fileEntries.push({
|
|
471
|
+
path: relPath,
|
|
472
|
+
source,
|
|
473
|
+
hash
|
|
474
|
+
});
|
|
475
|
+
if (!isProd && hash) fileCache.set(relPath, {
|
|
476
|
+
hash,
|
|
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)");
|
|
579
|
+
}
|
|
580
|
+
const { declaration } = (0, _animus_ui_extract_pipeline.assembleStylesheet)({
|
|
581
|
+
layers: options.layers,
|
|
582
|
+
variableCss: "",
|
|
583
|
+
globalCss: "",
|
|
584
|
+
split: true
|
|
585
|
+
});
|
|
586
|
+
layerDeclaration = declaration;
|
|
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 || ""), {
|
|
627
|
+
...lcssOpts,
|
|
628
|
+
minify: false
|
|
629
|
+
}).replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$")}\`;`;
|
|
630
|
+
if (id === RESOLVED_BRIDGE_ID) return `
|
|
631
|
+
import css from '${VIRTUAL_COMPONENTS_ID}';
|
|
632
|
+
|
|
633
|
+
const GLOBAL_KEY = '__animus_sheet_${(0, crypto.createHash)("md5").update(options.system).digest("hex").slice(0, 8)}__';
|
|
634
|
+
let sheet = globalThis[GLOBAL_KEY] || null;
|
|
635
|
+
|
|
636
|
+
if (typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in document) {
|
|
637
|
+
if (!sheet) {
|
|
638
|
+
sheet = new CSSStyleSheet();
|
|
639
|
+
globalThis[GLOBAL_KEY] = sheet;
|
|
640
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
641
|
+
}
|
|
642
|
+
sheet.replaceSync(css);
|
|
643
|
+
} else {
|
|
644
|
+
// Fallback: inject or update <style> tag
|
|
645
|
+
let el = document.querySelector('style[data-animus-components]');
|
|
646
|
+
if (!el) {
|
|
647
|
+
el = document.createElement('style');
|
|
648
|
+
el.setAttribute('data-animus-components', '');
|
|
649
|
+
document.head.appendChild(el);
|
|
650
|
+
}
|
|
651
|
+
el.textContent = css;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
if (import.meta.hot) {
|
|
655
|
+
import.meta.hot.accept('${VIRTUAL_COMPONENTS_ID}', (newModule) => {
|
|
656
|
+
if (sheet) {
|
|
657
|
+
sheet.replaceSync(newModule.default);
|
|
658
|
+
} else {
|
|
659
|
+
const el = document.querySelector('style[data-animus-components]');
|
|
660
|
+
if (el) el.textContent = newModule.default;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
`;
|
|
665
|
+
if (id === RESOLVED_SYSTEM_PROPS_ID) {
|
|
666
|
+
let moduleSource = `export const systemPropMap = ${storedSystemPropMapJson};\nexport const systemPropGroups = ${groupRegistryJson};`;
|
|
667
|
+
if (storedDynamicPropsJson !== "{}") {
|
|
668
|
+
const dynamicProps = JSON.parse(storedDynamicPropsJson);
|
|
669
|
+
const configEntries = {};
|
|
670
|
+
for (const [propName, meta] of Object.entries(dynamicProps)) configEntries[propName] = {
|
|
671
|
+
varName: meta.var_name,
|
|
672
|
+
slotClass: meta.slot_class,
|
|
673
|
+
...meta.transform_name ? { transformName: meta.transform_name } : {},
|
|
674
|
+
...meta.scale_values && Object.keys(meta.scale_values).length > 0 ? { scaleValues: meta.scale_values } : {}
|
|
675
|
+
};
|
|
676
|
+
moduleSource += `\nexport const dynamicPropConfig = ${JSON.stringify(configEntries)};`;
|
|
677
|
+
} else moduleSource += `\nexport const dynamicPropConfig = {};`;
|
|
678
|
+
moduleSource += `\nexport const transforms = ${storedTransformsSource};`;
|
|
679
|
+
return moduleSource;
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
},
|
|
683
|
+
transform(code, id) {
|
|
684
|
+
if (!storedManifest) return null;
|
|
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
|
+
}
|
|
732
|
+
},
|
|
733
|
+
transformIndexHtml: {
|
|
734
|
+
order: "pre",
|
|
735
|
+
handler() {
|
|
736
|
+
if (!layerDeclaration) return [];
|
|
737
|
+
return [{
|
|
738
|
+
tag: "style",
|
|
739
|
+
attrs: { "data-animus-layers": "" },
|
|
740
|
+
children: layerDeclaration,
|
|
741
|
+
injectTo: "head-prepend"
|
|
742
|
+
}];
|
|
743
|
+
}
|
|
744
|
+
},
|
|
745
|
+
async handleHotUpdate({ file, server: hmrServer, modules }) {
|
|
746
|
+
if (isProd) return;
|
|
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;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
//#endregion
|
|
870
|
+
exports.animusExtract = animusExtract;
|
|
871
|
+
exports.default = animusExtract;
|
|
872
|
+
exports.discoverFiles = discoverFiles;
|