@animus-ui/vite-plugin 0.1.0-next.23 → 0.1.0-next.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -11,8 +11,6 @@ export interface AnimusExtractOptions {
11
11
  include?: string[];
12
12
  /** Glob patterns to exclude. */
13
13
  exclude?: string[];
14
- /** Package name patterns to resolve and include in analysis. Defaults to ['@animus-ui/*']. */
15
- packagePatterns?: string[];
16
14
  /** When true, extraction failures throw instead of warning. Use in CI to enforce full extraction. */
17
15
  strict?: boolean;
18
16
  /** Enable verbose logging. Also activatable via ANIMUS_DEBUG=1 env var. */
@@ -49,6 +47,5 @@ export interface AnimusExtractOptions {
49
47
  layers?: string[];
50
48
  }
51
49
  export declare function animusExtract(options: AnimusExtractOptions): Plugin;
52
- export { evaluateTheme, evaluateThemeObject } from './theme-evaluator';
53
50
  export default animusExtract;
54
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAM3C,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAwUD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAu6BnE;AAED,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACvE,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAI3C,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AA6LD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA46BnE;AAED,eAAe,aAAa,CAAC"}
package/dist/index.mjs CHANGED
@@ -5,126 +5,12 @@ import { existsSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileS
5
5
  import { tmpdir } from "os";
6
6
  import { dirname, extname, join, relative, resolve } from "path";
7
7
  import { fileURLToPath } from "url";
8
+ import { applyPrefix, applyUnitFallback, detectRuntime, execSubprocess, extractSystemFilePackages } from "@animus-ui/extract/pipeline";
8
9
  import browserslist from "browserslist";
9
10
  import { browserslistToTargets, transform } from "lightningcss";
10
11
  //#region \0rolldown/runtime.js
11
12
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
12
13
  //#endregion
13
- //#region src/theme-evaluator.ts
14
- /**
15
- * Evaluate a theme object that has already been loaded/imported.
16
- *
17
- * If the theme has a `.manifest` property (from ThemeBuilder.build()),
18
- * reads structured data directly — no re-flattening or var() pattern-matching.
19
- * Falls back to legacy path for themes without manifests.
20
- */
21
- function evaluateThemeObject(theme) {
22
- if (theme.manifest && typeof theme.manifest === "object") {
23
- const manifest = theme.manifest;
24
- return {
25
- scalesJson: JSON.stringify(manifest.tokenMap),
26
- variableMapJson: JSON.stringify(manifest.variableMap),
27
- variableCss: manifest.variableCss,
28
- contextualVarsJson: JSON.stringify(manifest.contextualVars ?? {})
29
- };
30
- }
31
- console.warn("[animus] Theme has no .manifest property — using legacy evaluation. Update to @animus-ui/system >=0.2.0 for structured manifest support.");
32
- return evaluateThemeObjectLegacy(theme);
33
- }
34
- /** Legacy theme evaluation — flattens theme and pattern-matches var() strings. */
35
- function evaluateThemeObjectLegacy(theme) {
36
- const flat = {};
37
- for (const [scaleName, scaleValue] of Object.entries(theme)) {
38
- if (scaleName.startsWith("_")) continue;
39
- if (scaleName === "breakpoints") {
40
- flattenScale(flat, "breakpoints", scaleValue);
41
- continue;
42
- }
43
- if (typeof scaleValue === "object" && scaleValue !== null && !Array.isArray(scaleValue)) flattenScale(flat, scaleName, scaleValue);
44
- }
45
- const variableMap = {};
46
- for (const [tokenPath, value] of Object.entries(flat)) if (value.startsWith("var(") && value.endsWith(")")) variableMap[tokenPath] = value.slice(4, -1);
47
- const variableCss = buildVariableCss(theme);
48
- const contextualVars = theme._contextualVars && typeof theme._contextualVars === "object" && Object.keys(theme._contextualVars).length > 0 ? theme._contextualVars : {};
49
- return {
50
- scalesJson: JSON.stringify(flat),
51
- variableMapJson: JSON.stringify(variableMap),
52
- variableCss,
53
- contextualVarsJson: JSON.stringify(contextualVars)
54
- };
55
- }
56
- /**
57
- * Evaluate a theme module and flatten all scales to a JSON map.
58
- * Format: { "scale_name.key": "css_value" }
59
- *
60
- * Uses Vite's ssrLoadModule to evaluate the theme at build start,
61
- * so all computed scales (shadows, gradients that depend on colors)
62
- * are resolved to their final values.
63
- */
64
- async function evaluateTheme(ssrLoadModule, themePath) {
65
- const mod = await ssrLoadModule(themePath);
66
- const theme = mod.theme || mod.default;
67
- if (!theme) throw new Error(`Theme module at ${themePath} must export 'theme' or a default export`);
68
- return evaluateThemeObject(theme);
69
- }
70
- /**
71
- * Build the full CSS variable block from a theme's _variables and _tokens.
72
- *
73
- * Produces:
74
- * :root { --color-navy-500: #282a36; ... }
75
- * [data-color-mode="dark"] { --color-primary: #ff80bf; ... }
76
- */
77
- function buildVariableCss(theme) {
78
- const parts = [];
79
- if (theme._variables != null && typeof theme._variables === "object") {
80
- const rootLines = [];
81
- for (const categoryValue of Object.values(theme._variables)) {
82
- if (categoryValue == null || typeof categoryValue !== "object") continue;
83
- for (const [cssVar, cssValue] of Object.entries(categoryValue)) if (typeof cssValue === "string") rootLines.push(` ${cssVar}: ${cssValue};`);
84
- }
85
- if (rootLines.length > 0) parts.push(`:root {\n${rootLines.join("\n")}\n}`);
86
- }
87
- if (theme._tokens?.modes != null && typeof theme._tokens.modes === "object") for (const [modeName, modeTokens] of Object.entries(theme._tokens.modes)) {
88
- if (modeTokens == null || typeof modeTokens !== "object") continue;
89
- const modeLines = [];
90
- flattenModeTokens(modeLines, modeTokens, "");
91
- if (modeLines.length > 0) parts.push(`[data-color-mode="${modeName}"] {\n${modeLines.join("\n")}\n}`);
92
- }
93
- return parts.join("\n\n");
94
- }
95
- /**
96
- * Recursively flatten mode token objects into CSS variable declarations.
97
- *
98
- * Examples:
99
- * { primary: '#ff80bf' } → ' --color-primary: #ff80bf;'
100
- * { background: { _: '#fff', muted: '#f5f5f5' } }
101
- * → ' --color-background: #fff;'
102
- * ' --color-background-muted: #f5f5f5;'
103
- *
104
- * The special key `_` maps to the base name (no suffix).
105
- */
106
- function flattenModeTokens(lines, obj, prefix) {
107
- for (const [key, value] of Object.entries(obj)) {
108
- let namePart;
109
- if (key === "_") namePart = prefix;
110
- else namePart = prefix ? `${prefix}-${key}` : key;
111
- if (typeof value === "string" || typeof value === "number") lines.push(` --color-${namePart}: ${value};`);
112
- else if (value != null && typeof value === "object" && !Array.isArray(value)) flattenModeTokens(lines, value, namePart);
113
- }
114
- }
115
- function flattenScale(flat, prefix, obj, parentKey = "") {
116
- if (typeof obj !== "object" || obj === null) {
117
- const key = parentKey ? `${prefix}.${parentKey}` : prefix;
118
- flat[key] = String(obj);
119
- return;
120
- }
121
- for (const [key, value] of Object.entries(obj)) {
122
- const fullKey = parentKey ? `${parentKey}-${key}` : key;
123
- if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenScale(flat, prefix, value, fullKey);
124
- else flat[`${prefix}.${fullKey}`] = String(value);
125
- }
126
- }
127
- //#endregion
128
14
  //#region src/index.ts
129
15
  const __pluginDir = dirname(fileURLToPath(import.meta.url));
130
16
  const VIRTUAL_CSS_ID = "virtual:animus/styles.css";
@@ -171,93 +57,6 @@ function discoverFiles(dir, rootDir, excludePatterns) {
171
57
  return results;
172
58
  }
173
59
  /**
174
- * CSS properties that accept unitless numeric values.
175
- * Matches @emotion/unitless and React DOM's style handling.
176
- * Bare numerics on properties NOT in this set receive `px`.
177
- */
178
- const UNITLESS_PROPERTIES = new Set([
179
- "animation-iteration-count",
180
- "border-image-outset",
181
- "border-image-slice",
182
- "border-image-width",
183
- "box-flex",
184
- "box-flex-group",
185
- "box-ordinal-group",
186
- "column-count",
187
- "columns",
188
- "flex",
189
- "flex-grow",
190
- "flex-positive",
191
- "flex-shrink",
192
- "flex-negative",
193
- "flex-order",
194
- "grid-area",
195
- "grid-row",
196
- "grid-row-end",
197
- "grid-row-span",
198
- "grid-row-start",
199
- "grid-column",
200
- "grid-column-end",
201
- "grid-column-span",
202
- "grid-column-start",
203
- "font-weight",
204
- "line-clamp",
205
- "line-height",
206
- "opacity",
207
- "order",
208
- "orphans",
209
- "tab-size",
210
- "widows",
211
- "z-index",
212
- "zoom",
213
- "fill-opacity",
214
- "flood-opacity",
215
- "stop-opacity",
216
- "stroke-dasharray",
217
- "stroke-dashoffset",
218
- "stroke-miterlimit",
219
- "stroke-opacity",
220
- "stroke-width"
221
- ]);
222
- /**
223
- * Append `px` to bare numeric values in CSS declarations for properties
224
- * that expect length units. Unitless properties are preserved as-is.
225
- * Numbers inside CSS function calls (cubic-bezier, rgb, calc, etc.) are skipped.
226
- */
227
- function applyUnitFallback(css) {
228
- return css.replace(/([a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
229
- if (UNITLESS_PROPERTIES.has(prop)) return match;
230
- let depth = 0;
231
- let fixed = "";
232
- let i = 0;
233
- while (i < value.length) if (value[i] === "(") {
234
- depth++;
235
- fixed += value[i];
236
- i++;
237
- } else if (value[i] === ")") {
238
- depth--;
239
- fixed += value[i];
240
- i++;
241
- } else if (depth > 0) {
242
- fixed += value[i];
243
- i++;
244
- } else {
245
- const numMatch = value.slice(i).match(/^(-?\d+\.?\d*)/);
246
- if (numMatch) {
247
- const num = numMatch[1];
248
- const after = value[i + num.length];
249
- if (after && /[a-z%]/i.test(after)) fixed += num;
250
- else fixed += num + "px";
251
- i += num.length;
252
- } else {
253
- fixed += value[i];
254
- i++;
255
- }
256
- }
257
- return fixed !== value ? `${prop}:${fixed};` : match;
258
- });
259
- }
260
- /**
261
60
  * Resolve browser targets for Lightning CSS.
262
61
  * Priority: explicit config → project browserslist → 'defaults' fallback.
263
62
  */
@@ -309,29 +108,6 @@ function buildFileEntriesFromCache(cache, changedPath) {
309
108
  return entries;
310
109
  }
311
110
  /**
312
- * Apply namespace prefix to a variable map and CSS variable declarations.
313
- *
314
- * Variable map: `{ "colors.ember": "--color-ember" }` → `{ "colors.ember": "--prefix-color-ember" }`
315
- * Variable CSS: `--color-ember: #FF2800` → `--prefix-color-ember: #FF2800`
316
- * `var(--color-ember)` → `var(--prefix-color-ember)`
317
- */
318
- function applyPrefix(prefix, variableMapJson, variableCss) {
319
- if (!prefix) return {
320
- variableMapJson,
321
- variableCss
322
- };
323
- const map = JSON.parse(variableMapJson);
324
- const prefixed = {};
325
- for (const [key, varName] of Object.entries(map)) prefixed[key] = varName.startsWith("--") ? `--${prefix}-${varName.slice(2)}` : varName;
326
- let css = variableCss;
327
- css = css.replace(/--([a-zA-Z][\w-]*)\s*:/g, `--${prefix}-$1:`);
328
- css = css.replace(/var\(--([a-zA-Z][\w-]*)\)/g, `var(--${prefix}-$1)`);
329
- return {
330
- variableMapJson: JSON.stringify(prefixed),
331
- variableCss: css
332
- };
333
- }
334
- /**
335
111
  * The 7 Animus cascade layers in required order.
336
112
  * Consumer-provided `layers` must contain these as a subsequence.
337
113
  */
@@ -385,6 +161,7 @@ function animusExtract(options) {
385
161
  let storedManifest = null;
386
162
  let storedManifestJson = "";
387
163
  let resolvedComponentCss = "";
164
+ let systemResolveScript = null;
388
165
  let storedSheets = null;
389
166
  let storedSystemPropMapJson = "{}";
390
167
  let storedDynamicPropsJson = "{}";
@@ -403,37 +180,28 @@ function animusExtract(options) {
403
180
  resolvedSystemPath = resolve(rootDir, options.system);
404
181
  try {
405
182
  const ts = Date.now();
406
- const tmpScript = join(tmpdir(), `animus-system-${ts}.js`);
407
183
  const tmpOut = join(tmpdir(), `animus-system-${ts}.json`);
408
- writeFileSync(tmpScript, `const m = require(${JSON.stringify(resolvedSystemPath)});\nconst ds = m.ds || m.default || m.system;\nif (!ds || !ds.serialize) { throw new Error('Module does not export a SystemInstance with .serialize()'); }\nconst cfg = ds.serialize();\nconst tokens = m.tokens || m.theme || null;\nrequire('fs').writeFileSync(${JSON.stringify(tmpOut)}, JSON.stringify({\n propConfig: cfg.propConfig,\n groupRegistry: cfg.groupRegistry,\n tokens: tokens,\n transformNames: Object.keys(cfg.transforms || {}),\n globalStyles: cfg.globalStyles || null\n}));\n`);
409
- execSync(`bun run "${tmpScript}"`, {
410
- cwd: rootDir,
411
- encoding: "utf-8"
412
- });
184
+ execSubprocess(`const m = require(${JSON.stringify(resolvedSystemPath)});\nconst ds = m.ds || m.default || m.system;\nif (!ds || !ds.toConfig) { throw new Error('Module does not export a SystemInstance with .serialize()'); }\nconst cfg = ds.toConfig();\nconst tokens = m.tokens || m.theme || null;\nconst serialized = tokens && typeof tokens.serialize === 'function' ? tokens.serialize() : null;\nconst globalStyleBlocks = {};\nfor (const [key, val] of Object.entries(m)) {\n if (val && typeof val === 'object' && val.__brand === 'GlobalStyleBlock') {\n globalStyleBlocks[key] = val.styles;\n }\n}\nrequire('fs').writeFileSync(${JSON.stringify(tmpOut)}, JSON.stringify({\n propConfig: cfg.propConfig,\n groupRegistry: cfg.groupRegistry,\n serialized: serialized,\n transformNames: Object.keys(cfg.transforms || {}),\n globalStyleBlocks: Object.keys(globalStyleBlocks).length > 0 ? globalStyleBlocks : null\n}));\n`, rootDir);
413
185
  const result = readFileSync(tmpOut, "utf-8");
414
186
  try {
415
- unlinkSync(tmpScript);
416
187
  unlinkSync(tmpOut);
417
188
  } catch {}
418
189
  const parsed = JSON.parse(result);
419
190
  configJson = parsed.propConfig;
420
191
  groupRegistryJson = parsed.groupRegistry;
421
- if (parsed.tokens && Object.keys(parsed.tokens).length > 0) {
422
- const result = evaluateThemeObject(parsed.tokens);
423
- themeJson = result.scalesJson;
424
- variableMapJson = result.variableMapJson;
425
- variableCss = result.variableCss;
426
- contextualVarsJson = result.contextualVarsJson;
192
+ if (parsed.serialized) {
193
+ themeJson = parsed.serialized.scalesJson;
194
+ variableMapJson = parsed.serialized.variableMapJson;
195
+ variableCss = parsed.serialized.variableCss;
196
+ contextualVarsJson = parsed.serialized.contextualVarsJson;
427
197
  if (options.prefix) {
428
198
  const prefixed = applyPrefix(options.prefix, variableMapJson, variableCss);
429
199
  variableMapJson = prefixed.variableMapJson;
430
200
  variableCss = prefixed.variableCss;
431
201
  }
432
- } else if (logger) logger.warn("[animus] No tokens export found in system module — CSS variables will not be generated. Export your theme as `tokens` or `theme`.");
433
- if (parsed.globalStyles) {
434
- const hasReset = parsed.globalStyles.reset && Object.keys(parsed.globalStyles.reset).length > 0;
435
- const hasGlobal = parsed.globalStyles.global && Object.keys(parsed.globalStyles.global).length > 0;
436
- if (hasReset || hasGlobal) try {
202
+ } else throw new Error("[animus-extract] Theme must be built with createTheme().build(). No .serialize() method found on tokens export.");
203
+ if (parsed.globalStyleBlocks) {
204
+ if (Object.keys(parsed.globalStyleBlocks).length > 0) try {
437
205
  const gsTmp = Date.now();
438
206
  const gsThemeFile = join(tmpdir(), `animus-gs-theme-${gsTmp}.json`);
439
207
  const gsOut = join(tmpdir(), `animus-global-${gsTmp}.json`);
@@ -453,26 +221,27 @@ function animusExtract(options) {
453
221
  } catch {}
454
222
  const gsScriptPath = gsCandidates.find((p) => existsSync(p));
455
223
  if (!gsScriptPath) throw new Error(`resolve-global-styles not found in: ${gsCandidates.join(", ")}`);
456
- execSync(`bun run "${gsScriptPath}" "${resolvedSystemPath}" "${gsThemeFile}" "${gsOut}"`, {
224
+ execSync(`${detectRuntime()} run "${gsScriptPath}" "${resolvedSystemPath}" "${gsThemeFile}" "${gsOut}"`, {
457
225
  cwd: rootDir,
458
226
  encoding: "utf-8"
459
227
  });
460
228
  const gsResult = JSON.parse(readFileSync(gsOut, "utf-8"));
461
- const parts = [gsResult.reset, gsResult.global].filter(Boolean);
229
+ const parts = Object.values(gsResult).filter(Boolean);
462
230
  if (parts.length > 0) globalCss = `@layer global {\n${parts.join("\n\n")}\n}`;
463
231
  try {
464
232
  unlinkSync(gsThemeFile);
465
233
  unlinkSync(gsOut);
466
234
  } catch {}
467
235
  } catch (e) {
236
+ if (options.strict) throw new Error(`[animus-extract] Global styles resolution failed: ${e?.message || e}`);
468
237
  console.warn("[animus-extract] Global styles resolution failed:", e?.message || e);
469
238
  }
470
239
  }
471
240
  if (parsed.transformNames && parsed.transformNames.length > 0) {
472
241
  const tsTmp = Date.now();
473
242
  const tmpResolve = join(tmpdir(), `animus-transforms-resolve-${tsTmp}.js`);
474
- writeFileSync(tmpResolve, `const m = require(${JSON.stringify(resolvedSystemPath)});\nconst ds = m.ds || m.default || m.system;\nconst cfg = ds.serialize();\nconst css = require('fs').readFileSync(process.argv[2], 'utf-8');\nconst resolved = css.replace(/__TRANSFORM__(\\w+)__(.+?)__/g, (_, name, rawValue) => {\n const fn = cfg.transforms[name];\n if (!fn) return rawValue;\n const value = rawValue !== '' && !isNaN(Number(rawValue)) ? Number(rawValue) : rawValue;\n const result = fn(value);\n return typeof result === 'object' ? JSON.stringify(result) : String(result);\n});\nrequire('fs').writeFileSync(process.argv[3], resolved);\n`);
475
- globalThis.__animus_system_resolve_script = tmpResolve;
243
+ writeFileSync(tmpResolve, `const m = require(${JSON.stringify(resolvedSystemPath)});\nconst ds = m.ds || m.default || m.system;\nconst cfg = ds.toConfig();\nconst css = require('fs').readFileSync(process.argv[2], 'utf-8');\nconst resolved = css.replace(/__TRANSFORM__(\\w+)__(.+?)__/g, (_, name, rawValue) => {\n const fn = cfg.transforms[name];\n if (!fn) return rawValue;\n const value = rawValue !== '' && !isNaN(Number(rawValue)) ? Number(rawValue) : rawValue;\n const result = fn(value);\n return typeof result === 'object' ? JSON.stringify(result) : String(result);\n});\nrequire('fs').writeFileSync(process.argv[3], resolved);\n`);
244
+ systemResolveScript = tmpResolve;
476
245
  }
477
246
  } catch (e) {
478
247
  if (options.strict) throw new Error(`[animus-extract] Failed to load system from ${resolvedSystemPath}: ${e}`);
@@ -481,12 +250,19 @@ function animusExtract(options) {
481
250
  }
482
251
  /**
483
252
  * Run project analysis and update the manifest.
484
- * Uses fileCache if populated (dev mode), otherwise uses provided entries.
253
+ *
254
+ * Pipeline: NAPI analyzeProject → transform resolution (subprocess) → unit fallback.
255
+ * NAPI and unit fallback use @animus-ui/extract. Transform resolution stays as
256
+ * subprocess because it needs live JS functions from the system module (ESM isolation).
485
257
  */
486
258
  function runAnalysis(fileEntries) {
487
259
  try {
488
260
  const { analyzeProject } = __require("@animus-ui/extract");
489
- const manifestJson = analyzeProject(JSON.stringify(fileEntries), themeJson, variableMapJson, contextualVarsJson || null, configJson, groupRegistryJson, JSON.stringify(packageMap), !isProd, options.prefix || null);
261
+ const emitterConfig = JSON.stringify({
262
+ runtime_import: "@animus-ui/system",
263
+ css_module_id: "virtual:animus/styles.css"
264
+ });
265
+ const manifestJson = analyzeProject(JSON.stringify(fileEntries), themeJson, variableMapJson, contextualVarsJson || null, configJson, groupRegistryJson, JSON.stringify(packageMap), !isProd, options.prefix || null, emitterConfig);
490
266
  storedManifest = JSON.parse(manifestJson);
491
267
  storedManifestJson = manifestJson;
492
268
  storedSystemPropMapJson = JSON.stringify(storedManifest?.system_prop_map ?? {});
@@ -507,13 +283,12 @@ function animusExtract(options) {
507
283
  bridgeInjected = false;
508
284
  storedSheets = storedManifest?.sheets ?? null;
509
285
  const rawCss = storedManifest?.css || "";
510
- const systemResolveScript = globalThis.__animus_system_resolve_script;
511
286
  if (systemResolveScript && rawCss.includes("__TRANSFORM__")) try {
512
287
  const tsTmp = Date.now();
513
288
  const tmpIn = join(tmpdir(), `animus-css-${tsTmp}.css`);
514
289
  const tmpOut = join(tmpdir(), `animus-css-${tsTmp}.out.css`);
515
290
  writeFileSync(tmpIn, rawCss);
516
- execSync(`bun run "${systemResolveScript}" "${tmpIn}" "${tmpOut}"`, {
291
+ execSync(`${detectRuntime()} run "${systemResolveScript}" "${tmpIn}" "${tmpOut}"`, {
517
292
  cwd: rootDir,
518
293
  encoding: "utf-8"
519
294
  });
@@ -523,6 +298,7 @@ function animusExtract(options) {
523
298
  unlinkSync(tmpOut);
524
299
  } catch {}
525
300
  } catch (e) {
301
+ if (options.strict) throw new Error(`[animus-extract] Transform resolution failed: ${e?.message}`);
526
302
  console.warn("[animus-extract] Transform resolution failed:", e?.message);
527
303
  resolvedComponentCss = rawCss;
528
304
  }
@@ -575,19 +351,7 @@ function animusExtract(options) {
575
351
  });
576
352
  } catch {}
577
353
  const localFileCount = fileEntries.length;
578
- const patterns = options.packagePatterns ?? ["@animus-ui/*"];
579
- const packageSpecifiers = /* @__PURE__ */ new Set();
580
- for (const entry of fileEntries) {
581
- const importRegex = /from\s+['"]([^'"]+)['"]/g;
582
- let match;
583
- while ((match = importRegex.exec(entry.source)) !== null) {
584
- const source = match[1];
585
- for (const pattern of patterns) {
586
- const prefix = pattern.replace("*", "");
587
- if (source.startsWith(prefix) || source === pattern.replace("/*", "")) packageSpecifiers.add(source);
588
- }
589
- }
590
- }
354
+ const packageSpecifiers = extractSystemFilePackages(resolvedSystemPath);
591
355
  packageMap = {};
592
356
  for (const specifier of packageSpecifiers) try {
593
357
  const resolved = await this.resolve(specifier);
@@ -691,7 +455,7 @@ function animusExtract(options) {
691
455
  if (id === RESOLVED_BRIDGE_ID) return `
692
456
  import css from '${VIRTUAL_COMPONENTS_ID}';
693
457
 
694
- const GLOBAL_KEY = '__animus_component_sheet__';
458
+ const GLOBAL_KEY = '__animus_sheet_${createHash("md5").update(options.system).digest("hex").slice(0, 8)}__';
695
459
  let sheet = globalThis[GLOBAL_KEY] || null;
696
460
 
697
461
  if (typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in document) {
@@ -863,4 +627,4 @@ if (import.meta.hot) {
863
627
  };
864
628
  }
865
629
  //#endregion
866
- export { animusExtract, animusExtract as default, evaluateTheme, evaluateThemeObject };
630
+ export { animusExtract, animusExtract as default };
@@ -1,13 +1,10 @@
1
1
  /**
2
2
  * Standalone script executed via `bun run` to resolve global styles.
3
3
  *
4
- * Usage: bun run resolve-global-styles.ts <system-path> <theme-json> <output-file>
4
+ * Usage: bun run resolve-global-styles.ts <system-path> <theme-json-path> <output-file>
5
5
  *
6
- * Imports the system module, extracts globalStyles from .serialize(),
7
- * resolves prop shorthand (bg → background-color, etc.) using the full
8
- * prop config + theme + transforms, and writes the resolved CSS to output.
9
- *
10
- * @keyframes blocks are serialized as raw CSS (no prop resolution needed).
6
+ * Imports the system module for live transform functions (ESM isolation),
7
+ * then delegates all resolution logic to @animus-ui/extract/pipeline.
11
8
  */
12
9
  export {};
13
10
  //# sourceMappingURL=resolve-global-styles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-global-styles.d.ts","sourceRoot":"","sources":["../src/resolve-global-styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAsLH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"resolve-global-styles.d.ts","sourceRoot":"","sources":["../src/resolve-global-styles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkEH,OAAO,EAAE,CAAC"}
@@ -1,15 +1,13 @@
1
1
  import { readFileSync, writeFileSync } from "fs";
2
+ import { resolveGlobalStyles } from "@animus-ui/extract/pipeline";
2
3
  //#region src/resolve-global-styles.ts
3
4
  /**
4
5
  * Standalone script executed via `bun run` to resolve global styles.
5
6
  *
6
- * Usage: bun run resolve-global-styles.ts <system-path> <theme-json> <output-file>
7
+ * Usage: bun run resolve-global-styles.ts <system-path> <theme-json-path> <output-file>
7
8
  *
8
- * Imports the system module, extracts globalStyles from .serialize(),
9
- * resolves prop shorthand (bg → background-color, etc.) using the full
10
- * prop config + theme + transforms, and writes the resolved CSS to output.
11
- *
12
- * @keyframes blocks are serialized as raw CSS (no prop resolution needed).
9
+ * Imports the system module for live transform functions (ESM isolation),
10
+ * then delegates all resolution logic to @animus-ui/extract/pipeline.
13
11
  */
14
12
  const [systemPath, themeJsonPath, outputFile] = process.argv.slice(2);
15
13
  if (!systemPath || !themeJsonPath || !outputFile) {
@@ -18,90 +16,16 @@ if (!systemPath || !themeJsonPath || !outputFile) {
18
16
  }
19
17
  const mod = await import(systemPath);
20
18
  const ds = mod.ds || mod.default || mod.system;
21
- if (!ds || !ds.serialize) throw new Error("Module does not export a SystemInstance with .serialize()");
22
- const cfg = ds.serialize();
19
+ if (!ds || !ds.toConfig) throw new Error("Module does not export a SystemInstance with .serialize()");
20
+ const cfg = ds.toConfig();
23
21
  const flat = JSON.parse(readFileSync(themeJsonPath, "utf-8"));
24
22
  const propConfig = JSON.parse(cfg.propConfig);
25
23
  const transforms = cfg.transforms || {};
26
- const gs = cfg.globalStyles || {};
24
+ const globalStyleBlocks = {};
25
+ for (const [key, val] of Object.entries(mod)) if (val && typeof val === "object" && val.__brand === "GlobalStyleBlock") globalStyleBlocks[key] = val.styles;
27
26
  const variableMap = {};
28
27
  for (const [tokenPath, value] of Object.entries(flat)) if (typeof value === "string" && value.startsWith("var(") && value.endsWith(")")) variableMap[tokenPath] = value.slice(4, -1);
29
- function camelToKebab(s) {
30
- if (s.startsWith("Webkit")) return "-webkit-" + camelToKebab(s.slice(6));
31
- return s.replace(/[A-Z]/g, (m, i) => (i > 0 ? "-" : "") + m.toLowerCase());
32
- }
33
- /**
34
- * Resolve {scale.path} and {scale.path/alpha} token aliases in a CSS value string.
35
- * Mirrors the Rust theme_resolver's resolve_token_aliases logic.
36
- */
37
- function resolveTokenAliases(value) {
38
- if (!value.includes("{")) return value;
39
- return value.replace(/\{([^}]+)\}/g, (_match, content) => {
40
- const slashIdx = content.indexOf("/");
41
- const tokenPath = slashIdx >= 0 ? content.slice(0, slashIdx) : content;
42
- const alpha = slashIdx >= 0 ? parseInt(content.slice(slashIdx + 1), 10) : null;
43
- const dotIdx = tokenPath.indexOf(".");
44
- const flatKey = dotIdx >= 0 ? tokenPath.slice(0, dotIdx) + "." + tokenPath.slice(dotIdx + 1).replace(/\./g, "-") : tokenPath;
45
- let resolved;
46
- if (variableMap[flatKey]) resolved = `var(${variableMap[flatKey]})`;
47
- else if (flat[flatKey] != null) resolved = flat[flatKey];
48
- else return `{${content}}`;
49
- if (alpha === 0) return "transparent";
50
- if (alpha != null && alpha !== 100) return `color-mix(in srgb, ${resolved} ${alpha}%, transparent)`;
51
- return resolved;
52
- });
53
- }
54
- /**
55
- * Resolve a block of selectors → style objects using prop config + theme.
56
- * @keyframes selectors are delegated to serializeRawBlock.
57
- */
58
- function resolveBlock(selectors) {
59
- const rules = [];
60
- for (const [selector, styleObj] of Object.entries(selectors)) {
61
- if (selector.startsWith("@keyframes")) {
62
- const frames = [];
63
- for (const [pct, frameStyles] of Object.entries(styleObj)) if (typeof frameStyles === "object" && frameStyles !== null) {
64
- const decls = [];
65
- for (const [prop, raw] of Object.entries(frameStyles)) {
66
- const cfg = propConfig[prop];
67
- const cssProps = cfg?.properties?.length ? cfg.properties : cfg ? [cfg.property] : [prop];
68
- let resolved = raw;
69
- if (cfg?.scale) {
70
- const key = cfg.scale + "." + raw;
71
- if (flat[key] != null) resolved = flat[key];
72
- }
73
- resolved = resolveTokenAliases(String(resolved));
74
- for (const cssProp of cssProps) decls.push(` ${camelToKebab(cssProp)}: ${resolved};`);
75
- }
76
- frames.push(` ${pct} {\n${decls.join("\n")}\n }`);
77
- }
78
- rules.push(`${selector} {\n${frames.join("\n")}\n}`);
79
- continue;
80
- }
81
- const decls = [];
82
- for (const [prop, raw] of Object.entries(styleObj)) {
83
- const config = propConfig[prop];
84
- const cssProps = config?.properties?.length ? config.properties : config ? [config.property] : [prop];
85
- let resolved = raw;
86
- if (config?.scale) {
87
- const key = config.scale + "." + raw;
88
- if (flat[key] != null) resolved = flat[key];
89
- }
90
- if (config?.transform && transforms[config.transform]) {
91
- const fn = transforms[config.transform];
92
- const input = typeof resolved === "string" && !isNaN(Number(resolved)) ? Number(resolved) : resolved;
93
- resolved = String(fn(input));
94
- } else resolved = String(resolved);
95
- resolved = resolveTokenAliases(resolved);
96
- for (const cssProp of cssProps) decls.push(` ${camelToKebab(cssProp)}: ${resolved};`);
97
- }
98
- if (decls.length) rules.push(`${selector} {\n${decls.join("\n")}\n}`);
99
- }
100
- return rules.join("\n\n");
101
- }
102
- const result = {};
103
- if (gs.reset) result.reset = resolveBlock(gs.reset);
104
- if (gs.global) result.global = resolveBlock(gs.global);
28
+ const result = resolveGlobalStyles(globalStyleBlocks, propConfig, flat, variableMap, transforms);
105
29
  writeFileSync(outputFile, JSON.stringify(result));
106
30
  //#endregion
107
31
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@animus-ui/vite-plugin",
3
- "version": "0.1.0-next.23",
3
+ "version": "0.1.0-next.30",
4
4
  "description": "Animus static CSS extraction Vite plugin",
5
5
  "author": "codecaaron <airrobb@gmail.com>",
6
6
  "license": "MIT",
@@ -25,7 +25,8 @@
25
25
  "vite": ">=5.0.0"
26
26
  },
27
27
  "dependencies": {
28
- "@animus-ui/extract": "0.1.0-next.23",
28
+ "@animus-ui/extract": "0.1.0-next.30",
29
+ "@animus-ui/system": "0.1.0-next.30",
29
30
  "browserslist": "^4.24.0",
30
31
  "lightningcss": "^1.29.0"
31
32
  }
@@ -1,35 +0,0 @@
1
- /**
2
- * Evaluate a theme object that has already been loaded/imported.
3
- *
4
- * If the theme has a `.manifest` property (from ThemeBuilder.build()),
5
- * reads structured data directly — no re-flattening or var() pattern-matching.
6
- * Falls back to legacy path for themes without manifests.
7
- */
8
- export declare function evaluateThemeObject(theme: Record<string, any>): {
9
- scalesJson: string;
10
- variableMapJson: string;
11
- variableCss: string;
12
- contextualVarsJson: string;
13
- };
14
- /**
15
- * Evaluate a theme module and flatten all scales to a JSON map.
16
- * Format: { "scale_name.key": "css_value" }
17
- *
18
- * Uses Vite's ssrLoadModule to evaluate the theme at build start,
19
- * so all computed scales (shadows, gradients that depend on colors)
20
- * are resolved to their final values.
21
- */
22
- export declare function evaluateTheme(ssrLoadModule: (url: string) => Promise<Record<string, any>>, themePath: string): Promise<{
23
- scalesJson: string;
24
- variableMapJson: string;
25
- variableCss: string;
26
- contextualVarsJson: string;
27
- }>;
28
- /**
29
- * Resolve global style objects to a CSS string.
30
- *
31
- * Each key in `globalStyles` is a CSS selector, each value is a style object
32
- * using the same prop shorthand as component `.styles()` blocks.
33
- */
34
- export declare function resolveGlobalStyles(globalStyles: Record<string, Record<string, any>>, propConfigJson: string, flat: Record<string, string>, transforms: Record<string, (v: any) => any>): string;
35
- //# sourceMappingURL=theme-evaluator.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme-evaluator.d.ts","sourceRoot":"","sources":["../src/theme-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAkBA;AAoDD;;;;;;;GAOG;AACH,wBAAsB,aAAa,CACjC,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAC5D,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC,CAWD;AAuKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EACjD,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,GAC1C,MAAM,CAiCR"}