@tenphi/tasty 0.0.0-snapshot.d656d18 → 0.0.0-snapshot.d720187

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.
Files changed (43) hide show
  1. package/dist/{babel-_RVkw0iE.d.ts → babel-D6lOuSwk.d.ts} +2 -2
  2. package/dist/chunks/{collector-DBGcmUEq.js → collector-BVzrXWil.js} +4 -3
  3. package/dist/chunks/{collector-DBGcmUEq.js.map → collector-BVzrXWil.js.map} +1 -1
  4. package/dist/chunks/config-engine-7nX85gCT.js +403 -0
  5. package/dist/chunks/config-engine-7nX85gCT.js.map +1 -0
  6. package/dist/chunks/css-definitions-CfXdhJ-p.js +1194 -0
  7. package/dist/chunks/css-definitions-CfXdhJ-p.js.map +1 -0
  8. package/dist/chunks/{debug-BUwqo6vC.js → debug-DpMV1J0r.js} +4 -3
  9. package/dist/chunks/{debug-BUwqo6vC.js.map → debug-DpMV1J0r.js.map} +1 -1
  10. package/dist/chunks/hydration-CLdVKcH2.js +59 -0
  11. package/dist/chunks/hydration-CLdVKcH2.js.map +1 -0
  12. package/dist/chunks/{react-runtime-B3CmvDEQ.js → react-runtime-C5_xOQFf.js} +4 -3
  13. package/dist/chunks/{react-runtime-B3CmvDEQ.js.map → react-runtime-C5_xOQFf.js.map} +1 -1
  14. package/dist/chunks/{runtime-engine-Y00VWjrS.js → runtime-engine-BfqCASPn.js} +17 -1601
  15. package/dist/chunks/runtime-engine-BfqCASPn.js.map +1 -0
  16. package/dist/chunks/{style-engine-CqSmIlud.js → style-engine-CBuOt1e4.js} +192 -174
  17. package/dist/chunks/style-engine-CBuOt1e4.js.map +1 -0
  18. package/dist/chunks/{css-writer-PFzrSPeu.js → zero-engine-CJR3ZKcW.js} +3 -3
  19. package/dist/chunks/zero-engine-CJR3ZKcW.js.map +1 -0
  20. package/dist/{config-BI0y21JV.d.ts → config-BWYrv_Aa.d.ts} +4 -4
  21. package/dist/core/index.d.ts +2 -2
  22. package/dist/core/index.js +6 -4
  23. package/dist/{index-CrT0bsiI.d.ts → index-C5tyPTer.d.ts} +3 -12
  24. package/dist/index.d.ts +2 -2
  25. package/dist/index.js +6 -4
  26. package/dist/ssr/astro-client.js +1 -1
  27. package/dist/ssr/astro.js +2 -2
  28. package/dist/ssr/index.d.ts +5 -5
  29. package/dist/ssr/index.js +3 -3
  30. package/dist/ssr/next-config.d.ts +1 -1
  31. package/dist/ssr/next-config.js +2 -2
  32. package/dist/ssr/next.js +4 -4
  33. package/dist/zero/babel.d.ts +1 -1
  34. package/dist/zero/babel.js +63 -23
  35. package/dist/zero/babel.js.map +1 -1
  36. package/dist/zero/index.js +1 -1
  37. package/dist/zero/next.d.ts +1 -1
  38. package/package.json +9 -3
  39. package/dist/chunks/css-writer-PFzrSPeu.js.map +0 -1
  40. package/dist/chunks/hydrate-6YoHZP6A.js +0 -37
  41. package/dist/chunks/hydrate-6YoHZP6A.js.map +0 -1
  42. package/dist/chunks/runtime-engine-Y00VWjrS.js.map +0 -1
  43. package/dist/chunks/style-engine-CqSmIlud.js.map +0 -1
@@ -1,254 +1,8 @@
1
- import { F as RE_NUMBER, I as RE_RAW_UNIT, T as Lru, _ as setGlobalPredefinedTokens, c as getGlobalParseFunctions, d as normalizeColorTokenValue, h as resetGlobalPredefinedTokens, i as CUSTOM_UNITS, l as getGlobalParser, m as resetGlobalParseFunctions, o as customFunction, p as parseStyle } from "./dsl-CXvoaLnj.js";
2
- import { i as warn, n as isSelector, o as isDevEnv, r as resetStyleWarnings, t as mergeStyles } from "./shared-utils-OIg0N_dp.js";
3
- import { A as resetHandlers, E as STYLE_HANDLER_MAP, H as hasStylesGenerated, M as createStyle, O as normalizeHandlerDefinition, T as setGlobalPredefinedStates, U as markStylesGeneratedState, V as resetStyleChunks, W as resetStylesGeneratedState, a as registerBaseStyleProps, b as renderStyles, k as registerHandler, o as resetBaseStyleProps, y as clearPipelineCache } from "./style-engine-CqSmIlud.js";
4
- //#region src/functions/index.ts
5
- const FUNCTION_KEY = "@function";
6
- /** Descriptor keys that are not local-variable declarations. */
7
- const RESERVED_KEYS = new Set([
8
- "args",
9
- "returns",
10
- "result"
11
- ]);
12
- /**
13
- * Check if styles object has a local @function definition.
14
- */
15
- function hasLocalFunctions(styles) {
16
- return FUNCTION_KEY in styles;
17
- }
18
- /**
19
- * Extract local @function definitions from a styles object.
20
- * Returns null if none (fast path).
21
- */
22
- function extractLocalFunctions(styles) {
23
- const functions = styles[FUNCTION_KEY];
24
- if (!functions || typeof functions !== "object") return null;
25
- return functions;
26
- }
27
- /**
28
- * Parse a function name token into its CSS custom name.
29
- * Accepts `$$name`, `$name`, `--name`, or bare `name` → `--name`.
30
- */
31
- function parseFunctionName(token) {
32
- if (token.startsWith("$$")) return `--${token.slice(2)}`;
33
- if (token.startsWith("$")) return `--${token.slice(1)}`;
34
- if (token.startsWith("--")) return token;
35
- return `--${token}`;
36
- }
37
- /**
38
- * Parse a parameter / local-variable name token into its CSS custom name.
39
- * Accepts `$name`, `--name`, or bare `name` → `--name`.
40
- */
41
- function parseParamName(token) {
42
- if (token.startsWith("$")) return `--${token.slice(1)}`;
43
- if (token.startsWith("--")) return token;
44
- return `--${token}`;
45
- }
46
- /**
47
- * Format a single parameter (`--name <type>: default`).
48
- */
49
- function formatParameter(token, param) {
50
- const name = parseParamName(token);
51
- if (param === true) return name;
52
- if (typeof param === "string") return param ? `${name} ${param}` : name;
53
- let out = name;
54
- if (param.syntax) out += ` ${param.syntax}`;
55
- if (param.default !== void 0) out += `: ${parseStyle(param.default).output}`;
56
- return out;
57
- }
58
- /**
59
- * Format the function prelude: `@function --name(params) returns <type>`.
60
- */
61
- function formatFunctionPrelude(name, args, returns) {
62
- const cssName = parseFunctionName(name);
63
- let params = "";
64
- if (Array.isArray(args)) params = args.map((token) => formatParameter(token, true)).join(", ");
65
- else if (args) params = Object.entries(args).map(([token, param]) => formatParameter(token, param)).join(", ");
66
- const returnsPart = returns ? ` returns ${returns}` : "";
67
- return `@function ${cssName}(${params})${returnsPart}`;
68
- }
69
- /**
70
- * Format the inner declarations of a @function rule (local variables + result).
71
- */
72
- function formatFunctionDeclarations(def) {
73
- const parts = [];
74
- for (const key of Object.keys(def)) {
75
- if (RESERVED_KEYS.has(key) || !key.startsWith("$")) continue;
76
- const value = def[key];
77
- if (value === void 0) continue;
78
- parts.push(`${parseParamName(key)}: ${parseStyle(value).output};`);
79
- }
80
- parts.push(`result: ${parseStyle(def.result).output};`);
81
- return parts.join(" ");
82
- }
83
- /**
84
- * Format a complete @function rule as CSS.
85
- */
86
- function formatFunctionRule(name, def) {
87
- return `${formatFunctionPrelude(name, def.args, def.returns)} { ${formatFunctionDeclarations(def)} }`;
88
- }
89
- /** A key is a CSS-function key when it carries the `$`/`--` prefix. */
90
- function isCssFunctionKey(key) {
91
- return key.startsWith("$") || key.startsWith("--");
92
- }
93
- /** Discriminate a unified `functions` entry by its value type. */
94
- function isParseFunction(value) {
95
- return typeof value === "function";
96
- }
97
- /**
98
- * Split the unified `functions` map into the two internal registries:
99
- * - `parseFuncs`: JS parse functions (bare keys).
100
- * - `functionDefs`: declarative CSS `@function` definitions (`$$` keys).
101
- *
102
- * Entries whose key prefix does not match the value type are reported via
103
- * `onMismatch` and skipped.
104
- */
105
- function splitFunctions(map, onMismatch) {
106
- const parseFuncs = {};
107
- const functionDefs = {};
108
- for (const [key, value] of Object.entries(map)) {
109
- const cssKey = isCssFunctionKey(key);
110
- if (isParseFunction(value)) {
111
- if (cssKey) {
112
- onMismatch?.(key, "expected-definition");
113
- continue;
114
- }
115
- parseFuncs[key] = value;
116
- } else {
117
- if (!cssKey) {
118
- onMismatch?.(key, "expected-parse-function");
119
- continue;
120
- }
121
- functionDefs[key] = value;
122
- }
123
- }
124
- return {
125
- parseFuncs,
126
- functionDefs
127
- };
128
- }
129
- function escapeRegExp(str) {
130
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
131
- }
132
- const paramBareName = (token) => parseParamName(token).slice(2);
133
- /**
134
- * Rewrite `var(--name)` references for declared parameter/local names to a
135
- * collision-resistant, function-scoped prefix. This guarantees that later
136
- * value substitution only ever targets the function's own variables and can
137
- * never accidentally match a real element-level `var(--name)` introduced by an
138
- * argument value (e.g. the `--gap` used by Tasty units). Blanket prefix — no
139
- * exact-collision detection.
140
- */
141
- function prefixDeclaredVars(template, declared, prefixOf) {
142
- let out = template;
143
- for (const bare of declared) {
144
- const re = new RegExp(`var\\(\\s*--${escapeRegExp(bare)}(?![\\w-])`, "g");
145
- out = out.replace(re, `var(--${prefixOf(bare)}`);
146
- }
147
- return out;
148
- }
149
- /**
150
- * Replace the prefixed `var(--<prefixed>)` / `var(--<prefixed>, fallback)`
151
- * references with their mapped values, iterating so that local variables that
152
- * reference parameters (or other locals) resolve fully.
153
- */
154
- function substituteVars(template, valueMap) {
155
- let out = template;
156
- for (let iter = 0; iter < 30; iter++) {
157
- let changed = false;
158
- for (const [name, value] of valueMap) {
159
- const re = new RegExp(`var\\(\\s*--${escapeRegExp(name)}\\s*(?:,[^)]*)?\\)`, "g");
160
- out = out.replace(re, () => {
161
- changed = true;
162
- return value;
163
- });
164
- }
165
- if (!changed) break;
166
- }
167
- return out;
168
- }
169
- /** Function names currently being expanded, to guard against recursion cycles. */
170
- const inProgress = /* @__PURE__ */ new Set();
171
- /**
172
- * Compile a declarative `@function` definition into a parse-function closure
173
- * that fully inlines the call into plain CSS (the `@function` polyfill).
174
- *
175
- * Parameters and local variables are substituted directly with their values,
176
- * so no function-internal custom properties are emitted (collisions are
177
- * structurally impossible). Self/mutually-recursive functions are not expanded:
178
- * the cycle guard bails and the call is left untouched.
179
- */
180
- function compileFunctionClosure(name, def) {
181
- const cssName = parseFunctionName(name);
182
- const prefixOf = (bare) => `tf-${cssName.slice(2)}-${bare}`;
183
- const params = [];
184
- if (Array.isArray(def.args)) for (const token of def.args) params.push({ prefixed: prefixOf(paramBareName(token)) });
185
- else if (def.args) for (const [token, param] of Object.entries(def.args)) {
186
- let dflt;
187
- if (param && typeof param === "object" && param.default !== void 0) dflt = parseStyle(param.default).output;
188
- params.push({
189
- prefixed: prefixOf(paramBareName(token)),
190
- default: dflt
191
- });
192
- }
193
- const localKeys = Object.keys(def).filter((key) => !RESERVED_KEYS.has(key) && key.startsWith("$"));
194
- const declared = [...Array.isArray(def.args) ? def.args.map(paramBareName) : def.args ? Object.keys(def.args).map(paramBareName) : [], ...localKeys.map(paramBareName)];
195
- let built = false;
196
- let resultTemplate = "";
197
- const localTemplates = [];
198
- const build = () => {
199
- built = true;
200
- resultTemplate = prefixDeclaredVars(parseStyle(def.result).output, declared, prefixOf);
201
- for (const key of localKeys) {
202
- const value = def[key];
203
- if (value === void 0) continue;
204
- localTemplates.push({
205
- prefixed: prefixOf(paramBareName(key)),
206
- value: prefixDeclaredVars(parseStyle(value).output, declared, prefixOf)
207
- });
208
- }
209
- };
210
- return (groups) => {
211
- if (inProgress.has(cssName)) return "";
212
- inProgress.add(cssName);
213
- try {
214
- if (!built) build();
215
- const valueMap = /* @__PURE__ */ new Map();
216
- params.forEach((param, index) => {
217
- const arg = groups[index]?.output?.trim();
218
- valueMap.set(param.prefixed, arg || param.default || "");
219
- });
220
- for (const local of localTemplates) valueMap.set(local.prefixed, local.value);
221
- return substituteVars(resultTemplate, valueMap);
222
- } finally {
223
- inProgress.delete(cssName);
224
- }
225
- };
226
- }
227
- /** CSS function name -> serialized definition, to dedupe polyfill registration. */
228
- const registeredFunctionDefs = /* @__PURE__ */ new Map();
229
- /**
230
- * Register a declarative `@function` as a parse-function closure so the parser
231
- * inlines its call sites. Deduplicated by name + definition; a later, different
232
- * definition of the same name overrides the previous one (local wins).
233
- */
234
- function registerFunctionPolyfill(name, definition) {
235
- const cssName = parseFunctionName(name);
236
- const serialized = JSON.stringify(definition);
237
- if (registeredFunctionDefs.get(cssName) === serialized) return;
238
- registeredFunctionDefs.set(cssName, serialized);
239
- customFunction(cssName, compileFunctionClosure(name, definition));
240
- }
241
- /** Register all local `@function` definitions from a styles object (polyfill). */
242
- function registerLocalFunctionPolyfills(styles) {
243
- const local = extractLocalFunctions(styles);
244
- if (!local) return;
245
- for (const [name, definition] of Object.entries(local)) registerFunctionPolyfill(name, definition);
246
- }
247
- /** Clear polyfill registration bookkeeping (used by resetConfig in tests). */
248
- function resetFunctionPolyfills() {
249
- registeredFunctionDefs.clear();
250
- }
251
- //#endregion
1
+ import { T as Lru, h as resetGlobalPredefinedTokens, m as resetGlobalParseFunctions, p as parseStyle } from "./dsl-CXvoaLnj.js";
2
+ import { i as warn, n as isSelector, o as isDevEnv, r as resetStyleWarnings } from "./shared-utils-OIg0N_dp.js";
3
+ import { A as validateNamePrefix, D as makeKeyframeName, F as normalizePropertyDefinition, G as resetFunctionPolyfills, H as parseFunctionName, N as getEffectiveDefinition, O as rscClassRegexGlobal, R as formatFunctionDeclarations, S as PropertyTypeResolver, T as makeClassName, j as hashString, k as tastyClassRegex, m as formatCounterStyleDeclarations, v as fontFaceContentHash, y as formatFontFaceDeclarations, z as formatFunctionPrelude } from "./css-definitions-CfXdhJ-p.js";
4
+ import { C as STYLE_HANDLER_MAP, D as resetHandlers, G as resetGlobalPolyfillsState, H as hasStylesGenerated, K as resetStylesGeneratedState, R as resetStyleChunks, U as isFunctionsPolyfillEnabled, W as markStylesGeneratedState, a as registerBaseStyleProps, b as renderStyles, k as createStyle, o as resetBaseStyleProps, y as clearPipelineCache } from "./style-engine-CBuOt1e4.js";
5
+ import { n as applyStyleConfig, r as normalizeConfig, t as resolveRecipesWith } from "./config-engine-7nX85gCT.js";
252
6
  //#region src/prop-handlers.ts
