@tenphi/tasty 1.1.0 → 1.3.0

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 (257) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +1 -2
  2. package/dist/chunks/cacheKey.d.ts +1 -0
  3. package/dist/chunks/cacheKey.js +1 -2
  4. package/dist/chunks/cacheKey.js.map +1 -1
  5. package/dist/chunks/definitions.js +1 -2
  6. package/dist/chunks/definitions.js.map +1 -1
  7. package/dist/chunks/index.d.ts +1 -0
  8. package/dist/chunks/renderChunk.d.ts +1 -0
  9. package/dist/chunks/renderChunk.js +1 -2
  10. package/dist/chunks/renderChunk.js.map +1 -1
  11. package/dist/compute-styles.d.ts +31 -0
  12. package/dist/compute-styles.js +356 -0
  13. package/dist/compute-styles.js.map +1 -0
  14. package/dist/config.d.ts +7 -1
  15. package/dist/config.js +25 -22
  16. package/dist/config.js.map +1 -1
  17. package/dist/core/index.d.ts +4 -4
  18. package/dist/core/index.js +6 -6
  19. package/dist/counter-style/index.js +1 -1
  20. package/dist/counter-style/index.js.map +1 -1
  21. package/dist/debug.js +1 -2
  22. package/dist/debug.js.map +1 -1
  23. package/dist/font-face/index.js +1 -1
  24. package/dist/font-face/index.js.map +1 -1
  25. package/dist/hooks/index.d.ts +7 -0
  26. package/dist/hooks/resolve-ssr-collector.js +1 -2
  27. package/dist/hooks/resolve-ssr-collector.js.map +1 -1
  28. package/dist/hooks/useCounterStyle.js +2 -3
  29. package/dist/hooks/useCounterStyle.js.map +1 -1
  30. package/dist/hooks/useFontFace.js +2 -3
  31. package/dist/hooks/useFontFace.js.map +1 -1
  32. package/dist/hooks/useGlobalStyles.js +4 -5
  33. package/dist/hooks/useGlobalStyles.js.map +1 -1
  34. package/dist/hooks/useKeyframes.js +3 -4
  35. package/dist/hooks/useKeyframes.js.map +1 -1
  36. package/dist/hooks/useProperty.js +2 -3
  37. package/dist/hooks/useProperty.js.map +1 -1
  38. package/dist/hooks/useRawCSS.js +2 -3
  39. package/dist/hooks/useRawCSS.js.map +1 -1
  40. package/dist/hooks/useStyles.d.ts +3 -8
  41. package/dist/hooks/useStyles.js +7 -214
  42. package/dist/hooks/useStyles.js.map +1 -1
  43. package/dist/index.d.ts +5 -5
  44. package/dist/index.js +7 -7
  45. package/dist/injector/index.d.ts +1 -18
  46. package/dist/injector/index.js +5 -19
  47. package/dist/injector/index.js.map +1 -1
  48. package/dist/injector/injector.js +1 -2
  49. package/dist/injector/injector.js.map +1 -1
  50. package/dist/injector/sheet-manager.js +1 -2
  51. package/dist/injector/sheet-manager.js.map +1 -1
  52. package/dist/keyframes/index.js +1 -1
  53. package/dist/parser/classify.js +1 -2
  54. package/dist/parser/classify.js.map +1 -1
  55. package/dist/parser/const.js +14 -3
  56. package/dist/parser/const.js.map +1 -1
  57. package/dist/parser/lru.js +1 -1
  58. package/dist/parser/lru.js.map +1 -1
  59. package/dist/parser/parser.js +1 -2
  60. package/dist/parser/parser.js.map +1 -1
  61. package/dist/parser/tokenizer.js +1 -1
  62. package/dist/parser/tokenizer.js.map +1 -1
  63. package/dist/parser/types.js +1 -1
  64. package/dist/parser/types.js.map +1 -1
  65. package/dist/pipeline/conditions.js +1 -1
  66. package/dist/pipeline/conditions.js.map +1 -1
  67. package/dist/pipeline/exclusive.js +1 -2
  68. package/dist/pipeline/exclusive.js.map +1 -1
  69. package/dist/pipeline/index.js +2 -3
  70. package/dist/pipeline/index.js.map +1 -1
  71. package/dist/pipeline/materialize.js +1 -2
  72. package/dist/pipeline/materialize.js.map +1 -1
  73. package/dist/pipeline/parseStateKey.js +1 -2
  74. package/dist/pipeline/parseStateKey.js.map +1 -1
  75. package/dist/pipeline/simplify.js +1 -2
  76. package/dist/pipeline/simplify.js.map +1 -1
  77. package/dist/pipeline/warnings.js +1 -1
  78. package/dist/plugins/index.d.ts +2 -0
  79. package/dist/plugins/okhsl-plugin.js +1 -2
  80. package/dist/plugins/okhsl-plugin.js.map +1 -1
  81. package/dist/properties/index.js +2 -3
  82. package/dist/properties/index.js.map +1 -1
  83. package/dist/properties/property-type-resolver.js +1 -2
  84. package/dist/properties/property-type-resolver.js.map +1 -1
  85. package/dist/ssr/astro.js +1 -2
  86. package/dist/ssr/astro.js.map +1 -1
  87. package/dist/ssr/async-storage.js +1 -2
  88. package/dist/ssr/async-storage.js.map +1 -1
  89. package/dist/ssr/collect-auto-properties.js +1 -2
  90. package/dist/ssr/collect-auto-properties.js.map +1 -1
  91. package/dist/ssr/collector.js +4 -12
  92. package/dist/ssr/collector.js.map +1 -1
  93. package/dist/ssr/context.d.ts +2 -2
  94. package/dist/ssr/context.js +1 -2
  95. package/dist/ssr/context.js.map +1 -1
  96. package/dist/ssr/format-global-rules.js +1 -1
  97. package/dist/ssr/format-keyframes.js +1 -2
  98. package/dist/ssr/format-keyframes.js.map +1 -1
  99. package/dist/ssr/format-property.js +1 -2
  100. package/dist/ssr/format-property.js.map +1 -1
  101. package/dist/ssr/format-rules.js +1 -1
  102. package/dist/ssr/hydrate.js +1 -2
  103. package/dist/ssr/hydrate.js.map +1 -1
  104. package/dist/ssr/index.js +1 -2
  105. package/dist/ssr/index.js.map +1 -1
  106. package/dist/ssr/next.d.ts +2 -2
  107. package/dist/ssr/next.js +9 -5
  108. package/dist/ssr/next.js.map +1 -1
  109. package/dist/ssr/ssr-collector-ref.js +1 -1
  110. package/dist/states/index.js +1 -2
  111. package/dist/states/index.js.map +1 -1
  112. package/dist/static/index.js +1 -2
  113. package/dist/static/inject.d.ts +5 -0
  114. package/dist/static/inject.js +17 -0
  115. package/dist/static/inject.js.map +1 -0
  116. package/dist/static/tastyStatic.js +1 -2
  117. package/dist/static/tastyStatic.js.map +1 -1
  118. package/dist/static/types.js +1 -1
  119. package/dist/styles/border.d.ts +1 -1
  120. package/dist/styles/border.js +28 -22
  121. package/dist/styles/border.js.map +1 -1
  122. package/dist/styles/color.d.ts +1 -1
  123. package/dist/styles/color.js +2 -3
  124. package/dist/styles/color.js.map +1 -1
  125. package/dist/styles/const.js +17 -0
  126. package/dist/styles/const.js.map +1 -0
  127. package/dist/styles/createStyle.js +4 -5
  128. package/dist/styles/createStyle.js.map +1 -1
  129. package/dist/styles/dimension.js +15 -3
  130. package/dist/styles/dimension.js.map +1 -1
  131. package/dist/styles/directional.js +133 -0
  132. package/dist/styles/directional.js.map +1 -0
  133. package/dist/styles/display.d.ts +3 -10
  134. package/dist/styles/display.js +45 -39
  135. package/dist/styles/display.js.map +1 -1
  136. package/dist/styles/fade.d.ts +1 -1
  137. package/dist/styles/fade.js +9 -5
  138. package/dist/styles/fade.js.map +1 -1
  139. package/dist/styles/fill.d.ts +2 -2
  140. package/dist/styles/fill.js +3 -4
  141. package/dist/styles/fill.js.map +1 -1
  142. package/dist/styles/flow.js +1 -1
  143. package/dist/styles/gap.d.ts +1 -1
  144. package/dist/styles/gap.js +4 -3
  145. package/dist/styles/gap.js.map +1 -1
  146. package/dist/styles/height.d.ts +1 -1
  147. package/dist/styles/height.js +1 -2
  148. package/dist/styles/height.js.map +1 -1
  149. package/dist/styles/index.d.ts +0 -1
  150. package/dist/styles/index.js +3 -4
  151. package/dist/styles/index.js.map +1 -1
  152. package/dist/styles/inset.d.ts +1 -29
  153. package/dist/styles/inset.js +19 -135
  154. package/dist/styles/inset.js.map +1 -1
  155. package/dist/styles/list.d.ts +5 -5
  156. package/dist/styles/list.js +4 -2
  157. package/dist/styles/list.js.map +1 -1
  158. package/dist/styles/margin.d.ts +1 -1
  159. package/dist/styles/margin.js +17 -89
  160. package/dist/styles/margin.js.map +1 -1
  161. package/dist/styles/outline.d.ts +1 -1
  162. package/dist/styles/outline.js +6 -16
  163. package/dist/styles/outline.js.map +1 -1
  164. package/dist/styles/padding.d.ts +1 -1
  165. package/dist/styles/padding.js +17 -89
  166. package/dist/styles/padding.js.map +1 -1
  167. package/dist/styles/placement.d.ts +37 -0
  168. package/dist/styles/placement.js +74 -0
  169. package/dist/styles/placement.js.map +1 -0
  170. package/dist/styles/predefined.d.ts +4 -4
  171. package/dist/styles/predefined.js +8 -9
  172. package/dist/styles/predefined.js.map +1 -1
  173. package/dist/styles/preset.d.ts +1 -1
  174. package/dist/styles/preset.js +7 -7
  175. package/dist/styles/preset.js.map +1 -1
  176. package/dist/styles/radius.d.ts +1 -3
  177. package/dist/styles/radius.js +38 -6
  178. package/dist/styles/radius.js.map +1 -1
  179. package/dist/styles/scrollMargin.d.ts +24 -0
  180. package/dist/styles/scrollMargin.js +32 -0
  181. package/dist/styles/scrollMargin.js.map +1 -0
  182. package/dist/styles/scrollbar.d.ts +1 -1
  183. package/dist/styles/scrollbar.js +8 -5
  184. package/dist/styles/scrollbar.js.map +1 -1
  185. package/dist/styles/shadow.d.ts +1 -1
  186. package/dist/styles/shadow.js +4 -3
  187. package/dist/styles/shadow.js.map +1 -1
  188. package/dist/styles/shared.js +17 -0
  189. package/dist/styles/shared.js.map +1 -0
  190. package/dist/styles/transition.d.ts +1 -1
  191. package/dist/styles/transition.js +5 -4
  192. package/dist/styles/transition.js.map +1 -1
  193. package/dist/styles/types.d.ts +24 -7
  194. package/dist/styles/width.d.ts +1 -1
  195. package/dist/styles/width.js +1 -2
  196. package/dist/styles/width.js.map +1 -1
  197. package/dist/tasty.d.ts +29 -14
  198. package/dist/tasty.js +70 -62
  199. package/dist/tasty.js.map +1 -1
  200. package/dist/utils/cache-wrapper.js +1 -2
  201. package/dist/utils/cache-wrapper.js.map +1 -1
  202. package/dist/utils/case-converter.js +1 -1
  203. package/dist/utils/color-math.js +1 -1
  204. package/dist/utils/color-math.js.map +1 -1
  205. package/dist/utils/color-space.js +1 -2
  206. package/dist/utils/color-space.js.map +1 -1
  207. package/dist/utils/colors.js +1 -2
  208. package/dist/utils/colors.js.map +1 -1
  209. package/dist/utils/dotize.js +1 -1
  210. package/dist/utils/dotize.js.map +1 -1
  211. package/dist/utils/filter-base-props.js +1 -1
  212. package/dist/utils/get-display-name.js +1 -1
  213. package/dist/utils/has-keys.js +1 -1
  214. package/dist/utils/is-dev-env.js +1 -1
  215. package/dist/utils/is-dev-env.js.map +1 -1
  216. package/dist/utils/is-valid-element-type.js +1 -1
  217. package/dist/utils/merge-styles.js +1 -2
  218. package/dist/utils/merge-styles.js.map +1 -1
  219. package/dist/utils/mod-attrs.d.ts +0 -2
  220. package/dist/utils/mod-attrs.js +1 -2
  221. package/dist/utils/mod-attrs.js.map +1 -1
  222. package/dist/utils/process-tokens.d.ts +1 -5
  223. package/dist/utils/process-tokens.js +3 -11
  224. package/dist/utils/process-tokens.js.map +1 -1
  225. package/dist/utils/resolve-recipes.js +1 -2
  226. package/dist/utils/resolve-recipes.js.map +1 -1
  227. package/dist/utils/selector-transform.js +1 -1
  228. package/dist/utils/string.js +1 -1
  229. package/dist/utils/styles.d.ts +1 -1
  230. package/dist/utils/styles.js +2 -3
  231. package/dist/utils/styles.js.map +1 -1
  232. package/dist/utils/typography.js +1 -1
  233. package/dist/utils/typography.js.map +1 -1
  234. package/dist/utils/warnings.js +1 -1
  235. package/dist/zero/babel.d.ts +17 -2
  236. package/dist/zero/babel.js +105 -41
  237. package/dist/zero/babel.js.map +1 -1
  238. package/dist/zero/css-writer.js +1 -2
  239. package/dist/zero/css-writer.js.map +1 -1
  240. package/dist/zero/extractor.js +1 -2
  241. package/dist/zero/extractor.js.map +1 -1
  242. package/dist/zero/index.js +1 -2
  243. package/dist/zero/next.d.ts +12 -0
  244. package/dist/zero/next.js +5 -4
  245. package/dist/zero/next.js.map +1 -1
  246. package/docs/methodology.md +50 -1
  247. package/docs/runtime.md +90 -3
  248. package/docs/ssr.md +10 -10
  249. package/docs/styles.md +17 -0
  250. package/docs/tasty-static.md +87 -0
  251. package/package.json +12 -7
  252. package/dist/styles/align.d.ts +0 -15
  253. package/dist/styles/align.js +0 -14
  254. package/dist/styles/align.js.map +0 -1
  255. package/dist/styles/justify.d.ts +0 -15
  256. package/dist/styles/justify.js +0 -14
  257. package/dist/styles/justify.js.map +0 -1
