@animus-ui/vite-plugin 0.1.0-next.1

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.
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Serialize the prop config from @animus-ui/core for the Rust extraction pipeline.
3
+ *
4
+ * Maps JS transform functions to string identifiers that the Rust crate
5
+ * can dispatch to its native implementations.
6
+ */
7
+ interface PropConfig {
8
+ property: string;
9
+ properties?: string[];
10
+ scale?: string | Record<string, any> | any[];
11
+ transform?: Function;
12
+ }
13
+ /**
14
+ * Serialize all prop groups from the animus config into a JSON map.
15
+ * Prop config entries with inline scales (object/array) are skipped
16
+ * since the Rust crate only handles named theme scales.
17
+ */
18
+ export declare function serializeConfig(propRegistry: Record<string, PropConfig>): string;
19
+ /**
20
+ * Serialize the group registry from the Animus config.
21
+ * Maps each group name to its array of constituent prop names.
22
+ */
23
+ export declare function serializeGroupRegistry(groupRegistry: Record<string, string[]>): string;
24
+ export {};
25
+ //# sourceMappingURL=config-serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-serializer.d.ts","sourceRoot":"","sources":["../src/config-serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC;IAC7C,SAAS,CAAC,EAAE,QAAQ,CAAC;CACtB;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GACvC,MAAM,CA8BR;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACtC,MAAM,CAER"}
@@ -0,0 +1,60 @@
1
+ import type { Plugin } from 'vite';
2
+ export interface AnimusExtractOptions {
3
+ /**
4
+ * Theme data for the extraction pipeline.
5
+ *
6
+ * Two forms are accepted:
7
+ * - `string` — pre-serialized flat JSON (legacy; no CSS variable emission)
8
+ * - `{ scales: string; variables: string }` — pre-evaluated scales JSON
9
+ * plus fully-formed CSS variable declarations to prepend to the virtual
10
+ * stylesheet. The caller is responsible for evaluating the theme module
11
+ * (e.g. via `evaluateTheme` or a Bun subprocess) before passing it here.
12
+ *
13
+ * When omitted, the plugin auto-detects `src/theme.ts`, `src/theme.js`,
14
+ * `theme.ts`, or `theme.js` relative to the project root.
15
+ */
16
+ theme?: string | {
17
+ scales: string;
18
+ variables: string;
19
+ };
20
+ /**
21
+ * Explicit path to the theme module (relative to project root or absolute).
22
+ * Takes precedence over auto-detection, but lower priority than `theme`.
23
+ */
24
+ themePath?: string;
25
+ /**
26
+ * Path to a module exporting a SystemInstance from `@animus-ui/system`.
27
+ * The module is loaded via a single bun subprocess at build start.
28
+ * Replaces configPath + themePath when using the system package.
29
+ */
30
+ system?: string;
31
+ /**
32
+ * Path to a module exporting `getExtractConfig()`. The module is loaded via
33
+ * bun subprocess at build start. Use this for custom Animus instances created
34
+ * with `createAnimus().addGroup(...).build()`.
35
+ *
36
+ * When omitted, automatically imported from `@animus-ui/core`.
37
+ */
38
+ configPath?: string;
39
+ /**
40
+ * Pre-serialized prop config JSON. When omitted, automatically imported from
41
+ * `@animus-ui/core` via `getExtractConfig()`.
42
+ */
43
+ config?: string;
44
+ /** Pre-serialized group registry JSON. Maps group names to prop name arrays. */
45
+ groupRegistry?: string;
46
+ /** Glob patterns to include. Defaults to .ts/.tsx/.js/.jsx files. */
47
+ include?: string[];
48
+ /** Glob patterns to exclude. */
49
+ exclude?: string[];
50
+ /** Package name patterns to resolve and include in analysis. Defaults to ['@animus-ui/*']. */
51
+ packagePatterns?: string[];
52
+ /** When true, extraction failures throw instead of warning. Use in CI to enforce full extraction. */
53
+ strict?: boolean;
54
+ /** Enable verbose logging. Also activatable via ANIMUS_DEBUG=1 env var. */
55
+ verbose?: boolean;
56
+ }
57
+ export declare function animusExtract(options?: AnimusExtractOptions): Plugin;
58
+ export { evaluateTheme, evaluateThemeObject } from './theme-evaluator';
59
+ export default animusExtract;
60
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAU,MAAM,EAAE,MAAM,MAAM,CAAC;AAM3C,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,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;CACnB;AA0RD,wBAAgB,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAq5BxE;AAED,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACvE,eAAe,aAAa,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,818 @@
1
+ import { createRequire } from "node:module";
2
+ import { execSync } from "child_process";
3
+ import { createHash } from "crypto";
4
+ import { existsSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs";
5
+ import { tmpdir } from "os";
6
+ import { dirname, extname, join, relative, resolve } from "path";
7
+ import { fileURLToPath } from "url";
8
+ //#region \0rolldown/runtime.js
9
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
10
+ //#endregion
11
+ //#region src/theme-evaluator.ts
12
+ /**
13
+ * Evaluate a theme object that has already been loaded/imported.
14
+ *
15
+ * This is the pure logic: takes a theme object, flattens scales, builds variable CSS.
16
+ * Use this when you have already loaded the module yourself.
17
+ */
18
+ function evaluateThemeObject(theme) {
19
+ const flat = {};
20
+ for (const [scaleName, scaleValue] of Object.entries(theme)) {
21
+ if (scaleName.startsWith("_")) continue;
22
+ if (scaleName === "breakpoints") {
23
+ flattenScale(flat, "breakpoints", scaleValue);
24
+ continue;
25
+ }
26
+ if (typeof scaleValue === "object" && scaleValue !== null && !Array.isArray(scaleValue)) flattenScale(flat, scaleName, scaleValue);
27
+ }
28
+ const variableMap = {};
29
+ for (const [tokenPath, value] of Object.entries(flat)) if (value.startsWith("var(") && value.endsWith(")")) variableMap[tokenPath] = value.slice(4, -1);
30
+ const variableCss = buildVariableCss(theme);
31
+ return {
32
+ scalesJson: JSON.stringify(flat),
33
+ variableMapJson: JSON.stringify(variableMap),
34
+ variableCss
35
+ };
36
+ }
37
+ /**
38
+ * Evaluate a theme module and flatten all scales to a JSON map.
39
+ * Format: { "scale_name.key": "css_value" }
40
+ *
41
+ * Uses Vite's ssrLoadModule to evaluate the theme at build start,
42
+ * so all computed scales (shadows, gradients that depend on colors)
43
+ * are resolved to their final values.
44
+ */
45
+ async function evaluateTheme(ssrLoadModule, themePath) {
46
+ const mod = await ssrLoadModule(themePath);
47
+ const theme = mod.theme || mod.default;
48
+ if (!theme) throw new Error(`Theme module at ${themePath} must export 'theme' or a default export`);
49
+ return evaluateThemeObject(theme);
50
+ }
51
+ /**
52
+ * Build the full CSS variable block from a theme's _variables and _tokens.
53
+ *
54
+ * Produces:
55
+ * :root { --color-navy-500: #282a36; ... }
56
+ * [data-color-mode="dark"] { --color-primary: #ff80bf; ... }
57
+ */
58
+ function buildVariableCss(theme) {
59
+ const parts = [];
60
+ if (theme._variables != null && typeof theme._variables === "object") {
61
+ const rootLines = [];
62
+ for (const categoryValue of Object.values(theme._variables)) {
63
+ if (categoryValue == null || typeof categoryValue !== "object") continue;
64
+ for (const [cssVar, cssValue] of Object.entries(categoryValue)) if (typeof cssValue === "string") rootLines.push(` ${cssVar}: ${cssValue};`);
65
+ }
66
+ if (rootLines.length > 0) parts.push(`:root {\n${rootLines.join("\n")}\n}`);
67
+ }
68
+ if (theme._tokens?.modes != null && typeof theme._tokens.modes === "object") {
69
+ const defaultMode = typeof theme.mode === "string" ? theme.mode : Object.keys(theme._tokens.modes)[0];
70
+ for (const [modeName, modeTokens] of Object.entries(theme._tokens.modes)) {
71
+ if (modeName === defaultMode) continue;
72
+ if (modeTokens == null || typeof modeTokens !== "object") continue;
73
+ const modeLines = [];
74
+ flattenModeTokens(modeLines, modeTokens, "");
75
+ if (modeLines.length > 0) parts.push(`[data-color-mode="${modeName}"] {\n${modeLines.join("\n")}\n}`);
76
+ }
77
+ }
78
+ return parts.join("\n\n");
79
+ }
80
+ /**
81
+ * Recursively flatten mode token objects into CSS variable declarations.
82
+ *
83
+ * Examples:
84
+ * { primary: '#ff80bf' } → ' --color-primary: #ff80bf;'
85
+ * { background: { _: '#fff', muted: '#f5f5f5' } }
86
+ * → ' --color-background: #fff;'
87
+ * ' --color-background-muted: #f5f5f5;'
88
+ *
89
+ * The special key `_` maps to the base name (no suffix).
90
+ */
91
+ function flattenModeTokens(lines, obj, prefix) {
92
+ for (const [key, value] of Object.entries(obj)) {
93
+ let namePart;
94
+ if (key === "_") namePart = prefix;
95
+ else namePart = prefix ? `${prefix}-${key}` : key;
96
+ if (typeof value === "string" || typeof value === "number") lines.push(` --color-${namePart}: ${value};`);
97
+ else if (value != null && typeof value === "object" && !Array.isArray(value)) flattenModeTokens(lines, value, namePart);
98
+ }
99
+ }
100
+ function flattenScale(flat, prefix, obj, parentKey = "") {
101
+ if (typeof obj !== "object" || obj === null) {
102
+ const key = parentKey ? `${prefix}.${parentKey}` : prefix;
103
+ flat[key] = String(obj);
104
+ return;
105
+ }
106
+ for (const [key, value] of Object.entries(obj)) {
107
+ const fullKey = parentKey ? `${parentKey}-${key}` : key;
108
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) flattenScale(flat, prefix, value, fullKey);
109
+ else flat[`${prefix}.${fullKey}`] = String(value);
110
+ }
111
+ }
112
+ //#endregion
113
+ //#region src/index.ts
114
+ const __pluginDir = dirname(fileURLToPath(import.meta.url));
115
+ const VIRTUAL_CSS_ID = "virtual:animus/styles.css";
116
+ const RESOLVED_CSS_ID = "\0virtual:animus/styles.css";
117
+ const VIRTUAL_COMPONENTS_ID = "virtual:animus/components.js";
118
+ const RESOLVED_COMPONENTS_ID = "\0virtual:animus/components.js";
119
+ const VIRTUAL_BRIDGE_ID = "virtual:animus/hmr-bridge.js";
120
+ const RESOLVED_BRIDGE_ID = "\0virtual:animus/hmr-bridge.js";
121
+ const DEFAULT_EXTENSIONS = new Set([
122
+ ".ts",
123
+ ".tsx",
124
+ ".js",
125
+ ".jsx"
126
+ ]);
127
+ const DEFAULT_EXCLUDE = [
128
+ "node_modules",
129
+ "dist",
130
+ ".test.",
131
+ ".spec."
132
+ ];
133
+ function discoverFiles(dir, rootDir, excludePatterns) {
134
+ const results = [];
135
+ let entries;
136
+ try {
137
+ entries = readdirSync(dir, { encoding: "utf8" });
138
+ } catch {
139
+ return results;
140
+ }
141
+ for (const entry of entries) {
142
+ const fullPath = join(dir, entry);
143
+ const relativePath = relative(rootDir, fullPath);
144
+ if (excludePatterns.some((pattern) => fullPath.includes(pattern) || relativePath.includes(pattern))) continue;
145
+ let stat;
146
+ try {
147
+ stat = statSync(fullPath);
148
+ } catch {
149
+ continue;
150
+ }
151
+ if (stat.isDirectory()) results.push(...discoverFiles(fullPath, rootDir, excludePatterns));
152
+ else if (DEFAULT_EXTENSIONS.has(extname(entry))) results.push(fullPath);
153
+ }
154
+ return results;
155
+ }
156
+ /**
157
+ * Apply transform placeholders emitted by the Rust pipeline.
158
+ *
159
+ * Rust emits `__TRANSFORM__name__rawValue__` for props with transforms.
160
+ * This function resolves each placeholder using the actual JS transform
161
+ * functions from the config.
162
+ */
163
+ function applyTransformPlaceholders(css, transformRegistry) {
164
+ return css.replace(/__TRANSFORM__(\w+)__(.+?)__/g, (_match, name, rawValue) => {
165
+ const fn = transformRegistry.get(name);
166
+ if (!fn) {
167
+ console.warn(`[animus-extract] Unknown transform: "${name}" — using raw value`);
168
+ return rawValue;
169
+ }
170
+ const result = fn(rawValue !== "" && !isNaN(Number(rawValue)) ? Number(rawValue) : rawValue);
171
+ return typeof result === "object" ? JSON.stringify(result) : String(result);
172
+ });
173
+ }
174
+ /**
175
+ * CSS properties that accept unitless numeric values.
176
+ * Matches @emotion/unitless and React DOM's style handling.
177
+ * Bare numerics on properties NOT in this set receive `px`.
178
+ */
179
+ const UNITLESS_PROPERTIES = new Set([
180
+ "animation-iteration-count",
181
+ "border-image-outset",
182
+ "border-image-slice",
183
+ "border-image-width",
184
+ "box-flex",
185
+ "box-flex-group",
186
+ "box-ordinal-group",
187
+ "column-count",
188
+ "columns",
189
+ "flex",
190
+ "flex-grow",
191
+ "flex-positive",
192
+ "flex-shrink",
193
+ "flex-negative",
194
+ "flex-order",
195
+ "grid-area",
196
+ "grid-row",
197
+ "grid-row-end",
198
+ "grid-row-span",
199
+ "grid-row-start",
200
+ "grid-column",
201
+ "grid-column-end",
202
+ "grid-column-span",
203
+ "grid-column-start",
204
+ "font-weight",
205
+ "line-clamp",
206
+ "line-height",
207
+ "opacity",
208
+ "order",
209
+ "orphans",
210
+ "tab-size",
211
+ "widows",
212
+ "z-index",
213
+ "zoom",
214
+ "fill-opacity",
215
+ "flood-opacity",
216
+ "stop-opacity",
217
+ "stroke-dasharray",
218
+ "stroke-dashoffset",
219
+ "stroke-miterlimit",
220
+ "stroke-opacity",
221
+ "stroke-width"
222
+ ]);
223
+ /**
224
+ * Append `px` to bare numeric values in CSS declarations for properties
225
+ * that expect length units. Unitless properties are preserved as-is.
226
+ * Numbers inside CSS function calls (cubic-bezier, rgb, calc, etc.) are skipped.
227
+ */
228
+ function applyUnitFallback(css) {
229
+ return css.replace(/([a-z-]+)\s*:\s*([^;]+);/g, (match, prop, value) => {
230
+ if (UNITLESS_PROPERTIES.has(prop)) return match;
231
+ let depth = 0;
232
+ let fixed = "";
233
+ let i = 0;
234
+ while (i < value.length) if (value[i] === "(") {
235
+ depth++;
236
+ fixed += value[i];
237
+ i++;
238
+ } else if (value[i] === ")") {
239
+ depth--;
240
+ fixed += value[i];
241
+ i++;
242
+ } else if (depth > 0) {
243
+ fixed += value[i];
244
+ i++;
245
+ } else {
246
+ const numMatch = value.slice(i).match(/^(-?\d+\.?\d*)/);
247
+ if (numMatch) {
248
+ const num = numMatch[1];
249
+ const after = value[i + num.length];
250
+ if (after && /[a-z%]/i.test(after)) fixed += num;
251
+ else fixed += num + "px";
252
+ i += num.length;
253
+ } else {
254
+ fixed += value[i];
255
+ i++;
256
+ }
257
+ }
258
+ return fixed !== value ? `${prop}:${fixed};` : match;
259
+ });
260
+ }
261
+ /** Compute MD5 content hash for a string. */
262
+ function contentHash(source) {
263
+ return createHash("md5").update(source).digest("hex");
264
+ }
265
+ /**
266
+ * Reconstruct file entries from cache, including content hashes.
267
+ * For unchanged files (hash matches changedPath), sends empty source
268
+ * to avoid serializing full source text across the NAPI boundary.
269
+ * Rust cache-hit path never reads file.source, so empty string is safe.
270
+ */
271
+ function buildFileEntriesFromCache(cache, changedPath) {
272
+ const entries = [];
273
+ for (const [path, { hash, source }] of cache) entries.push({
274
+ path,
275
+ source: path === changedPath ? source : "",
276
+ hash
277
+ });
278
+ return entries;
279
+ }
280
+ /**
281
+ * Resolve transform placeholders in CSS via bun subprocess.
282
+ * Falls back to in-process resolution if the subprocess fails.
283
+ */
284
+ function resolveTransformPlaceholders(css, rootDir, configPath, transformRegistry) {
285
+ if (!css.includes("__TRANSFORM__")) return css;
286
+ try {
287
+ const ts = Date.now();
288
+ const tmpCss = join(tmpdir(), `animus-transforms-${ts}.css`);
289
+ const tmpOut = join(tmpdir(), `animus-transforms-${ts}.out.css`);
290
+ writeFileSync(tmpCss, css);
291
+ const candidates = [join(__pluginDir, "resolve-transforms.ts"), join(__pluginDir, "..", "src", "resolve-transforms.ts")];
292
+ try {
293
+ const pkgDir = dirname(__require.resolve("@animus-ui/vite-plugin/package.json", { paths: [rootDir] }));
294
+ candidates.push(join(pkgDir, "src", "resolve-transforms.ts"));
295
+ } catch {}
296
+ const scriptPath = candidates.find((p) => existsSync(p));
297
+ if (!scriptPath) throw new Error(`resolve-transforms.ts not found in: ${candidates.join(", ")}`);
298
+ execSync(`bun run "${scriptPath}" "${tmpCss}" "${tmpOut}" ${configPath ? `"${resolve(rootDir, configPath)}"` : ""}`, {
299
+ cwd: rootDir,
300
+ encoding: "utf-8"
301
+ });
302
+ const resolved = readFileSync(tmpOut, "utf-8");
303
+ try {
304
+ unlinkSync(tmpCss);
305
+ unlinkSync(tmpOut);
306
+ } catch {}
307
+ return resolved;
308
+ } catch (e) {
309
+ console.error("[animus-extract] Transform resolution failed:", e?.message || e);
310
+ return applyTransformPlaceholders(css, transformRegistry);
311
+ }
312
+ }
313
+ function animusExtract(options = {}) {
314
+ let themeJson = "{}";
315
+ let variableMapJson = "{}";
316
+ let configJson = "{}";
317
+ let groupRegistryJson = "{}";
318
+ let isProd = false;
319
+ let rootDir = "";
320
+ const verbose = options.verbose || process.env.ANIMUS_DEBUG === "1" || process.env.ANIMUS_DEBUG === "true";
321
+ let logger = null;
322
+ function log(msg) {
323
+ if (verbose) (logger ?? console).info(`[animus] ${msg}`);
324
+ }
325
+ function warn(msg) {
326
+ (logger ?? console).warn(`[animus] ${msg}`);
327
+ }
328
+ let variableCss = "";
329
+ let globalCss = "";
330
+ let transformRegistry = /* @__PURE__ */ new Map();
331
+ let storedManifest = null;
332
+ let storedManifestJson = "";
333
+ let resolvedComponentCss = "";
334
+ let storedSheets = null;
335
+ const fileCache = /* @__PURE__ */ new Map();
336
+ let packageMap = {};
337
+ let bridgeInjected = false;
338
+ let resolvedConfigPath = null;
339
+ let resolvedThemePath = null;
340
+ let resolvedSystemPath = null;
341
+ /** Resolve config via bun subprocess. Updates configJson + groupRegistryJson. */
342
+ function loadConfig() {
343
+ if (options.config) {
344
+ configJson = options.config;
345
+ groupRegistryJson = options.groupRegistry ?? groupRegistryJson;
346
+ return;
347
+ }
348
+ const configSource = options.configPath ? resolve(rootDir, options.configPath) : "@animus-ui/core";
349
+ const requireExpr = options.configPath ? `require('${configSource.replace(/\\/g, "/")}')` : `require('@animus-ui/core')`;
350
+ if (options.configPath) resolvedConfigPath = resolve(rootDir, options.configPath);
351
+ try {
352
+ const result = execSync(`bun -e "const m = ${requireExpr}; const r = m.getExtractConfig(); process.stdout.write(JSON.stringify(r))"`, {
353
+ cwd: rootDir,
354
+ encoding: "utf-8"
355
+ });
356
+ const { propConfig, groupRegistry } = JSON.parse(result);
357
+ configJson = propConfig;
358
+ groupRegistryJson = groupRegistry;
359
+ } catch (e) {
360
+ if (options.strict) throw new Error(`[animus-extract] Failed to auto-import config from ${configSource}: ${e}`);
361
+ console.warn(`[animus-extract] Failed to auto-import config from ${configSource}:`, e);
362
+ }
363
+ }
364
+ /** Resolve theme via bun subprocess. Updates themeJson + variableCss. */
365
+ function loadTheme() {
366
+ if (options.theme) {
367
+ if (typeof options.theme === "string") {
368
+ themeJson = options.theme;
369
+ variableCss = "";
370
+ } else {
371
+ themeJson = options.theme.scales;
372
+ variableCss = options.theme.variables;
373
+ }
374
+ return;
375
+ }
376
+ resolvedThemePath = null;
377
+ if (options.themePath) {
378
+ const p = resolve(rootDir, options.themePath);
379
+ if (existsSync(p)) resolvedThemePath = p;
380
+ else if (options.strict) throw new Error(`[animus-extract] Theme file not found: ${p}`);
381
+ else console.warn(`[animus-extract] Theme file not found: ${p}`);
382
+ } else {
383
+ const candidates = [
384
+ join(rootDir, "src", "theme.ts"),
385
+ join(rootDir, "src", "theme.js"),
386
+ join(rootDir, "theme.ts"),
387
+ join(rootDir, "theme.js")
388
+ ];
389
+ for (const candidate of candidates) if (existsSync(candidate)) {
390
+ resolvedThemePath = candidate;
391
+ break;
392
+ }
393
+ }
394
+ if (resolvedThemePath) try {
395
+ const themeJson_ = execSync(`bun -e "${[
396
+ `const m = require(${JSON.stringify(resolvedThemePath)});`,
397
+ `const theme = m.theme || m.default;`,
398
+ `process.stdout.write(JSON.stringify(theme || {}));`
399
+ ].join(" ").replace(/"/g, "\\\"")}"`, {
400
+ cwd: rootDir,
401
+ encoding: "utf-8"
402
+ });
403
+ const theme = JSON.parse(themeJson_);
404
+ if (theme && Object.keys(theme).length > 0) {
405
+ const result = evaluateThemeObject(theme);
406
+ themeJson = result.scalesJson;
407
+ variableMapJson = result.variableMapJson;
408
+ variableCss = result.variableCss;
409
+ }
410
+ } catch (e) {
411
+ if (options.strict) throw new Error(`[animus-extract] Failed to load theme from ${resolvedThemePath}: ${e}`);
412
+ console.warn(`[animus-extract] Failed to load theme from ${resolvedThemePath}:`, e);
413
+ }
414
+ }
415
+ /**
416
+ * Load a SystemInstance via single bun subprocess.
417
+ * Replaces loadConfig() + loadTheme() when `system` option is provided.
418
+ * The subprocess imports the module and calls .serialize().
419
+ */
420
+ function loadSystem() {
421
+ if (!options.system) return;
422
+ resolvedSystemPath = resolve(rootDir, options.system);
423
+ try {
424
+ const ts = Date.now();
425
+ const tmpScript = join(tmpdir(), `animus-system-${ts}.js`);
426
+ const tmpOut = join(tmpdir(), `animus-system-${ts}.json`);
427
+ 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();\nrequire('fs').writeFileSync(${JSON.stringify(tmpOut)}, JSON.stringify({\n propConfig: cfg.propConfig,\n groupRegistry: cfg.groupRegistry,\n tokens: cfg.tokens,\n transformNames: Object.keys(cfg.transforms || {}),\n globalStyles: cfg.globalStyles || null\n}));\n`);
428
+ execSync(`bun run "${tmpScript}"`, {
429
+ cwd: rootDir,
430
+ encoding: "utf-8"
431
+ });
432
+ const result = readFileSync(tmpOut, "utf-8");
433
+ try {
434
+ unlinkSync(tmpScript);
435
+ unlinkSync(tmpOut);
436
+ } catch {}
437
+ const parsed = JSON.parse(result);
438
+ configJson = parsed.propConfig;
439
+ groupRegistryJson = parsed.groupRegistry;
440
+ if (parsed.tokens && Object.keys(parsed.tokens).length > 0) {
441
+ const result = evaluateThemeObject(parsed.tokens);
442
+ themeJson = result.scalesJson;
443
+ variableMapJson = result.variableMapJson;
444
+ variableCss = result.variableCss;
445
+ }
446
+ if (parsed.globalStyles) {
447
+ const hasReset = parsed.globalStyles.reset && Object.keys(parsed.globalStyles.reset).length > 0;
448
+ const hasGlobal = parsed.globalStyles.global && Object.keys(parsed.globalStyles.global).length > 0;
449
+ if (hasReset || hasGlobal) try {
450
+ const gsTmp = Date.now();
451
+ const gsThemeFile = join(tmpdir(), `animus-gs-theme-${gsTmp}.json`);
452
+ const gsOut = join(tmpdir(), `animus-global-${gsTmp}.json`);
453
+ writeFileSync(gsThemeFile, themeJson);
454
+ const gsCandidates = [join(__pluginDir, "resolve-global-styles.ts"), join(__pluginDir, "..", "src", "resolve-global-styles.ts")];
455
+ try {
456
+ const pkgDir = dirname(__require.resolve("@animus-ui/vite-plugin/package.json", { paths: [rootDir] }));
457
+ gsCandidates.push(join(pkgDir, "src", "resolve-global-styles.ts"));
458
+ } catch {}
459
+ const gsScriptPath = gsCandidates.find((p) => existsSync(p));
460
+ if (!gsScriptPath) throw new Error(`resolve-global-styles.ts not found in: ${gsCandidates.join(", ")}`);
461
+ execSync(`bun run "${gsScriptPath}" "${resolvedSystemPath}" "${gsThemeFile}" "${gsOut}"`, {
462
+ cwd: rootDir,
463
+ encoding: "utf-8"
464
+ });
465
+ const gsResult = JSON.parse(readFileSync(gsOut, "utf-8"));
466
+ const parts = [gsResult.reset, gsResult.global].filter(Boolean);
467
+ if (parts.length > 0) globalCss = `@layer global {\n${parts.join("\n\n")}\n}`;
468
+ try {
469
+ unlinkSync(gsThemeFile);
470
+ unlinkSync(gsOut);
471
+ } catch {}
472
+ } catch (e) {
473
+ console.warn("[animus-extract] Global styles resolution failed:", e?.message || e);
474
+ }
475
+ }
476
+ if (parsed.transformNames && parsed.transformNames.length > 0) {
477
+ const tsTmp = Date.now();
478
+ const tmpResolve = join(tmpdir(), `animus-transforms-resolve-${tsTmp}.js`);
479
+ 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`);
480
+ globalThis.__animus_system_resolve_script = tmpResolve;
481
+ }
482
+ } catch (e) {
483
+ if (options.strict) throw new Error(`[animus-extract] Failed to load system from ${resolvedSystemPath}: ${e}`);
484
+ console.warn(`[animus-extract] Failed to load system from ${resolvedSystemPath}:`, e);
485
+ }
486
+ }
487
+ /**
488
+ * Run project analysis and update the manifest.
489
+ * Uses fileCache if populated (dev mode), otherwise uses provided entries.
490
+ */
491
+ function runAnalysis(fileEntries) {
492
+ try {
493
+ const { analyzeProject } = __require("@animus-ui/extract");
494
+ const manifestJson = analyzeProject(JSON.stringify(fileEntries), themeJson, variableMapJson, configJson, groupRegistryJson, JSON.stringify(packageMap), !isProd);
495
+ storedManifest = JSON.parse(manifestJson);
496
+ storedManifestJson = manifestJson;
497
+ bridgeInjected = false;
498
+ storedSheets = storedManifest?.sheets ?? null;
499
+ const rawCss = storedManifest?.css || "";
500
+ const systemResolveScript = globalThis.__animus_system_resolve_script;
501
+ if (systemResolveScript && rawCss.includes("__TRANSFORM__") && isProd) try {
502
+ const tsTmp = Date.now();
503
+ const tmpIn = join(tmpdir(), `animus-css-${tsTmp}.css`);
504
+ const tmpOut = join(tmpdir(), `animus-css-${tsTmp}.out.css`);
505
+ writeFileSync(tmpIn, rawCss);
506
+ execSync(`bun run "${systemResolveScript}" "${tmpIn}" "${tmpOut}"`, {
507
+ cwd: rootDir,
508
+ encoding: "utf-8"
509
+ });
510
+ resolvedComponentCss = readFileSync(tmpOut, "utf-8");
511
+ try {
512
+ unlinkSync(tmpIn);
513
+ unlinkSync(tmpOut);
514
+ } catch {}
515
+ } catch (e) {
516
+ console.warn("[animus-extract] System transform resolution failed:", e?.message);
517
+ resolvedComponentCss = resolveTransformPlaceholders(rawCss, rootDir, options.configPath, transformRegistry);
518
+ }
519
+ else resolvedComponentCss = resolveTransformPlaceholders(rawCss, rootDir, options.configPath, transformRegistry);
520
+ resolvedComponentCss = applyUnitFallback(resolvedComponentCss);
521
+ } catch (e) {
522
+ if (options.strict) throw new Error(`[animus-extract] analyzeProject failed: ${e}`);
523
+ console.warn("[animus-extract] analyzeProject failed:", e);
524
+ }
525
+ }
526
+ return {
527
+ name: "animus-extract",
528
+ enforce: "pre",
529
+ configResolved(config) {
530
+ isProd = config.command === "build";
531
+ rootDir = config.root;
532
+ logger = config.logger;
533
+ },
534
+ async buildStart() {
535
+ let t0 = performance.now();
536
+ if (options.system) loadSystem();
537
+ else {
538
+ loadConfig();
539
+ loadTheme();
540
+ }
541
+ if (verbose) {
542
+ const propCount = Object.keys(JSON.parse(configJson)).length;
543
+ const groupCount = Object.keys(JSON.parse(groupRegistryJson)).length;
544
+ log(`System loaded: ${propCount} props, ${groupCount} groups (${Math.round(performance.now() - t0)}ms)`);
545
+ }
546
+ t0 = performance.now();
547
+ const excludePatterns = options.exclude ?? DEFAULT_EXCLUDE;
548
+ const filePaths = discoverFiles(rootDir, rootDir, excludePatterns);
549
+ const fileEntries = [];
550
+ for (const filePath of filePaths) try {
551
+ const source = readFileSync(filePath, "utf-8");
552
+ const relPath = relative(rootDir, filePath);
553
+ const hash = !isProd ? contentHash(source) : void 0;
554
+ fileEntries.push({
555
+ path: relPath,
556
+ source,
557
+ hash
558
+ });
559
+ if (!isProd && hash) fileCache.set(relPath, {
560
+ hash,
561
+ source
562
+ });
563
+ } catch {}
564
+ const localFileCount = fileEntries.length;
565
+ const patterns = options.packagePatterns ?? ["@animus-ui/*"];
566
+ const packageSpecifiers = /* @__PURE__ */ new Set();
567
+ for (const entry of fileEntries) {
568
+ const importRegex = /from\s+['"]([^'"]+)['"]/g;
569
+ let match;
570
+ while ((match = importRegex.exec(entry.source)) !== null) {
571
+ const source = match[1];
572
+ for (const pattern of patterns) {
573
+ const prefix = pattern.replace("*", "");
574
+ if (source.startsWith(prefix) || source === pattern.replace("/*", "")) packageSpecifiers.add(source);
575
+ }
576
+ }
577
+ }
578
+ packageMap = {};
579
+ for (const specifier of packageSpecifiers) try {
580
+ const resolved = await this.resolve(specifier);
581
+ if (resolved && resolved.id) {
582
+ const absPath = resolved.id;
583
+ const relPath = relative(rootDir, absPath);
584
+ if (!fileEntries.some((e) => e.path === relPath)) {
585
+ const entrySource = readFileSync(absPath, "utf-8");
586
+ const entryHash = !isProd ? contentHash(entrySource) : void 0;
587
+ fileEntries.push({
588
+ path: relPath,
589
+ source: entrySource,
590
+ hash: entryHash
591
+ });
592
+ if (!isProd && entryHash) fileCache.set(relPath, {
593
+ hash: entryHash,
594
+ source: entrySource
595
+ });
596
+ }
597
+ const pkgFiles = discoverFiles(dirname(absPath), rootDir, excludePatterns);
598
+ for (const pkgFile of pkgFiles) {
599
+ const pkgRelPath = relative(rootDir, pkgFile);
600
+ if (!fileEntries.some((e) => e.path === pkgRelPath)) {
601
+ const pkgSource = readFileSync(pkgFile, "utf-8");
602
+ const pkgHash = !isProd ? contentHash(pkgSource) : void 0;
603
+ fileEntries.push({
604
+ path: pkgRelPath,
605
+ source: pkgSource,
606
+ hash: pkgHash
607
+ });
608
+ if (!isProd && pkgHash) fileCache.set(pkgRelPath, {
609
+ hash: pkgHash,
610
+ source: pkgSource
611
+ });
612
+ }
613
+ }
614
+ packageMap[specifier] = relPath;
615
+ }
616
+ } catch {}
617
+ const packageFileCount = fileEntries.length - localFileCount;
618
+ log(`Discovered ${fileEntries.length} files (${packageFileCount} from packages) (${Math.round(performance.now() - t0)}ms)`);
619
+ t0 = performance.now();
620
+ runAnalysis(fileEntries);
621
+ if (storedManifest) {
622
+ const report = storedManifest.report;
623
+ if (report) {
624
+ log(`Extracted ${report.components_extracted}/${report.components_total} components (${Math.round(performance.now() - t0)}ms)`);
625
+ log(`Reconciliation: ${report.components_extracted} kept, ${report.variants_eliminated} variants pruned, ${report.states_eliminated} states pruned`);
626
+ const details = report.eliminated_details || [];
627
+ for (const d of details) if (d.kind === "component") warn(`⚠ ${d.component} eliminated: ${d.reason}`);
628
+ else if (d.kind === "variant") warn(`⚠ ${d.component} variant '${d.name}' pruned: ${d.reason}`);
629
+ else if (d.kind === "state") warn(`⚠ ${d.component} state '${d.name}' pruned: ${d.reason}`);
630
+ }
631
+ const diagnostics = storedManifest.diagnostics || [];
632
+ for (const d of diagnostics) if (d.kind === "bail") warn(`⚠ ${d.component} not extracted: ${d.message}`);
633
+ else if (d.kind === "skip") warn(`⚠ ${d.component}: skipped ${d.message}`);
634
+ log(`CSS: ${resolvedComponentCss.length} bytes (${Object.keys(storedManifest.components || {}).length} components)`);
635
+ if (!isProd && storedSheets) {
636
+ const staticSize = (storedSheets.declaration + variableCss + globalCss).length;
637
+ const componentSize = resolvedComponentCss.length;
638
+ log(`Delivery: split mode — static ${staticSize} bytes, components ${componentSize} bytes (adopted stylesheet)`);
639
+ } else log("Delivery: single file mode (production)");
640
+ }
641
+ },
642
+ resolveId(id) {
643
+ if (id === VIRTUAL_CSS_ID) return RESOLVED_CSS_ID;
644
+ if (id === VIRTUAL_COMPONENTS_ID) return RESOLVED_COMPONENTS_ID;
645
+ if (id === VIRTUAL_BRIDGE_ID) return RESOLVED_BRIDGE_ID;
646
+ return null;
647
+ },
648
+ load(id) {
649
+ if (id === RESOLVED_CSS_ID) {
650
+ if (!isProd && storedSheets) return [
651
+ storedSheets.declaration,
652
+ variableCss,
653
+ globalCss
654
+ ].filter(Boolean).join("\n");
655
+ return [
656
+ variableCss,
657
+ globalCss,
658
+ resolvedComponentCss
659
+ ].filter(Boolean).join("\n");
660
+ }
661
+ if (id === RESOLVED_COMPONENTS_ID) return `export default \`${(resolvedComponentCss || "").replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$/g, "\\$")}\`;`;
662
+ if (id === RESOLVED_BRIDGE_ID) return `
663
+ import css from '${VIRTUAL_COMPONENTS_ID}';
664
+
665
+ const GLOBAL_KEY = '__animus_component_sheet__';
666
+ let sheet = globalThis[GLOBAL_KEY] || null;
667
+
668
+ if (typeof CSSStyleSheet !== 'undefined' && 'adoptedStyleSheets' in document) {
669
+ if (!sheet) {
670
+ sheet = new CSSStyleSheet();
671
+ globalThis[GLOBAL_KEY] = sheet;
672
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
673
+ }
674
+ sheet.replaceSync(css);
675
+ } else {
676
+ // Fallback: inject or update <style> tag
677
+ let el = document.querySelector('style[data-animus-components]');
678
+ if (!el) {
679
+ el = document.createElement('style');
680
+ el.setAttribute('data-animus-components', '');
681
+ document.head.appendChild(el);
682
+ }
683
+ el.textContent = css;
684
+ }
685
+
686
+ if (import.meta.hot) {
687
+ import.meta.hot.accept('${VIRTUAL_COMPONENTS_ID}', (newModule) => {
688
+ if (sheet) {
689
+ sheet.replaceSync(newModule.default);
690
+ } else {
691
+ const el = document.querySelector('style[data-animus-components]');
692
+ if (el) el.textContent = newModule.default;
693
+ }
694
+ });
695
+ }
696
+ `;
697
+ return null;
698
+ },
699
+ transform(code, id) {
700
+ if (!storedManifest) return null;
701
+ if (!/\.[jt]sx?$/.test(id)) return null;
702
+ if (id.includes("node_modules")) return null;
703
+ const relativePath = relative(rootDir, id);
704
+ if (!storedManifest.files?.[relativePath]?.length) return null;
705
+ try {
706
+ const { transformFile } = __require("@animus-ui/extract");
707
+ const result = transformFile(code, relativePath, storedManifestJson);
708
+ if (!result.hasComponents) return null;
709
+ let transformedCode = result.code;
710
+ if (!isProd && !bridgeInjected && storedSheets) {
711
+ transformedCode = `import '${VIRTUAL_BRIDGE_ID}';\n${transformedCode}`;
712
+ bridgeInjected = true;
713
+ log("HMR bridge injected via transform");
714
+ }
715
+ if (verbose) log(`transform ${relativePath}: ${storedManifest.files?.[relativePath]?.length ?? 0} components`);
716
+ return {
717
+ code: transformedCode,
718
+ map: null
719
+ };
720
+ } catch (e) {
721
+ if (options.strict) throw new Error(`[animus-extract] Failed to transform ${id}: ${e}`);
722
+ console.warn(`[animus-extract] Failed to transform ${id}:`, e);
723
+ return null;
724
+ }
725
+ },
726
+ handleHotUpdate({ file, server: hmrServer, modules }) {
727
+ if (isProd) return;
728
+ const ext = extname(file);
729
+ if (!DEFAULT_EXTENSIONS.has(ext)) return;
730
+ if ((options.exclude ?? DEFAULT_EXCLUDE).some((pattern) => file.includes(pattern) || relative(rootDir, file).includes(pattern))) return;
731
+ const absFile = resolve(file);
732
+ const relPath = relative(rootDir, absFile);
733
+ const isConfigChange = resolvedConfigPath && absFile === resolve(resolvedConfigPath);
734
+ const isThemeChange = resolvedThemePath && absFile === resolve(resolvedThemePath);
735
+ const isSystemChange = resolvedSystemPath && absFile === resolve(resolvedSystemPath);
736
+ if (isConfigChange || isThemeChange || isSystemChange) {
737
+ const resetStart = performance.now();
738
+ log(`HMR geological reset: ${relPath}`);
739
+ if (isSystemChange) loadSystem();
740
+ else {
741
+ if (isConfigChange) loadConfig();
742
+ if (isThemeChange) loadTheme();
743
+ }
744
+ try {
745
+ const { clearAnalysisCache } = __require("@animus-ui/extract");
746
+ clearAnalysisCache();
747
+ } catch {}
748
+ const fileEntries = [];
749
+ for (const [path, { hash, source }] of fileCache) fileEntries.push({
750
+ path,
751
+ source,
752
+ hash
753
+ });
754
+ runAnalysis(fileEntries);
755
+ log(`HMR geological reset complete: ${Math.round(performance.now() - resetStart)}ms`);
756
+ const geologicalModules = [...modules];
757
+ const cssModule = hmrServer.moduleGraph.getModuleById(RESOLVED_CSS_ID);
758
+ if (cssModule) {
759
+ hmrServer.moduleGraph.invalidateModule(cssModule);
760
+ geologicalModules.push(cssModule);
761
+ }
762
+ const compModule = hmrServer.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID);
763
+ if (compModule) {
764
+ hmrServer.moduleGraph.invalidateModule(compModule);
765
+ geologicalModules.push(compModule);
766
+ }
767
+ return geologicalModules;
768
+ }
769
+ let source;
770
+ try {
771
+ source = readFileSync(absFile, "utf-8");
772
+ } catch {
773
+ return;
774
+ }
775
+ const hash = contentHash(source);
776
+ const cached = fileCache.get(relPath);
777
+ if (cached && cached.hash === hash) {
778
+ log(`HMR skip: ${relPath} (unchanged)`);
779
+ return [];
780
+ }
781
+ fileCache.set(relPath, {
782
+ hash,
783
+ source
784
+ });
785
+ const hmrStart = performance.now();
786
+ const prevReplacements = /* @__PURE__ */ new Map();
787
+ if (storedManifest?.components) for (const [id, desc] of Object.entries(storedManifest.components)) prevReplacements.set(id, desc.replacement ?? "");
788
+ const analysisStart = performance.now();
789
+ runAnalysis(buildFileEntriesFromCache(fileCache, relPath));
790
+ const analysisMs = Math.round(performance.now() - analysisStart);
791
+ const modulesToUpdate = [...modules];
792
+ const compModule = hmrServer.moduleGraph.getModuleById(RESOLVED_COMPONENTS_ID);
793
+ if (compModule) {
794
+ hmrServer.moduleGraph.invalidateModule(compModule);
795
+ modulesToUpdate.push(compModule);
796
+ }
797
+ if (storedManifest?.components) {
798
+ const staleFiles = /* @__PURE__ */ new Set();
799
+ for (const [id, desc] of Object.entries(storedManifest.components)) if ((desc.replacement ?? "") !== (prevReplacements.get(id) ?? "")) staleFiles.add(desc.file);
800
+ for (const defFile of staleFiles) {
801
+ const absDefPath = resolve(rootDir, defFile);
802
+ if (absDefPath === absFile) continue;
803
+ const defModule = hmrServer.moduleGraph.getModuleById(absDefPath) ?? hmrServer.moduleGraph.getModulesByFile(absDefPath)?.values().next().value;
804
+ if (defModule) {
805
+ log(`HMR invalidate: ${defFile} (replacement changed)`);
806
+ hmrServer.moduleGraph.invalidateModule(defModule);
807
+ modulesToUpdate.push(defModule);
808
+ }
809
+ }
810
+ }
811
+ const hmrMs = Math.round(performance.now() - hmrStart);
812
+ log(`HMR update: ${relPath} — analysis ${analysisMs}ms, ${modulesToUpdate.length - modules.length} modules invalidated, total ${hmrMs}ms`);
813
+ if (modulesToUpdate.length > modules.length) return modulesToUpdate;
814
+ }
815
+ };
816
+ }
817
+ //#endregion
818
+ export { animusExtract, animusExtract as default, evaluateTheme, evaluateThemeObject };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Standalone script executed via `bun run` to resolve global styles.
3
+ *
4
+ * Usage: bun run resolve-global-styles.ts <system-path> <theme-json> <output-file>
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).
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=resolve-global-styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-global-styles.d.ts","sourceRoot":"","sources":["../src/resolve-global-styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Standalone script executed via `bun run` to apply transform placeholders.
3
+ *
4
+ * Usage: bun run resolve-transforms.ts <input-file> <output-file> [config-path]
5
+ *
6
+ * Reads CSS from input file containing __TRANSFORM__name__rawValue__ placeholders,
7
+ * loads transform functions from @animus-ui/core (and optionally a custom config),
8
+ * applies them, and writes the resolved CSS to the output file.
9
+ *
10
+ * When config-path is provided, transforms from that module are loaded IN ADDITION
11
+ * to core transforms. Custom transforms take precedence on name collisions.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=resolve-transforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-transforms.d.ts","sourceRoot":"","sources":["../src/resolve-transforms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Evaluate a theme object that has already been loaded/imported.
3
+ *
4
+ * This is the pure logic: takes a theme object, flattens scales, builds variable CSS.
5
+ * Use this when you have already loaded the module yourself.
6
+ */
7
+ export declare function evaluateThemeObject(theme: Record<string, any>): {
8
+ scalesJson: string;
9
+ variableMapJson: string;
10
+ variableCss: string;
11
+ };
12
+ /**
13
+ * Evaluate a theme module and flatten all scales to a JSON map.
14
+ * Format: { "scale_name.key": "css_value" }
15
+ *
16
+ * Uses Vite's ssrLoadModule to evaluate the theme at build start,
17
+ * so all computed scales (shadows, gradients that depend on colors)
18
+ * are resolved to their final values.
19
+ */
20
+ export declare function evaluateTheme(ssrLoadModule: (url: string) => Promise<Record<string, any>>, themePath: string): Promise<{
21
+ scalesJson: string;
22
+ variableMapJson: string;
23
+ variableCss: string;
24
+ }>;
25
+ /**
26
+ * Resolve global style objects to a CSS string.
27
+ *
28
+ * Each key in `globalStyles` is a CSS selector, each value is a style object
29
+ * using the same prop shorthand as component `.styles()` blocks.
30
+ */
31
+ export declare function resolveGlobalStyles(globalStyles: Record<string, Record<string, any>>, propConfigJson: string, flat: Record<string, string>, transforms: Record<string, (v: any) => any>): string;
32
+ //# sourceMappingURL=theme-evaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-evaluator.d.ts","sourceRoot":"","sources":["../src/theme-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;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;CACrB,CAuCA;AAED;;;;;;;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;CACrB,CAAC,CAWD;AA6KD;;;;;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"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@animus-ui/vite-plugin",
3
+ "version": "0.1.0-next.1",
4
+ "description": "Animus static CSS extraction Vite plugin",
5
+ "author": "codecaaron <airrobb@gmail.com>",
6
+ "license": "MIT",
7
+ "main": "./dist/index.mjs",
8
+ "module": "./dist/index.mjs",
9
+ "types": "./dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/codecaaron/animus"
19
+ },
20
+ "scripts": {
21
+ "build": "tsdown && tsc -p tsconfig.build.json",
22
+ "compile": "tsc --noEmit"
23
+ },
24
+ "peerDependencies": {
25
+ "vite": ">=5.0.0"
26
+ },
27
+ "dependencies": {
28
+ "@animus-ui/core": "0.1.0-next.1",
29
+ "@animus-ui/extract": "0.1.0-next.1"
30
+ }
31
+ }