253
7
  /**
254
8
  * Props middleware for tasty components.
@@ -370,318 +124,6 @@ function resetPropHandlers() {
370
124
  propHandlerRegistry.apply = null;
371
125
  }
372
126
  //#endregion
373
- //#region src/properties/index.ts
374
- const PROPERTIES_KEY = "@property";
375
- /**
376
- * Valid CSS custom property name pattern (after the -- prefix).
377
- * Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.
378
- */
379
- const VALID_PROPERTY_NAME_PATTERN = /^[a-z_][a-z0-9-_]*$/i;
380
- /**
381
- * Validate a CSS custom property name (the part after --).
382
- * Returns true if the name is valid for use as a CSS custom property.
383
- */
384
- function isValidPropertyName(name) {
385
- return VALID_PROPERTY_NAME_PATTERN.test(name);
386
- }
387
- /**
388
- * Check if styles object has local @property definition.
389
- * Fast path: single property lookup.
390
- */
391
- function hasLocalProperties(styles) {
392
- return PROPERTIES_KEY in styles;
393
- }
394
- /**
395
- * Extract local @property from styles object.
396
- * Returns null if no local properties (fast path).
397
- */
398
- function extractLocalProperties(styles) {
399
- const properties = styles[PROPERTIES_KEY];
400
- if (!properties || typeof properties !== "object") return null;
401
- return properties;
402
- }
403
- /**
404
- * Parse a property token name and return the CSS property name and whether it's a color.
405
- * Supports tasty token syntax and validates the property name.
406
- *
407
- * Token formats:
408
- * - `$name` → { cssName: '--name', isColor: false }
409
- * - `#name` → { cssName: '--name-color', isColor: true }
410
- * - `--name` → { cssName: '--name', isColor: false } (legacy, auto-detect color by suffix)
411
- * - `name` → { cssName: '--name', isColor: false } (legacy)
412
- *
413
- * @param token - The property token to parse
414
- * @returns Parsed result with cssName, isColor, isValid, and optional error
415
- */
416
- function parsePropertyToken(token) {
417
- if (!token || typeof token !== "string") return {
418
- cssName: "",
419
- isColor: false,
420
- isValid: false,
421
- error: "Property token must be a non-empty string"
422
- };
423
- let name;
424
- let isColor;
425
- if (token.startsWith("$")) {
426
- name = token.slice(1);
427
- isColor = false;
428
- } else if (token.startsWith("#")) {
429
- name = token.slice(1);
430
- isColor = true;
431
- } else if (token.startsWith("--")) {
432
- name = token.slice(2);
433
- isColor = token.endsWith("-color");
434
- } else {
435
- name = token;
436
- isColor = token.endsWith("-color");
437
- }
438
- if (!name) return {
439
- cssName: "",
440
- isColor,
441
- isValid: false,
442
- error: "Property name cannot be empty"
443
- };
444
- if (!isValidPropertyName(name)) return {
445
- cssName: "",
446
- isColor,
447
- isValid: false,
448
- error: `Invalid property name "${name}". Must start with a letter or underscore, followed by letters, digits, hyphens, or underscores.`
449
- };
450
- let cssName;
451
- if (token.startsWith("#")) cssName = `--${name}-color`;
452
- else cssName = `--${name}`;
453
- return {
454
- cssName,
455
- isColor,
456
- isValid: true
457
- };
458
- }
459
- /**
460
- * Normalize a property definition to a consistent string representation.
461
- * Used for comparing definitions to detect type conflicts.
462
- *
463
- * Only `syntax` and `inherits` are compared — `initialValue` is intentionally
464
- * excluded because different components may set different defaults for the
465
- * same typed property (e.g. auto-inferred `0px` vs explicit `6px`).
466
- *
467
- * Keys are sorted alphabetically to ensure consistent comparison:
468
- * { inherits: true, syntax: '<color>' } === { syntax: '<color>', inherits: true }
469
- */
470
- function normalizePropertyDefinition(def) {
471
- const normalized = {};
472
- if (def.inherits !== void 0) normalized.inherits = def.inherits;
473
- if (def.syntax !== void 0) normalized.syntax = def.syntax;
474
- return JSON.stringify(normalized);
475
- }
476
- /**
477
- * Get the effective property definition for a token.
478
- * For color tokens (#name), auto-sets syntax to '<color>' and defaults initialValue to 'transparent'.
479
- *
480
- * @param token - Property token ($name, #name, --name, or plain name)
481
- * @param userDefinition - User-provided definition options
482
- * @returns Effective definition with cssName, definition, isValid, and optional error
483
- */
484
- function getEffectiveDefinition(token, userDefinition) {
485
- const parsed = parsePropertyToken(token);
486
- if (!parsed.isValid) return {
487
- cssName: "",
488
- definition: userDefinition,
489
- isValid: false,
490
- error: parsed.error
491
- };
492
- if (parsed.isColor) return {
493
- cssName: parsed.cssName,
494
- definition: {
495
- syntax: "<color>",
496
- inherits: userDefinition.inherits,
497
- initialValue: userDefinition.initialValue ?? "transparent"
498
- },
499
- isValid: true
500
- };
501
- return {
502
- cssName: parsed.cssName,
503
- definition: userDefinition,
504
- isValid: true
505
- };
506
- }
507
- const UNIT_TO_SYNTAX = {};
508
- const LENGTH_UNITS = [
509
- "px",
510
- "em",
511
- "rem",
512
- "vw",
513
- "vh",
514
- "vmin",
515
- "vmax",
516
- "ch",
517
- "ex",
518
- "cap",
519
- "ic",
520
- "lh",
521
- "rlh",
522
- "svw",
523
- "svh",
524
- "lvw",
525
- "lvh",
526
- "dvw",
527
- "dvh",
528
- "cqw",
529
- "cqh",
530
- "cqi",
531
- "cqb",
532
- "cqmin",
533
- "cqmax"
534
- ];
535
- const ANGLE_UNITS = [
536
- "deg",
537
- "rad",
538
- "grad",
539
- "turn"
540
- ];
541
- const TIME_UNITS = ["ms", "s"];
542
- for (const u of LENGTH_UNITS) UNIT_TO_SYNTAX[u] = {
543
- syntax: "<length-percentage>",
544
- initialValue: "0px"
545
- };
546
- UNIT_TO_SYNTAX["%"] = {
547
- syntax: "<length-percentage>",
548
- initialValue: "0px"
549
- };
550
- for (const u of ANGLE_UNITS) UNIT_TO_SYNTAX[u] = {
551
- syntax: "<angle>",
552
- initialValue: "0deg"
553
- };
554
- for (const u of TIME_UNITS) UNIT_TO_SYNTAX[u] = {
555
- syntax: "<time>",
556
- initialValue: "0s"
557
- };
558
- /**
559
- * Infer CSS @property syntax from a concrete value.
560
- * Detects numeric types: \<number\>, \<length-percentage\>, \<angle\>, \<time\>.
561
- * Length and percentage values both map to \<length-percentage\> for maximum flexibility.
562
- * Color properties are handled separately via the #name token convention
563
- * (--name-color gets \<color\> syntax automatically in getEffectiveDefinition).
564
- *
565
- * @param value - The CSS value to infer from (e.g. '10px', '1', '45deg')
566
- * @returns Inferred syntax and initial value, or null if not inferable
567
- */
568
- function inferSyntaxFromValue(value) {
569
- if (!value || typeof value !== "string") return null;
570
- const trimmed = value.trim();
571
- if (!trimmed) return null;
572
- if (RE_NUMBER.test(trimmed)) {
573
- if (parseFloat(trimmed) === 0) return null;
574
- return {
575
- syntax: "<number>",
576
- initialValue: "0"
577
- };
578
- }
579
- const unitMatch = trimmed.match(RE_RAW_UNIT);
580
- if (unitMatch) {
581
- const mapping = UNIT_TO_SYNTAX[unitMatch[2]];
582
- if (mapping) return mapping;
583
- }
584
- return null;
585
- }
586
- //#endregion
587
- //#region src/utils/hash.ts
588
- /**
589
- * Fast string hash (djb2) for deduplication keys.
590
- * Not cryptographic — just collision-resistant enough for cache keys.
591
- */
592
- function hashString(str) {
593
- let hash = 5381;
594
- for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) | 0;
595
- return (hash >>> 0).toString(36);
596
- }
597
- //#endregion
598
- //#region src/utils/name-prefix.ts
599
- /**
600
- * Name prefix utilities for generated identifiers.
601
- *
602
- * Tasty generates three kinds of identifiers from content hashes:
603
- * - class names (used in DOM `class` attribute)
604
- * - keyframe names (used in CSS `animation`)
605
- * - counter-style names (used in CSS `list-style-type`)
606
- *
607
- * All three derive from a single configurable prefix so that an app
608
- * can namespace every identifier under one string. Discriminator letters
609
- * (`k`, `c`) keep the three kinds visually distinct in devtools — they
610
- * are not required for correctness (CSS keeps these in separate
611
- * namespaces), only for readability.
612
- *
613
- * The runtime / SSR / RSC paths must agree on the prefix; otherwise the
614
- * client-side hash for a given style will not match the server-rendered
615
- * class and hydration breaks. The zero-runtime build path uses a
616
- * different default (`'ts'`) so its classes can't collide with runtime
617
- * (`'t'`) classes when both are loaded on the same page.
618
- */
619
- /** Default prefix used by the runtime / SSR / RSC paths. */
620
- const DEFAULT_NAME_PREFIX = "t";
621
- /** Default prefix used by the zero-runtime (`tastyStatic`) build path. */
622
- const DEFAULT_ZERO_NAME_PREFIX = "ts";
623
- /**
624
- * Allowed shape: starts with a letter or underscore, then letters/
625
- * digits/underscore/hyphen. Length capped at 32 to keep generated
626
- * names sane. Matches the CSS identifier rules for the common case
627
- * while keeping the surface conservative.
628
- */
629
- const NAME_PREFIX_PATTERN = /^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$/;
630
- /**
631
- * Validate a `namePrefix` value.
632
- * Throws a TypeError with a descriptive message on invalid input so
633
- * misconfiguration fails loudly at `configure()` time rather than
634
- * surfacing later as broken hydration.
635
- */
636
- function validateNamePrefix(prefix) {
637
- if (typeof prefix !== "string") throw new TypeError(`[Tasty] namePrefix must be a string, got ${typeof prefix}.`);
638
- if (!NAME_PREFIX_PATTERN.test(prefix)) throw new TypeError(`[Tasty] namePrefix "${prefix}" is invalid. It must start with a letter (a-z, A-Z) or "_", contain only letters, digits, "_" or "-", and be 1-32 characters long. Examples: "t", "ts", "myapp-", "_foo".`);
639
- }
640
- /**
641
- * Build a class name: `${prefix}${hash}`.
642
- * The hash is appended verbatim — supply a separator inside the prefix
643
- * itself if you want one (e.g. `'myapp-'`).
644
- */
645
- function makeClassName(prefix, hash) {
646
- return `${prefix}${hash}`;
647
- }
648
- /**
649
- * Build a keyframe name: `${prefix}k${suffix}`.
650
- * The `k` discriminator keeps keyframe names visually distinct from
651
- * class names sharing the same prefix. `suffix` is typically a content
652
- * hash but may be a counter for ad-hoc allocation.
653
- */
654
- function makeKeyframeName(prefix, suffix) {
655
- return `${prefix}k${suffix}`;
656
- }
657
- /**
658
- * Build a counter-style name: `${prefix}c${suffix}`.
659
- * The `c` discriminator keeps counter-style names visually distinct
660
- * from class names sharing the same prefix.
661
- */
662
- function makeCounterStyleName(prefix, suffix) {
663
- return `${prefix}c${suffix}`;
664
- }
665
- /** Escape a string for safe inclusion in a regex literal. */
666
- function escapeRegex(str) {
667
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
668
- }
669
- /**
670
- * Regex matching any tasty class for the given prefix.
671
- * Used by the runtime GC's DOM scan and class-allocation bookkeeping.
672
- */
673
- function tastyClassRegex(prefix) {
674
- return new RegExp(`^${escapeRegex(prefix)}[a-z0-9]+$`);
675
- }
676
- /**
677
- * Global regex extracting tasty class names from RSC-inlined CSS.
678
- * Looks for the doubled-specificity pattern `.cls.cls` that
679
- * `formatRules()` always emits, which makes extraction reliable.
680
- */
681
- function rscClassRegexGlobal(prefix) {
682
- return new RegExp(`\\.(${escapeRegex(prefix)}[a-z0-9]+)\\.\\1`, "g");
683
- }
684
- //#endregion
685
127
  //#region src/injector/batch.ts