@@ -12,10 +12,6 @@ import { Tokens } from "../types.js";
12
12
  * @returns CSSProperties object or undefined if no tokens to process
13
13
  */
14
14
  declare function processTokens(tokens: Tokens | undefined): CSSProperties | undefined;
15
- /**
16
- * Stringify tokens for memoization key.
17
- */
18
- declare function stringifyTokens(tokens: Tokens | undefined): string;
19
15
  //#endregion
20
- export { processTokens, stringifyTokens };
16
+ export { processTokens };
21
17
  //# sourceMappingURL=process-tokens.d.ts.map
@@ -1,7 +1,6 @@
1
- import { hslToRgbValues } from "./color-math.js";
1
+ import "./color-math.js";
2
2
  import { getColorSpaceComponents, getColorSpaceSuffix } from "./color-space.js";
3
3
  import { normalizeColorTokenValue, parseStyle } from "./styles.js";
4
-
5
4
  //#region src/utils/process-tokens.ts
6
5
  /**
7
6
  * Extract color components in the configured color space.
@@ -78,14 +77,7 @@ function processTokens(tokens) {
78
77
  }
79
78
  return result;
80
79
  }
81
- /**
82
- * Stringify tokens for memoization key.
83
- */
84
- function stringifyTokens(tokens) {
85
- if (!tokens) return "";
86
- return JSON.stringify(tokens);
87
- }
88
-
89
80
  //#endregion
90
- export { processTokens, stringifyTokens };
81
+ export { processTokens };
82
+
91
83
  //# sourceMappingURL=process-tokens.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"process-tokens.js","names":[],"sources":["../../src/utils/process-tokens.ts"],"sourcesContent":["import type { Tokens, TokenValue } from '../types';\n\nimport type { CSSProperties } from './css-types';\n\nimport { getColorSpaceComponents, getColorSpaceSuffix } from './color-space';\nimport { normalizeColorTokenValue, parseStyle } from './styles';\n\nexport { hslToRgbValues } from './color-math';\n\nconst devMode = process.env.NODE_ENV !== 'production';\n\n/**\n * Extract color components in the configured color space.\n * Returns a CSS variable reference for token colors, or decomposed\n * components as a space-separated string.\n */\nfunction extractColorSpaceValue(\n colorValue: string,\n parsedOutput: string,\n): string {\n const suffix = getColorSpaceSuffix();\n\n // If the parsed output references a color variable, use the companion variant\n const varMatch = parsedOutput.match(/var\\(--([a-z0-9-]+)-color\\)/);\n if (varMatch) {\n return `var(--${varMatch[1]}-color-${suffix})`;\n }\n\n // Try the original color value first, then parsed output\n const components = getColorSpaceComponents(colorValue);\n if (components !== colorValue) return components;\n\n const componentsFromParsed = getColorSpaceComponents(parsedOutput);\n if (componentsFromParsed !== parsedOutput) return componentsFromParsed;\n\n // Fallback: return the parsed output\n return parsedOutput;\n}\n\n/**\n * Check if a value is a valid token value (string, number, or boolean - not object).\n * Returns false for `false` values (they mean \"skip this token\").\n */\nfunction isValidTokenValue(\n value: unknown,\n): value is Exclude<TokenValue, undefined | null | false> {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n\n if (typeof value === 'object') {\n if (devMode) {\n console.warn(\n 'Tasty: Object values are not allowed in tokens prop. ' +\n 'Tokens do not support state-based styling. Use a primitive value instead.',\n );\n }\n return false;\n }\n\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n );\n}\n\n/**\n * Process a single token value through the tasty parser.\n * Numbers are converted to strings; 0 stays as \"0\".\n */\nfunction processTokenValue(value: string | number): string {\n if (typeof value === 'number') {\n // 0 should remain as \"0\", not converted to any unit\n if (value === 0) {\n return '0';\n }\n return parseStyle(String(value)).output;\n }\n return parseStyle(value).output;\n}\n\n/**\n * Process tokens object into inline style properties.\n * - $name -> --name with parsed value\n * - #name -> --name-color AND --name-color-{colorSpace} with parsed values\n *\n * @param tokens - The tokens object to process\n * @returns CSSProperties object or undefined if no tokens to process\n */\nexport function processTokens(\n tokens: Tokens | undefined,\n): CSSProperties | undefined {\n if (!tokens) {\n return undefined;\n }\n\n const keys = Object.keys(tokens);\n if (keys.length === 0) {\n return undefined;\n }\n\n let result: Record<string, string> | undefined;\n\n for (const key of keys) {\n const value = tokens[key as keyof Tokens];\n\n // Skip undefined/null values\n if (!isValidTokenValue(value)) {\n continue;\n }\n\n if (key.startsWith('$')) {\n // Custom property token: $name -> --name\n const propName = `--${key.slice(1)}`;\n // Boolean true for custom properties converts to empty string (valid CSS value)\n const effectiveValue = value === true ? '' : value;\n const processedValue = processTokenValue(effectiveValue);\n\n if (!result) result = {};\n result[propName] = processedValue;\n } else if (key.startsWith('#')) {\n const colorName = key.slice(1);\n const suffix = getColorSpaceSuffix();\n\n // Normalize color token value (true → 'transparent', false is already filtered by isValidTokenValue)\n const effectiveValue = normalizeColorTokenValue(value);\n // Skip if normalized to null (shouldn't happen since false is filtered by isValidTokenValue)\n if (effectiveValue === null) continue;\n\n const originalValue =\n typeof effectiveValue === 'number'\n ? String(effectiveValue)\n : effectiveValue;\n const lowerValue = originalValue.toLowerCase();\n const processedValue = processTokenValue(effectiveValue);\n\n if (!result) result = {};\n result[`--${colorName}-color`] = processedValue;\n\n // Skip component generation for #current values (currentcolor is dynamic, cannot decompose)\n if (/^#current(?:\\.|$)/i.test(lowerValue)) {\n continue;\n }\n\n result[`--${colorName}-color-${suffix}`] = extractColorSpaceValue(\n originalValue,\n processedValue,\n );\n }\n }\n\n return result as CSSProperties | undefined;\n}\n\n/**\n * Stringify tokens for memoization key.\n */\nexport function stringifyTokens(tokens: Tokens | undefined): string {\n if (!tokens) return '';\n return JSON.stringify(tokens);\n}\n"],"mappings":";;;;;;;;;;AAgBA,SAAS,uBACP,YACA,cACQ;CACR,MAAM,SAAS,qBAAqB;CAGpC,MAAM,WAAW,aAAa,MAAM,8BAA8B;AAClE,KAAI,SACF,QAAO,SAAS,SAAS,GAAG,SAAS,OAAO;CAI9C,MAAM,aAAa,wBAAwB,WAAW;AACtD,KAAI,eAAe,WAAY,QAAO;CAEtC,MAAM,uBAAuB,wBAAwB,aAAa;AAClE,KAAI,yBAAyB,aAAc,QAAO;AAGlD,QAAO;;;;;;AAOT,SAAS,kBACP,OACwD;AACxD,KAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,MACrD,QAAO;AAGT,KAAI,OAAO,UAAU,UAAU;AAE3B,UAAQ,KACN,iIAED;AAEH,SAAO;;AAGT,QACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU;;;;;;AAQrB,SAAS,kBAAkB,OAAgC;AACzD,KAAI,OAAO,UAAU,UAAU;AAE7B,MAAI,UAAU,EACZ,QAAO;AAET,SAAO,WAAW,OAAO,MAAM,CAAC,CAAC;;AAEnC,QAAO,WAAW,MAAM,CAAC;;;;;;;;;;AAW3B,SAAgB,cACd,QAC2B;AAC3B,KAAI,CAAC,OACH;CAGF,MAAM,OAAO,OAAO,KAAK,OAAO;AAChC,KAAI,KAAK,WAAW,EAClB;CAGF,IAAI;AAEJ,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,OAAO;AAGrB,MAAI,CAAC,kBAAkB,MAAM,CAC3B;AAGF,MAAI,IAAI,WAAW,IAAI,EAAE;GAEvB,MAAM,WAAW,KAAK,IAAI,MAAM,EAAE;GAGlC,MAAM,iBAAiB,kBADA,UAAU,OAAO,KAAK,MACW;AAExD,OAAI,CAAC,OAAQ,UAAS,EAAE;AACxB,UAAO,YAAY;aACV,IAAI,WAAW,IAAI,EAAE;GAC9B,MAAM,YAAY,IAAI,MAAM,EAAE;GAC9B,MAAM,SAAS,qBAAqB;GAGpC,MAAM,iBAAiB,yBAAyB,MAAM;AAEtD,OAAI,mBAAmB,KAAM;GAE7B,MAAM,gBACJ,OAAO,mBAAmB,WACtB,OAAO,eAAe,GACtB;GACN,MAAM,aAAa,cAAc,aAAa;GAC9C,MAAM,iBAAiB,kBAAkB,eAAe;AAExD,OAAI,CAAC,OAAQ,UAAS,EAAE;AACxB,UAAO,KAAK,UAAU,WAAW;AAGjC,OAAI,qBAAqB,KAAK,WAAW,CACvC;AAGF,UAAO,KAAK,UAAU,SAAS,YAAY,uBACzC,eACA,eACD;;;AAIL,QAAO;;;;;AAMT,SAAgB,gBAAgB,QAAoC;AAClE,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,KAAK,UAAU,OAAO"}
