@tenphi/tasty 0.0.0-snapshot.1c5dff6 → 0.0.0-snapshot.1f1c00a

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 (331) hide show
  1. package/README.md +377 -238
  2. package/dist/babel-rZcEwPLM.d.ts +82 -0
  3. package/dist/chunks/async-storage-CG5HnUT5.js +20 -0
  4. package/dist/chunks/async-storage-CG5HnUT5.js.map +1 -0
  5. package/dist/chunks/build-config-CL4eRu2z.js +42 -0
  6. package/dist/chunks/build-config-CL4eRu2z.js.map +1 -0
  7. package/dist/chunks/collector-D2ARGT2L.js +208 -0
  8. package/dist/chunks/collector-D2ARGT2L.js.map +1 -0
  9. package/dist/chunks/config-engine-DEmmPPdp.js +272 -0
  10. package/dist/chunks/config-engine-DEmmPPdp.js.map +1 -0
  11. package/dist/chunks/css-definitions-DotDkIXt.js +876 -0
  12. package/dist/chunks/css-definitions-DotDkIXt.js.map +1 -0
  13. package/dist/chunks/css-resources-P7IBUUZs.js +142 -0
  14. package/dist/chunks/css-resources-P7IBUUZs.js.map +1 -0
  15. package/dist/chunks/debug-D8pg9RGI.js +502 -0
  16. package/dist/chunks/debug-D8pg9RGI.js.map +1 -0
  17. package/dist/chunks/dsl-DRhFWQzo.js +1378 -0
  18. package/dist/chunks/dsl-DRhFWQzo.js.map +1 -0
  19. package/dist/chunks/hydration-TRcz5vnk.js +41 -0
  20. package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
  21. package/dist/chunks/react-runtime-BpxlOTbO.js +562 -0
  22. package/dist/chunks/react-runtime-BpxlOTbO.js.map +1 -0
  23. package/dist/chunks/runtime-engine-CKFXUH-A.js +2416 -0
  24. package/dist/chunks/runtime-engine-CKFXUH-A.js.map +1 -0
  25. package/dist/chunks/runtime-state-BxaLhVdP.js +127 -0
  26. package/dist/chunks/runtime-state-BxaLhVdP.js.map +1 -0
  27. package/dist/{utils/merge-styles.js → chunks/shared-utils-y-HjvPN4.js} +61 -58
  28. package/dist/chunks/shared-utils-y-HjvPN4.js.map +1 -0
  29. package/dist/chunks/style-engine-BBbBuSkT.js +5260 -0
  30. package/dist/chunks/style-engine-BBbBuSkT.js.map +1 -0
  31. package/dist/{zero/extractor.js → chunks/zero-engine-gJrKBRiw.js} +126 -66
  32. package/dist/chunks/zero-engine-gJrKBRiw.js.map +1 -0
  33. package/dist/collector-w4mBj5gd.d.ts +164 -0
  34. package/dist/config-CZWCzBr6.d.ts +1349 -0
  35. package/dist/core/index.d.ts +5 -33
  36. package/dist/core/index.js +9 -26
  37. package/dist/index-G9JuwM9p.d.ts +1920 -0
  38. package/dist/index.d.ts +5 -48
  39. package/dist/index.js +11 -33
  40. package/dist/{utils/merge-styles.d.ts → merge-styles-VXwJ0Frr.d.ts} +3 -4
  41. package/dist/ssr/astro-client.d.ts +1 -0
  42. package/dist/ssr/astro-client.js +6 -0
  43. package/dist/ssr/astro-client.js.map +1 -0
  44. package/dist/ssr/astro-middleware-extract-static.d.ts +10 -0
  45. package/dist/ssr/astro-middleware-extract-static.js +9 -0
  46. package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
  47. package/dist/ssr/astro-middleware-extract.d.ts +10 -0
  48. package/dist/ssr/astro-middleware-extract.js +9 -0
  49. package/dist/ssr/astro-middleware-extract.js.map +1 -0
  50. package/dist/ssr/astro-middleware-static.d.ts +17 -0
  51. package/dist/ssr/astro-middleware-static.js +7 -0
  52. package/dist/ssr/astro-middleware-static.js.map +1 -0
  53. package/dist/ssr/astro-middleware.d.ts +18 -0
  54. package/dist/ssr/astro-middleware.js +7 -0
  55. package/dist/ssr/astro-middleware.js.map +1 -0
  56. package/dist/ssr/astro.d.ts +127 -0
  57. package/dist/ssr/astro.js +221 -0
  58. package/dist/ssr/astro.js.map +1 -0
  59. package/dist/ssr/index.d.ts +36 -0
  60. package/dist/ssr/index.js +10 -0
  61. package/dist/ssr/index.js.map +1 -0
  62. package/dist/ssr/next-config.d.ts +64 -0
  63. package/dist/ssr/next-config.js +128 -0
  64. package/dist/ssr/next-config.js.map +1 -0
  65. package/dist/ssr/next.d.ts +46 -0
  66. package/dist/ssr/next.js +60 -0
  67. package/dist/ssr/next.js.map +1 -0
  68. package/dist/static/index.d.ts +90 -5
  69. package/dist/static/index.js +31 -4
  70. package/dist/static/index.js.map +1 -0
  71. package/dist/static/inject.d.ts +4 -0
  72. package/dist/static/inject.js +17 -0
  73. package/dist/static/inject.js.map +1 -0
  74. package/dist/types-JEY8PVYP.d.ts +1567 -0
  75. package/dist/zero/babel.d.ts +2 -113
  76. package/dist/zero/babel.js +230 -106
  77. package/dist/zero/babel.js.map +1 -1
  78. package/dist/zero/index.d.ts +80 -3
  79. package/dist/zero/index.js +2 -4
  80. package/dist/zero/next.d.ts +57 -33
  81. package/dist/zero/next.js +93 -59
  82. package/dist/zero/next.js.map +1 -1
  83. package/docs/README.md +39 -0
  84. package/docs/adoption.md +323 -0
  85. package/docs/ai-agents.md +250 -0
  86. package/docs/comparison.md +448 -0
  87. package/docs/configuration.md +684 -56
  88. package/docs/debug.md +170 -343
  89. package/docs/design-system.md +455 -0
  90. package/docs/dsl.md +1065 -0
  91. package/docs/getting-started.md +240 -0
  92. package/docs/injector.md +159 -53
  93. package/docs/methodology.md +648 -0
  94. package/docs/migration-v3.md +285 -0
  95. package/docs/pipeline.md +765 -0
  96. package/docs/plugins.md +353 -0
  97. package/docs/react-api.md +683 -0
  98. package/docs/runtime-benchmarks.md +388 -0
  99. package/docs/ssr.md +610 -0
  100. package/docs/styles.md +211 -63
  101. package/docs/tasty-static.md +273 -67
  102. package/package.json +182 -58
  103. package/tasty.config.ts +6 -0
  104. package/dist/_virtual/_rolldown/runtime.js +0 -8
  105. package/dist/chunks/cacheKey.js +0 -70
  106. package/dist/chunks/cacheKey.js.map +0 -1
  107. package/dist/chunks/definitions.d.ts +0 -37
  108. package/dist/chunks/definitions.js +0 -260
  109. package/dist/chunks/definitions.js.map +0 -1
  110. package/dist/chunks/renderChunk.js +0 -61
  111. package/dist/chunks/renderChunk.js.map +0 -1
  112. package/dist/config.d.ts +0 -288
  113. package/dist/config.js +0 -403
  114. package/dist/config.js.map +0 -1
  115. package/dist/debug.d.ts +0 -204
  116. package/dist/debug.js +0 -733
  117. package/dist/debug.js.map +0 -1
  118. package/dist/hooks/useGlobalStyles.d.ts +0 -27
  119. package/dist/hooks/useGlobalStyles.js +0 -56
  120. package/dist/hooks/useGlobalStyles.js.map +0 -1
  121. package/dist/hooks/useKeyframes.d.ts +0 -56
  122. package/dist/hooks/useKeyframes.js +0 -54
  123. package/dist/hooks/useKeyframes.js.map +0 -1
  124. package/dist/hooks/useProperty.d.ts +0 -79
  125. package/dist/hooks/useProperty.js +0 -91
  126. package/dist/hooks/useProperty.js.map +0 -1
  127. package/dist/hooks/useRawCSS.d.ts +0 -53
  128. package/dist/hooks/useRawCSS.js +0 -28
  129. package/dist/hooks/useRawCSS.js.map +0 -1
  130. package/dist/hooks/useStyles.d.ts +0 -40
  131. package/dist/hooks/useStyles.js +0 -169
  132. package/dist/hooks/useStyles.js.map +0 -1
  133. package/dist/injector/index.d.ts +0 -157
  134. package/dist/injector/index.js +0 -154
  135. package/dist/injector/index.js.map +0 -1
  136. package/dist/injector/injector.d.ts +0 -139
  137. package/dist/injector/injector.js +0 -417
  138. package/dist/injector/injector.js.map +0 -1
  139. package/dist/injector/sheet-manager.d.ts +0 -135
  140. package/dist/injector/sheet-manager.js +0 -728
  141. package/dist/injector/sheet-manager.js.map +0 -1
  142. package/dist/injector/types.d.ts +0 -144
  143. package/dist/keyframes/index.js +0 -206
  144. package/dist/keyframes/index.js.map +0 -1
  145. package/dist/parser/classify.js +0 -319
  146. package/dist/parser/classify.js.map +0 -1
  147. package/dist/parser/const.js +0 -33
  148. package/dist/parser/const.js.map +0 -1
  149. package/dist/parser/lru.js +0 -109
  150. package/dist/parser/lru.js.map +0 -1
  151. package/dist/parser/parser.d.ts +0 -25
  152. package/dist/parser/parser.js +0 -116
  153. package/dist/parser/parser.js.map +0 -1
  154. package/dist/parser/tokenizer.js +0 -69
  155. package/dist/parser/tokenizer.js.map +0 -1
  156. package/dist/parser/types.d.ts +0 -51
  157. package/dist/parser/types.js +0 -46
  158. package/dist/parser/types.js.map +0 -1
  159. package/dist/pipeline/conditions.d.ts +0 -134
  160. package/dist/pipeline/conditions.js +0 -406
  161. package/dist/pipeline/conditions.js.map +0 -1
  162. package/dist/pipeline/exclusive.js +0 -231
  163. package/dist/pipeline/exclusive.js.map +0 -1
  164. package/dist/pipeline/index.d.ts +0 -53
  165. package/dist/pipeline/index.js +0 -660
  166. package/dist/pipeline/index.js.map +0 -1
  167. package/dist/pipeline/materialize.js +0 -856
  168. package/dist/pipeline/materialize.js.map +0 -1
  169. package/dist/pipeline/parseStateKey.d.ts +0 -15
  170. package/dist/pipeline/parseStateKey.js +0 -451
  171. package/dist/pipeline/parseStateKey.js.map +0 -1
  172. package/dist/pipeline/simplify.js +0 -516
  173. package/dist/pipeline/simplify.js.map +0 -1
  174. package/dist/pipeline/warnings.js +0 -18
  175. package/dist/pipeline/warnings.js.map +0 -1
  176. package/dist/plugins/okhsl-plugin.d.ts +0 -35
  177. package/dist/plugins/okhsl-plugin.js +0 -371
  178. package/dist/plugins/okhsl-plugin.js.map +0 -1
  179. package/dist/plugins/types.d.ts +0 -69
  180. package/dist/properties/index.js +0 -221
  181. package/dist/properties/index.js.map +0 -1
  182. package/dist/properties/property-type-resolver.d.ts +0 -24
  183. package/dist/properties/property-type-resolver.js +0 -83
  184. package/dist/properties/property-type-resolver.js.map +0 -1
  185. package/dist/states/index.d.ts +0 -49
  186. package/dist/states/index.js +0 -416
  187. package/dist/states/index.js.map +0 -1
  188. package/dist/static/tastyStatic.d.ts +0 -46
  189. package/dist/static/tastyStatic.js +0 -31
  190. package/dist/static/tastyStatic.js.map +0 -1
  191. package/dist/static/types.d.ts +0 -49
  192. package/dist/static/types.js +0 -24
  193. package/dist/static/types.js.map +0 -1
  194. package/dist/styles/align.d.ts +0 -15
  195. package/dist/styles/align.js +0 -14
  196. package/dist/styles/align.js.map +0 -1
  197. package/dist/styles/border.d.ts +0 -25
  198. package/dist/styles/border.js +0 -114
  199. package/dist/styles/border.js.map +0 -1
  200. package/dist/styles/color.d.ts +0 -14
  201. package/dist/styles/color.js +0 -23
  202. package/dist/styles/color.js.map +0 -1
  203. package/dist/styles/createStyle.js +0 -77
  204. package/dist/styles/createStyle.js.map +0 -1
  205. package/dist/styles/dimension.js +0 -97
  206. package/dist/styles/dimension.js.map +0 -1
  207. package/dist/styles/display.d.ts +0 -37
  208. package/dist/styles/display.js +0 -67
  209. package/dist/styles/display.js.map +0 -1
  210. package/dist/styles/fade.d.ts +0 -15
  211. package/dist/styles/fade.js +0 -58
  212. package/dist/styles/fade.js.map +0 -1
  213. package/dist/styles/fill.d.ts +0 -42
  214. package/dist/styles/fill.js +0 -52
  215. package/dist/styles/fill.js.map +0 -1
  216. package/dist/styles/flow.d.ts +0 -16
  217. package/dist/styles/flow.js +0 -12
  218. package/dist/styles/flow.js.map +0 -1
  219. package/dist/styles/gap.d.ts +0 -31
  220. package/dist/styles/gap.js +0 -37
  221. package/dist/styles/gap.js.map +0 -1
  222. package/dist/styles/height.d.ts +0 -17
  223. package/dist/styles/height.js +0 -20
  224. package/dist/styles/height.js.map +0 -1
  225. package/dist/styles/index.d.ts +0 -2
  226. package/dist/styles/index.js +0 -9
  227. package/dist/styles/index.js.map +0 -1
  228. package/dist/styles/inset.d.ts +0 -52
  229. package/dist/styles/inset.js +0 -150
  230. package/dist/styles/inset.js.map +0 -1
  231. package/dist/styles/justify.d.ts +0 -15
  232. package/dist/styles/justify.js +0 -14
  233. package/dist/styles/justify.js.map +0 -1
  234. package/dist/styles/list.d.ts +0 -16
  235. package/dist/styles/list.js +0 -98
  236. package/dist/styles/list.js.map +0 -1
  237. package/dist/styles/margin.d.ts +0 -24
  238. package/dist/styles/margin.js +0 -104
  239. package/dist/styles/margin.js.map +0 -1
  240. package/dist/styles/outline.d.ts +0 -29
  241. package/dist/styles/outline.js +0 -65
  242. package/dist/styles/outline.js.map +0 -1
  243. package/dist/styles/padding.d.ts +0 -24
  244. package/dist/styles/padding.js +0 -104
  245. package/dist/styles/padding.js.map +0 -1
  246. package/dist/styles/predefined.d.ts +0 -73
  247. package/dist/styles/predefined.js +0 -241
  248. package/dist/styles/predefined.js.map +0 -1
  249. package/dist/styles/preset.d.ts +0 -47
  250. package/dist/styles/preset.js +0 -126
  251. package/dist/styles/preset.js.map +0 -1
  252. package/dist/styles/radius.d.ts +0 -14
  253. package/dist/styles/radius.js +0 -51
  254. package/dist/styles/radius.js.map +0 -1
  255. package/dist/styles/scrollbar.d.ts +0 -23
  256. package/dist/styles/scrollbar.js +0 -34
  257. package/dist/styles/scrollbar.js.map +0 -1
  258. package/dist/styles/shadow.d.ts +0 -14
  259. package/dist/styles/shadow.js +0 -24
  260. package/dist/styles/shadow.js.map +0 -1
  261. package/dist/styles/styledScrollbar.d.ts +0 -47
  262. package/dist/styles/styledScrollbar.js +0 -38
  263. package/dist/styles/styledScrollbar.js.map +0 -1
  264. package/dist/styles/transition.d.ts +0 -14
  265. package/dist/styles/transition.js +0 -158
  266. package/dist/styles/transition.js.map +0 -1
  267. package/dist/styles/types.d.ts +0 -498
  268. package/dist/styles/width.d.ts +0 -17
  269. package/dist/styles/width.js +0 -20
  270. package/dist/styles/width.js.map +0 -1
  271. package/dist/tasty.d.ts +0 -982
  272. package/dist/tasty.js +0 -206
  273. package/dist/tasty.js.map +0 -1
  274. package/dist/tokens/typography.d.ts +0 -19
  275. package/dist/tokens/typography.js +0 -237
  276. package/dist/tokens/typography.js.map +0 -1
  277. package/dist/types.d.ts +0 -184
  278. package/dist/utils/cache-wrapper.js +0 -26
  279. package/dist/utils/cache-wrapper.js.map +0 -1
  280. package/dist/utils/case-converter.js +0 -8
  281. package/dist/utils/case-converter.js.map +0 -1
  282. package/dist/utils/colors.d.ts +0 -5
  283. package/dist/utils/colors.js +0 -9
  284. package/dist/utils/colors.js.map +0 -1
  285. package/dist/utils/css-types.d.ts +0 -7
  286. package/dist/utils/dotize.d.ts +0 -26
  287. package/dist/utils/dotize.js +0 -122
  288. package/dist/utils/dotize.js.map +0 -1
  289. package/dist/utils/filter-base-props.d.ts +0 -15
  290. package/dist/utils/filter-base-props.js +0 -45
  291. package/dist/utils/filter-base-props.js.map +0 -1
  292. package/dist/utils/get-display-name.d.ts +0 -7
  293. package/dist/utils/get-display-name.js +0 -10
  294. package/dist/utils/get-display-name.js.map +0 -1
  295. package/dist/utils/hsl-to-rgb.js +0 -38
  296. package/dist/utils/hsl-to-rgb.js.map +0 -1
  297. package/dist/utils/is-dev-env.js +0 -19
  298. package/dist/utils/is-dev-env.js.map +0 -1
  299. package/dist/utils/is-valid-element-type.js +0 -15
  300. package/dist/utils/is-valid-element-type.js.map +0 -1
  301. package/dist/utils/merge-styles.js.map +0 -1
  302. package/dist/utils/mod-attrs.d.ts +0 -8
  303. package/dist/utils/mod-attrs.js +0 -21
  304. package/dist/utils/mod-attrs.js.map +0 -1
  305. package/dist/utils/okhsl-to-rgb.js +0 -296
  306. package/dist/utils/okhsl-to-rgb.js.map +0 -1
  307. package/dist/utils/process-tokens.d.ts +0 -31
  308. package/dist/utils/process-tokens.js +0 -171
  309. package/dist/utils/process-tokens.js.map +0 -1
  310. package/dist/utils/resolve-recipes.d.ts +0 -17
  311. package/dist/utils/resolve-recipes.js +0 -147
  312. package/dist/utils/resolve-recipes.js.map +0 -1
  313. package/dist/utils/selector-transform.js +0 -32
  314. package/dist/utils/selector-transform.js.map +0 -1
  315. package/dist/utils/string.js +0 -8
  316. package/dist/utils/string.js.map +0 -1
  317. package/dist/utils/styles.d.ts +0 -177
  318. package/dist/utils/styles.js +0 -730
  319. package/dist/utils/styles.js.map +0 -1
  320. package/dist/utils/typography.d.ts +0 -36
  321. package/dist/utils/typography.js +0 -53
  322. package/dist/utils/typography.js.map +0 -1
  323. package/dist/utils/warnings.d.ts +0 -16
  324. package/dist/utils/warnings.js +0 -16
  325. package/dist/utils/warnings.js.map +0 -1
  326. package/dist/zero/css-writer.d.ts +0 -45
  327. package/dist/zero/css-writer.js +0 -74
  328. package/dist/zero/css-writer.js.map +0 -1
  329. package/dist/zero/extractor.d.ts +0 -24
  330. package/dist/zero/extractor.js.map +0 -1
  331. package/docs/usage.md +0 -695
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astro.js","names":[],"sources":["../../src/ssr/astro-extraction.ts","../../src/ssr/astro.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { ServerStyleArtifact } from './collector';\nimport { findUnsafeCSSResource } from './css-resources';\n\nconst METADATA_START = '<template data-tasty-extract>';\nconst METADATA_END = '</template>';\n\ninterface ExtractablePage {\n path: string;\n html: string;\n artifacts: ServerStyleArtifact[];\n styleStart: number;\n replacementEnd: number;\n styleOpen: string;\n}\n\nexport function createExtractionMetadata(\n artifacts: ServerStyleArtifact[],\n): string {\n const encoded = Buffer.from(JSON.stringify(artifacts), 'utf8').toString(\n 'base64',\n );\n return `${METADATA_START}${encoded}${METADATA_END}`;\n}\n\nfunction parseExtractablePage(\n path: string,\n html: string,\n): ExtractablePage | null {\n const metadataStart = html.indexOf(METADATA_START);\n if (metadataStart === -1) return null;\n\n const metadataContentStart = metadataStart + METADATA_START.length;\n const metadataEnd = html.indexOf(METADATA_END, metadataContentStart);\n if (metadataEnd === -1) return null;\n\n const encoded = html.slice(metadataContentStart, metadataEnd);\n let artifacts: ServerStyleArtifact[];\n try {\n artifacts = JSON.parse(\n Buffer.from(encoded, 'base64').toString('utf8'),\n ) as ServerStyleArtifact[];\n } catch {\n return null;\n }\n\n const styleStart = html.lastIndexOf('<style data-tasty-ssr', metadataStart);\n if (styleStart === -1) return null;\n const styleOpenEnd = html.indexOf('>', styleStart);\n const styleEnd = html.indexOf('</style>', styleOpenEnd + 1);\n if (\n styleOpenEnd === -1 ||\n styleEnd === -1 ||\n styleEnd + '</style>'.length !== metadataStart\n ) {\n return null;\n }\n\n return {\n path,\n html,\n artifacts,\n styleStart,\n replacementEnd: metadataEnd + METADATA_END.length,\n styleOpen: html.slice(styleStart, styleOpenEnd + 1),\n };\n}\n\nasync function findHTMLFiles(dir: string): Promise<string[]> {\n const paths: string[] = [];\n const entries = await readdir(dir, { withFileTypes: true });\n entries.sort((a, b) => a.name.localeCompare(b.name));\n for (const entry of entries) {\n const path = join(dir, entry.name);\n if (entry.isDirectory()) {\n paths.push(...(await findHTMLFiles(path)));\n } else if (entry.isFile() && entry.name.endsWith('.html')) {\n paths.push(path);\n }\n }\n return paths;\n}\n\nfunction crossOriginAssetsPrefix(\n assetsPrefix?: string | Record<string, string>,\n site?: URL,\n): string | null {\n if (!assetsPrefix) return null;\n const prefix =\n typeof assetsPrefix === 'string'\n ? assetsPrefix\n : assetsPrefix.css || assetsPrefix.fallback;\n if (!/^(?:[a-z][a-z\\d+.-]*:|\\/\\/)/i.test(prefix)) return null;\n if (!site) return prefix;\n\n try {\n const prefixURL = prefix.startsWith('//')\n ? new URL(`${site.protocol}${prefix}`)\n : new URL(prefix);\n return prefixURL.origin === site.origin ? null : prefix;\n } catch {\n return prefix;\n }\n}\n\nfunction validateExtractedURLs(\n pages: ExtractablePage[],\n assetsPrefix?: string | Record<string, string>,\n site?: URL,\n): void {\n const externalPrefix = crossOriginAssetsPrefix(assetsPrefix, site);\n for (const page of pages) {\n for (const artifact of page.artifacts) {\n const unsafe = findUnsafeCSSResource(\n artifact.css,\n externalPrefix !== null,\n );\n if (unsafe) {\n const reason = unsafe.rootRelative\n ? `root-relative CSS URL \"${unsafe.url}\" would resolve against the external assetsPrefix \"${externalPrefix}\" instead of the page origin`\n : `page-relative CSS URL \"${unsafe.url}\" cannot preserve its target`;\n throw new Error(\n `[Tasty] Astro CSS extraction cannot preserve ${reason} in ${page.path} (${artifact.kind} artifact ${artifact.id}). Use an absolute URL or a data URL${externalPrefix ? '' : ', or a root-relative URL such as url(/path/to/asset)'}.`,\n );\n }\n }\n }\n}\n\n/** Find artifacts emitted by every styled page, in the first page's order. */\nfunction selectSharedArtifacts(\n pages: ExtractablePage[],\n): ServerStyleArtifact[] {\n if (pages.length < 2) return [];\n\n const source = pages[0].artifacts;\n const otherIds = pages\n .slice(1)\n .map((page) => new Set(page.artifacts.map(({ id }) => id)));\n\n return source.filter(({ id }) => otherIds.every((ids) => ids.has(id)));\n}\n\nfunction stylesheetHref(\n base: string,\n assets: string,\n filename: string,\n assetsPrefix?: string | Record<string, string>,\n): string {\n const assetsPath = assets.replace(/^\\/+|\\/+$/g, '');\n if (assetsPrefix) {\n const prefix =\n typeof assetsPrefix === 'string'\n ? assetsPrefix\n : assetsPrefix.css || assetsPrefix.fallback;\n return `${prefix.replace(/\\/+$/g, '')}/${assetsPath}/${filename}`;\n }\n\n const basePath = base === '/' ? '' : `/${base.replace(/^\\/+|\\/+$/g, '')}`;\n return `${basePath}/${assetsPath}/${filename}`;\n}\n\nfunction stylesheetLink(page: ExtractablePage, href: string): string {\n const nonceAttr = page.styleOpen.match(/\\snonce=\"[^\"]*\"/)?.[0] ?? '';\n return `<link rel=\"stylesheet\" href=\"${href}\" data-tasty-ssr${nonceAttr}>`;\n}\n\nfunction transformPage(page: ExtractablePage, hrefs: string[]): string {\n const replacement = hrefs.map((href) => stylesheetLink(page, href)).join('');\n\n return (\n page.html.slice(0, page.styleStart) +\n replacement +\n page.html.slice(page.replacementEnd)\n );\n}\n\nasync function writeStylesheet(\n assetDir: string,\n scope: 'shared' | 'page',\n artifacts: ServerStyleArtifact[],\n): Promise<string | null> {\n if (artifacts.length === 0) return null;\n\n const css = artifacts.map(({ css }) => css).join('\\n');\n const hash = createHash('sha256').update(css).digest('hex').slice(0, 12);\n const filename = `tasty.${scope}.${hash}.css`;\n await writeFile(join(assetDir, filename), css);\n return filename;\n}\n\nexport async function extractAstroCSS(options: {\n dir: URL;\n base: string;\n assets: string;\n assetsPrefix?: string | Record<string, string>;\n site?: URL;\n}): Promise<void> {\n const outputDir = fileURLToPath(options.dir);\n const paths = await findHTMLFiles(outputDir);\n const pages = (\n await Promise.all(\n paths.map(async (path) =>\n parseExtractablePage(path, await readFile(path, 'utf8')),\n ),\n )\n ).filter((page): page is ExtractablePage => page !== null);\n if (pages.length === 0) return;\n validateExtractedURLs(pages, options.assetsPrefix, options.site);\n\n const shared = selectSharedArtifacts(pages);\n const assetDir = join(outputDir, options.assets);\n await mkdir(assetDir, { recursive: true });\n const sharedFilename = await writeStylesheet(assetDir, 'shared', shared);\n const sharedHref = sharedFilename\n ? stylesheetHref(\n options.base,\n options.assets,\n sharedFilename,\n options.assetsPrefix,\n )\n : null;\n const sharedIds = new Set(shared.map(({ id }) => id));\n\n for (const page of pages) {\n const remainder = page.artifacts.filter(({ id }) => !sharedIds.has(id));\n const pageFilename = await writeStylesheet(assetDir, 'page', remainder);\n const hrefs = sharedHref ? [sharedHref] : [];\n if (pageFilename) {\n hrefs.push(\n stylesheetHref(\n options.base,\n options.assets,\n pageFilename,\n options.assetsPrefix,\n ),\n );\n }\n await writeFile(page.path, transformPage(page, hrefs));\n }\n}\n","/**\n * Astro integration for Tasty SSR.\n *\n * Provides:\n * - tastyIntegration() — Astro Integration API (recommended)\n * - tastyMiddleware() — manual middleware for advanced composition\n *\n * Import from '@tenphi/tasty/ssr/astro'.\n */\n\nimport { getRuntimeConfigState } from '../config-resources';\nimport { getSSRCollector, runWithCollector } from './async-storage';\nimport { createExtractionMetadata, extractAstroCSS } from './astro-extraction';\nimport { ServerStyleCollector } from './collector';\nimport { registerSSRCollectorGetterGlobal } from './ssr-collector-ref';\n\n// Wire up ALS-based collector discovery so computeStyles() can find\n// the collector set by tastyMiddleware's runWithCollector().\n// Uses globalThis so the getter is visible across Astro's separate\n// module graphs (middleware vs page components).\nregisterSSRCollectorGetterGlobal(getSSRCollector);\n\nexport interface TastyMiddlewareOptions {\n /**\n * Whether to embed the class-list script for client hydration.\n * Set to false to skip class transfer (e.g. for CSP restrictions).\n * Without it, client components may re-inject CSS that already exists\n * in server-rendered `<style>` tags. Default: true.\n */\n transferCache?: boolean;\n}\n\ninterface InternalTastyMiddlewareOptions extends TastyMiddlewareOptions {\n extractionMetadata?: boolean;\n}\n\n/**\n * Create an Astro middleware that collects Tasty styles during SSR.\n *\n * All React components rendered during the request will have their\n * computeStyles() calls captured by the collector via AsyncLocalStorage.\n * After rendering, the middleware injects the collected CSS into </head>.\n *\n * @example Manual middleware setup\n * ```ts\n * // src/middleware.ts\n * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';\n * export const onRequest = tastyMiddleware();\n * ```\n *\n * @example Composing with other middleware\n * ```ts\n * // src/middleware.ts\n * import { sequence } from 'astro:middleware';\n * import { tastyMiddleware } from '@tenphi/tasty/ssr/astro';\n *\n * export const onRequest = sequence(\n * tastyMiddleware(),\n * myOtherMiddleware,\n * );\n * ```\n */\nexport function tastyMiddleware(options?: TastyMiddlewareOptions) {\n const internalOptions = options as InternalTastyMiddlewareOptions | undefined;\n return async (\n context: { isPrerendered?: boolean },\n next: () => Promise<Response>,\n ): Promise<Response> => {\n const transferCache = options?.transferCache ?? true;\n const extractionMetadata =\n internalOptions?.extractionMetadata === true &&\n context.isPrerendered === true;\n const collector = new ServerStyleCollector();\n\n // Run the entire request — including body stream consumption — inside\n // the ALS context so that components rendering lazily during stream\n // reads can still find the collector via getSSRCollector().\n type Rendered =\n | { response: Response }\n | { html: string | null; status: number; headers: Headers };\n\n const rendered = await runWithCollector<Promise<Rendered>>(\n collector,\n async (): Promise<Rendered> => {\n const response = await next();\n const body = response.body;\n\n // Only process HTML responses. Reading a non-HTML body (e.g. an\n // image, font, or JSON endpoint) as UTF-8 text corrupts binary\n // payloads: every byte >= 0x80 is decoded to U+FFFD and re-encoded\n // as EF BF BD. Pass anything that isn't HTML straight through.\n const contentType = response.headers.get('content-type') ?? '';\n if (!body || !contentType.includes('text/html')) {\n return { response };\n }\n\n const reader = body.pipeThrough(new TextDecoderStream()).getReader();\n const parts: string[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n parts.push(value);\n }\n return {\n html: parts.join(''),\n status: response.status,\n headers: response.headers,\n };\n },\n );\n\n // Non-HTML responses are returned untouched to avoid corrupting\n // binary payloads.\n if ('response' in rendered) {\n return rendered.response;\n }\n\n if (!rendered.html) {\n return new Response(null, {\n status: rendered.status,\n headers: rendered.headers,\n });\n }\n\n let { html } = rendered;\n\n const css = collector.getCSS();\n if (!css) {\n return new Response(html, {\n status: rendered.status,\n headers: rendered.headers,\n });\n }\n\n const nonce = getRuntimeConfigState()?.nonce;\n const nonceAttr = nonce ? ` nonce=\"${nonce}\"` : '';\n const styleTag = `<style data-tasty-ssr${nonceAttr}>${css}</style>`;\n const metadataTag = extractionMetadata\n ? createExtractionMetadata(collector.getArtifacts())\n : '';\n\n let cacheTag = '';\n if (transferCache) {\n const classNames = collector.getRenderedClassNames();\n if (classNames.length > 0) {\n const classListJSON = classNames.map((n) => `\"${n}\"`).join(',');\n cacheTag = `<script${nonceAttr}>(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})</script>`;\n }\n }\n\n const injection = styleTag + metadataTag + cacheTag;\n const idx = html.indexOf('</head>');\n if (idx !== -1) {\n html = html.slice(0, idx) + injection + html.slice(idx);\n } else {\n html = injection + html;\n }\n\n const headers = new Headers(rendered.headers);\n headers.delete('content-length');\n\n return new Response(html, {\n status: rendered.status,\n headers,\n });\n };\n}\n\n// ============================================================================\n// Astro Integration API\n// ============================================================================\n\n/**\n * Package subpaths of the middleware entrypoints registered by\n * `tastyIntegration()`.\n *\n * These must be bare specifiers rather than\n * `new URL('./astro-middleware.js', import.meta.url)`. The bundler is free to\n * hoist `tastyIntegration` into a shared chunk at a different directory depth\n * than `dist/ssr/`, which makes a relative URL resolve to a file that does not\n * exist and breaks the build for every consumer. A package subpath is resolved\n * by the consumer through our `exports` map, so it never depends on the\n * chunk layout.\n *\n * There are separate entrypoints instead of one parameterised entrypoint because\n * `addMiddleware()` cannot pass options: the integration runs when the Astro\n * config is loaded, while the middleware module is evaluated in the server\n * runtime — a different process for built output — so module-level state set\n * by the integration is not visible to the middleware.\n */\nconst MIDDLEWARE_ENTRYPOINT = '@tenphi/tasty/ssr/astro-middleware';\nconst MIDDLEWARE_ENTRYPOINT_STATIC =\n '@tenphi/tasty/ssr/astro-middleware-static';\nconst MIDDLEWARE_ENTRYPOINT_EXTRACT =\n '@tenphi/tasty/ssr/astro-middleware-extract';\nconst MIDDLEWARE_ENTRYPOINT_EXTRACT_STATIC =\n '@tenphi/tasty/ssr/astro-middleware-extract-static';\n\nexport interface TastyIntegrationCSSOptions {\n /**\n * CSS delivery mode. Extraction only applies to prerendered builds.\n * Extracted CSS preserves resource URLs verbatim, so use absolute URLs or\n * data URLs. Root-relative URLs are also supported unless `assetsPrefix`\n * sends CSS to an external origin. The build rejects resource URLs whose\n * targets would change after extraction.\n */\n mode?: 'inline' | 'extract';\n}\n\nexport interface TastyIntegrationOptions {\n /**\n * Enable island hydration support.\n *\n * When `true` (default): injects a client hydration script via\n * `injectScript('before-hydration')` and sets `transferCache: true`\n * on the middleware. Islands skip the style pipeline during hydration.\n *\n * When `false`: no client JS is shipped and `transferCache` is set\n * to `false`. Use this for fully static sites without `client:*`\n * directives.\n */\n islands?: boolean;\n /** Configure inline or build-wide extracted CSS delivery. */\n css?: TastyIntegrationCSSOptions;\n}\n\n/**\n * Astro integration that automatically sets up Tasty SSR.\n *\n * Registers middleware for cross-component CSS deduplication and\n * optionally injects a client hydration script for island support.\n *\n * @example Basic setup (with islands)\n * ```ts\n * // astro.config.mjs\n * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';\n *\n * export default defineConfig({\n * integrations: [tastyIntegration()],\n * });\n * ```\n *\n * @example Static-only (no client JS)\n * ```ts\n * // astro.config.mjs\n * import { tastyIntegration } from '@tenphi/tasty/ssr/astro';\n *\n * export default defineConfig({\n * integrations: [tastyIntegration({ islands: false })],\n * });\n * ```\n */\nexport function tastyIntegration(options?: TastyIntegrationOptions) {\n const { islands = true } = options ?? {};\n const cssMode = options?.css?.mode ?? 'inline';\n let base = '/';\n let assets = '_astro';\n let assetsPrefix: string | Record<string, string> | undefined;\n let site: URL | undefined;\n\n return {\n name: '@tenphi/tasty',\n hooks: {\n 'astro:config:setup': ({\n addMiddleware,\n injectScript,\n }: {\n addMiddleware: (middleware: {\n entrypoint: string | URL;\n order: 'pre' | 'post';\n }) => void;\n injectScript: (\n stage: 'head-inline' | 'before-hydration' | 'page' | 'page-ssr',\n content: string,\n ) => void;\n }) => {\n addMiddleware({\n entrypoint:\n cssMode === 'extract'\n ? islands\n ? MIDDLEWARE_ENTRYPOINT_EXTRACT\n : MIDDLEWARE_ENTRYPOINT_EXTRACT_STATIC\n : islands\n ? MIDDLEWARE_ENTRYPOINT\n : MIDDLEWARE_ENTRYPOINT_STATIC,\n order: 'pre',\n });\n\n if (islands) {\n injectScript(\n 'before-hydration',\n `import \"@tenphi/tasty/ssr/astro-client\";`,\n );\n }\n },\n 'astro:config:done': ({\n config,\n }: {\n config: {\n base?: string;\n site?: URL;\n build?: {\n assets?: string;\n assetsPrefix?: string | Record<string, string>;\n };\n };\n }) => {\n base = config.base ?? '/';\n assets = config.build?.assets ?? '_astro';\n assetsPrefix = config.build?.assetsPrefix;\n site = config.site;\n },\n 'astro:build:done': async ({ dir }: { dir: URL }) => {\n if (cssMode !== 'extract') return;\n await extractAstroCSS({ dir, base, assets, assetsPrefix, site });\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { i as ServerStyleArtifactKind, n as createServerStyleCollector, r as ServerStyleArtifact, t as ServerStyleCollector } from "../collector-w4mBj5gd.js";
2
+ //#region src/ssr/async-storage.d.ts
3
+ /**
4
+ * Run a function with a ServerStyleCollector bound to the current
5
+ * async context. All useStyles() calls within `fn` (and any async
6
+ * continuations) will find this collector via getSSRCollector().
7
+ */
8
+ export declare function runWithCollector<T>(collector: ServerStyleCollector, fn: () => T): T;
9
+ /**
10
+ * Retrieve the ServerStyleCollector bound to the current async context.
11
+ * Returns null when called outside of runWithCollector() or on the client.
12
+ */
13
+ export declare function getSSRCollector(): ServerStyleCollector | null;
14
+ //#endregion
15
+ //#region src/ssr/hydrate.d.ts
16
+ /**
17
+ * Client-side cache hydration for SSR/RSC.
18
+ *
19
+ * Registers class names rendered on the server with the client injector. If
20
+ * the runtime is not initialized yet, registration is deferred until its first
21
+ * use. With hash-based naming, only the class name list needs to cross the
22
+ * wire — no cache keys or counters.
23
+ */
24
+ /**
25
+ * Hydrate the client-side style registry from the server's class name list.
26
+ *
27
+ * Call this before ReactDOM.hydrateRoot() or ensure it runs before
28
+ * any tasty() component renders on the client.
29
+ *
30
+ * When called without arguments, reads the class list from `window.__TASTY__`
31
+ * (populated by inline scripts emitted during SSR/RSC streaming).
32
+ */
33
+ export declare function hydrateTastyClasses(classes?: string[]): void;
34
+ //#endregion
35
+ export { type ServerStyleArtifact, type ServerStyleArtifactKind, ServerStyleCollector, createServerStyleCollector };
36
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { r as registerSSRCollectorGetterGlobal } from "../chunks/runtime-state-BxaLhVdP.js";
2
+ import { n as createServerStyleCollector, t as ServerStyleCollector } from "../chunks/collector-D2ARGT2L.js";
3
+ import { n as runWithCollector, t as getSSRCollector } from "../chunks/async-storage-CG5HnUT5.js";
4
+ import { t as hydrateTastyClasses } from "../chunks/hydration-TRcz5vnk.js";
5
+ //#region src/ssr/index.ts
6
+ registerSSRCollectorGetterGlobal(getSSRCollector);
7
+ //#endregion
8
+ export { ServerStyleCollector, createServerStyleCollector, getSSRCollector, hydrateTastyClasses, runWithCollector };
9
+
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/ssr/index.ts"],"sourcesContent":["/**\n * SSR entry point for @tenphi/tasty.\n *\n * Provides the core SSR infrastructure: ServerStyleCollector,\n * AsyncLocalStorage integration, and cache hydration.\n *\n * Import from '@tenphi/tasty/ssr'.\n */\n\n// Core collector\nexport { ServerStyleCollector, createServerStyleCollector } from './collector';\nexport type { ServerStyleArtifact, ServerStyleArtifactKind } from './collector';\n\n// AsyncLocalStorage integration for Astro / generic frameworks\nexport { runWithCollector, getSSRCollector } from './async-storage';\n\n// Client-side cache hydration\nexport { hydrateTastyClasses } from './hydrate';\n\n// Register the ALS getter so hooks can find the collector\n// without importing 'node:async_hooks' in the browser bundle.\n// Uses globalThis so the getter is visible across separate module graphs.\nimport { getSSRCollector } from './async-storage';\nimport { registerSSRCollectorGetterGlobal } from './ssr-collector-ref';\n\nregisterSSRCollectorGetterGlobal(getSSRCollector);\n"],"mappings":";;;;;AAyBA,iCAAiC,eAAe"}
@@ -0,0 +1,64 @@
1
+ import { t as TastyConfig } from "../config-CZWCzBr6.js";
2
+ //#region src/ssr/next-config.d.ts
3
+ interface NextConfig {
4
+ basePath?: string;
5
+ env?: Record<string, string | undefined>;
6
+ headers?: () => Promise<NextHeader[]>;
7
+ output?: string;
8
+ [key: string]: unknown;
9
+ }
10
+ interface NextHeader {
11
+ source: string;
12
+ headers: {
13
+ key: string;
14
+ value: string;
15
+ }[];
16
+ [key: string]: unknown;
17
+ }
18
+ export interface TastyNextOptions {
19
+ /**
20
+ * Next.js project directory. Useful when the build command runs from a
21
+ * monorepo root rather than the app directory.
22
+ *
23
+ * @default process.cwd()
24
+ */
25
+ rootDir?: string;
26
+ /**
27
+ * Tasty configuration used to generate the shared stylesheet.
28
+ * `config` takes precedence when both config sources are provided.
29
+ */
30
+ config?: TastyConfig;
31
+ /**
32
+ * Project-relative path to a TypeScript/JavaScript module whose default
33
+ * export is a Tasty configuration object.
34
+ *
35
+ * @example './app/tasty.config.ts'
36
+ */
37
+ configFile?: string;
38
+ /**
39
+ * Filesystem directory for generated stylesheets. When it is inside the
40
+ * project's `public` directory, its public URL is inferred automatically.
41
+ *
42
+ * @default 'public/_tasty'
43
+ */
44
+ outputDir?: string;
45
+ /**
46
+ * Root-relative URL path corresponding to `outputDir`. This is required
47
+ * when `outputDir` is outside the project's `public` directory.
48
+ * The Next.js `basePath` is prepended automatically.
49
+ *
50
+ * @default inferred from outputDir
51
+ * @example '/assets/tasty'
52
+ */
53
+ publicPath?: string;
54
+ /** Whether to generate and register the shared stylesheet. @default true */
55
+ enabled?: boolean;
56
+ }
57
+ /**
58
+ * Add a cacheable, content-hashed stylesheet for Tasty configuration globals.
59
+ * Route-dependent component and hook styles continue to stream through
60
+ * `TastyRegistry` so App Router navigation and Suspense remain correct.
61
+ */
62
+ export declare function withTastyNext(options: TastyNextOptions): (nextConfig?: NextConfig) => NextConfig;
63
+ //#endregion
64
+ //# sourceMappingURL=next-config.d.ts.map
@@ -0,0 +1,128 @@
1
+ import { L as DEFAULT_PROPERTIES, U as parseFunctionName, V as formatFunctionRule, a as formatPropertyCSS, b as formatFontFaceRule, h as formatCounterStyleRule, n as formatGlobalRules, t as createServerStyleArtifact, v as fontFaceContentHash } from "../chunks/css-definitions-DotDkIXt.js";
2
+ import { b as renderStyles } from "../chunks/style-engine-BBbBuSkT.js";
3
+ import { n as functionsForExtraction, r as resetZeroConfig, t as configureZero } from "../chunks/build-config-CL4eRu2z.js";
4
+ import { t as findUnsafeCSSResource } from "../chunks/css-resources-P7IBUUZs.js";
5
+ import { createHash } from "node:crypto";
6
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { createRequire } from "node:module";
8
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
9
+ //#region src/ssr/next-config.ts
10
+ const ENV_KEY = "TASTY_NEXT_SHARED_CSS_HREF";
11
+ const packageRequire = createRequire(import.meta.url);
12
+ function loadConfig(projectDir, options) {
13
+ if (options.config) return options.config;
14
+ if (!options.configFile) throw new Error("[Tasty] withTastyNext() requires either `config` or `configFile`.");
15
+ const configPath = resolve(projectDir, options.configFile);
16
+ let jitiPath;
17
+ try {
18
+ jitiPath = packageRequire.resolve("jiti");
19
+ } catch (error) {
20
+ if (error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND") throw new Error("[Tasty] `configFile` requires the optional `jiti` package. Install `jiti` or pass the imported config through `config` instead.", { cause: error });
21
+ throw error;
22
+ }
23
+ const { createJiti } = packageRequire(jitiPath);
24
+ const loaded = createJiti(projectDir, { moduleCache: false })(configPath);
25
+ return loaded && typeof loaded === "object" && "default" in loaded ? loaded.default : loaded;
26
+ }
27
+ function collectSharedCSS(config) {
28
+ resetZeroConfig();
29
+ try {
30
+ const resolved = configureZero(config);
31
+ const { normalized } = resolved;
32
+ const artifacts = [];
33
+ const append = (kind, key, css) => {
34
+ if (!css) return;
35
+ artifacts.push(createServerStyleArtifact(kind, key, css, artifacts.length));
36
+ };
37
+ for (const [token, definition] of Object.entries({
38
+ ...DEFAULT_PROPERTIES,
39
+ ...normalized.properties
40
+ })) append("property", `__prop:${token}`, formatPropertyCSS(token, definition));
41
+ for (const [family, input] of Object.entries(normalized.fontFaces)) {
42
+ const descriptors = Array.isArray(input) ? input : [input];
43
+ for (const descriptor of descriptors) append("font-face", fontFaceContentHash(family, descriptor), formatFontFaceRule(family, descriptor));
44
+ }
45
+ for (const [name, descriptors] of Object.entries(normalized.counterStyles)) append("counter-style", name, formatCounterStyleRule(name, descriptors));
46
+ for (const [name, definition] of Object.entries(functionsForExtraction(resolved) ?? {})) append("function", parseFunctionName(name), formatFunctionRule(name, definition));
47
+ if (Object.keys(normalized.tokens).length > 0) {
48
+ const rules = renderStyles(normalized.tokens, ":root");
49
+ append("global", "__global:tokens", formatGlobalRules(rules));
50
+ }
51
+ for (const [selector, styles] of Object.entries(normalized.globalStyles)) {
52
+ if (Object.keys(styles).length === 0) continue;
53
+ const rules = renderStyles(styles, selector);
54
+ append("global", `__global:styles:${selector}`, formatGlobalRules(rules));
55
+ }
56
+ for (const artifact of artifacts) {
57
+ const unsafe = findUnsafeCSSResource(artifact.css, false);
58
+ if (unsafe) throw new Error(`[Tasty] Next.js shared CSS extraction cannot preserve page-relative CSS URL "${unsafe.url}" in ${artifact.kind} artifact ${artifact.id}. Use an absolute URL, a data URL, or a root-relative URL such as url(/path/to/asset).`);
59
+ }
60
+ return artifacts.map(({ css }) => css).join("\n");
61
+ } finally {
62
+ resetZeroConfig();
63
+ }
64
+ }
65
+ function inferPublicPath(projectDir, outputDir) {
66
+ const publicDir = resolve(projectDir, "public");
67
+ const relativePath = relative(publicDir, outputDir);
68
+ if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) throw new Error("[Tasty] `publicPath` is required when `outputDir` is outside the Next.js public directory.");
69
+ return `/${relativePath.split(sep).filter(Boolean).join("/")}`;
70
+ }
71
+ function normalizeURLPath(path, option) {
72
+ if (!path.startsWith("/") || path.startsWith("//") || path.includes("?") || path.includes("#") || path.includes("\\")) throw new Error(`[Tasty] \`${option}\` must be a root-relative URL path.`);
73
+ let end = path.length;
74
+ while (end > 0 && path[end - 1] === "/") end--;
75
+ return path.slice(0, end);
76
+ }
77
+ function writeSharedCSS(outputDir, css) {
78
+ mkdirSync(outputDir, { recursive: true });
79
+ const filename = `tasty.shared.${createHash("sha256").update(css).digest("hex").slice(0, 12)}.css`;
80
+ const outputPath = join(outputDir, filename);
81
+ let existing;
82
+ try {
83
+ existing = readFileSync(outputPath, "utf8");
84
+ } catch {}
85
+ if (existing !== css) writeFileSync(outputPath, css);
86
+ return filename;
87
+ }
88
+ function withTastyNext(options) {
89
+ return (nextConfig = {}) => {
90
+ if (options.enabled === false) return nextConfig;
91
+ const projectDir = resolve(process.cwd(), options.rootDir ?? ".");
92
+ const outputDir = resolve(projectDir, options.outputDir ?? "public/_tasty");
93
+ const publicPath = normalizeURLPath(options.publicPath ?? inferPublicPath(projectDir, outputDir), "publicPath");
94
+ const basePath = normalizeURLPath(nextConfig.basePath || "/", "basePath");
95
+ const css = collectSharedCSS(loadConfig(projectDir, options));
96
+ if (!css) return nextConfig;
97
+ const filename = writeSharedCSS(outputDir, css);
98
+ const href = `${basePath}${publicPath}/${filename}`;
99
+ const headerSource = `${publicPath}/${filename}`;
100
+ const existingEnv = nextConfig.env ?? {};
101
+ const existingHeaders = nextConfig.headers;
102
+ if (existingEnv[ENV_KEY] && existingEnv[ENV_KEY] !== href) throw new Error(`[Tasty] Next.js env key ${ENV_KEY} is reserved by withTastyNext().`);
103
+ const generatedConfig = {
104
+ ...nextConfig,
105
+ env: {
106
+ ...existingEnv,
107
+ [ENV_KEY]: href
108
+ }
109
+ };
110
+ if (nextConfig.output === "export") return generatedConfig;
111
+ return {
112
+ ...generatedConfig,
113
+ async headers() {
114
+ return [...existingHeaders ? await existingHeaders() : [], {
115
+ source: headerSource,
116
+ headers: [{
117
+ key: "Cache-Control",
118
+ value: "public, max-age=31536000, immutable"
119
+ }]
120
+ }];
121
+ }
122
+ };
123
+ };
124
+ }
125
+ //#endregion
126
+ export { withTastyNext };
127
+
128
+ //# sourceMappingURL=next-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next-config.js","names":[],"sources":["../../src/ssr/next-config.ts"],"sourcesContent":["/**\n * Next.js configuration wrapper for extracting Tasty's configured global CSS.\n *\n * Import this Node-only module from `next.config.ts`. The client-safe registry\n * remains available from `@tenphi/tasty/ssr/next`.\n */\n\nimport { createHash } from 'node:crypto';\nimport { mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path';\nimport type { createJiti as createJitiType } from 'jiti';\n\nimport type { TastyConfig } from '../config';\nimport { formatCounterStyleRule } from '../counter-style';\nimport { fontFaceContentHash, formatFontFaceRule } from '../font-face';\nimport { formatFunctionRule, parseFunctionName } from '../functions';\nimport { renderStyles } from '../pipeline';\nimport type { StyleResult } from '../pipeline';\nimport { DEFAULT_PROPERTIES } from '../properties/defaults';\nimport {\n configureZero,\n functionsForExtraction,\n resetZeroConfig,\n} from '../zero/config';\nimport { createServerStyleArtifact } from './artifacts';\nimport type { ServerStyleArtifact, ServerStyleArtifactKind } from './artifacts';\nimport { findUnsafeCSSResource } from './css-resources';\nimport { formatGlobalRules } from './format-global-rules';\nimport { formatPropertyCSS } from './format-property';\n\nconst ENV_KEY = 'TASTY_NEXT_SHARED_CSS_HREF';\nconst packageRequire = createRequire(import.meta.url);\n\ninterface NextConfig {\n basePath?: string;\n env?: Record<string, string | undefined>;\n headers?: () => Promise<NextHeader[]>;\n output?: string;\n [key: string]: unknown;\n}\n\ninterface NextHeader {\n source: string;\n headers: { key: string; value: string }[];\n [key: string]: unknown;\n}\n\nexport interface TastyNextOptions {\n /**\n * Next.js project directory. Useful when the build command runs from a\n * monorepo root rather than the app directory.\n *\n * @default process.cwd()\n */\n rootDir?: string;\n\n /**\n * Tasty configuration used to generate the shared stylesheet.\n * `config` takes precedence when both config sources are provided.\n */\n config?: TastyConfig;\n\n /**\n * Project-relative path to a TypeScript/JavaScript module whose default\n * export is a Tasty configuration object.\n *\n * @example './app/tasty.config.ts'\n */\n configFile?: string;\n\n /**\n * Filesystem directory for generated stylesheets. When it is inside the\n * project's `public` directory, its public URL is inferred automatically.\n *\n * @default 'public/_tasty'\n */\n outputDir?: string;\n\n /**\n * Root-relative URL path corresponding to `outputDir`. This is required\n * when `outputDir` is outside the project's `public` directory.\n * The Next.js `basePath` is prepended automatically.\n *\n * @default inferred from outputDir\n * @example '/assets/tasty'\n */\n publicPath?: string;\n\n /** Whether to generate and register the shared stylesheet. @default true */\n enabled?: boolean;\n}\n\nfunction loadConfig(\n projectDir: string,\n options: TastyNextOptions,\n): TastyConfig {\n if (options.config) return options.config;\n if (!options.configFile) {\n throw new Error(\n '[Tasty] withTastyNext() requires either `config` or `configFile`.',\n );\n }\n\n const configPath = resolve(projectDir, options.configFile);\n let jitiPath: string;\n try {\n jitiPath = packageRequire.resolve('jiti');\n } catch (error) {\n if (\n error instanceof Error &&\n 'code' in error &&\n error.code === 'MODULE_NOT_FOUND'\n ) {\n throw new Error(\n '[Tasty] `configFile` requires the optional `jiti` package. Install `jiti` or pass the imported config through `config` instead.',\n { cause: error },\n );\n }\n throw error;\n }\n const { createJiti } = packageRequire(jitiPath) as {\n createJiti: typeof createJitiType;\n };\n const jiti = createJiti(projectDir, { moduleCache: false });\n const loaded = jiti(configPath) as TastyConfig | { default: TastyConfig };\n\n return loaded && typeof loaded === 'object' && 'default' in loaded\n ? loaded.default\n : loaded;\n}\n\nfunction collectSharedCSS(config: TastyConfig): string {\n resetZeroConfig();\n try {\n const resolved = configureZero(config);\n const { normalized } = resolved;\n const artifacts: ServerStyleArtifact[] = [];\n const append = (\n kind: ServerStyleArtifactKind,\n key: string,\n css: string,\n ) => {\n if (!css) return;\n\n artifacts.push(\n createServerStyleArtifact(kind, key, css, artifacts.length),\n );\n };\n\n for (const [token, definition] of Object.entries({\n ...DEFAULT_PROPERTIES,\n ...normalized.properties,\n })) {\n append(\n 'property',\n `__prop:${token}`,\n formatPropertyCSS(token, definition),\n );\n }\n\n for (const [family, input] of Object.entries(normalized.fontFaces)) {\n const descriptors = Array.isArray(input) ? input : [input];\n for (const descriptor of descriptors) {\n append(\n 'font-face',\n fontFaceContentHash(family, descriptor),\n formatFontFaceRule(family, descriptor),\n );\n }\n }\n\n for (const [name, descriptors] of Object.entries(\n normalized.counterStyles,\n )) {\n append('counter-style', name, formatCounterStyleRule(name, descriptors));\n }\n\n for (const [name, definition] of Object.entries(\n functionsForExtraction(resolved) ?? {},\n )) {\n append(\n 'function',\n parseFunctionName(name),\n formatFunctionRule(name, definition),\n );\n }\n\n if (Object.keys(normalized.tokens).length > 0) {\n const rules = renderStyles(normalized.tokens, ':root') as StyleResult[];\n append('global', '__global:tokens', formatGlobalRules(rules));\n }\n\n for (const [selector, styles] of Object.entries(normalized.globalStyles)) {\n if (Object.keys(styles).length === 0) continue;\n const rules = renderStyles(styles, selector) as StyleResult[];\n append('global', `__global:styles:${selector}`, formatGlobalRules(rules));\n }\n\n for (const artifact of artifacts) {\n const unsafe = findUnsafeCSSResource(artifact.css, false);\n if (unsafe) {\n throw new Error(\n `[Tasty] Next.js shared CSS extraction cannot preserve page-relative CSS URL \"${unsafe.url}\" in ${artifact.kind} artifact ${artifact.id}. Use an absolute URL, a data URL, or a root-relative URL such as url(/path/to/asset).`,\n );\n }\n }\n\n return artifacts.map(({ css }) => css).join('\\n');\n } finally {\n resetZeroConfig();\n }\n}\n\nfunction inferPublicPath(projectDir: string, outputDir: string): string {\n const publicDir = resolve(projectDir, 'public');\n const relativePath = relative(publicDir, outputDir);\n if (\n relativePath === '..' ||\n relativePath.startsWith(`..${sep}`) ||\n isAbsolute(relativePath)\n ) {\n throw new Error(\n '[Tasty] `publicPath` is required when `outputDir` is outside the Next.js public directory.',\n );\n }\n\n return `/${relativePath.split(sep).filter(Boolean).join('/')}`;\n}\n\nfunction normalizeURLPath(path: string, option: string): string {\n if (\n !path.startsWith('/') ||\n path.startsWith('//') ||\n path.includes('?') ||\n path.includes('#') ||\n path.includes('\\\\')\n ) {\n throw new Error(`[Tasty] \\`${option}\\` must be a root-relative URL path.`);\n }\n\n let end = path.length;\n while (end > 0 && path[end - 1] === '/') end--;\n return path.slice(0, end);\n}\n\nfunction writeSharedCSS(outputDir: string, css: string): string {\n mkdirSync(outputDir, { recursive: true });\n const hash = createHash('sha256').update(css).digest('hex').slice(0, 12);\n const filename = `tasty.shared.${hash}.css`;\n\n const outputPath = join(outputDir, filename);\n let existing: string | undefined;\n try {\n existing = readFileSync(outputPath, 'utf8');\n } catch {\n // The file does not exist yet (or is unreadable); write it below.\n }\n if (existing !== css) writeFileSync(outputPath, css);\n\n return filename;\n}\n\n/**\n * Add a cacheable, content-hashed stylesheet for Tasty configuration globals.\n * Route-dependent component and hook styles continue to stream through\n * `TastyRegistry` so App Router navigation and Suspense remain correct.\n */\nexport function withTastyNext(options: TastyNextOptions) {\n return (nextConfig: NextConfig = {}): NextConfig => {\n if (options.enabled === false) return nextConfig;\n\n const projectDir = resolve(process.cwd(), options.rootDir ?? '.');\n const outputDir = resolve(projectDir, options.outputDir ?? 'public/_tasty');\n const publicPath = normalizeURLPath(\n options.publicPath ?? inferPublicPath(projectDir, outputDir),\n 'publicPath',\n );\n const basePath = normalizeURLPath(nextConfig.basePath || '/', 'basePath');\n const css = collectSharedCSS(loadConfig(projectDir, options));\n\n if (!css) return nextConfig;\n\n const filename = writeSharedCSS(outputDir, css);\n const href = `${basePath}${publicPath}/${filename}`;\n const headerSource = `${publicPath}/${filename}`;\n const existingEnv = nextConfig.env ?? {};\n const existingHeaders = nextConfig.headers;\n if (existingEnv[ENV_KEY] && existingEnv[ENV_KEY] !== href) {\n throw new Error(\n `[Tasty] Next.js env key ${ENV_KEY} is reserved by withTastyNext().`,\n );\n }\n\n const generatedConfig: NextConfig = {\n ...nextConfig,\n env: {\n ...existingEnv,\n [ENV_KEY]: href,\n },\n };\n\n // `headers()` is not emitted by Next's static export. Avoid introducing an\n // unsupported-config warning; the content hash still makes the asset safe\n // for the static host to cache immutably.\n if (nextConfig.output === 'export') return generatedConfig;\n\n return {\n ...generatedConfig,\n async headers() {\n const headers = existingHeaders ? await existingHeaders() : [];\n return [\n ...headers,\n {\n source: headerSource,\n headers: [\n {\n key: 'Cache-Control',\n value: 'public, max-age=31536000, immutable',\n },\n ],\n },\n ];\n },\n };\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { t as ServerStyleCollector } from "../collector-w4mBj5gd.js";
2
+ import { ReactNode } from "react";
3
+ //#region src/ssr/next.d.ts
4
+ export interface TastyRegistryProps {
5
+ children: ReactNode;
6
+ /**
7
+ * Whether to embed the class-list script for client hydration.
8
+ * Set to false to skip class transfer (e.g. for CSP restrictions).
9
+ * Without it, client components may re-inject CSS that already exists
10
+ * in server-rendered `<style>` tags. Default: true.
11
+ */
12
+ transferCache?: boolean;
13
+ /**
14
+ * URL of a stylesheet containing Tasty's configured global CSS.
15
+ * `withTastyNext()` sets this automatically. Pass false to disable it for
16
+ * this registry, or a URL to use a manually generated stylesheet.
17
+ */
18
+ sharedStylesheet?: string | false;
19
+ }
20
+ /**
21
+ * Next.js App Router registry for Tasty SSR.
22
+ *
23
+ * Wraps the component tree with a ServerStyleCollector and flushes
24
+ * collected CSS into the HTML stream via useServerInsertedHTML.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * // app/tasty-registry.tsx
29
+ * 'use client';
30
+ * import { TastyRegistry } from '@tenphi/tasty/ssr/next';
31
+ * export default function TastyStyleRegistry({ children }) {
32
+ * return <TastyRegistry>{children}</TastyRegistry>;
33
+ * }
34
+ *
35
+ * // app/layout.tsx
36
+ * import TastyStyleRegistry from './tasty-registry';
37
+ * export default function RootLayout({ children }) {
38
+ * return <html><body>
39
+ * <TastyStyleRegistry>{children}</TastyStyleRegistry>
40
+ * </body></html>;
41
+ * }
42
+ * ```
43
+ */
44
+ export declare function TastyRegistry({ children, transferCache, sharedStylesheet }: TastyRegistryProps): import("react").FunctionComponentElement<import("react").ProviderProps<ServerStyleCollector | null>>;
45
+ //#endregion
46
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { n as registerSSRCollectorGetter, u as getRuntimeConfigState } from "../chunks/runtime-state-BxaLhVdP.js";
3
+ import { u as getTastySSRContext } from "../chunks/react-runtime-BpxlOTbO.js";
4
+ import { t as ServerStyleCollector } from "../chunks/collector-D2ARGT2L.js";
5
+ import { t as hydrateTastyClasses } from "../chunks/hydration-TRcz5vnk.js";
6
+ import { Fragment, createElement, useState } from "react";
7
+ import { useServerInsertedHTML } from "next/navigation";
8
+ //#region src/ssr/next.ts
9
+ if (typeof window !== "undefined") hydrateTastyClasses();
10
+ function TastyRegistry({ children, transferCache = true, sharedStylesheet = process.env.TASTY_NEXT_SHARED_CSS_HREF }) {
11
+ const isClient = typeof window !== "undefined";
12
+ const [collector] = useState(() => {
13
+ if (isClient) return null;
14
+ const instance = new ServerStyleCollector();
15
+ registerSSRCollectorGetter(() => instance);
16
+ if (sharedStylesheet) {
17
+ instance.collectInternals();
18
+ instance.flushCSS();
19
+ }
20
+ return instance;
21
+ });
22
+ const [streamState] = useState(() => ({ sharedStylesheetFlushed: false }));
23
+ const nonce = getRuntimeConfigState()?.nonce;
24
+ useServerInsertedHTML(() => {
25
+ if (!collector) return null;
26
+ const css = collector.flushCSS();
27
+ const classNames = collector.getRenderedClassNames();
28
+ let linkEl = null;
29
+ if (sharedStylesheet && !streamState.sharedStylesheetFlushed) {
30
+ streamState.sharedStylesheetFlushed = true;
31
+ linkEl = createElement("link", {
32
+ key: "tasty-shared-styles",
33
+ rel: "stylesheet",
34
+ href: sharedStylesheet,
35
+ "data-tasty-ssr": "",
36
+ nonce
37
+ });
38
+ }
39
+ if (!css) return linkEl;
40
+ const styleEl = createElement("style", {
41
+ key: "tasty-ssr-styles",
42
+ "data-tasty-ssr": "",
43
+ nonce,
44
+ dangerouslySetInnerHTML: { __html: css }
45
+ });
46
+ if (!transferCache || classNames.length === 0) return linkEl ? createElement(Fragment, null, linkEl, styleEl) : styleEl;
47
+ const classListJSON = classNames.map((n) => `"${n}"`).join(",");
48
+ const scriptEl = createElement("script", {
49
+ key: "tasty-ssr-cache",
50
+ nonce,
51
+ dangerouslySetInnerHTML: { __html: `(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})` }
52
+ });
53
+ return createElement(Fragment, null, linkEl, styleEl, scriptEl);
54
+ });
55
+ return createElement(getTastySSRContext().Provider, { value: collector }, children);
56
+ }
57
+ //#endregion
58
+ export { TastyRegistry };
59
+
60
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","names":[],"sources":["../../src/ssr/next.ts"],"sourcesContent":["/**\n * Next.js integration for Tasty SSR.\n *\n * Provides TastyRegistry for App Router (streaming via useServerInsertedHTML).\n *\n * Import from '@tenphi/tasty/ssr/next'.\n */\n\n'use client';\n\n/// <reference path=\"./next-navigation.d.ts\" />\n\nimport { createElement, Fragment, useState, type ReactNode } from 'react';\nimport { useServerInsertedHTML } from 'next/navigation';\n\nimport { getRuntimeConfigState } from '../config-resources';\nimport { ServerStyleCollector } from './collector';\nimport { getTastySSRContext } from './context';\nimport { hydrateTastyClasses } from './hydrate';\nimport { registerSSRCollectorGetter } from './ssr-collector-ref';\n\n// Auto-hydrate on module load (client only).\n// Reads the class name list from `window.__TASTY__` populated by streaming scripts.\nif (typeof window !== 'undefined') {\n hydrateTastyClasses();\n}\n\nexport interface TastyRegistryProps {\n children: ReactNode;\n /**\n * Whether to embed the class-list script for client hydration.\n * Set to false to skip class transfer (e.g. for CSP restrictions).\n * Without it, client components may re-inject CSS that already exists\n * in server-rendered `<style>` tags. Default: true.\n */\n transferCache?: boolean;\n /**\n * URL of a stylesheet containing Tasty's configured global CSS.\n * `withTastyNext()` sets this automatically. Pass false to disable it for\n * this registry, or a URL to use a manually generated stylesheet.\n */\n sharedStylesheet?: string | false;\n}\n\n/**\n * Next.js App Router registry for Tasty SSR.\n *\n * Wraps the component tree with a ServerStyleCollector and flushes\n * collected CSS into the HTML stream via useServerInsertedHTML.\n *\n * @example\n * ```tsx\n * // app/tasty-registry.tsx\n * 'use client';\n * import { TastyRegistry } from '@tenphi/tasty/ssr/next';\n * export default function TastyStyleRegistry({ children }) {\n * return <TastyRegistry>{children}</TastyRegistry>;\n * }\n *\n * // app/layout.tsx\n * import TastyStyleRegistry from './tasty-registry';\n * export default function RootLayout({ children }) {\n * return <html><body>\n * <TastyStyleRegistry>{children}</TastyStyleRegistry>\n * </body></html>;\n * }\n * ```\n */\nexport function TastyRegistry({\n children,\n transferCache = true,\n sharedStylesheet = process.env.TASTY_NEXT_SHARED_CSS_HREF,\n}: TastyRegistryProps) {\n const isClient = typeof window !== 'undefined';\n\n const [collector] = useState(() => {\n if (isClient) return null;\n\n const instance = new ServerStyleCollector();\n\n registerSSRCollectorGetter(() => instance);\n\n // The generated stylesheet already contains configured global artifacts.\n // Mark that first batch as flushed so only request-specific styles stream.\n if (sharedStylesheet) {\n instance.collectInternals();\n instance.flushCSS();\n }\n\n return instance;\n });\n const [streamState] = useState(() => ({ sharedStylesheetFlushed: false }));\n const nonce = getRuntimeConfigState()?.nonce;\n\n useServerInsertedHTML(() => {\n if (!collector) return null;\n\n const css = collector.flushCSS();\n const classNames = collector.getRenderedClassNames();\n\n let linkEl = null;\n if (sharedStylesheet && !streamState.sharedStylesheetFlushed) {\n streamState.sharedStylesheetFlushed = true;\n linkEl = createElement('link', {\n key: 'tasty-shared-styles',\n rel: 'stylesheet',\n href: sharedStylesheet,\n 'data-tasty-ssr': '',\n nonce,\n });\n }\n\n if (!css) return linkEl;\n\n const styleEl = createElement('style', {\n key: 'tasty-ssr-styles',\n 'data-tasty-ssr': '',\n nonce,\n dangerouslySetInnerHTML: { __html: css },\n });\n\n if (!transferCache || classNames.length === 0) {\n return linkEl ? createElement(Fragment, null, linkEl, styleEl) : styleEl;\n }\n\n const classListJSON = classNames.map((n) => `\"${n}\"`).join(',');\n\n const scriptEl = createElement('script', {\n key: 'tasty-ssr-cache',\n nonce,\n dangerouslySetInnerHTML: {\n __html: `(window.__TASTY__=window.__TASTY__||[]).push(${classListJSON})`,\n },\n });\n\n return createElement(Fragment, null, linkEl, styleEl, scriptEl);\n });\n\n return createElement(\n getTastySSRContext().Provider,\n { value: collector },\n children,\n );\n}\n"],"mappings":";;;;;;;;AAuBA,IAAI,OAAO,WAAW,aACpB,oBAAoB;AA4CtB,SAAgB,cAAc,EAC5B,UACA,gBAAgB,MAChB,mBAAmB,QAAQ,IAAI,8BACV;CACrB,MAAM,WAAW,OAAO,WAAW;CAEnC,MAAM,CAAC,aAAa,eAAe;EACjC,IAAI,UAAU,OAAO;EAErB,MAAM,WAAW,IAAI,qBAAqB;EAE1C,iCAAiC,QAAQ;EAIzC,IAAI,kBAAkB;GACpB,SAAS,iBAAiB;GAC1B,SAAS,SAAS;EACpB;EAEA,OAAO;CACT,CAAC;CACD,MAAM,CAAC,eAAe,gBAAgB,EAAE,yBAAyB,MAAM,EAAE;CACzE,MAAM,QAAQ,sBAAsB,CAAC,EAAE;CAEvC,4BAA4B;EAC1B,IAAI,CAAC,WAAW,OAAO;EAEvB,MAAM,MAAM,UAAU,SAAS;EAC/B,MAAM,aAAa,UAAU,sBAAsB;EAEnD,IAAI,SAAS;EACb,IAAI,oBAAoB,CAAC,YAAY,yBAAyB;GAC5D,YAAY,0BAA0B;GACtC,SAAS,cAAc,QAAQ;IAC7B,KAAK;IACL,KAAK;IACL,MAAM;IACN,kBAAkB;IAClB;GACF,CAAC;EACH;EAEA,IAAI,CAAC,KAAK,OAAO;EAEjB,MAAM,UAAU,cAAc,SAAS;GACrC,KAAK;GACL,kBAAkB;GAClB;GACA,yBAAyB,EAAE,QAAQ,IAAI;EACzC,CAAC;EAED,IAAI,CAAC,iBAAiB,WAAW,WAAW,GAC1C,OAAO,SAAS,cAAc,UAAU,MAAM,QAAQ,OAAO,IAAI;EAGnE,MAAM,gBAAgB,WAAW,KAAK,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG;EAE9D,MAAM,WAAW,cAAc,UAAU;GACvC,KAAK;GACL;GACA,yBAAyB,EACvB,QAAQ,gDAAgD,cAAc,GACxE;EACF,CAAC;EAED,OAAO,cAAc,UAAU,MAAM,QAAQ,SAAS,QAAQ;CAChE,CAAC;CAED,OAAO,cACL,mBAAmB,CAAC,CAAC,UACrB,EAAE,OAAO,UAAU,GACnB,QACF;AACF"}
@@ -1,5 +1,90 @@
1
- import { Styles } from "../styles/types.js";
2
- import { mergeStyles } from "../utils/merge-styles.js";
3
- import { StaticStyle, createStaticStyle, isStaticStyle } from "./types.js";
4
- import { tastyStatic } from "./tastyStatic.js";
5
- export { type StaticStyle, type Styles, createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
1
+ import { Y as Styles } from "../types-JEY8PVYP.js";
2
+ import { t as mergeStyles } from "../merge-styles-VXwJ0Frr.js";
3
+ //#region src/static/types.d.ts
4
+ /**
5
+ * Static style definition returned by tastyStatic().
6
+ *
7
+ * Supports both explicit className access and implicit string coercion via toString().
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const button = tastyStatic({ fill: '#blue' });
12
+ *
13
+ * // Both work in JSX:
14
+ * <div className={button} /> // Uses toString()
15
+ * <div className={button.className} /> // Explicit
16
+ *
17
+ * // Extension:
18
+ * const primary = tastyStatic(button, { fill: '#purple' });
19
+ * ```
20
+ */
21
+ interface StaticStyle {
22
+ /**
23
+ * Generated className(s) for use in JSX.
24
+ * May contain multiple space-separated class names due to chunking.
25
+ */
26
+ className: string;
27
+ /**
28
+ * The original (or merged) styles object.
29
+ * Available for extension via tastyStatic(base, overrides).
30
+ */
31
+ styles: Styles;
32
+ /**
33
+ * Returns className for implicit string coercion.
34
+ * Enables `<div className={button} />` syntax.
35
+ */
36
+ toString(): string;
37
+ }
38
+ /**
39
+ * Create a StaticStyle object.
40
+ * Used internally by the Babel plugin to generate output.
41
+ */
42
+ export declare function createStaticStyle(className: string, styles: Styles): StaticStyle;
43
+ /**
44
+ * Type guard to check if a value is a StaticStyle object.
45
+ */
46
+ export declare function isStaticStyle(value: unknown): value is StaticStyle;
47
+ //#endregion
48
+ //#region src/static/tastyStatic.d.ts
49
+ /**
50
+ * Generate styles and return a StaticStyle object.
51
+ * The object has `className`, `styles`, and `toString()`.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * const button = tastyStatic({
56
+ * fill: '#blue',
57
+ * padding: '2x',
58
+ * });
59
+ * // After build: { className: 'ts3f2a1b', styles: {...}, toString() }
60
+ *
61
+ * <div className={button} /> // Works via toString()
62
+ * ```
63
+ */
64
+ export declare function tastyStatic(styles: Styles): StaticStyle;
65
+ /**
66
+ * Extend an existing StaticStyle with additional styles.
67
+ * Uses mergeStyles() internally for proper nested selector handling.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * const button = tastyStatic({ fill: '#blue' });
72
+ * const primary = tastyStatic(button, { fill: '#purple' });
73
+ * // After build: { className: 'ts8c4d2e', styles: {...merged...}, toString() }
74
+ * ```
75
+ */
76
+ export declare function tastyStatic(base: StaticStyle, styles: Styles): StaticStyle;
77
+ /**
78
+ * Generate styles for a specific CSS selector.
79
+ * The call is completely removed after build transformation.
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * tastyStatic('.heading', { preset: 'h1', color: '#primary' });
84
+ * // After build: (removed)
85
+ * ```
86
+ */
87
+ export declare function tastyStatic(selector: string, styles: Styles): void;
88
+ //#endregion
89
+ export { type StaticStyle, type Styles, mergeStyles };
90
+ //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,32 @@
1
- import { mergeStyles } from "../utils/merge-styles.js";
2
- import { createStaticStyle, isStaticStyle } from "./types.js";
3
- import { tastyStatic } from "./tastyStatic.js";
1
+ import { t as mergeStyles } from "../chunks/shared-utils-y-HjvPN4.js";
2
+ //#region src/static/types.ts
3
+ function createStaticStyle(className, styles) {
4
+ return {
5
+ className,
6
+ styles,
7
+ toString() {
8
+ return this.className;
9
+ }
10
+ };
11
+ }
12
+ function isStaticStyle(value) {
13
+ return typeof value === "object" && value !== null && "className" in value && "styles" in value && "toString" in value && typeof value.className === "string" && typeof value.styles === "object";
14
+ }
15
+ //#endregion
16
+ //#region src/static/tastyStatic.ts
17
+ function tastyStatic(stylesOrBaseOrSelector, styles) {
18
+ if (typeof stylesOrBaseOrSelector === "string") {
19
+ if (process.env.NODE_ENV !== "production") console.warn(`[Tasty] tastyStatic('${stylesOrBaseOrSelector}', styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.`);
20
+ return;
21
+ }
22
+ if (isStaticStyle(stylesOrBaseOrSelector)) {
23
+ if (process.env.NODE_ENV !== "production") console.warn("[Tasty] tastyStatic(base, styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.");
24
+ return createStaticStyle("__TASTY_STATIC_NOT_TRANSFORMED__", mergeStyles(stylesOrBaseOrSelector.styles, styles || {}));
25
+ }
26
+ if (process.env.NODE_ENV !== "production") console.warn("[Tasty] tastyStatic(styles) was called at runtime. This indicates the Babel plugin is not configured. Add @tenphi/tasty/babel-plugin to your Babel config.");
27
+ return createStaticStyle("__TASTY_STATIC_NOT_TRANSFORMED__", stylesOrBaseOrSelector);
28
+ }
29
+ //#endregion
30
+ export { createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
4
31
 
5
- export { createStaticStyle, isStaticStyle, mergeStyles, tastyStatic };
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/static/types.ts","../../src/static/tastyStatic.ts"],"sourcesContent":["import type { Styles } from '../styles/types';\n\n/**\n * Static style definition returned by tastyStatic().\n *\n * Supports both explicit className access and implicit string coercion via toString().\n *\n * @example\n * ```typescript\n * const button = tastyStatic({ fill: '#blue' });\n *\n * // Both work in JSX:\n * <div className={button} /> // Uses toString()\n * <div className={button.className} /> // Explicit\n *\n * // Extension:\n * const primary = tastyStatic(button, { fill: '#purple' });\n * ```\n */\nexport interface StaticStyle {\n /**\n * Generated className(s) for use in JSX.\n * May contain multiple space-separated class names due to chunking.\n */\n className: string;\n\n /**\n * The original (or merged) styles object.\n * Available for extension via tastyStatic(base, overrides).\n */\n styles: Styles;\n\n /**\n * Returns className for implicit string coercion.\n * Enables `<div className={button} />` syntax.\n */\n toString(): string;\n}\n\n/**\n * Create a StaticStyle object.\n * Used internally by the Babel plugin to generate output.\n */\nexport function createStaticStyle(\n className: string,\n styles: Styles,\n): StaticStyle {\n return {\n className,\n styles,\n toString() {\n return this.className;\n },\n };\n}\n\n/**\n * Type guard to check if a value is a StaticStyle object.\n */\nexport function isStaticStyle(value: unknown): value is StaticStyle {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'className' in value &&\n 'styles' in value &&\n 'toString' in value &&\n typeof (value as StaticStyle).className === 'string' &&\n typeof (value as StaticStyle).styles === 'object'\n );\n}\n","import type { Styles } from '../styles/types';\nimport { mergeStyles } from '../utils/merge-styles';\n\nimport type { StaticStyle } from './types';\nimport { createStaticStyle, isStaticStyle } from './types';\n\n/**\n * Generate styles and return a StaticStyle object.\n * The object has `className`, `styles`, and `toString()`.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({\n * fill: '#blue',\n * padding: '2x',\n * });\n * // After build: { className: 'ts3f2a1b', styles: {...}, toString() }\n *\n * <div className={button} /> // Works via toString()\n * ```\n */\nexport function tastyStatic(styles: Styles): StaticStyle;\n\n/**\n * Extend an existing StaticStyle with additional styles.\n * Uses mergeStyles() internally for proper nested selector handling.\n *\n * @example\n * ```typescript\n * const button = tastyStatic({ fill: '#blue' });\n * const primary = tastyStatic(button, { fill: '#purple' });\n * // After build: { className: 'ts8c4d2e', styles: {...merged...}, toString() }\n * ```\n */\nexport function tastyStatic(base: StaticStyle, styles: Styles): StaticStyle;\n\n/**\n * Generate styles for a specific CSS selector.\n * The call is completely removed after build transformation.\n *\n * @example\n * ```typescript\n * tastyStatic('.heading', { preset: 'h1', color: '#primary' });\n * // After build: (removed)\n * ```\n */\nexport function tastyStatic(selector: string, styles: Styles): void;\n\n/**\n * Build-time only function for zero-runtime static site generation.\n *\n * This function is transformed by the Babel plugin:\n * - `tastyStatic(styles)` → StaticStyle object with className\n * - `tastyStatic(base, styles)` → StaticStyle object with merged styles\n * - `tastyStatic(selector, styles)` → removed entirely\n *\n * At runtime (during development/build), this function returns a placeholder.\n * In production, all calls are replaced/removed by the build plugin.\n */\nexport function tastyStatic(\n stylesOrBaseOrSelector: Styles | StaticStyle | string,\n styles?: Styles,\n): StaticStyle | void {\n // This code only executes if the Babel plugin hasn't processed the file yet.\n // In a properly configured build, this function is never called at runtime.\n\n if (typeof stylesOrBaseOrSelector === 'string') {\n // Selector mode: tastyStatic(selector, styles)\n // The plugin will remove this call entirely\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n `[Tasty] tastyStatic('${stylesOrBaseOrSelector}', styles) was called at runtime. ` +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n return; // void\n }\n\n if (isStaticStyle(stylesOrBaseOrSelector)) {\n // Extension mode: tastyStatic(base, styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[Tasty] tastyStatic(base, styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n // Merge styles for dev mode preview (won't have real classNames)\n const mergedStyles = mergeStyles(\n stylesOrBaseOrSelector.styles,\n styles || {},\n );\n return createStaticStyle('__TASTY_STATIC_NOT_TRANSFORMED__', mergedStyles);\n }\n\n // Styles mode: tastyStatic(styles)\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[Tasty] tastyStatic(styles) was called at runtime. ' +\n 'This indicates the Babel plugin is not configured. ' +\n 'Add @tenphi/tasty/babel-plugin to your Babel config.',\n );\n }\n\n // Return placeholder - styles won't be applied without the plugin\n return createStaticStyle(\n '__TASTY_STATIC_NOT_TRANSFORMED__',\n stylesOrBaseOrSelector,\n );\n}\n"],"mappings":";;AA2CA,SAAgB,kBACd,WACA,QACa;CACb,OAAO;EACL;EACA;EACA,WAAW;GACT,OAAO,KAAK;EACd;CACF;AACF;AAKA,SAAgB,cAAc,OAAsC;CAClE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,eAAe,SACf,YAAY,SACZ,cAAc,SACd,OAAQ,MAAsB,cAAc,YAC5C,OAAQ,MAAsB,WAAW;AAE7C;;;ACVA,SAAgB,YACd,wBACA,QACoB;CAIpB,IAAI,OAAO,2BAA2B,UAAU;EAG9C,IAAA,QAAA,IAAA,aAA6B,cAC3B,QAAQ,KACN,wBAAwB,uBAAuB,0IAGjD;EAEF;CACF;CAEA,IAAI,cAAc,sBAAsB,GAAG;EAEzC,IAAA,QAAA,IAAA,aAA6B,cAC3B,QAAQ,KACN,kKAGF;EAOF,OAAO,kBAAkB,oCAJJ,YACnB,uBAAuB,QACvB,UAAU,CAAC,CAEgD,CAAY;CAC3E;CAGA,IAAA,QAAA,IAAA,aAA6B,cAC3B,QAAQ,KACN,4JAGF;CAIF,OAAO,kBACL,oCACA,sBACF;AACF"}
@@ -0,0 +1,4 @@
1
+ //#region src/static/inject.d.ts
2
+ export declare function injectCSS(id: string, css: string): void;
3
+ //#endregion
4
+ //# sourceMappingURL=inject.d.ts.map
@@ -0,0 +1,17 @@
1
+ //#region src/static/inject.ts
2
+ const _ids = /* @__PURE__ */ new Set();
3
+ function injectCSS(id, css) {
4
+ if (_ids.has(id) || typeof document === "undefined") return;
5
+ _ids.add(id);
6
+ let el = document.head.querySelector("style[data-tasty-static]");
7
+ if (!el) {
8
+ el = document.createElement("style");
9
+ el.setAttribute("data-tasty-static", "");
10
+ document.head.appendChild(el);
11
+ }
12
+ el.appendChild(document.createTextNode(css + "\n"));
13
+ }
14
+ //#endregion
15
+ export { injectCSS };
16
+
17
+ //# sourceMappingURL=inject.js.map