686
128
  /**
687
129
  * Deferred CSSOM writes ("batched injection").
@@ -887,99 +329,6 @@ function resetStyleBatch() {
887
329
  warnedNoProvider = false;
888
330
  }
889
331
  //#endregion
890
- //#region src/properties/property-type-resolver.ts
891
- /**
892
- * PropertyTypeResolver
893
- *
894
- * Automatically infers CSS @property types from custom property values.
895
- * Supports deferred resolution for var() reference chains of arbitrary depth.
896
- */
897
- /**
898
- * One `--name: value` declaration, with the surrounding whitespace dropped.
899
- *
900
- * The value is anchored on `\S` at both ends so no two quantifiers can claim
901
- * the same character. A lazy `(.+?)\s*$` is quadratic on a value of nothing
902
- * but spaces — every split point gets tried (CodeQL `js/polynomial-redos`).
903
- */
904
- const CUSTOM_PROP_DECL = /^\s*(--[a-z0-9_-]+)\s*:\s*(\S|\S.*\S)\s*$/i;
905
- const SINGLE_VAR_REF = /^var\((--[a-z0-9_-]+)\)$/i;
906
- var PropertyTypeResolver = class {
907
- /** propName → the prop it depends on */
908
- pendingDeps = /* @__PURE__ */ new Map();
909
- /** propName → list of props waiting on it */
910
- reverseDeps = /* @__PURE__ */ new Map();
911
- /**
912
- * Scan CSS declarations and auto-register @property for custom properties
913
- * whose types can be inferred from their values.
914
- */
915
- scanDeclarations(declarations, isPropertyDefined, registerProperty) {
916
- if (!declarations.includes("--")) return;
917
- const parts = declarations.split(/;+/);
918
- for (const part of parts) {
919
- if (!part.trim()) continue;
920
- const match = CUSTOM_PROP_DECL.exec(part);
921
- if (!match) continue;
922
- const propName = match[1];
923
- const value = match[2].trim();
924
- if (isPropertyDefined(propName)) continue;
925
- if (propName.endsWith("-color")) {
926
- registerProperty(propName, "<color>", "transparent");
927
- continue;
928
- }
929
- if (propName.endsWith("-line-height")) {
930
- registerProperty(propName, "<number> | <length-percentage>", "0");
931
- continue;
932
- }
933
- if (propName.endsWith("-opacity")) {
934
- registerProperty(propName, "<number> | <percentage>", "0");
935
- continue;
936
- }
937
- const varMatch = SINGLE_VAR_REF.exec(value);
938
- if (varMatch) {
939
- const depName = varMatch[1];
940
- this.addDependency(propName, depName);
941
- continue;
942
- }
943
- if (this.isComplexValue(value)) continue;
944
- const inferred = inferSyntaxFromValue(value);
945
- if (!inferred) continue;
946
- this.resolve(propName, inferred.syntax, inferred.initialValue, isPropertyDefined, registerProperty);
947
- }
948
- }
949
- addDependency(propName, depName) {
950
- if (propName === depName) return;
951
- this.pendingDeps.set(propName, depName);
952
- let dependents = this.reverseDeps.get(depName);
953
- if (!dependents) {
954
- dependents = [];
955
- this.reverseDeps.set(depName, dependents);
956
- }
957
- if (!dependents.includes(propName)) dependents.push(propName);
958
- }
959
- resolve(propName, syntax, initialValue, isPropertyDefined, registerProperty, resolving) {
960
- if (!resolving) resolving = /* @__PURE__ */ new Set();
961
- if (resolving.has(propName)) return;
962
- resolving.add(propName);
963
- if (!isPropertyDefined(propName)) registerProperty(propName, syntax, initialValue);
964
- const dependents = this.reverseDeps.get(propName);
965
- if (dependents) {
966
- this.reverseDeps.delete(propName);
967
- for (const dependent of dependents) {
968
- this.pendingDeps.delete(dependent);
969
- if (isPropertyDefined(dependent)) continue;
970
- this.resolve(dependent, syntax, initialValue, isPropertyDefined, registerProperty, resolving);
971
- }
972
- }
973
- }
974
- isComplexValue(value) {
975
- if (value.includes("calc(")) return true;
976
- const firstVar = value.indexOf("var(");
977
- if (firstVar === -1) return false;
978
- if (value.indexOf("var(", firstVar + 4) !== -1) return true;
979
- return !SINGLE_VAR_REF.test(value);
980
- }
981
- };
982
- //#endregion
983
332
  //#region src/injector/sheet-manager.ts