1
+ {"version":3,"file":"process-tokens.js","names":[],"sources":["../../src/utils/process-tokens.ts"],"sourcesContent":["import type { Tokens, TokenValue } from '../types';\n\nimport type { CSSProperties } from './css-types';\n\nimport { getColorSpaceComponents, getColorSpaceSuffix } from './color-space';\nimport { normalizeColorTokenValue, parseStyle } from './styles';\n\nexport { hslToRgbValues } from './color-math';\n\nconst devMode = process.env.NODE_ENV !== 'production';\n\n/**\n * Extract color components in the configured color space.\n * Returns a CSS variable reference for token colors, or decomposed\n * components as a space-separated string.\n */\nfunction extractColorSpaceValue(\n colorValue: string,\n parsedOutput: string,\n): string {\n const suffix = getColorSpaceSuffix();\n\n // If the parsed output references a color variable, use the companion variant\n const varMatch = parsedOutput.match(/var\\(--([a-z0-9-]+)-color\\)/);\n if (varMatch) {\n return `var(--${varMatch[1]}-color-${suffix})`;\n }\n\n // Try the original color value first, then parsed output\n const components = getColorSpaceComponents(colorValue);\n if (components !== colorValue) return components;\n\n const componentsFromParsed = getColorSpaceComponents(parsedOutput);\n if (componentsFromParsed !== parsedOutput) return componentsFromParsed;\n\n // Fallback: return the parsed output\n return parsedOutput;\n}\n\n/**\n * Check if a value is a valid token value (string, number, or boolean - not object).\n * Returns false for `false` values (they mean \"skip this token\").\n */\nfunction isValidTokenValue(\n value: unknown,\n): value is Exclude<TokenValue, undefined | null | false> {\n if (value === undefined || value === null || value === false) {\n return false;\n }\n\n if (typeof value === 'object') {\n if (devMode) {\n console.warn(\n 'Tasty: Object values are not allowed in tokens prop. ' +\n 'Tokens do not support state-based styling. Use a primitive value instead.',\n );\n }\n return false;\n }\n\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n );\n}\n\n/**\n * Process a single token value through the tasty parser.\n * Numbers are converted to strings; 0 stays as \"0\".\n */\nfunction processTokenValue(value: string | number): string {\n if (typeof value === 'number') {\n // 0 should remain as \"0\", not converted to any unit\n if (value === 0) {\n return '0';\n }\n return parseStyle(String(value)).output;\n }\n return parseStyle(value).output;\n}\n\n/**\n * Process tokens object into inline style properties.\n * - $name -> --name with parsed value\n * - #name -> --name-color AND --name-color-{colorSpace} with parsed values\n *\n * @param tokens - The tokens object to process\n * @returns CSSProperties object or undefined if no tokens to process\n */\nexport function processTokens(\n tokens: Tokens | undefined,\n): CSSProperties | undefined {\n if (!tokens) {\n return undefined;\n }\n\n const keys = Object.keys(tokens);\n if (keys.length === 0) {\n return undefined;\n }\n\n let result: Record<string, string> | undefined;\n\n for (const key of keys) {\n const value = tokens[key as keyof Tokens];\n\n // Skip undefined/null values\n if (!isValidTokenValue(value)) {\n continue;\n }\n\n if (key.startsWith('$')) {\n // Custom property token: $name -> --name\n const propName = `--${key.slice(1)}`;\n // Boolean true for custom properties converts to empty string (valid CSS value)\n const effectiveValue = value === true ? '' : value;\n const processedValue = processTokenValue(effectiveValue);\n\n if (!result) result = {};\n result[propName] = processedValue;\n } else if (key.startsWith('#')) {\n const colorName = key.slice(1);\n const suffix = getColorSpaceSuffix();\n\n // Normalize color token value (true → 'transparent', false is already filtered by isValidTokenValue)\n const effectiveValue = normalizeColorTokenValue(value);\n // Skip if normalized to null (shouldn't happen since false is filtered by isValidTokenValue)\n if (effectiveValue === null) continue;\n\n const originalValue =\n typeof effectiveValue === 'number'\n ? String(effectiveValue)\n : effectiveValue;\n const lowerValue = originalValue.toLowerCase();\n const processedValue = processTokenValue(effectiveValue);\n\n if (!result) result = {};\n result[`--${colorName}-color`] = processedValue;\n\n // Skip component generation for #current values (currentcolor is dynamic, cannot decompose)\n if (/^#current(?:\\.|$)/i.test(lowerValue)) {\n continue;\n }\n\n result[`--${colorName}-color-${suffix}`] = extractColorSpaceValue(\n originalValue,\n processedValue,\n );\n }\n }\n\n return result as CSSProperties | undefined;\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAS,uBACP,YACA,cACQ;CACR,MAAM,SAAS,qBAAqB;CAGpC,MAAM,WAAW,aAAa,MAAM,8BAA8B;AAClE,KAAI,SACF,QAAO,SAAS,SAAS,GAAG,SAAS,OAAO;CAI9C,MAAM,aAAa,wBAAwB,WAAW;AACtD,KAAI,eAAe,WAAY,QAAO;CAEtC,MAAM,uBAAuB,wBAAwB,aAAa;AAClE,KAAI,yBAAyB,aAAc,QAAO;AAGlD,QAAO;;;;;;AAOT,SAAS,kBACP,OACwD;AACxD,KAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,MACrD,QAAO;AAGT,KAAI,OAAO,UAAU,UAAU;AAE3B,UAAQ,KACN,iIAED;AAEH,SAAO;;AAGT,QACE,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU;;;;;;AAQrB,SAAS,kBAAkB,OAAgC;AACzD,KAAI,OAAO,UAAU,UAAU;AAE7B,MAAI,UAAU,EACZ,QAAO;AAET,SAAO,WAAW,OAAO,MAAM,CAAC,CAAC;;AAEnC,QAAO,WAAW,MAAM,CAAC;;;;;;;;;;AAW3B,SAAgB,cACd,QAC2B;AAC3B,KAAI,CAAC,OACH;CAGF,MAAM,OAAO,OAAO,KAAK,OAAO;AAChC,KAAI,KAAK,WAAW,EAClB;CAGF,IAAI;AAEJ,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,QAAQ,OAAO;AAGrB,MAAI,CAAC,kBAAkB,MAAM,CAC3B;AAGF,MAAI,IAAI,WAAW,IAAI,EAAE;GAEvB,MAAM,WAAW,KAAK,IAAI,MAAM,EAAE;GAGlC,MAAM,iBAAiB,kBADA,UAAU,OAAO,KAAK,MACW;AAExD,OAAI,CAAC,OAAQ,UAAS,EAAE;AACxB,UAAO,YAAY;aACV,IAAI,WAAW,IAAI,EAAE;GAC9B,MAAM,YAAY,IAAI,MAAM,EAAE;GAC9B,MAAM,SAAS,qBAAqB;GAGpC,MAAM,iBAAiB,yBAAyB,MAAM;AAEtD,OAAI,mBAAmB,KAAM;GAE7B,MAAM,gBACJ,OAAO,mBAAmB,WACtB,OAAO,eAAe,GACtB;GACN,MAAM,aAAa,cAAc,aAAa;GAC9C,MAAM,iBAAiB,kBAAkB,eAAe;AAExD,OAAI,CAAC,OAAQ,UAAS,EAAE;AACxB,UAAO,KAAK,UAAU,WAAW;AAGjC,OAAI,qBAAqB,KAAK,WAAW,CACvC;AAGF,UAAO,KAAK,UAAU,SAAS,YAAY,uBACzC,eACA,eACD;;;AAIL,QAAO"}
@@ -2,7 +2,6 @@ import { isDevEnv } from "./is-dev-env.js";
2
2
  import { isSelector } from "../pipeline/index.js";
3
3
  import { getGlobalRecipes } from "../config.js";
4
4
  import { mergeStyles } from "./merge-styles.js";
5
-
6
5
  //#region src/utils/resolve-recipes.ts
7
6
  /**
8
7
  * Recipe resolution utility.
@@ -141,7 +140,7 @@ function resolveRecipes(styles) {
141
140
  }
142
141
  return changed ? result : styles;
143
142
  }
144
-
145
143
  //#endregion
146
144
  export { resolveRecipes };
145
+
147
146
  //# sourceMappingURL=resolve-recipes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;;AAkB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,IAAI;AAEvC,KAAI,eAAe,IAAI;AACrB,MAAI,YAAY,OAAQ,QAAO;AAE/B,SAAO;GAAE,MADK,WAAW,QAAQ;GACX,MAAM;GAAM;;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,WAAW;CAC7C,MAAM,WAAW,QAAQ,MAAM,aAAa,EAAE;AAE9C,QAAO;EACL,MAAM,SAAS,MAAM,KAAK,SAAS,OAAO,WAAW,SAAS;EAC9D,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}
1
+ {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;;AAkB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,IAAI;AAEvC,KAAI,eAAe,IAAI;AACrB,MAAI,YAAY,OAAQ,QAAO;AAE/B,SAAO;GAAE,MADK,WAAW,QAAQ;GACX,MAAM;GAAM;;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,WAAW;CAC7C,MAAM,WAAW,QAAQ,MAAM,aAAa,EAAE;AAE9C,QAAO;EACL,MAAM,SAAS,MAAM,KAAK,SAAS,OAAO,WAAW,SAAS;EAC9D,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}
@@ -26,7 +26,7 @@ const ELEMENT_NAME_RE = /(^|[\s>+~,(])([A-Z][a-zA-Z0-9]*)/g;
26
26
  function transformSelectorContent(content) {
27
27
  return content.replace(ELEMENT_NAME_RE, (_, prefix, name) => `${prefix}[data-element="${name}"]`);
28
28
  }
29
-
30
29
  //#endregion
31
30
  export { transformSelectorContent };
31
+
32
32
  //# sourceMappingURL=selector-transform.js.map
@@ -2,7 +2,7 @@
2
2
  function toSnakeCase(str) {
3
3
  return str.replace(/[A-Z]/g, (s) => `-${s.toLowerCase()}`);
4
4
  }
5
-
6
5
  //#endregion
7
6
  export { toSnakeCase };
7
+
8
8
  //# sourceMappingURL=string.js.map
@@ -23,7 +23,7 @@ type StylePropValue<T = string> = StyleValue<T> | StyleValueStateMap<T>;
23
23
  type CSSMap = {
24
24
  $?: string | string[];
25
25
  } & Record<string, string | string[]>;
26
- type StyleHandlerResult = CSSMap | CSSMap[] | void;
26
+ type StyleHandlerResult = CSSMap | CSSMap[] | null | void;
27
27
  type RawStyleHandler = (value: StyleValueStateMap) => StyleHandlerResult;
28
28
  type StyleHandler = RawStyleHandler & {
29
29
  __lookupStyles: string[];
@@ -1,7 +1,6 @@
1
- import { getNamedColorHex, getRgbValuesFromRgbaString, hexToRgb, strToRgb } from "./color-math.js";
1
+ import { getNamedColorHex } from "./color-math.js";
2
2
  import { StyleParser } from "../parser/parser.js";
3
3
  import { okhslFunc } from "../plugins/okhsl-plugin.js";
4
-
5
4
  //#region src/utils/styles.ts
6
5
  /**
7
6
  * Normalize a color token value.
@@ -215,7 +214,7 @@ function stringifyStyles(styles) {
215
214
  _topLevelCache.set(styles, result);
216
215
  return result;
217
216
  }
218
-
219
217
  //#endregion
220
218
  export { CUSTOM_UNITS, DIRECTIONS, customFunc, filterMods, getGlobalFuncs, getGlobalParser, getGlobalPredefinedTokens, normalizeColorTokenValue, parseColor, parseStyle, resetGlobalPredefinedTokens, setGlobalPredefinedTokens, stringifyStyles };
219
+
221
220
  //# sourceMappingURL=styles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"styles.js","names":[],"sources":["../../src/utils/styles.ts"],"sourcesContent":["import { StyleParser } from '../parser/parser';\nimport { okhslFunc } from '../plugins/okhsl-plugin';\n\nimport type { ProcessedStyle, StyleDetails } from '../parser/types';\n\nimport { getNamedColorHex } from './color-math';\n\nexport {\n getNamedColorHex,\n getRgbValuesFromRgbaString,\n hexToRgb,\n strToRgb,\n} from './color-math';\n\nexport type StyleValue<T = string> = T | boolean | number | null | undefined;\n\n/**\n * Normalize a color token value.\n * - Boolean `true` is converted to `'transparent'`\n * - Boolean `false` returns `null` (signals the token should be skipped)\n * - Other values are returned as-is\n *\n * @param value - The raw token value\n * @returns Normalized value or null if the token should be skipped\n */\nexport function normalizeColorTokenValue<T>(\n value: T | boolean,\n): T | 'transparent' | null {\n if (value === true) {\n return 'transparent';\n }\n if (value === false) {\n return null;\n }\n return value as T;\n}\n\nexport type StyleValueStateMap<T = string> = Record<\n string,\n StyleValue<T> | '@inherit'\n>;\n\n/**\n * Combined type for style values that can be either a direct value or a state map.\n * Use this for component props that accept style values.\n */\nexport type StylePropValue<T = string> = StyleValue<T> | StyleValueStateMap<T>;\n\nexport type CSSMap = { $?: string | string[] } & Record<\n string,\n string | string[]\n>;\n\nexport type StyleHandlerResult = CSSMap | CSSMap[] | void;\n\nexport type RawStyleHandler = (value: StyleValueStateMap) => StyleHandlerResult;\n\nexport type StyleHandler = RawStyleHandler & {\n __lookupStyles: string[];\n};\n\n/**\n * Handler definition forms for configure() and plugins.\n * - Function only: lookup styles inferred from key name\n * - Single property tuple: ['styleName', handler]\n * - Multi-property tuple: [['style1', 'style2'], handler]\n */\nexport type StyleHandlerDefinition =\n | RawStyleHandler\n | [string, RawStyleHandler]\n | [string[], RawStyleHandler];\n\nexport interface ParsedColor {\n color?: string;\n name?: string;\n opacity?: number;\n}\n\nexport type StyleMap = Record<string, StyleValue | StyleValueStateMap>;\n\nconst devMode = process.env.NODE_ENV !== 'production';\n\n// Precompiled regex patterns for parseColor optimization\n// Match var(--name-color...) and extract the name, regardless of fallbacks\nconst COLOR_VAR_PATTERN = /var\\(--([a-z0-9-]+)-color/;\nconst COLOR_VAR_COMPONENTS_PATTERN =\n /var\\(--([a-z0-9-]+)-color-(?:rgb|hsl|oklch)/;\nconst RGB_ALPHA_PATTERN = /\\/\\s*([0-9.]+)\\)/;\nconst RE_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;\nconst RE_VAR_COLOR = /^var\\(--[a-z0-9-]+-color/;\n\nfunction isSimpleColorFast(val: string): boolean {\n const c0 = val.charCodeAt(0);\n\n switch (c0) {\n case 35: // '#'\n return RE_HEX_COLOR.test(val);\n case 114: // 'r'\n return val.charCodeAt(1) === 103 && val.charCodeAt(2) === 98; // 'rgb'\n case 104: // 'h'\n return val.charCodeAt(1) === 115 && val.charCodeAt(2) === 108; // 'hsl'\n case 108: // 'l'\n return val.charCodeAt(1) === 99 && val.charCodeAt(2) === 104; // 'lch'\n case 111: // 'o'\n return val.startsWith('oklch(') || val.startsWith('okhsl(');\n case 118: // 'v'\n return RE_VAR_COLOR.test(val);\n case 99: // 'c'\n return val === 'currentColor' || val === 'currentcolor';\n case 116: // 't'\n return val === 'transparent';\n default:\n return getNamedColorHex().has(val.toLowerCase());\n }\n}\n\n// Rate limiting for dev warnings to avoid spam\nlet colorWarningCount = 0;\nconst MAX_COLOR_WARNINGS = 10;\n\nexport const CUSTOM_UNITS = {\n r: '6px',\n cr: '10px',\n bw: '1px',\n ow: '3px',\n x: '8px',\n sf: function sf(num: number) {\n return `minmax(0, ${num}fr)`;\n },\n};\n\nexport const DIRECTIONS = ['top', 'right', 'bottom', 'left'];\n\n// Lazy-initialized to break the circular dependency:\n// parser.ts → classify.ts → utils/styles.ts → parser.ts\nlet __tastyParser: StyleParser | null = null;\n\nfunction getOrCreateParser(): StyleParser {\n if (!__tastyParser) {\n __tastyParser = new StyleParser({ units: CUSTOM_UNITS });\n __tastyParser.setFuncs(__tastyFuncs);\n }\n return __tastyParser;\n}\n\n// Registry for user-provided custom functions that the parser can call.\n// It is updated through the `customFunc` helper exported below.\n// okhsl is registered as a built-in function so it works regardless of\n// tree-shaking or module initialization order.\nconst __tastyFuncs: Record<string, (groups: StyleDetails[]) => string> = {\n okhsl: okhslFunc,\n};\n\nexport function customFunc(\n name: string,\n fn: (groups: StyleDetails[]) => string,\n) {\n __tastyFuncs[name] = fn;\n getOrCreateParser().setFuncs(__tastyFuncs);\n}\n\n/**\n * Get the global StyleParser instance.\n * Used by configure() to apply parser configuration.\n */\nexport function getGlobalParser(): StyleParser {\n return getOrCreateParser();\n}\n\n/**\n * Get the current custom functions registry.\n * Used by configure() to merge with new functions.\n */\nexport function getGlobalFuncs(): Record<\n string,\n (groups: StyleDetails[]) => string\n> {\n return __tastyFuncs;\n}\n\n// ============================================================================\n// Global Predefined Tokens\n// ============================================================================\n\n/**\n * Storage for predefined tokens that are replaced during style parsing.\n * Keys are token names (with $ or # prefix), values are pre-processed CSS values.\n */\nlet __globalPredefinedTokens: Record<string, string> | null = null;\n\n/**\n * Set global predefined tokens.\n * Called from configure() after processing token values.\n * Merges with existing tokens (new tokens override existing ones with same key).\n * Keys are normalized to lowercase (parser lowercases input before classification).\n * @internal\n */\nexport function setGlobalPredefinedTokens(\n tokens: Record<string, string>,\n): void {\n // Normalize keys to lowercase for case-insensitive matching\n const normalizedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(tokens)) {\n const lowerKey = key.toLowerCase();\n const lowerValue = value.toLowerCase();\n\n // Warn if trying to use bare #current to define other color tokens\n // #current represents currentcolor which cannot be used as a base for recursive token resolution\n // Note: #current.5 (with opacity) is allowed since it resolves to a concrete color-mix value\n if (lowerKey.startsWith('#') && lowerValue === '#current') {\n console.warn(\n `Tasty: Using #current to define color token \"${key}\" is not supported. ` +\n `The #current token represents currentcolor which cannot be used as a base for other tokens.`,\n );\n continue; // Skip this token\n }\n\n normalizedTokens[lowerKey] = value;\n }\n // Merge with existing tokens (consistent with how states, units, funcs are handled)\n __globalPredefinedTokens = __globalPredefinedTokens\n ? { ...__globalPredefinedTokens, ...normalizedTokens }\n : normalizedTokens;\n // Clear parser cache since token values affect parsing\n getOrCreateParser().clearCache();\n}\n\n/**\n * Get the current global predefined tokens.\n * Returns null if no tokens are configured.\n */\nexport function getGlobalPredefinedTokens(): Record<string, string> | null {\n return __globalPredefinedTokens;\n}\n\n/**\n * Reset global predefined tokens.\n * Used for testing.\n * @internal\n */\nexport function resetGlobalPredefinedTokens(): void {\n __globalPredefinedTokens = null;\n // Clear parser cache since token availability affects parsing\n getOrCreateParser().clearCache();\n}\n\n/**\n *\n * @param {String} value\n * @param {Number} mode\n * @returns {Object<String,String|Array>}\n */\nexport function parseStyle(value: StyleValue): ProcessedStyle {\n let str: string;\n\n if (typeof value === 'string') {\n str = value;\n } else if (typeof value === 'number') {\n str = String(value);\n } else {\n // boolean, null, undefined, objects etc. → empty string\n str = '';\n }\n\n return getOrCreateParser().process(str);\n}\n\n/**\n * Parse color. Find it value, name and opacity.\n * Optimized to avoid heavy parseStyle calls for simple color patterns.\n */\nexport function parseColor(val: string, ignoreError = false): ParsedColor {\n // Early return for non-strings or empty values\n if (typeof val !== 'string') {\n val = String(val ?? '');\n }\n\n val = val.trim();\n if (!val) return {};\n\n // Fast path: Check if it's a simple color pattern that doesn't need full parsing\n const isSimpleColor = isSimpleColorFast(val);\n\n let firstColor: string;\n if (isSimpleColor) {\n // For simple colors, use the value directly without parsing\n firstColor = val;\n } else {\n const processed = parseStyle(val);\n const extractedColor = processed.groups.find((g) => g.colors.length)\n ?.colors[0];\n\n if (!extractedColor) {\n // Rate-limited warning to avoid spam\n if (!ignoreError && devMode && colorWarningCount < MAX_COLOR_WARNINGS) {\n console.warn('Tasty: unable to parse color:', val);\n colorWarningCount++;\n if (colorWarningCount === MAX_COLOR_WARNINGS) {\n console.warn(\n 'Tasty: color parsing warnings will be suppressed from now on',\n );\n }\n }\n return {};\n }\n\n firstColor = extractedColor;\n }\n\n // Extract color name (if present) from variable pattern using precompiled regex\n let nameMatch = firstColor.match(COLOR_VAR_PATTERN);\n if (!nameMatch) {\n nameMatch = firstColor.match(COLOR_VAR_COMPONENTS_PATTERN);\n }\n\n let opacity: number | undefined;\n if (\n firstColor.startsWith('rgb') ||\n firstColor.startsWith('hsl') ||\n firstColor.startsWith('lch') ||\n firstColor.startsWith('oklch') ||\n firstColor.startsWith('okhsl')\n ) {\n const alphaMatch = firstColor.match(RGB_ALPHA_PATTERN);\n if (alphaMatch) {\n const v = parseFloat(alphaMatch[1]);\n if (!isNaN(v)) opacity = v * 100;\n }\n }\n\n return {\n color: firstColor,\n name: nameMatch ? nameMatch[1] : undefined,\n opacity,\n };\n}\n\nexport function filterMods(mods: string[], allowedMods: string[]): string[] {\n return mods.filter((mod) => allowedMods.includes(mod));\n}\n\n// ============================================================================\n// Style Stringification\n// ============================================================================\n\nconst _innerCache = new WeakMap();\nconst _topLevelCache = new WeakMap<object, string>();\n\nexport function stringifyStyles(styles: unknown): string {\n if (styles == null || typeof styles !== 'object') return '';\n\n const cached = _topLevelCache.get(styles as object);\n if (cached !== undefined) return cached;\n\n const obj = styles as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const v = obj[k];\n if (v === undefined || typeof v === 'function' || typeof v === 'symbol')\n continue;\n\n const c0 = k.charCodeAt(0);\n const needsInnerSort =\n ((c0 >= 65 && c0 <= 90) || c0 === 38) &&\n v &&\n typeof v === 'object' &&\n !Array.isArray(v);\n\n let sv: string;\n if (needsInnerSort) {\n sv = _innerCache.get(v);\n if (sv === undefined) {\n const innerObj = v as Record<string, unknown>;\n const innerKeys = Object.keys(innerObj).sort();\n const innerParts: string[] = [];\n for (const ik of innerKeys) {\n const ivs = JSON.stringify(innerObj[ik]);\n if (ivs !== undefined)\n innerParts.push(JSON.stringify(ik) + ':' + ivs);\n }\n sv = '{' + innerParts.join(',') + '}';\n _innerCache.set(v, sv);\n }\n } else {\n sv = JSON.stringify(v);\n }\n parts.push(JSON.stringify(k) + ':' + sv);\n }\n const result = '{' + parts.join(',') + '}';\n _topLevelCache.set(styles as object, result);\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"styles.js","names":[],"sources":["../../src/utils/styles.ts"],"sourcesContent":["import { StyleParser } from '../parser/parser';\nimport { okhslFunc } from '../plugins/okhsl-plugin';\n\nimport type { ProcessedStyle, StyleDetails } from '../parser/types';\n\nimport { getNamedColorHex } from './color-math';\n\nexport {\n getNamedColorHex,\n getRgbValuesFromRgbaString,\n hexToRgb,\n strToRgb,\n} from './color-math';\n\nexport type StyleValue<T = string> = T | boolean | number | null | undefined;\n\n/**\n * Normalize a color token value.\n * - Boolean `true` is converted to `'transparent'`\n * - Boolean `false` returns `null` (signals the token should be skipped)\n * - Other values are returned as-is\n *\n * @param value - The raw token value\n * @returns Normalized value or null if the token should be skipped\n */\nexport function normalizeColorTokenValue<T>(\n value: T | boolean,\n): T | 'transparent' | null {\n if (value === true) {\n return 'transparent';\n }\n if (value === false) {\n return null;\n }\n return value as T;\n}\n\nexport type StyleValueStateMap<T = string> = Record<\n string,\n StyleValue<T> | '@inherit'\n>;\n\n/**\n * Combined type for style values that can be either a direct value or a state map.\n * Use this for component props that accept style values.\n */\nexport type StylePropValue<T = string> = StyleValue<T> | StyleValueStateMap<T>;\n\nexport type CSSMap = { $?: string | string[] } & Record<\n string,\n string | string[]\n>;\n\nexport type StyleHandlerResult = CSSMap | CSSMap[] | null | void;\n\nexport type RawStyleHandler = (value: StyleValueStateMap) => StyleHandlerResult;\n\nexport type StyleHandler = RawStyleHandler & {\n __lookupStyles: string[];\n};\n\n/**\n * Handler definition forms for configure() and plugins.\n * - Function only: lookup styles inferred from key name\n * - Single property tuple: ['styleName', handler]\n * - Multi-property tuple: [['style1', 'style2'], handler]\n */\nexport type StyleHandlerDefinition =\n | RawStyleHandler\n | [string, RawStyleHandler]\n | [string[], RawStyleHandler];\n\nexport interface ParsedColor {\n color?: string;\n name?: string;\n opacity?: number;\n}\n\nexport type StyleMap = Record<string, StyleValue | StyleValueStateMap>;\n\nconst devMode = process.env.NODE_ENV !== 'production';\n\n// Precompiled regex patterns for parseColor optimization\n// Match var(--name-color...) and extract the name, regardless of fallbacks\nconst COLOR_VAR_PATTERN = /var\\(--([a-z0-9-]+)-color/;\nconst COLOR_VAR_COMPONENTS_PATTERN =\n /var\\(--([a-z0-9-]+)-color-(?:rgb|hsl|oklch)/;\nconst RGB_ALPHA_PATTERN = /\\/\\s*([0-9.]+)\\)/;\nconst RE_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;\nconst RE_VAR_COLOR = /^var\\(--[a-z0-9-]+-color/;\n\nfunction isSimpleColorFast(val: string): boolean {\n const c0 = val.charCodeAt(0);\n\n switch (c0) {\n case 35: // '#'\n return RE_HEX_COLOR.test(val);\n case 114: // 'r'\n return val.charCodeAt(1) === 103 && val.charCodeAt(2) === 98; // 'rgb'\n case 104: // 'h'\n return val.charCodeAt(1) === 115 && val.charCodeAt(2) === 108; // 'hsl'\n case 108: // 'l'\n return val.charCodeAt(1) === 99 && val.charCodeAt(2) === 104; // 'lch'\n case 111: // 'o'\n return val.startsWith('oklch(') || val.startsWith('okhsl(');\n case 118: // 'v'\n return RE_VAR_COLOR.test(val);\n case 99: // 'c'\n return val === 'currentColor' || val === 'currentcolor';\n case 116: // 't'\n return val === 'transparent';\n default:\n return getNamedColorHex().has(val.toLowerCase());\n }\n}\n\n// Rate limiting for dev warnings to avoid spam\nlet colorWarningCount = 0;\nconst MAX_COLOR_WARNINGS = 10;\n\nexport const CUSTOM_UNITS = {\n r: '6px',\n cr: '10px',\n bw: '1px',\n ow: '3px',\n x: '8px',\n sf: function sf(num: number) {\n return `minmax(0, ${num}fr)`;\n },\n};\n\nexport const DIRECTIONS = ['top', 'right', 'bottom', 'left'];\n\n// Lazy-initialized to break the circular dependency:\n// parser.ts → classify.ts → utils/styles.ts → parser.ts\nlet __tastyParser: StyleParser | null = null;\n\nfunction getOrCreateParser(): StyleParser {\n if (!__tastyParser) {\n __tastyParser = new StyleParser({ units: CUSTOM_UNITS });\n __tastyParser.setFuncs(__tastyFuncs);\n }\n return __tastyParser;\n}\n\n// Registry for user-provided custom functions that the parser can call.\n// It is updated through the `customFunc` helper exported below.\n// okhsl is registered as a built-in function so it works regardless of\n// tree-shaking or module initialization order.\nconst __tastyFuncs: Record<string, (groups: StyleDetails[]) => string> = {\n okhsl: okhslFunc,\n};\n\nexport function customFunc(\n name: string,\n fn: (groups: StyleDetails[]) => string,\n) {\n __tastyFuncs[name] = fn;\n getOrCreateParser().setFuncs(__tastyFuncs);\n}\n\n/**\n * Get the global StyleParser instance.\n * Used by configure() to apply parser configuration.\n */\nexport function getGlobalParser(): StyleParser {\n return getOrCreateParser();\n}\n\n/**\n * Get the current custom functions registry.\n * Used by configure() to merge with new functions.\n */\nexport function getGlobalFuncs(): Record<\n string,\n (groups: StyleDetails[]) => string\n> {\n return __tastyFuncs;\n}\n\n// ============================================================================\n// Global Predefined Tokens\n// ============================================================================\n\n/**\n * Storage for predefined tokens that are replaced during style parsing.\n * Keys are token names (with $ or # prefix), values are pre-processed CSS values.\n */\nlet __globalPredefinedTokens: Record<string, string> | null = null;\n\n/**\n * Set global predefined tokens.\n * Called from configure() after processing token values.\n * Merges with existing tokens (new tokens override existing ones with same key).\n * Keys are normalized to lowercase (parser lowercases input before classification).\n * @internal\n */\nexport function setGlobalPredefinedTokens(\n tokens: Record<string, string>,\n): void {\n // Normalize keys to lowercase for case-insensitive matching\n const normalizedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(tokens)) {\n const lowerKey = key.toLowerCase();\n const lowerValue = value.toLowerCase();\n\n // Warn if trying to use bare #current to define other color tokens\n // #current represents currentcolor which cannot be used as a base for recursive token resolution\n // Note: #current.5 (with opacity) is allowed since it resolves to a concrete color-mix value\n if (lowerKey.startsWith('#') && lowerValue === '#current') {\n console.warn(\n `Tasty: Using #current to define color token \"${key}\" is not supported. ` +\n `The #current token represents currentcolor which cannot be used as a base for other tokens.`,\n );\n continue; // Skip this token\n }\n\n normalizedTokens[lowerKey] = value;\n }\n // Merge with existing tokens (consistent with how states, units, funcs are handled)\n __globalPredefinedTokens = __globalPredefinedTokens\n ? { ...__globalPredefinedTokens, ...normalizedTokens }\n : normalizedTokens;\n // Clear parser cache since token values affect parsing\n getOrCreateParser().clearCache();\n}\n\n/**\n * Get the current global predefined tokens.\n * Returns null if no tokens are configured.\n */\nexport function getGlobalPredefinedTokens(): Record<string, string> | null {\n return __globalPredefinedTokens;\n}\n\n/**\n * Reset global predefined tokens.\n * Used for testing.\n * @internal\n */\nexport function resetGlobalPredefinedTokens(): void {\n __globalPredefinedTokens = null;\n // Clear parser cache since token availability affects parsing\n getOrCreateParser().clearCache();\n}\n\n/**\n *\n * @param {String} value\n * @param {Number} mode\n * @returns {Object<String,String|Array>}\n */\nexport function parseStyle(value: StyleValue): ProcessedStyle {\n let str: string;\n\n if (typeof value === 'string') {\n str = value;\n } else if (typeof value === 'number') {\n str = String(value);\n } else {\n // boolean, null, undefined, objects etc. → empty string\n str = '';\n }\n\n return getOrCreateParser().process(str);\n}\n\n/**\n * Parse color. Find it value, name and opacity.\n * Optimized to avoid heavy parseStyle calls for simple color patterns.\n */\nexport function parseColor(val: string, ignoreError = false): ParsedColor {\n // Early return for non-strings or empty values\n if (typeof val !== 'string') {\n val = String(val ?? '');\n }\n\n val = val.trim();\n if (!val) return {};\n\n // Fast path: Check if it's a simple color pattern that doesn't need full parsing\n const isSimpleColor = isSimpleColorFast(val);\n\n let firstColor: string;\n if (isSimpleColor) {\n // For simple colors, use the value directly without parsing\n firstColor = val;\n } else {\n const processed = parseStyle(val);\n const extractedColor = processed.groups.find((g) => g.colors.length)\n ?.colors[0];\n\n if (!extractedColor) {\n // Rate-limited warning to avoid spam\n if (!ignoreError && devMode && colorWarningCount < MAX_COLOR_WARNINGS) {\n console.warn('Tasty: unable to parse color:', val);\n colorWarningCount++;\n if (colorWarningCount === MAX_COLOR_WARNINGS) {\n console.warn(\n 'Tasty: color parsing warnings will be suppressed from now on',\n );\n }\n }\n return {};\n }\n\n firstColor = extractedColor;\n }\n\n // Extract color name (if present) from variable pattern using precompiled regex\n let nameMatch = firstColor.match(COLOR_VAR_PATTERN);\n if (!nameMatch) {\n nameMatch = firstColor.match(COLOR_VAR_COMPONENTS_PATTERN);\n }\n\n let opacity: number | undefined;\n if (\n firstColor.startsWith('rgb') ||\n firstColor.startsWith('hsl') ||\n firstColor.startsWith('lch') ||\n firstColor.startsWith('oklch') ||\n firstColor.startsWith('okhsl')\n ) {\n const alphaMatch = firstColor.match(RGB_ALPHA_PATTERN);\n if (alphaMatch) {\n const v = parseFloat(alphaMatch[1]);\n if (!isNaN(v)) opacity = v * 100;\n }\n }\n\n return {\n color: firstColor,\n name: nameMatch ? nameMatch[1] : undefined,\n opacity,\n };\n}\n\nexport function filterMods(mods: string[], allowedMods: string[]): string[] {\n return mods.filter((mod) => allowedMods.includes(mod));\n}\n\n// ============================================================================\n// Style Stringification\n// ============================================================================\n\nconst _innerCache = new WeakMap();\nconst _topLevelCache = new WeakMap<object, string>();\n\nexport function stringifyStyles(styles: unknown): string {\n if (styles == null || typeof styles !== 'object') return '';\n\n const cached = _topLevelCache.get(styles as object);\n if (cached !== undefined) return cached;\n\n const obj = styles as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const v = obj[k];\n if (v === undefined || typeof v === 'function' || typeof v === 'symbol')\n continue;\n\n const c0 = k.charCodeAt(0);\n const needsInnerSort =\n ((c0 >= 65 && c0 <= 90) || c0 === 38) &&\n v &&\n typeof v === 'object' &&\n !Array.isArray(v);\n\n let sv: string;\n if (needsInnerSort) {\n sv = _innerCache.get(v);\n if (sv === undefined) {\n const innerObj = v as Record<string, unknown>;\n const innerKeys = Object.keys(innerObj).sort();\n const innerParts: string[] = [];\n for (const ik of innerKeys) {\n const ivs = JSON.stringify(innerObj[ik]);\n if (ivs !== undefined)\n innerParts.push(JSON.stringify(ik) + ':' + ivs);\n }\n sv = '{' + innerParts.join(',') + '}';\n _innerCache.set(v, sv);\n }\n } else {\n sv = JSON.stringify(v);\n }\n parts.push(JSON.stringify(k) + ':' + sv);\n }\n const result = '{' + parts.join(',') + '}';\n _topLevelCache.set(styles as object, result);\n return result;\n}\n"],"mappings":"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"}
@@ -45,7 +45,7 @@ function generateTypographyTokens(presets) {
45
45
  }
46
46
  return tokens;
47
47
  }
48
-
49
48
  //#endregion
50
49
  export { generateTypographyTokens };
50
+
51
51
  //# sourceMappingURL=typography.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"typography.js","names":[],"sources":["../../src/utils/typography.ts"],"sourcesContent":["import type { ConfigTokens } from '../styles/types';\n\nconst RESERVED_PRESET_NAMES = new Set([\n 'strong',\n 'bold',\n 'italic',\n 'icon',\n 'tight',\n]);\n\n/**\n * Typography preset configuration.\n * Each preset defines font properties that get expanded into CSS custom properties.\n *\n * Use with `generateTypographyTokens()` to create typography tokens for your design system.\n */\nexport interface TypographyPreset {\n fontSize: string;\n lineHeight: string;\n letterSpacing?: string;\n fontWeight: string | number;\n boldFontWeight?: string | number;\n iconSize?: string;\n textTransform?: string;\n fontFamily?: string;\n fontStyle?: string;\n}\n\n/**\n * Generate typography tokens with $ prefix for CSS custom properties.\n *\n * Each preset generates the following CSS custom properties:\n * - `${name}-font-size`\n * - `${name}-line-height`\n * - `${name}-letter-spacing`\n * - `${name}-font-weight`\n * - `${name}-bold-font-weight` (if defined)\n * - `${name}-icon-size` (if defined)\n * - `${name}-text-transform` (if defined)\n * - `${name}-font-family` (if defined)\n * - `${name}-font-style` (if defined)\n *\n * @param presets - Typography presets object\n * @returns ConfigTokens object with $ prefixed keys\n *\n * @example\n * const customTokens = generateTypographyTokens({\n * myHeading: { fontSize: '24px', lineHeight: '32px', fontWeight: '700' },\n * body: { fontSize: '16px', lineHeight: '24px', fontWeight: '400' },\n * });\n */\nexport function generateTypographyTokens(\n presets: Record<string, TypographyPreset>,\n): ConfigTokens {\n const tokens: Record<`$${string}`, string | number> = {};\n\n for (const [name, preset] of Object.entries(presets)) {\n if (RESERVED_PRESET_NAMES.has(name)) {\n throw new Error(\n `Invalid typography preset name \"${name}\". This name is reserved as a preset modifier.`,\n );\n }\n\n tokens[`$${name}-font-size`] = preset.fontSize;\n tokens[`$${name}-line-height`] = preset.lineHeight;\n tokens[`$${name}-letter-spacing`] = preset.letterSpacing ?? '0';\n tokens[`$${name}-font-weight`] = preset.fontWeight;\n\n if (preset.boldFontWeight !== undefined) {\n tokens[`$${name}-bold-font-weight`] = preset.boldFontWeight;\n }\n\n if (preset.iconSize !== undefined) {\n tokens[`$${name}-icon-size`] = preset.iconSize;\n }\n\n if (preset.textTransform !== undefined) {\n tokens[`$${name}-text-transform`] = preset.textTransform;\n }\n\n if (preset.fontFamily !== undefined) {\n tokens[`$${name}-font-family`] = preset.fontFamily;\n }\n\n if (preset.fontStyle !== undefined) {\n tokens[`$${name}-font-style`] = preset.fontStyle;\n }\n }\n\n return tokens as ConfigTokens;\n}\n"],"mappings":";AAEA,MAAM,wBAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AA2CF,SAAgB,yBACd,SACc;CACd,MAAM,SAAgD,EAAE;AAExD,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,QAAQ,EAAE;AACpD,MAAI,sBAAsB,IAAI,KAAK,CACjC,OAAM,IAAI,MACR,mCAAmC,KAAK,gDACzC;AAGH,SAAO,IAAI,KAAK,eAAe,OAAO;AACtC,SAAO,IAAI,KAAK,iBAAiB,OAAO;AACxC,SAAO,IAAI,KAAK,oBAAoB,OAAO,iBAAiB;AAC5D,SAAO,IAAI,KAAK,iBAAiB,OAAO;AAExC,MAAI,OAAO,mBAAmB,OAC5B,QAAO,IAAI,KAAK,sBAAsB,OAAO;AAG/C,MAAI,OAAO,aAAa,OACtB,QAAO,IAAI,KAAK,eAAe,OAAO;AAGxC,MAAI,OAAO,kBAAkB,OAC3B,QAAO,IAAI,KAAK,oBAAoB,OAAO;AAG7C,MAAI,OAAO,eAAe,OACxB,QAAO,IAAI,KAAK,iBAAiB,OAAO;AAG1C,MAAI,OAAO,cAAc,OACvB,QAAO,IAAI,KAAK,gBAAgB,OAAO;;AAI3C,QAAO"}