984
333
  const supportsConstructableSheets = typeof CSSStyleSheet !== "undefined" && (() => {
985
334
  try {
@@ -1940,114 +1289,6 @@ var SheetManager = class {
1940
1289
  }
1941
1290
  };
1942
1291
  //#endregion
1943
- //#region src/font-face/index.ts
1944
- const FONT_FACE_KEY = "@font-face";
1945
- /**
1946
- * Check if styles object has local @font-face definition.
1947
- */
1948
- function hasLocalFontFace(styles) {
1949
- return FONT_FACE_KEY in styles;
1950
- }
1951
- /**
1952
- * Extract local @font-face from styles object.
1953
- * Returns null if no local font faces (fast path).
1954
- */
1955
- function extractLocalFontFace(styles) {
1956
- const fontFace = styles[FONT_FACE_KEY];
1957
- if (!fontFace || typeof fontFace !== "object") return null;
1958
- return fontFace;
1959
- }
1960
- const FONT_FACE_DESCRIPTOR_MAP = {
1961
- fontWeight: "font-weight",
1962
- fontStyle: "font-style",
1963
- fontStretch: "font-stretch",
1964
- fontDisplay: "font-display",
1965
- unicodeRange: "unicode-range",
1966
- ascentOverride: "ascent-override",
1967
- descentOverride: "descent-override",
1968
- lineGapOverride: "line-gap-override",
1969
- sizeAdjust: "size-adjust",
1970
- fontFeatureSettings: "font-feature-settings",
1971
- fontVariationSettings: "font-variation-settings"
1972
- };
1973
- /**
1974
- * Format the inner declarations of a @font-face rule (no wrapper).
1975
- * Used by the injector which needs selector and declarations separately.
1976
- */
1977
- function formatFontFaceDeclarations(family, descriptors) {
1978
- const parts = [];
1979
- parts.push(`font-family: "${family}";`);
1980
- parts.push(`src: ${descriptors.src};`);
1981
- for (const [key, cssName] of Object.entries(FONT_FACE_DESCRIPTOR_MAP)) {
1982
- const value = descriptors[key];
1983
- if (value !== void 0) parts.push(`${cssName}: ${value};`);
1984
- }
1985
- return parts.join(" ");
1986
- }
1987
- /**
1988
- * Format a single @font-face rule as CSS.
1989
- */
1990
- function formatFontFaceRule(family, descriptors) {
1991
- return `@font-face { ${formatFontFaceDeclarations(family, descriptors)} }`;
1992
- }
1993
- /**
1994
- * Generate a content hash for deduplication of a single font-face rule.
1995
- */
1996
- function fontFaceContentHash(family, descriptors) {
1997
- return JSON.stringify({
1998
- family,
1999
- ...descriptors
2000
- });
2001
- }
2002
- //#endregion
2003
- //#region src/counter-style/index.ts
2004
- const COUNTER_STYLE_KEY = "@counter-style";
2005
- /**
2006
- * Check if styles object has local @counter-style definition.
2007
- */
2008
- function hasLocalCounterStyle(styles) {
2009
- return COUNTER_STYLE_KEY in styles;
2010
- }
2011
- /**
2012
- * Extract local @counter-style from styles object.
2013
- * Returns null if no local counter styles (fast path).
2014
- */
2015
- function extractLocalCounterStyle(styles) {
2016
- const counterStyle = styles[COUNTER_STYLE_KEY];
2017
- if (!counterStyle || typeof counterStyle !== "object") return null;
2018
- return counterStyle;
2019
- }
2020
- const COUNTER_STYLE_DESCRIPTOR_MAP = {
2021
- system: "system",
2022
- symbols: "symbols",
2023
- additiveSymbols: "additive-symbols",
2024
- prefix: "prefix",
2025
- suffix: "suffix",
2026
- negative: "negative",
2027
- range: "range",
2028
- pad: "pad",
2029
- fallback: "fallback",
2030
- speakAs: "speak-as"
2031
- };
2032
- /**
2033
- * Format the inner declarations of a @counter-style rule (no wrapper).
2034
- * Used by the injector which needs selector and declarations separately.
2035
- */
2036
- function formatCounterStyleDeclarations(descriptors) {
2037
- const parts = [];
2038
- for (const [key, cssName] of Object.entries(COUNTER_STYLE_DESCRIPTOR_MAP)) {
2039
- const value = descriptors[key];
2040
- if (value !== void 0) parts.push(`${cssName}: ${value};`);
2041
- }
2042
- return parts.join(" ");
2043
- }
2044
- /**
2045
- * Format a @counter-style rule as CSS.
2046
- */
2047
- function formatCounterStyleRule(name, descriptors) {
2048
- return `@counter-style ${name} { ${formatCounterStyleDeclarations(descriptors)} }`;
2049
- }
2050
- //#endregion
2051
1292
  //#region src/injector/injector.ts
2052
1293
  /**
2053
1294
  * How long a class is left alone after it was last known to be wanted.
@@ -2998,54 +2239,6 @@ var StyleInjector = class {
2998
2239
  }
2999
2240
  };
3000
2241
  //#endregion
3001
- //#region src/utils/typography.ts
3002
- const RESERVED_PRESET_NAMES = new Set([
3003
- "strong",
3004
- "bold",
3005
- "italic",
3006
- "icon",
3007
- "tight"
3008
- ]);
3009
- /**
3010
- * Generate typography tokens with $ prefix for CSS custom properties.
3011
- *
3012
- * Each preset generates the following CSS custom properties:
3013
- * - `${name}-font-size`
3014
- * - `${name}-line-height`
3015
- * - `${name}-letter-spacing`
3016
- * - `${name}-font-weight`
3017
- * - `${name}-bold-font-weight` (if defined)
3018
- * - `${name}-icon-size` (if defined)
3019
- * - `${name}-text-transform` (if defined)
3020
- * - `${name}-font-family` (if defined)
3021
- * - `${name}-font-style` (if defined)
3022
- *
3023
- * @param presets - Typography presets object
3024
- * @returns ConfigTokens object with $ prefixed keys
3025
- *
3026
- * @example
3027
- * const customTokens = generateTypographyTokens({
3028
- * myHeading: { fontSize: '24px', lineHeight: '32px', fontWeight: '700' },
3029
- * body: { fontSize: '16px', lineHeight: '24px', fontWeight: '400' },
3030
- * });
3031
- */
3032
- function generateTypographyTokens(presets) {
3033
- const tokens = {};
3034
- for (const [name, preset] of Object.entries(presets)) {
3035
- if (RESERVED_PRESET_NAMES.has(name)) throw new Error(`Invalid typography preset name "${name}". This name is reserved as a preset modifier.`);
3036
- tokens[`$${name}-font-size`] = preset.fontSize;
3037
- tokens[`$${name}-line-height`] = preset.lineHeight;
3038
- tokens[`$${name}-letter-spacing`] = preset.letterSpacing ?? "normal";
3039
- tokens[`$${name}-font-weight`] = preset.fontWeight;
3040
- if (preset.boldFontWeight !== void 0) tokens[`$${name}-bold-font-weight`] = preset.boldFontWeight;
3041
- if (preset.iconSize !== void 0) tokens[`$${name}-icon-size`] = preset.iconSize;
3042
- if (preset.textTransform !== void 0) tokens[`$${name}-text-transform`] = preset.textTransform;
3043
- if (preset.fontFamily !== void 0) tokens[`$${name}-font-family`] = preset.fontFamily;
3044
- if (preset.fontStyle !== void 0) tokens[`$${name}-font-style`] = preset.fontStyle;
3045
- }
3046
- return tokens;
3047
- }
3048
- //#endregion
3049
2242
  //#region src/config.ts
3050
2243
  /**
3051
2244
  * Tasty Configuration Module
@@ -3060,12 +2253,12 @@ function generateTypographyTokens(presets) {
3060
2253
  * reconfigure will emit a warning and be ignored.
3061
2254
  */
3062
2255
  const emittedWarnings = /* @__PURE__ */ new Set();
3063
- const devMode$1 = isDevEnv();
2256
+ const devMode = isDevEnv();
3064
2257
  /**
3065
2258
  * Emit a warning only once
3066
2259
  */
3067
2260
  function warnOnce(key, message) {
3068
- if (devMode$1 && !emittedWarnings.has(key)) {
2261
+ if (devMode && !emittedWarnings.has(key)) {
3069
2262
  emittedWarnings.add(key);
3070
2263
  console.warn(message);
3071
2264
  }
@@ -3075,7 +2268,6 @@ let globalKeyframes = null;
3075
2268
  let globalFontFace = null;
3076
2269
  let globalCounterStyle = null;
3077
2270
  let globalFunction = null;
3078
- let globalPolyfills = null;
3079
2271
  let globalProperties = null;
3080
2272
  let globalRecipes = null;
3081
2273
  let globalConfigTokens = null;
@@ -3084,7 +2276,6 @@ const GTKEY_TOKENS = "__tasty_cfg_tokens__";
3084
2276
  const GTKEY_FONT_FACE = "__tasty_cfg_font_face__";
3085
2277
  const GTKEY_COUNTER_STYLE = "__tasty_cfg_counter_style__";
3086
2278
  const GTKEY_FUNCTION = "__tasty_cfg_function__";
3087
- const GTKEY_POLYFILLS = "__tasty_cfg_polyfills__";
3088
2279
  const GTKEY_PROPERTIES = "__tasty_cfg_properties__";
3089
2280
  const GTKEY_GLOBAL_STYLES = "__tasty_cfg_global_styles__";
3090
2281
  function setOnGlobalThis(key, value) {
@@ -3099,7 +2290,6 @@ function clearGlobalThisConfig() {
3099
2290
  delete g[GTKEY_FONT_FACE];
3100
2291
  delete g[GTKEY_COUNTER_STYLE];
3101
2292
  delete g[GTKEY_FUNCTION];
3102
- delete g[GTKEY_POLYFILLS];
3103
2293
  delete g[GTKEY_PROPERTIES];
3104
2294
  delete g[GTKEY_GLOBAL_STYLES];
3105
2295
  }
@@ -3370,28 +2560,6 @@ function setGlobalFunction(functions) {
3370
2560
  setOnGlobalThis(GTKEY_FUNCTION, globalFunction);
3371
2561
  }
3372
2562
  /**
3373
- * Whether the CSS `@function` polyfill (inline expansion) is enabled.
3374
- * Reads from globalThis first for cross-module SSR/zero-runtime support.
3375
- */
3376
- function isFunctionsPolyfillEnabled() {
3377
- return (globalPolyfills ?? getFromGlobalThis(GTKEY_POLYFILLS) ?? null)?.functions === true;
3378
- }
3379
- /**
3380
- * Set global polyfill toggles (called from configure).
3381
- * Internal use only.
3382
- */
3383
- function setGlobalPolyfills(polyfills) {
3384
- if (hasStylesGenerated()) {
3385
- warnOnce("polyfills-after-styles", "[Tasty] Cannot update polyfills after styles have been generated.\nThe new polyfill toggles will be ignored.");
3386
- return;
3387
- }
3388
- globalPolyfills = {
3389
- ...globalPolyfills ?? {},
3390
- ...polyfills
3391
- };
3392
- setOnGlobalThis(GTKEY_POLYFILLS, globalPolyfills);
3393
- }
3394
- /**
3395
2563
  * Check if any global recipes are configured.
3396
2564
  * Fast path: returns false if no recipes were ever set.
3397
2565
  */
@@ -3414,7 +2582,7 @@ function setGlobalRecipes(recipes) {
3414
2582
  warnOnce("recipes-after-styles", "[Tasty] Cannot update recipes after styles have been generated.\nThe new recipes will be ignored.");
3415
2583
  return;
3416
2584
  }
3417
- if (devMode$1) for (const [name, recipeStyles] of Object.entries(recipes)) {
2585
+ if (devMode) for (const [name, recipeStyles] of Object.entries(recipes)) {
3418
2586
  if (name === "none") warnOnce("recipe-reserved-none", "[Tasty] Recipe name \"none\" is reserved. It is used as a keyword meaning \"no base recipes\" (e.g. recipe: 'none / post-recipe'). Choose a different name for your recipe.");
3419
2587
  for (const key of Object.keys(recipeStyles)) {
3420
2588
  if (isSelector(key)) warnOnce(`recipe-selector-${name}-${key}`, `[Tasty] Recipe "${name}" contains sub-element key "${key}". Recipes must be flat styles without sub-element keys. Remove the sub-element key from the recipe definition.`);
@@ -3506,207 +2674,16 @@ function configure(config = {}) {
3506
2674
  return;
3507
2675
  }
3508
2676
  if (config.namePrefix !== void 0) validateNamePrefix(config.namePrefix);
3509
- let mergedStates = {};
3510
- let mergedUnits = {};
3511
- let mergedFunctions = {};
3512
- let mergedHandlers = {};
3513
- /** Where each handler key came from, so registration warnings can name it. */
3514
- const handlerSources = /* @__PURE__ */ new Map();
3515
- let mergedPropHandlers = {};
3516
- /** Where each prop handler key came from, for its warnings. */
3517
- const propHandlerSources = /* @__PURE__ */ new Map();
3518
- const mergedBaseStyleProps = [];
3519
- let mergedProperties = {};
3520
- let mergedKeyframes = {};
3521
- let mergedFontFaces = {};
3522
- let mergedCounterStyles = {};
3523
- let mergedReplaceTokens = {};
3524
- let mergedConfigTokens = {};
3525
- let mergedRecipes = {};
3526
- let mergedPresets = {};
3527
- const mergedGlobalStyles = {};
3528
- if (config.plugins) for (const plugin of config.plugins) {
3529
- if (plugin.states) mergedStates = {
3530
- ...mergedStates,
3531
- ...plugin.states
3532
- };
3533
- if (plugin.units) mergedUnits = {
3534
- ...mergedUnits,
3535
- ...plugin.units
3536
- };
3537
- if (plugin.functions) mergedFunctions = {
3538
- ...mergedFunctions,
3539
- ...plugin.functions
3540
- };
3541
- if (plugin.handlers) {
3542
- mergedHandlers = {
3543
- ...mergedHandlers,
3544
- ...plugin.handlers
3545
- };
3546
- for (const key of Object.keys(plugin.handlers)) handlerSources.set(key, `plugin "${plugin.name}"`);
3547
- }
3548
- if (plugin.propHandlers) {
3549
- mergedPropHandlers = {
3550
- ...mergedPropHandlers,
3551
- ...plugin.propHandlers
3552
- };
3553
- for (const key of Object.keys(plugin.propHandlers)) propHandlerSources.set(key, `plugin "${plugin.name}"`);
3554
- }
3555
- if (plugin.baseStyleProps) mergedBaseStyleProps.push(...plugin.baseStyleProps);
3556
- if (plugin.properties) mergedProperties = {
3557
- ...mergedProperties,
3558
- ...plugin.properties
3559
- };
3560
- if (plugin.keyframes) mergedKeyframes = {
3561
- ...mergedKeyframes,
3562
- ...plugin.keyframes
3563
- };
3564
- if (plugin.fontFaces) mergedFontFaces = {
3565
- ...mergedFontFaces,
3566
- ...plugin.fontFaces
3567
- };
3568
- if (plugin.counterStyles) mergedCounterStyles = {
3569
- ...mergedCounterStyles,
3570
- ...plugin.counterStyles
3571
- };
3572
- if (plugin.replaceTokens) mergedReplaceTokens = {
3573
- ...mergedReplaceTokens,
3574
- ...plugin.replaceTokens
3575
- };
3576
- if (plugin.tokens) mergedConfigTokens = {
3577
- ...mergedConfigTokens,
3578
- ...plugin.tokens
3579
- };
3580
- if (plugin.recipes) mergedRecipes = {
3581
- ...mergedRecipes,
3582
- ...plugin.recipes
3583
- };
3584
- if (plugin.presets) mergedPresets = {
3585
- ...mergedPresets,
3586
- ...plugin.presets
3587
- };
3588
- if (plugin.globalStyles) for (const [sel, styles] of Object.entries(plugin.globalStyles)) mergedGlobalStyles[sel] = mergedGlobalStyles[sel] ? {
3589
- ...mergedGlobalStyles[sel],
3590
- ...styles
3591
- } : styles;
3592
- }
3593
- if (config.states) mergedStates = {
3594
- ...mergedStates,
3595
- ...config.states
3596
- };
3597
- if (config.units) mergedUnits = {
3598
- ...mergedUnits,
3599
- ...config.units
3600
- };
3601
- if (config.functions) mergedFunctions = {
3602
- ...mergedFunctions,
3603
- ...config.functions
3604
- };
3605
- if (config.handlers) {
3606
- mergedHandlers = {
3607
- ...mergedHandlers,
3608
- ...config.handlers
3609
- };
3610
- for (const key of Object.keys(config.handlers)) handlerSources.set(key, "configure()");
3611
- }
3612
- if (config.propHandlers) {
3613
- mergedPropHandlers = {
3614
- ...mergedPropHandlers,
3615
- ...config.propHandlers
3616
- };
3617
- for (const key of Object.keys(config.propHandlers)) propHandlerSources.set(key, "configure()");
3618
- }
3619
- if (config.baseStyleProps) mergedBaseStyleProps.push(...config.baseStyleProps);
3620
- if (config.properties) mergedProperties = {
3621
- ...mergedProperties,
3622
- ...config.properties
3623
- };
3624
- if (config.keyframes) mergedKeyframes = {
3625
- ...mergedKeyframes,
3626
- ...config.keyframes
3627
- };
3628
- if (config.fontFaces) mergedFontFaces = {
3629
- ...mergedFontFaces,
3630
- ...config.fontFaces
3631
- };
3632
- if (config.counterStyles) mergedCounterStyles = {
3633
- ...mergedCounterStyles,
3634
- ...config.counterStyles
3635
- };
3636
- if (config.replaceTokens) mergedReplaceTokens = {
3637
- ...mergedReplaceTokens,
3638
- ...config.replaceTokens
3639
- };
3640
- if (config.presets) mergedPresets = {
3641
- ...mergedPresets,
3642
- ...config.presets
3643
- };
3644
- if (Object.keys(mergedPresets).length > 0) mergedConfigTokens = {
3645
- ...generateTypographyTokens(mergedPresets),
3646
- ...mergedConfigTokens
3647
- };
3648
- if (config.tokens) mergedConfigTokens = {
3649
- ...mergedConfigTokens,
3650
- ...config.tokens
3651
- };
3652
- if (config.recipes) mergedRecipes = {
3653
- ...mergedRecipes,
3654
- ...config.recipes
3655
- };
3656
- if (config.globalStyles) for (const [sel, styles] of Object.entries(config.globalStyles)) mergedGlobalStyles[sel] = mergedGlobalStyles[sel] ? {
3657
- ...mergedGlobalStyles[sel],
3658
- ...styles
3659
- } : styles;
3660
- if (devMode$1) {
3661
- const tokenKeys = new Set(Object.keys(mergedConfigTokens));
3662
- for (const key of Object.keys(mergedReplaceTokens)) if (tokenKeys.has(key)) warnOnce(`token-conflict-${key}`, `[Tasty] Token "${key}" is defined in both \`tokens\` and \`replaceTokens\`. \`replaceTokens\` performs parse-time substitution, so the \`tokens\` CSS custom property will be injected but never used by Tasty styles. Remove it from one of the two.`);
3663
- }
3664
- if (config.colorSpace) warnOnce("colorSpace-deprecated", "[Tasty] configure({ colorSpace }) no longer has any effect and will be removed in the next major. Colors are emitted as authored, and opacity is applied with relative color syntax, which reads a color's channels in the browser. To address channels yourself, write `oklch(from var(--name-color) l c h)` against the token.");
3665
- if (Object.keys(mergedStates).length > 0) setGlobalPredefinedStates(mergedStates);
3666
- const parser = getGlobalParser();
3667
- if (config.parserCacheSize !== void 0) parser.updateOptions({ cacheSize: config.parserCacheSize });
3668
- if (Object.keys(mergedUnits).length > 0) {
3669
- const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;
3670
- parser.setUnits({
3671
- ...currentUnits,
3672
- ...mergedUnits
3673
- });
3674
- }
3675
- if (config.polyfills) setGlobalPolyfills(config.polyfills);
3676
- const { parseFuncs, functionDefs } = splitFunctions(mergedFunctions, (key, kind) => {
3677
- warnOnce(`functions-mismatch-${key}`, kind === "expected-definition" ? `[Tasty] functions["${key}"]: a "$$"-prefixed key denotes a CSS @function and expects a definition object, but received a function. Entry ignored.` : `[Tasty] functions["${key}"]: a bare key denotes a parse function and expects a function value, but received an object. Did you mean "$$${key}"? Entry ignored.`);
3678
- });
3679
- if (Object.keys(parseFuncs).length > 0) {
3680
- const currentFuncs = getGlobalParseFunctions();
3681
- const finalFuncs = {
3682
- ...currentFuncs,
3683
- ...parseFuncs
3684
- };
3685
- parser.setFunctions(finalFuncs);
3686
- Object.assign(currentFuncs, parseFuncs);
3687
- }
3688
- if (Object.keys(functionDefs).length > 0) if (isFunctionsPolyfillEnabled()) for (const [name, definition] of Object.entries(functionDefs)) registerFunctionPolyfill(name, definition);
3689
- else setGlobalFunction(functionDefs);
2677
+ const normalized = normalizeConfig(config);
2678
+ const { propHandlers: mergedPropHandlers, propHandlerSources, baseStyleProps: mergedBaseStyleProps, properties: mergedProperties, keyframes: mergedKeyframes, fontFaces: mergedFontFaces, counterStyles: mergedCounterStyles, tokens: mergedConfigTokens, recipes: mergedRecipes, globalStyles: mergedGlobalStyles } = normalized;
2679
+ const functionDefs = applyStyleConfig(config, normalized, warnOnce);
2680
+ if (!isFunctionsPolyfillEnabled() && Object.keys(functionDefs).length > 0) setGlobalFunction(functionDefs);
3690
2681
  if (Object.keys(mergedKeyframes).length > 0) setGlobalKeyframes(mergedKeyframes);
3691
2682
  if (Object.keys(mergedProperties).length > 0) setGlobalProperties(mergedProperties);
3692
2683
  if (Object.keys(mergedFontFaces).length > 0) setGlobalFontFace(mergedFontFaces);
3693
2684
  if (Object.keys(mergedCounterStyles).length > 0) setGlobalCounterStyle(mergedCounterStyles);
3694
- if (Object.keys(mergedHandlers).length > 0) for (const [name, definition] of Object.entries(mergedHandlers)) registerHandler(normalizeHandlerDefinition(name, definition), {
3695
- key: name,
3696
- source: handlerSources.get(name)
3697
- });
3698
2685
  if (Object.keys(mergedPropHandlers).length > 0) for (const [name, definition] of Object.entries(mergedPropHandlers)) registerPropHandler(name, definition, { source: propHandlerSources.get(name) });
3699
2686
  if (mergedBaseStyleProps.length > 0) registerBaseStyleProps(mergedBaseStyleProps);
3700
- if (Object.keys(mergedReplaceTokens).length > 0) {
3701
- const processedTokens = {};
3702
- for (const [key, value] of Object.entries(mergedReplaceTokens)) if (key.startsWith("#")) {
3703
- const normalized = normalizeColorTokenValue(value);
3704
- if (normalized === null) continue;
3705
- processedTokens[key] = String(normalized);
3706
- } else if (value === false) continue;
3707
- else processedTokens[key] = String(value);
3708
- setGlobalPredefinedTokens(processedTokens);
3709
- }
3710
2687
  if (Object.keys(mergedConfigTokens).length > 0) setGlobalConfigTokens(mergedConfigTokens);
3711
2688
  if (Object.keys(mergedRecipes).length > 0) setGlobalRecipes(mergedRecipes);
3712
2689
  if (Object.keys(mergedGlobalStyles).length > 0) setGlobalStyles(mergedGlobalStyles);
@@ -3761,7 +2738,7 @@ function resetConfig() {
3761
2738
  globalFontFace = null;
3762
2739
  globalCounterStyle = null;
3763
2740
  globalFunction = null;
3764
- globalPolyfills = null;
2741
+ resetGlobalPolyfillsState();
3765
2742
  globalRecipes = null;
3766
2743
  globalConfigTokens = null;
3767
2744
  globalStyles = null;
@@ -4060,251 +3037,6 @@ function createInjector(config = {}) {
4060
3037
  });
4061
3038
  }
4062
3039
  //#endregion
4063
- //#region src/keyframes/index.ts
4064
- const KEYFRAMES_KEY = "@keyframes";
4065
- /**
4066
- * Pattern to extract animation names from CSS animation property values.
4067
- * Animation name is typically the first identifier in the shorthand.
4068
- * Handles: "fadeIn 300ms ease-in", "pulse 1s infinite", etc.
4069
- *
4070
- * CSS animation shorthand order (all optional except name):
4071
- * animation: name | duration | timing | delay | iteration | direction | fill-mode | play-state
4072
- *
4073
- * Animation names must:
4074
- * - Start with a letter, underscore, or hyphen (but not a digit or CSS keyword)
4075
- * - Not be CSS keywords: none, initial, inherit, unset, revert
4076
- */
4077
- const CSS_KEYWORDS = new Set([
4078
- "none",
4079
- "initial",
4080
- "inherit",
4081
- "unset",
4082
- "revert",
4083
- "auto",
4084
- "normal",
4085
- "running",
4086
- "paused"
4087
- ]);
4088
- /**
4089
- * Pattern to match animation name at the start of an animation value.
4090
- * Must start with letter, underscore, or hyphen (not digit).
4091
- */
4092
- const ANIMATION_NAME_PATTERN = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/;
4093
- /**
4094
- * Check if styles object has local @keyframes definition.
4095
- * Fast path: single property lookup.
4096
- */
4097
- function hasLocalKeyframes(styles) {
4098
- return KEYFRAMES_KEY in styles;
4099
- }
4100
- /**
4101
- * Extract local @keyframes from styles object.
4102
- * Returns null if no local keyframes (fast path).
4103
- */
4104
- function extractLocalKeyframes(styles) {
4105
- const keyframes = styles[KEYFRAMES_KEY];
4106
- if (!keyframes || typeof keyframes !== "object") return null;
4107
- return keyframes;
4108
- }
4109
- /**
4110
- * Merge local and global keyframes.
4111
- * Local keyframes take priority over global.
4112
- * Returns null if no keyframes exist (fast path).
4113
- */
4114
- function mergeKeyframes(local, global) {
4115
- if (!local && !global) return null;
4116
- if (!local) return global;
4117
- if (!global) return local;
4118
- return {
4119
- ...global,
4120
- ...local
4121
- };
4122
- }
4123
- /**
4124
- * Extract animation name from a single animation value.
4125
- * Returns null if no valid name found.
4126
- *
4127
- * Examples:
4128
- * - "fadeIn 300ms ease-in" → "fadeIn"
4129
- * - "1s pulse infinite" → "pulse" (name can be anywhere)
4130
- * - "none" → null (CSS keyword)
4131
- * - "300ms ease-in" → null (no name, just duration/timing)
4132
- */
4133
- function extractAnimationNameFromValue(value) {
4134
- const trimmed = value.trim();
4135
- if (!trimmed) return null;
4136
- const parts = trimmed.split(/\s+/);
4137
- for (const part of parts) {
4138
- if (CSS_KEYWORDS.has(part.toLowerCase())) continue;
4139
- if (/^-?[\d.]+m?s$/i.test(part)) continue;
4140
- if (part === "infinite" || /^\d+$/.test(part)) continue;
4141
- if ([
4142
- "normal",
4143
- "reverse",
4144
- "alternate",
4145
- "alternate-reverse"
4146
- ].includes(part.toLowerCase())) continue;
4147
- if ([
4148
- "forwards",
4149
- "backwards",
4150
- "both"
4151
- ].includes(part.toLowerCase())) continue;
4152
- if (["running", "paused"].includes(part.toLowerCase())) continue;
4153
- if (/^(ease|linear|ease-in|ease-out|ease-in-out|step-start|step-end)$/i.test(part)) continue;
4154
- if (/^(cubic-bezier|steps)\(/i.test(part)) continue;
4155
- const match = ANIMATION_NAME_PATTERN.exec(part);
4156
- if (match) return match[1];
4157
- }
4158
- return null;
4159
- }
4160
- /**
4161
- * Extract all animation names from an animation property value.
4162
- * Handles multiple animations separated by commas.
4163
- *
4164
- * Example: "fadeIn 300ms, slideIn 500ms ease-out" → ["fadeIn", "slideIn"]
4165
- */
4166
- function extractAnimationNamesFromAnimationValue(value) {
4167
- const names = [];
4168
- const animations = value.split(",");
4169
- for (const animation of animations) {
4170
- const name = extractAnimationNameFromValue(animation);
4171
- if (name && !names.includes(name)) names.push(name);
4172
- }
4173
- return names;
4174
- }
4175
- /**
4176
- * Extract animation names from a style value (handles mappings and arrays).
4177
- */
4178
- function extractAnimationNamesFromStyleValue(value, names) {
4179
- if (typeof value === "string") for (const name of extractAnimationNamesFromAnimationValue(value)) names.add(name);
4180
- else if (Array.isArray(value)) for (const v of value) extractAnimationNamesFromStyleValue(v, names);
4181
- else if (value && typeof value === "object") for (const v of Object.values(value)) extractAnimationNamesFromStyleValue(v, names);
4182
- }
4183
- /**
4184
- * Extract all animation names referenced in styles.
4185
- * Scans 'animation' and 'animationName' properties including in state mappings.
4186
- * Returns empty set if no animation properties found (fast path).
4187
- */
4188
- function extractAnimationNamesFromStyles(styles) {
4189
- const names = /* @__PURE__ */ new Set();
4190
- if ("animation" in styles) extractAnimationNamesFromStyleValue(styles.animation, names);
4191
- if ("animationName" in styles) extractAnimationNamesFromStyleValue(styles.animationName, names);
4192
- for (const [key, value] of Object.entries(styles)) {
4193
- if (key === "$" || key === KEYFRAMES_KEY) continue;
4194
- if ((key.startsWith("&") || key.startsWith(".") || /^[A-Z]/.test(key)) && value && typeof value === "object") {
4195
- const nestedNames = extractAnimationNamesFromStyles(value);
4196
- for (const name of nestedNames) names.add(name);
4197
- }
4198
- }
4199
- return names;
4200
- }
4201
- /**
4202
- * Replace animation names in CSS declarations with injected names.
4203
- * Optimized to avoid regex creation - uses simple string replacement.
4204
- *
4205
- * @param declarations CSS declarations string
4206
- * @param nameMap Map from original name to injected name (only contains names that differ)
4207
- * @returns Updated declarations string
4208
- */
4209
- /**
4210
- * The name a local `@keyframes` block is emitted under.
4211
- *
4212
- * Content-addressed, and a pure function of what was authored: the same steps
4213
- * always produce the same name, and different steps under the same authored
4214
- * name cannot collide. That is what lets the server and the client agree —
4215
- * a name handed out by whichever injector happened to see the definition first
4216
- * depends on run order, so the two would disagree on both the animation name
4217
- * and the class that references it.
4218
- */
4219
- function resolveKeyframesName(authored, steps) {
4220
- return `${authored}-${hashString(JSON.stringify(steps))}`;
4221
- }
4222
- /**
4223
- * The emitted name for every local `@keyframes` in `styles`, by authored name.
4224
- * Pure — the server, the RSC pass and the client all reach the same answer.
4225
- */
4226
- function resolveKeyframesNames(used) {
4227
- const names = /* @__PURE__ */ new Map();
4228
- for (const [authored, steps] of Object.entries(used)) names.set(authored, resolveKeyframesName(authored, steps));
4229
- return names;
4230
- }
4231
- /**
4232
- * Whether `declarations` names `animation` as an animation to run.
4233
- *
4234
- * Only `animation` and `animation-name` values count, and only whole tokens
4235
- * within them: an animation called `crossfade` does not use keyframes called
4236
- * `fade`. Same parsing as `replaceAnimationNames`, so the two agree on what
4237
- * counts as a reference.
4238
- */
4239
- function referencesAnimation(declarations, animation) {
4240
- if (!declarations.includes("animation")) return false;
4241
- for (const part of declarations.split(";")) {
4242
- const colonIdx = part.indexOf(":");
4243
- if (colonIdx === -1) continue;
4244
- const prop = part.slice(0, colonIdx).trim().toLowerCase();
4245
- if (prop !== "animation" && prop !== "animation-name") continue;
4246
- const value = part.slice(colonIdx + 1);
4247
- if (replaceWord(value, animation, "\0") !== value) return true;
4248
- }
4249
- return false;
4250
- }
4251
- function replaceAnimationNames(declarations, nameMap) {
4252
- if (!declarations.includes("animation")) return declarations;
4253
- const parts = declarations.split(";");
4254
- let modified = false;
4255
- for (let i = 0; i < parts.length; i++) {
4256
- const part = parts[i];
4257
- const colonIdx = part.indexOf(":");
4258
- if (colonIdx === -1) continue;
4259
- const prop = part.slice(0, colonIdx).trim().toLowerCase();
4260
- if (prop === "animation" || prop === "animation-name") {
4261
- const prefix = part.slice(0, colonIdx + 1);
4262
- let value = part.slice(colonIdx + 1);
4263
- for (const [original, injected] of nameMap) {
4264
- const newValue = replaceWord(value, original, injected);
4265
- if (newValue !== value) {
4266
- value = newValue;
4267
- modified = true;
4268
- }
4269
- }
4270
- parts[i] = prefix + value;
4271
- }
4272
- }
4273
- return modified ? parts.join(";") : declarations;
4274
- }
4275
- /**
4276
- * Replace a word in a string (word boundary aware, no regex).
4277
- */
4278
- function replaceWord(str, word, replacement) {
4279
- let result = str;
4280
- let idx = 0;
4281
- while ((idx = result.indexOf(word, idx)) !== -1) {
4282
- const before = idx === 0 ? " " : result[idx - 1];
4283
- const after = idx + word.length >= result.length ? " " : result[idx + word.length];
4284
- const isWordBoundaryBefore = !/[a-zA-Z0-9_-]/.test(before);
4285
- const isWordBoundaryAfter = !/[a-zA-Z0-9_-]/.test(after);
4286
- if (isWordBoundaryBefore && isWordBoundaryAfter) {
4287
- result = result.slice(0, idx) + replacement + result.slice(idx + word.length);
4288
- idx += replacement.length;
4289
- } else idx += word.length;
4290
- }
4291
- return result;
4292
- }
4293
- /**
4294
- * Filter keyframes to only those that are actually used.
4295
- * Returns null if no keyframes are used (fast path).
4296
- */
4297
- function filterUsedKeyframes(keyframes, usedNames) {
4298
- if (!keyframes || usedNames.size === 0) return null;
4299
- const used = {};
4300
- let hasAny = false;
4301
- for (const name of usedNames) if (keyframes[name]) {
4302
- used[name] = keyframes[name];
4303
- hasAny = true;
4304
- }
4305
- return hasAny ? used : null;
4306
- }
4307
- //#endregion
4308
3040
  //#region src/ssr/ssr-collector-ref.ts
4309
3041
  const GETTER_KEY = "__tasty_ssr_collector_getter__";
4310
3042
  let _getSSRCollector = null;
@@ -4331,307 +3063,10 @@ function getRegisteredSSRCollector() {
4331
3063
  return getter ? getter() : null;
4332
3064
  }
4333
3065
  //#endregion
4334
- //#region src/ssr/format-property.ts
4335
- /**
4336
- * Format a single @property rule as a CSS string.
4337
- *
4338
- * Returns the full `@property --name { ... }` text, or empty string
4339
- * if the token is invalid.
4340
- */
4341
- function formatPropertyCSS(token, definition) {
4342
- const result = getEffectiveDefinition(token, definition);
4343
- if (!result.isValid) return "";
4344
- return buildPropertyRule(result.cssName, result.definition);
4345
- }
4346
- function buildPropertyRule(cssName, definition) {
4347
- const parts = [];
4348
- if (definition.syntax != null) {
4349
- let syntax = String(definition.syntax).trim();
4350
- if (!/^['"]/u.test(syntax)) syntax = `"${syntax}"`;
4351
- parts.push(`syntax: ${syntax};`);
4352
- }
4353
- const inherits = definition.inherits ?? true;
4354
- parts.push(`inherits: ${inherits ? "true" : "false"};`);
4355
- if (definition.initialValue != null) {
4356
- let initialValueStr;
4357
- if (typeof definition.initialValue === "number") initialValueStr = String(definition.initialValue);
4358
- else initialValueStr = parseStyle(definition.initialValue).output;
4359
- parts.push(`initial-value: ${initialValueStr};`);
4360
- }
4361
- return `@property ${cssName} { ${parts.join(" ").trim()} }`;
4362
- }
4363
- //#endregion
4364
- //#region src/ssr/format-keyframes.ts
4365
- /**
4366
- * Convert keyframes steps to a CSS string.
4367
- * Replicates SheetManager.stepsToCSS() without the class instance.
4368
- */
4369
- function stepsToCSS(steps) {
4370
- const rules = [];
4371
- for (const [key, value] of Object.entries(steps)) {
4372
- if (typeof value === "string") {
4373
- rules.push(`${key} { ${value.trim()} }`);
4374
- continue;
4375
- }
4376
- const styleMap = value || {};
4377
- const styleNames = Object.keys(styleMap).sort();
4378
- const handlerQueue = [];
4379
- const seenHandlers = /* @__PURE__ */ new Set();
4380
- styleNames.forEach((styleName) => {
4381
- let handlers = STYLE_HANDLER_MAP[styleName];
4382
- if (!handlers) handlers = STYLE_HANDLER_MAP[styleName] = [createStyle(styleName)];
4383
- handlers.forEach((handler) => {
4384
- if (!seenHandlers.has(handler)) {
4385
- seenHandlers.add(handler);
4386
- handlerQueue.push(handler);
4387
- }
4388
- });
4389
- });
4390
- const declarationPairs = [];
4391
- handlerQueue.forEach((handler) => {
4392
- const result = handler(handler.__lookupStyles.reduce((acc, name) => {
4393
- const v = styleMap[name];
4394
- if (v !== void 0) acc[name] = v;
4395
- return acc;
4396
- }, {}));
4397
- if (!result) return;
4398
- (Array.isArray(result) ? result : [result]).forEach((cssMap) => {
4399
- if (!cssMap || typeof cssMap !== "object") return;
4400
- const { $: _$, ...props } = cssMap;
4401
- Object.entries(props).forEach(([prop, val]) => {
4402
- if (val == null || val === "") return;
4403
- if (Array.isArray(val)) val.forEach((v) => {
4404
- if (v != null && v !== "") declarationPairs.push({
4405
- prop,
4406
- value: String(v)
4407
- });
4408
- });
4409
- else declarationPairs.push({
4410
- prop,
4411
- value: String(val)
4412
- });
4413
- });
4414
- });
4415
- });
4416
- const declarations = declarationPairs.map((d) => `${d.prop}: ${d.value}`).join("; ");
4417
- rules.push(`${key} { ${declarations.trim()} }`);
4418
- }
4419
- return rules.join(" ");
4420
- }
4421
- /**
4422
- * Format a @keyframes rule as a CSS string.
4423
- */
4424
- function formatKeyframesCSS(name, steps) {
4425
- return `@keyframes ${name} { ${stepsToCSS(steps)} }`;
4426
- }
4427
- //#endregion
4428
- //#region src/ssr/format-rules.ts
4429
- /**
4430
- * Resolve selectors for a rule, applying className-based specificity doubling
4431
- * and rootPrefix handling. Mirrors the logic in StyleInjector.inject().
4432
- */
4433
- function resolveSelector(rule, className) {
4434
- let selector = rule.selector;
4435
- if (rule.needsClassName) {
4436
- const selectorParts = selector ? selector.split("|||") : [""];
4437
- const classPrefix = `.${className}.${className}`;
4438
- selector = selectorParts.map((part) => {
4439
- const classSelector = part ? `${classPrefix}${part}` : classPrefix;
4440
- if (rule.rootPrefix) return `${rule.rootPrefix} ${classSelector}`;
4441
- return classSelector;
4442
- }).join(", ");
4443
- }
4444
- return selector;
4445
- }
4446
- /**
4447
- * Group rules by selector + at-rules + startingStyle and merge their declarations.
4448
- * Mirrors the grouping logic in SheetManager.insertRule().
4449
- */
4450
- function groupRules(rules) {
4451
- const groupMap = /* @__PURE__ */ new Map();
4452
- const order = [];
4453
- const atKey = (at) => at && at.length ? at.join("|") : "";
4454
- for (const r of rules) {
4455
- const key = `${atKey(r.atRules)}||${r.selector}||${r.startingStyle ? "1" : "0"}`;
4456
- const existing = groupMap.get(key);
4457
- if (existing) existing.declarations = existing.declarations ? `${existing.declarations} ${r.declarations}` : r.declarations;
4458
- else {
4459
- groupMap.set(key, {
4460
- selector: r.selector,
4461
- atRules: r.atRules,
4462
- startingStyle: r.startingStyle,
4463
- declarations: r.declarations
4464
- });
4465
- order.push(key);
4466
- }
4467
- }
4468
- return order.map((key) => groupMap.get(key));
4469
- }
4470
- /**
4471
- * Format an array of StyleResult rules into a CSS text string.
4472
- *
4473
- * Applies className-based specificity doubling (.cls.cls),
4474
- * groups rules by selector + at-rules, and wraps with at-rule blocks.
4475
- *
4476
- * Produces the same CSS text as SheetManager.insertRule() would insert
4477
- * into the DOM, but as a plain string suitable for SSR output.
4478
- */
4479
- function formatRules(rules, className) {
4480
- if (rules.length === 0) return "";
4481
- const grouped = groupRules(rules.map((rule) => ({
4482
- selector: resolveSelector(rule, className),
4483
- declarations: rule.declarations,
4484
- atRules: rule.atRules,
4485
- startingStyle: rule.startingStyle
4486
- })));
4487
- const cssRules = [];
4488
- for (const rule of grouped) {
4489
- const innerContent = rule.startingStyle ? `@starting-style { ${rule.declarations} }` : rule.declarations;
4490
- const baseRule = `${rule.selector} { ${innerContent} }`;
4491
- let fullRule = baseRule;
4492
- if (rule.atRules && rule.atRules.length > 0) fullRule = rule.atRules.reduce((css, atRule) => `${atRule} { ${css} }`, baseRule);
4493
- cssRules.push(fullRule);
4494
- }
4495
- return cssRules.join("\n");
4496
- }
4497
- //#endregion
4498
3066
  //#region src/utils/resolve-recipes.ts
4499
- /**
4500
- * Recipe resolution utility.
4501
- *
4502
- * Resolves `recipe` style properties by looking up predefined recipe styles
4503
- * from global configuration and merging them with the component's own styles.
4504
- *
4505
- * Resolution order per level (top-level and each sub-element independently):
4506
- * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2
4507
- *
4508
- * The `/` separator splits base recipes (before component styles)
4509
- * from post recipes (after component styles). All merges use mergeStyles
4510
- * semantics: primitives and state maps with '' key fully replace;
4511
- * state maps without '' key extend the existing value.
4512
- *
4513
- * Returns the same object reference if no recipes are present (zero overhead).
4514
- */
4515
- const devMode = isDevEnv();
4516
- /**
4517
- * Parse a recipe string into base and post recipe name groups.
4518
- *
4519
- * Syntax: `'base1 base2 / post1 post2'`
4520
- * - Names are space-separated within each group
4521
- * - `/` separates base (before component) from post (after component) groups
4522
- * - `/` is optional; if absent, all names are base
4523
- * - `none` as the sole base value means "no base recipes"
4524
- *
4525
- * Returns `{ base: null, post: null }` if the string is empty or invalid.
4526
- */
4527
- function parseRecipeNames(value) {
4528
- const empty = {
4529
- base: null,
4530
- post: null
4531
- };
4532
- if (typeof value !== "string") return empty;
4533
- const trimmed = value.trim();
4534
- if (trimmed === "") return empty;
4535
- const slashIndex = trimmed.indexOf("/");
4536
- if (slashIndex === -1) {
4537
- if (trimmed === "none") return empty;
4538
- return {
4539
- base: splitNames(trimmed),
4540
- post: null
4541
- };
4542
- }
4543
- const basePart = trimmed.slice(0, slashIndex);
4544
- const postPart = trimmed.slice(slashIndex + 1);
4545
- return {
4546
- base: basePart.trim() === "none" ? null : splitNames(basePart),
4547
- post: splitNames(postPart)
4548
- };
4549
- }
4550
- function splitNames(s) {
4551
- const names = s.split(/\s+/).filter(Boolean);
4552
- return names.length > 0 ? names : null;
4553
- }
4554
- /**
4555
- * Collect merged styles for a list of recipe names.
4556
- * Each recipe is flat-spread on top of the previous.
4557
- */
4558
- function collectRecipeStyles(names, recipes) {
4559
- let merged = {};
4560
- for (const name of names) {
4561
- const recipeStyles = recipes[name];
4562
- if (!recipeStyles) {
4563
- if (devMode) console.warn(`[Tasty] Recipe "${name}" not found. Make sure it is defined in configure({ recipes: { ... } }).`);
4564
- continue;
4565
- }
4566
- merged = {
4567
- ...merged,
4568
- ...recipeStyles
4569
- };
4570
- }
4571
- return merged;
4572
- }
4573
- /**
4574
- * Resolve recipe references in a flat styles object (no sub-elements).
4575
- * Returns null if no `recipe` key is present.
4576
- *
4577
- * Resolution: base recipes → component styles → post recipes (all via mergeStyles)
4578
- */
4579
- function resolveRecipesForLevel(styles, recipes) {
4580
- if (!("recipe" in styles)) return null;
4581
- const { base, post } = parseRecipeNames(styles.recipe);
4582
- const { recipe: _recipe, ...allRest } = styles;
4583
- const flatStyles = {};
4584
- const selectorStyles = {};
4585
- for (const key of Object.keys(allRest)) if (isSelector(key)) selectorStyles[key] = allRest[key];
4586
- else flatStyles[key] = allRest[key];
4587
- if (!base && !post) return allRest;
4588
- let result;
4589
- if (base) result = mergeStyles(collectRecipeStyles(base, recipes), flatStyles);
4590
- else result = { ...flatStyles };
4591
- if (post) {
4592
- const postStyles = collectRecipeStyles(post, recipes);
4593
- result = mergeStyles(result, postStyles);
4594
- }
4595
- for (const key of Object.keys(selectorStyles)) result[key] = selectorStyles[key];
4596
- return result;
4597
- }
4598
- /**
4599
- * Resolve all `recipe` style properties in a styles object.
4600
- *
4601
- * Handles both top-level and sub-element recipe references.
4602
- * Returns the same object reference if no recipes are present anywhere
4603
- * (zero overhead for the common case).
4604
- *
4605
- * @param styles - The styles object potentially containing `recipe` keys
4606
- * @returns Resolved styles with recipe values merged in, or the original object if unchanged
4607
- */
3067
+ /** Resolve recipe references against the globally configured recipe map. */
4608
3068
  function resolveRecipes(styles) {
4609
- const recipes = getGlobalRecipes();
4610
- if (!recipes) return styles;
4611
- let changed = false;
4612
- const topResolved = resolveRecipesForLevel(styles, recipes);
4613
- let result;
4614
- if (topResolved) {
4615
- changed = true;
4616
- result = topResolved;
4617
- } else result = styles;
4618
- const keys = Object.keys(result);
4619
- for (const key of keys) {
4620
- if (!isSelector(key)) continue;
4621
- const subStyles = result[key];
4622
- if (!subStyles || typeof subStyles !== "object" || Array.isArray(subStyles)) continue;
4623
- const subRecord = subStyles;
4624
- if (!("recipe" in subRecord)) continue;
4625
- const subResolved = resolveRecipesForLevel(subRecord, recipes);
4626
- if (subResolved) {
4627
- if (!changed) {
4628
- changed = true;
4629
- result = { ...styles };
4630
- }
4631
- result[key] = subResolved;
4632
- }
4633
- }
4634
- return changed ? result : styles;
3069
+ return resolveRecipesWith(styles, getGlobalRecipes());
4635
3070
  }
4636
3071
  //#endregion
4637
3072
  //#region src/utils/cache-wrapper.ts
@@ -4651,25 +3086,6 @@ function cacheWrapper(handler, limit = 1e3) {
4651
3086
  };
4652
3087
  }
4653
3088
  //#endregion
4654
- //#region src/ssr/format-global-rules.ts
4655
- /**
4656
- * Format an array of global StyleResult rules into a CSS text string.
4657
- *
4658
- * Rules already have their full selectors applied by renderStyles().
4659
- * Handles rootPrefix prepending and at-rule wrapping.
4660
- */
4661
- function formatGlobalRules(rules) {
4662
- if (rules.length === 0) return "";
4663
- const cssRules = [];
4664
- for (const rule of rules) {
4665
- const baseRule = `${rule.rootPrefix ? `${rule.rootPrefix} ${rule.selector}` : rule.selector} { ${rule.declarations} }`;
4666
- let fullRule = baseRule;
4667
- if (rule.atRules && rule.atRules.length > 0) fullRule = rule.atRules.reduce((css, atRule) => `${atRule} { ${css} }`, baseRule);
4668
- cssRules.push(fullRule);
4669
- }
4670
- return cssRules.join("\n");
4671
- }
4672
- //#endregion
4673
3089
  //#region src/utils/client-state.ts
4674
3090
  /**
4675
3091
  * Build a per-(injector, root) client state cache for the standalone style
@@ -4714,6 +3130,6 @@ function depsEqual(a, b) {
4714
3130
  return true;
4715
3131
  }
4716
3132
  //#endregion
4717
- export { getNamePrefix as $, holdKeyframes as A, extractLocalProperties as At, ChunkSheetRegistry as B, destroy as C, DEFAULT_ZERO_NAME_PREFIX as Ct, getCSSText as D, tastyClassRegex as Dt, gc as E, makeKeyframeName as Et, isPropertyDefined as F, formatFunctionRule as Ft, getGlobalConfigTokens as G, configure as H, keyframes as I, hasLocalFunctions as It, getGlobalFunctions as J, getGlobalCounterStyles as K, ownKeyframes as L, parseFunctionName as Lt, injectGlobal as M, parsePropertyToken as Mt, injectRawCSS as N, propHandlerRegistry as Nt, getCSSTextForNode as O, validateNamePrefix as Ot, injector as P, extractLocalFunctions as Pt, getGlobalStyles as Q, property as R, registerFunctionPolyfill as Rt, createInjector as S, DEFAULT_NAME_PREFIX as St, func as T, makeCounterStyleName as Tt, getConfig as U, chunkSheetRegistry as V, getEffectiveProperties as W, getGlobalKeyframes as X, getGlobalInjector as Y, getGlobalRecipes as Z, referencesAnimation as _, closeBatchWindow as _t, resolveRecipes as a, resetConfig as at, cleanup as b, openBatchWindow as bt, formatPropertyCSS as c, extractLocalCounterStyle as ct, registerSSRCollectorGetterGlobal as d, extractLocalFontFace as dt, hasGlobalKeyframes as et, extractAnimationNamesFromStyles as f, fontFaceContentHash as ft, mergeKeyframes as g, PropertyTypeResolver as gt, hasLocalKeyframes as h, SheetManager as ht, cacheWrapper as i, isTestEnvironment as it, inject as j, hasLocalProperties as jt, getRawCSSText as k, hashString as kt, getRegisteredSSRCollector as l, formatCounterStyleRule as lt, filterUsedKeyframes as m, hasLocalFontFace as mt, createClientState as n, isConfigLocked as nt, formatRules as o, generateTypographyTokens as ot, extractLocalKeyframes as p, formatFontFaceRule as pt, getGlobalFontFaces as q, formatGlobalRules as r, isFunctionsPolyfillEnabled as rt, formatKeyframesCSS as s, StyleInjector as st, depsEqual as t, hasGlobalRecipes as tt, registerSSRCollectorGetter as u, hasLocalCounterStyle as ut, replaceAnimationNames as v, flushStyles as vt, fontFace as w, makeClassName as wt, counterStyle as x, resetStyleBatch as xt, resolveKeyframesNames as y, hasPendingStyleWrites as yt, touch as z, registerLocalFunctionPolyfills as zt };
3133
+ export { resetStyleBatch as $, configure as A, getGlobalStyles as B, isPropertyDefined as C, touch as D, property as E, getGlobalFontFaces as F, isTestEnvironment as G, hasGlobalKeyframes as H, getGlobalFunctions as I, SheetManager as J, resetConfig as K, getGlobalInjector as L, getEffectiveProperties as M, getGlobalConfigTokens as N, ChunkSheetRegistry as O, getGlobalCounterStyles as P, openBatchWindow as Q, getGlobalKeyframes as R, injector as S, ownKeyframes as T, hasGlobalRecipes as U, getNamePrefix as V, isConfigLocked as W, flushStyles as X, closeBatchWindow as Y, hasPendingStyleWrites as Z, getRawCSSText as _, getRegisteredSSRCollector as a, injectGlobal as b, cleanup as c, destroy as d, propHandlerRegistry as et, fontFace as f, getCSSTextForNode as g, getCSSText as h, resolveRecipes as i, getConfig as j, chunkSheetRegistry as k, counterStyle as l, gc as m, createClientState as n, registerSSRCollectorGetter as o, func as p, StyleInjector as q, cacheWrapper as r, registerSSRCollectorGetterGlobal as s, depsEqual as t, createInjector as u, holdKeyframes as v, keyframes as w, injectRawCSS as x, inject as y, getGlobalRecipes as z };
4718
3134
 
4719
- //# sourceMappingURL=runtime-engine-Y00VWjrS.js.map
3135
+ //# sourceMappingURL=runtime-engine-BfqCASPn.js.map