1
+ {"version":3,"file":"typography.js","names":[],"sources":["../../src/utils/typography.ts"],"sourcesContent":["import type { ConfigTokens } from '../styles/types';\n\nconst RESERVED_PRESET_NAMES = new Set([\n 'strong',\n 'bold',\n 'italic',\n 'icon',\n 'tight',\n]);\n\n/**\n * Typography preset configuration.\n * Each preset defines font properties that get expanded into CSS custom properties.\n *\n * Use with `generateTypographyTokens()` to create typography tokens for your design system.\n */\nexport interface TypographyPreset {\n fontSize: string;\n lineHeight: string;\n letterSpacing?: string;\n fontWeight: string | number;\n boldFontWeight?: string | number;\n iconSize?: string;\n textTransform?: string;\n fontFamily?: string;\n fontStyle?: string;\n}\n\n/**\n * Generate typography tokens with $ prefix for CSS custom properties.\n *\n * Each preset generates the following CSS custom properties:\n * - `${name}-font-size`\n * - `${name}-line-height`\n * - `${name}-letter-spacing`\n * - `${name}-font-weight`\n * - `${name}-bold-font-weight` (if defined)\n * - `${name}-icon-size` (if defined)\n * - `${name}-text-transform` (if defined)\n * - `${name}-font-family` (if defined)\n * - `${name}-font-style` (if defined)\n *\n * @param presets - Typography presets object\n * @returns ConfigTokens object with $ prefixed keys\n *\n * @example\n * const customTokens = generateTypographyTokens({\n * myHeading: { fontSize: '24px', lineHeight: '32px', fontWeight: '700' },\n * body: { fontSize: '16px', lineHeight: '24px', fontWeight: '400' },\n * });\n */\nexport function generateTypographyTokens(\n presets: Record<string, TypographyPreset>,\n): ConfigTokens {\n const tokens: Record<`$${string}`, string | number> = {};\n\n for (const [name, preset] of Object.entries(presets)) {\n if (RESERVED_PRESET_NAMES.has(name)) {\n throw new Error(\n `Invalid typography preset name \"${name}\". This name is reserved as a preset modifier.`,\n );\n }\n\n tokens[`$${name}-font-size`] = preset.fontSize;\n tokens[`$${name}-line-height`] = preset.lineHeight;\n tokens[`$${name}-letter-spacing`] = preset.letterSpacing ?? '0';\n tokens[`$${name}-font-weight`] = preset.fontWeight;\n\n if (preset.boldFontWeight !== undefined) {\n tokens[`$${name}-bold-font-weight`] = preset.boldFontWeight;\n }\n\n if (preset.iconSize !== undefined) {\n tokens[`$${name}-icon-size`] = preset.iconSize;\n }\n\n if (preset.textTransform !== undefined) {\n tokens[`$${name}-text-transform`] = preset.textTransform;\n }\n\n if (preset.fontFamily !== undefined) {\n tokens[`$${name}-font-family`] = preset.fontFamily;\n }\n\n if (preset.fontStyle !== undefined) {\n tokens[`$${name}-font-style`] = preset.fontStyle;\n }\n }\n\n return tokens as ConfigTokens;\n}\n"],"mappings":";AAEA,MAAM,wBAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AA2CF,SAAgB,yBACd,SACc;CACd,MAAM,SAAgD,EAAE;AAExD,MAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,QAAQ,EAAE;AACpD,MAAI,sBAAsB,IAAI,KAAK,CACjC,OAAM,IAAI,MACR,mCAAmC,KAAK,gDACzC;AAGH,SAAO,IAAI,KAAK,eAAe,OAAO;AACtC,SAAO,IAAI,KAAK,iBAAiB,OAAO;AACxC,SAAO,IAAI,KAAK,oBAAoB,OAAO,iBAAiB;AAC5D,SAAO,IAAI,KAAK,iBAAiB,OAAO;AAExC,MAAI,OAAO,mBAAmB,KAAA,EAC5B,QAAO,IAAI,KAAK,sBAAsB,OAAO;AAG/C,MAAI,OAAO,aAAa,KAAA,EACtB,QAAO,IAAI,KAAK,eAAe,OAAO;AAGxC,MAAI,OAAO,kBAAkB,KAAA,EAC3B,QAAO,IAAI,KAAK,oBAAoB,OAAO;AAG7C,MAAI,OAAO,eAAe,KAAA,EACxB,QAAO,IAAI,KAAK,iBAAiB,OAAO;AAG1C,MAAI,OAAO,cAAc,KAAA,EACvB,QAAO,IAAI,KAAK,gBAAgB,OAAO;;AAI3C,QAAO"}
@@ -10,7 +10,7 @@ function deprecationWarning(condition, { property, name, betterAlternative, reas
10
10
  if (reason) warn(`Reason: ${typeof reason === "function" ? reason() : reason}`);
11
11
  console.groupEnd();
12
12
  }
13
-
14
13
  //#endregion
15
14
  export { deprecationWarning, warn };
15
+
16
16
  //# sourceMappingURL=warnings.js.map
@@ -3,7 +3,7 @@ import { StyleDetails, UnitHandler } from "../parser/types.js";
3
3
  import { StyleHandlerDefinition } from "../utils/styles.js";
4
4
  import { ConfigTokens, RecipeStyles } from "../styles/types.js";
5
5
  import { TastyPlugin } from "../plugins/types.js";
6
- import * as _babel_core0 from "@babel/core";
6
+ import * as _$_babel_core0 from "@babel/core";
7
7
  import { PluginPass } from "@babel/core";
8
8
 
9
9
  //#region src/zero/babel.d.ts
@@ -158,10 +158,25 @@ interface TastyZeroBabelOptions {
158
158
  * @default true
159
159
  */
160
160
  injectImport?: boolean;
161
+ /**
162
+ * Output mode for extracted CSS.
163
+ *
164
+ * - `'file'` (default): CSS is written to a single output file and
165
+ * the `@tenphi/tasty/static` import is rewritten to import that file.
166
+ * - `'inject'`: CSS is embedded inline in the JS output and injected
167
+ * at runtime via a tiny injector from `@tenphi/tasty/static/inject`.
168
+ * No CSS file is written. Each `tastyStatic` call becomes
169
+ * self-contained. Best for reusable components and extensions.
170
+ *
171
+ * When `mode` is `'inject'`, `output` and `injectImport` are ignored.
172
+ *
173
+ * @default 'file'
174
+ */
175
+ mode?: 'file' | 'inject';
161
176
  }
162
177
  /** Clear the shared CSSWriter cache. Exposed for testing. */
163
178
  declare function clearWriterCache(): void;
164
- declare const _default: (api: object, options: TastyZeroBabelOptions | null | undefined, dirname: string) => _babel_core0.PluginObj<PluginPass>;
179
+ declare const _default: (api: object, options: TastyZeroBabelOptions | null | undefined, dirname: string) => _$_babel_core0.PluginObj<PluginPass>;
165
180
  //#endregion
166
181
  export { TastyZeroBabelOptions, TastyZeroConfig, clearWriterCache, _default as default };
167
182
  //# sourceMappingURL=babel.d.ts.map
@@ -9,7 +9,6 @@ import * as path from "path";
9
9
  import { declare } from "@babel/helper-plugin-utils";
10
10
  import * as t from "@babel/types";
11
11
  import { createJiti } from "jiti";
12
-
13
12
  //#region src/zero/babel.ts
14
13
  /**
15
14
  * Babel plugin for zero-runtime tasty static site generation.
@@ -58,10 +57,11 @@ function clearWriterCache() {
58
57
  }
59
58
  var babel_default = declare((api, options) => {
60
59
  api.assertVersion(7);
60
+ const mode = options.mode ?? "file";
61
61
  const outputPath = options.output || "tasty.css";
62
62
  const resolvedOutputPath = path.resolve(outputPath);
63
63
  const injectImport = options.injectImport ?? true;
64
- if (injectImport) {
64
+ if (mode === "file" && injectImport) {
65
65
  const dir = path.dirname(resolvedOutputPath);
66
66
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
67
67
  if (!fs.existsSync(resolvedOutputPath)) fs.writeFileSync(resolvedOutputPath, "/* Generated by @tenphi/tasty/zero - DO NOT EDIT */\n");
@@ -86,10 +86,12 @@ var babel_default = declare((api, options) => {
86
86
  if (cached) resetConfig();
87
87
  configure(resolvedConfig);
88
88
  const newWriter = new CSSWriter(outputPath, { devMode });
89
- const tokenStyles = getGlobalConfigTokens();
90
- if (tokenStyles && Object.keys(tokenStyles).length > 0) {
91
- const result = extractStylesForSelector(":root", tokenStyles);
92
- if (result.css) newWriter.add(":root:tokens", result.css);
89
+ if (mode !== "inject") {
90
+ const tokenStyles = getGlobalConfigTokens();
91
+ if (tokenStyles && Object.keys(tokenStyles).length > 0) {
92
+ const result = extractStylesForSelector(":root", tokenStyles);
93
+ if (result.css) newWriter.add(":root:tokens", result.css);
94
+ }
93
95
  }
94
96
  writerCache.set(resolvedOutputPath, {
95
97
  writer: newWriter,
@@ -103,6 +105,14 @@ var babel_default = declare((api, options) => {
103
105
  const globalRegistry = entry.registry;
104
106
  const config = entry.config;
105
107
  const devMode = config.devMode ?? false;
108
+ let tokenCSS;
109
+ if (mode === "inject") {
110
+ const tokenStyles = getGlobalConfigTokens();
111
+ if (tokenStyles && Object.keys(tokenStyles).length > 0) {
112
+ const result = extractStylesForSelector(":root", tokenStyles);
113
+ if (result.css) tokenCSS = result.css;
114
+ }
115
+ }
106
116
  return {
107
117
  name: "tasty-zero",
108
118
  pre() {
@@ -113,7 +123,8 @@ var babel_default = declare((api, options) => {
113
123
  visitor: {
114
124
  ImportDeclaration(nodePath, state) {
115
125
  const source = nodePath.node.source.value;
116
- if (source === "@tenphi/tasty/static" || source.endsWith("/tasty/static")) if (injectImport) {
126
+ if (source === "@tenphi/tasty/static" || source.endsWith("/tasty/static")) if (mode === "inject") nodePath.replaceWith(t.importDeclaration([t.importSpecifier(t.identifier("_$i"), t.identifier("injectCSS"))], t.stringLiteral("@tenphi/tasty/static/inject")));
127
+ else if (injectImport) {
117
128
  let importPath = resolvedOutputPath;
118
129
  if (state.filename) {
119
130
  const sourceDir = path.dirname(state.filename);
@@ -130,9 +141,9 @@ var babel_default = declare((api, options) => {
130
141
  const args = path.node.arguments;
131
142
  if (args.length === 0) throw path.buildCodeFrameError("tastyStatic() requires at least one argument");
132
143
  const firstArg = args[0];
133
- if (t.isStringLiteral(firstArg)) handleSelectorMode(path, args, cssWriter, state.sourceFile, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
134
- else if (t.isObjectExpression(firstArg)) handleStylesMode(path, args, cssWriter, state, globalRegistry, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
135
- else if (t.isIdentifier(firstArg)) handleExtensionMode(path, args, cssWriter, state, globalRegistry, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
144
+ if (t.isStringLiteral(firstArg)) handleSelectorMode(path, args, cssWriter, mode, state.sourceFile, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
145
+ else if (t.isObjectExpression(firstArg)) handleStylesMode(path, args, cssWriter, state, globalRegistry, mode, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
146
+ else if (t.isIdentifier(firstArg)) handleExtensionMode(path, args, cssWriter, state, globalRegistry, mode, config.keyframes, config.autoPropertyTypes, config.fontFace, config.counterStyle);
136
147
  else throw path.buildCodeFrameError("tastyStatic() first argument must be an object (styles), identifier (base StaticStyle), or string (selector)");
137
148
  },
138
149
  VariableDeclarator(path, state) {
@@ -156,6 +167,16 @@ var babel_default = declare((api, options) => {
156
167
  }
157
168
  },
158
169
  post() {
170
+ if (mode === "inject") {
171
+ if (this._fileAddedCSS && tokenCSS) {
172
+ const program = this.file.ast.program;
173
+ const injectCall = createInjectCallAST(":root", tokenCSS);
174
+ let insertIndex = 0;
175
+ for (let i = 0; i < program.body.length; i++) if (t.isImportDeclaration(program.body[i])) insertIndex = i + 1;
176
+ program.body.splice(insertIndex, 0, t.expressionStatement(injectCall));
177
+ }
178
+ return;
179
+ }
159
180
  if (this._fileAddedCSS && cssWriter.size > 0) cssWriter.write();
160
181
  }
161
182
  };
@@ -185,30 +206,30 @@ function extractClassNameFromStaticStyleObject(node) {
185
206
  /**
186
207
  * Handle tastyStatic(styles) - returns StaticStyle object
187
208
  */
188
- function handleStylesMode(path, args, cssWriter, state, globalRegistry, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
209
+ function handleStylesMode(path, args, cssWriter, state, globalRegistry, mode, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
189
210
  const stylesArg = args[0];
190
211
  if (!t.isObjectExpression(stylesArg)) throw path.buildCodeFrameError("tastyStatic(styles) argument must be a static object literal");
191
212
  const styles = resolveRecipes(evaluateObjectExpression(stylesArg, path));
192
213
  const { keyframes, nameMap } = extractKeyframesFromStyles(styles, globalKeyframes);
193
- for (const kf of keyframes) cssWriter.add(kf.css, kf.css, state.sourceFile);
194
214
  const properties = extractPropertiesFromStyles(styles, { autoPropertyTypes });
195
- for (const prop of properties) cssWriter.add(prop.css, prop.css, state.sourceFile);
196
- for (const ff of extractFontFaceFromStyles(styles, globalFontFace)) cssWriter.add(ff.css, ff.css, state.sourceFile);
197
- for (const cs of extractCounterStyleFromStyles(styles, globalCounterStyle)) cssWriter.add(cs.css, cs.css, state.sourceFile);
215
+ const fontFaces = extractFontFaceFromStyles(styles, globalFontFace);
216
+ const counterStyles = extractCounterStyleFromStyles(styles, globalCounterStyle);
198
217
  const chunks = extractStylesWithChunks(styles);
199
- for (const chunk of chunks) {
200
- const css = nameMap.size > 0 ? replaceAnimationNamesInCSS(chunk.css, nameMap) : chunk.css;
201
- cssWriter.add(chunk.className, css, state.sourceFile);
202
- }
203
218
  const className = chunks.length > 0 ? chunks.map((c) => c.className).join(" ") : "";
204
219
  const staticStyleObject = createStaticStyleAST(className, styles);
205
- path.replaceWith(staticStyleObject);
220
+ if (mode === "inject") {
221
+ const injectCall = createInjectCallAST(className, collectAllCSS(keyframes, properties, fontFaces, counterStyles, chunks, nameMap));
222
+ path.replaceWith(t.sequenceExpression([injectCall, staticStyleObject]));
223
+ } else {
224
+ writeCSSToWriter(cssWriter, keyframes, properties, fontFaces, counterStyles, chunks, nameMap, state.sourceFile);
225
+ path.replaceWith(staticStyleObject);
226
+ }
206
227
  registerIfVariableDeclaration(path, className, styles, state, globalRegistry);
207
228
  }
208
229
  /**
209
230
  * Handle tastyStatic(base, styles) - extends base with additional styles
210
231
  */
211
- function handleExtensionMode(path, args, cssWriter, state, globalRegistry, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
232
+ function handleExtensionMode(path, args, cssWriter, state, globalRegistry, mode, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
212
233
  if (args.length < 2) throw path.buildCodeFrameError("tastyStatic(base, styles) requires two arguments");
213
234
  const baseArg = args[0];
214
235
  const stylesArg = args[1];
@@ -220,25 +241,25 @@ function handleExtensionMode(path, args, cssWriter, state, globalRegistry, globa
220
241
  const overrideStyles = evaluateObjectExpression(stylesArg, path);
221
242
  const mergedStyles = resolveRecipes(mergeStyles(baseEntry.styles, overrideStyles));
222
243
  const { keyframes, nameMap } = extractKeyframesFromStyles(mergedStyles, globalKeyframes);
223
- for (const kf of keyframes) cssWriter.add(kf.css, kf.css, state.sourceFile);
224
244
  const properties = extractPropertiesFromStyles(mergedStyles, { autoPropertyTypes });
225
- for (const prop of properties) cssWriter.add(prop.css, prop.css, state.sourceFile);
226
- for (const ff of extractFontFaceFromStyles(mergedStyles, globalFontFace)) cssWriter.add(ff.css, ff.css, state.sourceFile);
227
- for (const cs of extractCounterStyleFromStyles(mergedStyles, globalCounterStyle)) cssWriter.add(cs.css, cs.css, state.sourceFile);
245
+ const fontFaces = extractFontFaceFromStyles(mergedStyles, globalFontFace);
246
+ const counterStyles = extractCounterStyleFromStyles(mergedStyles, globalCounterStyle);
228
247
  const chunks = extractStylesWithChunks(mergedStyles);
229
- for (const chunk of chunks) {
230
- const css = nameMap.size > 0 ? replaceAnimationNamesInCSS(chunk.css, nameMap) : chunk.css;
231
- cssWriter.add(chunk.className, css, state.sourceFile);
232
- }
233
248
  const className = chunks.length > 0 ? chunks.map((c) => c.className).join(" ") : "";
234
249
  const staticStyleObject = createStaticStyleAST(className, mergedStyles);
235
- path.replaceWith(staticStyleObject);
250
+ if (mode === "inject") {
251
+ const injectCall = createInjectCallAST(className, collectAllCSS(keyframes, properties, fontFaces, counterStyles, chunks, nameMap));
252
+ path.replaceWith(t.sequenceExpression([injectCall, staticStyleObject]));
253
+ } else {
254
+ writeCSSToWriter(cssWriter, keyframes, properties, fontFaces, counterStyles, chunks, nameMap, state.sourceFile);
255
+ path.replaceWith(staticStyleObject);
256
+ }
236
257
  registerIfVariableDeclaration(path, className, mergedStyles, state, globalRegistry);
237
258
  }
238
259
  /**
239
260
  * Handle tastyStatic(selector, styles) - removes the call entirely
240
261
  */
241
- function handleSelectorMode(path, args, cssWriter, sourceFile, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
262
+ function handleSelectorMode(path, args, cssWriter, mode, sourceFile, globalKeyframes, autoPropertyTypes, globalFontFace, globalCounterStyle) {
242
263
  if (args.length < 2) throw path.buildCodeFrameError("tastyStatic(selector, styles) requires two arguments");
243
264
  const selectorArg = args[0];
244
265
  const stylesArg = args[1];
@@ -247,17 +268,60 @@ function handleSelectorMode(path, args, cssWriter, sourceFile, globalKeyframes,
247
268
  const selector = selectorArg.value;
248
269
  const styles = resolveRecipes(evaluateObjectExpression(stylesArg, path));
249
270
  const { keyframes, nameMap } = extractKeyframesFromStyles(styles, globalKeyframes);
250
- for (const kf of keyframes) cssWriter.add(kf.css, kf.css, sourceFile);
251
271
  const properties = extractPropertiesFromStyles(styles, { autoPropertyTypes });
252
- for (const prop of properties) cssWriter.add(prop.css, prop.css, sourceFile);
253
- for (const ff of extractFontFaceFromStyles(styles, globalFontFace)) cssWriter.add(ff.css, ff.css, sourceFile);
254
- for (const cs of extractCounterStyleFromStyles(styles, globalCounterStyle)) cssWriter.add(cs.css, cs.css, sourceFile);
272
+ const fontFaces = extractFontFaceFromStyles(styles, globalFontFace);
273
+ const counterStyles = extractCounterStyleFromStyles(styles, globalCounterStyle);
255
274
  const result = extractStylesForSelector(selector, styles);
256
- const css = nameMap.size > 0 ? replaceAnimationNamesInCSS(result.css, nameMap) : result.css;
257
- cssWriter.add(selector, css, sourceFile);
258
- const parent = path.parentPath;
259
- if (parent && t.isExpressionStatement(parent.node)) parent.remove();
260
- else path.replaceWith(t.identifier("undefined"));
275
+ const selectorCSS = nameMap.size > 0 ? replaceAnimationNamesInCSS(result.css, nameMap) : result.css;
276
+ if (mode === "inject") {
277
+ const cssParts = [];
278
+ for (const kf of keyframes) cssParts.push(kf.css);
279
+ for (const prop of properties) cssParts.push(prop.css);
280
+ for (const ff of fontFaces) cssParts.push(ff.css);
281
+ for (const cs of counterStyles) cssParts.push(cs.css);
282
+ cssParts.push(selectorCSS);
283
+ const injectCall = createInjectCallAST(selector, cssParts.join("\n"));
284
+ const parent = path.parentPath;
285
+ if (parent && t.isExpressionStatement(parent.node)) parent.replaceWith(t.expressionStatement(injectCall));
286
+ else path.replaceWith(injectCall);
287
+ } else {
288
+ writeCSSToWriter(cssWriter, keyframes, properties, fontFaces, counterStyles, [], nameMap, sourceFile);
289
+ cssWriter.add(selector, selectorCSS, sourceFile);
290
+ const parent = path.parentPath;
291
+ if (parent && t.isExpressionStatement(parent.node)) parent.remove();
292
+ else path.replaceWith(t.identifier("undefined"));
293
+ }
294
+ }
295
+ /**
296
+ * Collect all extracted CSS parts into a single string (for inject mode).
297
+ */
298
+ function collectAllCSS(keyframes, properties, fontFaces, counterStyles, chunks, nameMap) {
299
+ const parts = [];
300
+ for (const kf of keyframes) parts.push(kf.css);
301
+ for (const prop of properties) parts.push(prop.css);
302
+ for (const ff of fontFaces) parts.push(ff.css);
303
+ for (const cs of counterStyles) parts.push(cs.css);
304
+ for (const chunk of chunks) parts.push(nameMap.size > 0 ? replaceAnimationNamesInCSS(chunk.css, nameMap) : chunk.css);
305
+ return parts.join("\n");
306
+ }
307
+ /**
308
+ * Write all extracted CSS parts to a CSSWriter (for file mode).
309
+ */
310
+ function writeCSSToWriter(cssWriter, keyframes, properties, fontFaces, counterStyles, chunks, nameMap, sourceFile) {
311
+ for (const kf of keyframes) cssWriter.add(kf.css, kf.css, sourceFile);
312
+ for (const prop of properties) cssWriter.add(prop.css, prop.css, sourceFile);
313
+ for (const ff of fontFaces) cssWriter.add(ff.css, ff.css, sourceFile);
314
+ for (const cs of counterStyles) cssWriter.add(cs.css, cs.css, sourceFile);
315
+ for (const chunk of chunks) {
316
+ const css = nameMap.size > 0 ? replaceAnimationNamesInCSS(chunk.css, nameMap) : chunk.css;
317
+ cssWriter.add(chunk.className, css, sourceFile);
318
+ }
319
+ }
320
+ /**
321
+ * Create an `_$i(id, css)` call expression AST node for inject mode.
322
+ */
323
+ function createInjectCallAST(id, css) {
324
+ return t.callExpression(t.identifier("_$i"), [t.stringLiteral(id), t.stringLiteral(css)]);
261
325
  }
262
326
  /**
263
327
  * Create a StaticStyle object AST node
@@ -368,7 +432,7 @@ function replaceAnimationNamesInCSS(css, nameMap) {
368
432
  function escapeRegex(str) {
369
433
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
370
434
  }
371
-
372
435
  //#endregion
373
436
  export { clearWriterCache, babel_default as default };
437
+
374
438
  //# sourceMappingURL=babel.js.map