meno-core 1.0.53 → 1.0.54

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 (387) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +48 -57
  3. package/build-astro.ts +131 -113
  4. package/build-next.ts +109 -96
  5. package/build-static.test.ts +39 -10
  6. package/build-static.ts +120 -122
  7. package/dist/bin/cli.js +34 -38
  8. package/dist/bin/cli.js.map +2 -2
  9. package/dist/build-static.js +12 -11
  10. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  11. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  12. package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
  13. package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
  14. package/dist/chunks/chunk-3XER4E5W.js +168 -0
  15. package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
  16. package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
  17. package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
  18. package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
  19. package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
  20. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
  21. package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
  22. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
  23. package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
  24. package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
  25. package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
  26. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
  27. package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
  28. package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
  29. package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
  30. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
  31. package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
  32. package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
  33. package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
  34. package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
  35. package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
  36. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
  37. package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
  38. package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
  39. package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
  40. package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
  41. package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
  42. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
  43. package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
  44. package/dist/chunks/configService-PRJZF7Y6.js +14 -0
  45. package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
  46. package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
  47. package/dist/entries/server-router.js +14 -19
  48. package/dist/entries/server-router.js.map +2 -2
  49. package/dist/lib/client/index.js +638 -332
  50. package/dist/lib/client/index.js.map +2 -2
  51. package/dist/lib/server/index.js +177 -235
  52. package/dist/lib/server/index.js.map +2 -2
  53. package/dist/lib/shared/index.js +80 -44
  54. package/dist/lib/shared/index.js.map +2 -2
  55. package/dist/lib/shared/richtext/index.js +1 -1
  56. package/dist/lib/test-utils/index.js +38 -60
  57. package/dist/lib/test-utils/index.js.map +2 -2
  58. package/entries/client-router.tsx +10 -8
  59. package/entries/server-router.tsx +1 -7
  60. package/lib/client/ClientInitializer.ts +8 -8
  61. package/lib/client/ErrorBoundary.test.tsx +146 -143
  62. package/lib/client/ErrorBoundary.tsx +175 -127
  63. package/lib/client/componentRegistry.test.ts +96 -108
  64. package/lib/client/componentRegistry.ts +1 -2
  65. package/lib/client/contexts/ThemeContext.tsx +3 -2
  66. package/lib/client/core/ComponentBuilder.test.ts +513 -560
  67. package/lib/client/core/ComponentBuilder.ts +318 -146
  68. package/lib/client/core/ComponentRenderer.test.tsx +1 -2
  69. package/lib/client/core/ComponentRenderer.tsx +46 -33
  70. package/lib/client/core/builders/embedBuilder.ts +241 -53
  71. package/lib/client/core/builders/linkBuilder.ts +71 -44
  72. package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
  73. package/lib/client/core/builders/listBuilder.ts +115 -86
  74. package/lib/client/core/builders/localeListBuilder.ts +95 -60
  75. package/lib/client/core/builders/types.ts +5 -5
  76. package/lib/client/core/cmsTemplateProcessor.ts +7 -7
  77. package/lib/client/elementRegistry.ts +3 -3
  78. package/lib/client/fontFamiliesService.test.ts +2 -10
  79. package/lib/client/fontFamiliesService.ts +3 -3
  80. package/lib/client/hmr/HMRManager.tsx +8 -0
  81. package/lib/client/hmrCssReload.ts +14 -8
  82. package/lib/client/hmrWebSocket.ts +9 -14
  83. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  84. package/lib/client/hooks/useColorVariables.ts +12 -10
  85. package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
  86. package/lib/client/hooks/useVariables.ts +4 -4
  87. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  88. package/lib/client/hydration/HydrationUtils.ts +3 -4
  89. package/lib/client/i18nConfigService.test.ts +2 -7
  90. package/lib/client/i18nConfigService.ts +2 -2
  91. package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
  92. package/lib/client/meno-filter/MenoFilter.ts +5 -9
  93. package/lib/client/meno-filter/bindings.ts +15 -40
  94. package/lib/client/meno-filter/init.ts +1 -1
  95. package/lib/client/meno-filter/renderer.ts +23 -29
  96. package/lib/client/meno-filter/script.generated.ts +1 -3
  97. package/lib/client/meno-filter/ui.ts +3 -5
  98. package/lib/client/meno-filter/updates.ts +15 -21
  99. package/lib/client/navigation.test.ts +159 -159
  100. package/lib/client/navigation.ts +0 -1
  101. package/lib/client/responsiveStyleResolver.test.ts +230 -228
  102. package/lib/client/responsiveStyleResolver.ts +13 -16
  103. package/lib/client/routing/RouteLoader.test.ts +23 -24
  104. package/lib/client/routing/RouteLoader.ts +22 -35
  105. package/lib/client/routing/Router.tsx +31 -3
  106. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  107. package/lib/client/scripts/ScriptExecutor.ts +15 -33
  108. package/lib/client/services/PrefetchService.test.ts +2 -2
  109. package/lib/client/services/PrefetchService.ts +10 -24
  110. package/lib/client/styleProcessor.test.ts +9 -9
  111. package/lib/client/styleProcessor.ts +18 -15
  112. package/lib/client/styles/StyleInjector.test.ts +122 -115
  113. package/lib/client/styles/StyleInjector.ts +9 -6
  114. package/lib/client/styles/UtilityClassCollector.ts +19 -26
  115. package/lib/client/styles/cspNonce.test.ts +2 -5
  116. package/lib/client/templateEngine.test.ts +554 -482
  117. package/lib/client/templateEngine.ts +200 -64
  118. package/lib/client/theme.ts +0 -1
  119. package/lib/client/utils/toast.ts +0 -1
  120. package/lib/server/__integration__/api-routes.test.ts +8 -4
  121. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  122. package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
  123. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  124. package/lib/server/__integration__/static-assets.test.ts +1 -1
  125. package/lib/server/__integration__/test-helpers.ts +84 -70
  126. package/lib/server/ab/generateFunctions.ts +12 -10
  127. package/lib/server/astro/cmsPageEmitter.ts +23 -38
  128. package/lib/server/astro/componentEmitter.ts +25 -36
  129. package/lib/server/astro/cssCollector.ts +10 -26
  130. package/lib/server/astro/nodeToAstro.test.ts +1750 -30
  131. package/lib/server/astro/nodeToAstro.ts +198 -187
  132. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
  133. package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
  134. package/lib/server/astro/pageEmitter.ts +9 -13
  135. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  136. package/lib/server/astro/tailwindMapper.ts +33 -40
  137. package/lib/server/astro/templateTransformer.ts +14 -17
  138. package/lib/server/createServer.ts +5 -17
  139. package/lib/server/cssGenerator.test.ts +35 -44
  140. package/lib/server/cssGenerator.ts +6 -17
  141. package/lib/server/fileWatcher.test.ts +124 -10
  142. package/lib/server/fileWatcher.ts +124 -118
  143. package/lib/server/index.ts +7 -1
  144. package/lib/server/jsonLoader.test.ts +39 -2
  145. package/lib/server/jsonLoader.ts +33 -31
  146. package/lib/server/middleware/cors.test.ts +20 -20
  147. package/lib/server/middleware/cors.ts +28 -4
  148. package/lib/server/middleware/errorHandler.test.ts +5 -3
  149. package/lib/server/middleware/errorHandler.ts +3 -8
  150. package/lib/server/middleware/index.ts +0 -1
  151. package/lib/server/middleware/logger.test.ts +7 -5
  152. package/lib/server/middleware/logger.ts +10 -22
  153. package/lib/server/pageCache.test.ts +76 -77
  154. package/lib/server/pageCache.ts +0 -1
  155. package/lib/server/projectContext.ts +4 -3
  156. package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
  157. package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
  158. package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
  159. package/lib/server/providers/fileSystemPageProvider.ts +31 -12
  160. package/lib/server/routes/api/cms.test.ts +26 -14
  161. package/lib/server/routes/api/cms.ts +9 -14
  162. package/lib/server/routes/api/components.ts +30 -34
  163. package/lib/server/routes/api/config.ts +0 -1
  164. package/lib/server/routes/api/core-routes.ts +47 -74
  165. package/lib/server/routes/api/functions.ts +8 -16
  166. package/lib/server/routes/api/index.ts +3 -6
  167. package/lib/server/routes/api/pages.ts +8 -11
  168. package/lib/server/routes/api/shared.test.ts +1 -1
  169. package/lib/server/routes/api/shared.ts +12 -9
  170. package/lib/server/routes/api/variables.test.ts +1 -3
  171. package/lib/server/routes/api/variables.ts +1 -1
  172. package/lib/server/routes/index.ts +23 -26
  173. package/lib/server/routes/pages.ts +34 -29
  174. package/lib/server/routes/static.ts +16 -4
  175. package/lib/server/runtime/bundler.ts +47 -32
  176. package/lib/server/runtime/fs.ts +3 -13
  177. package/lib/server/runtime/httpServer.ts +5 -9
  178. package/lib/server/services/ColorService.ts +32 -27
  179. package/lib/server/services/EnumService.test.ts +2 -6
  180. package/lib/server/services/EnumService.ts +7 -2
  181. package/lib/server/services/VariableService.test.ts +1 -5
  182. package/lib/server/services/VariableService.ts +6 -1
  183. package/lib/server/services/cmsService.test.ts +116 -78
  184. package/lib/server/services/cmsService.ts +24 -54
  185. package/lib/server/services/componentService.test.ts +122 -36
  186. package/lib/server/services/componentService.ts +54 -38
  187. package/lib/server/services/configService.test.ts +9 -31
  188. package/lib/server/services/configService.ts +20 -27
  189. package/lib/server/services/fileWatcherService.ts +40 -6
  190. package/lib/server/services/index.ts +0 -1
  191. package/lib/server/services/pageService.test.ts +1 -3
  192. package/lib/server/services/pageService.ts +14 -13
  193. package/lib/server/ssr/attributeBuilder.ts +16 -6
  194. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  195. package/lib/server/ssr/clientDataInjector.ts +7 -21
  196. package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
  197. package/lib/server/ssr/cssCollector.ts +1 -1
  198. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  199. package/lib/server/ssr/errorOverlay.ts +3 -9
  200. package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
  201. package/lib/server/ssr/htmlGenerator.test.ts +120 -43
  202. package/lib/server/ssr/htmlGenerator.ts +120 -85
  203. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  204. package/lib/server/ssr/imageMetadata.ts +25 -19
  205. package/lib/server/ssr/jsCollector.test.ts +3 -13
  206. package/lib/server/ssr/jsCollector.ts +3 -8
  207. package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
  208. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  209. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  210. package/lib/server/ssr/ssrRenderer.test.ts +196 -246
  211. package/lib/server/ssr/ssrRenderer.ts +609 -225
  212. package/lib/server/ssrRenderer.test.ts +1044 -950
  213. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  214. package/lib/server/utils/jsonLineMapper.ts +1 -1
  215. package/lib/server/validateStyleCoverage.ts +18 -20
  216. package/lib/server/webflow/buildWebflow.ts +41 -53
  217. package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
  218. package/lib/server/webflow/nodeToWebflow.ts +195 -258
  219. package/lib/server/webflow/styleMapper.test.ts +15 -56
  220. package/lib/server/webflow/styleMapper.ts +33 -41
  221. package/lib/server/webflow/types.ts +1 -8
  222. package/lib/server/websocketManager.ts +16 -19
  223. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  224. package/lib/shared/attributeNodeUtils.ts +5 -12
  225. package/lib/shared/breakpoints.ts +4 -11
  226. package/lib/shared/cmsQueryParser.test.ts +50 -42
  227. package/lib/shared/cmsQueryParser.ts +49 -31
  228. package/lib/shared/colorVariableUtils.test.ts +5 -5
  229. package/lib/shared/colorVariableUtils.ts +3 -8
  230. package/lib/shared/componentRefs.ts +1 -5
  231. package/lib/shared/constants.test.ts +3 -3
  232. package/lib/shared/constants.ts +4 -8
  233. package/lib/shared/cssGeneration.test.ts +262 -144
  234. package/lib/shared/cssGeneration.ts +189 -516
  235. package/lib/shared/cssNamedColors.ts +152 -30
  236. package/lib/shared/cssProperties.test.ts +5 -6
  237. package/lib/shared/cssProperties.ts +296 -112
  238. package/lib/shared/elementClassName.test.ts +109 -109
  239. package/lib/shared/elementClassName.ts +1 -1
  240. package/lib/shared/elementUtils.ts +12 -16
  241. package/lib/shared/errorLogger.ts +2 -10
  242. package/lib/shared/errors.test.ts +2 -13
  243. package/lib/shared/errors.ts +2 -8
  244. package/lib/shared/expressionEvaluator.test.ts +119 -0
  245. package/lib/shared/expressionEvaluator.ts +43 -24
  246. package/lib/shared/fontLoader.test.ts +19 -5
  247. package/lib/shared/friendlyError.ts +2 -3
  248. package/lib/shared/gradientUtils.test.ts +1 -5
  249. package/lib/shared/gradientUtils.ts +2 -6
  250. package/lib/shared/hrefRefs.ts +2 -10
  251. package/lib/shared/i18n.test.ts +1 -1
  252. package/lib/shared/i18n.ts +13 -34
  253. package/lib/shared/index.ts +4 -0
  254. package/lib/shared/inlineSvgStyleRules.ts +2 -4
  255. package/lib/shared/interactiveStyleMappings.test.ts +11 -33
  256. package/lib/shared/interactiveStyleMappings.ts +9 -16
  257. package/lib/shared/interactiveStyles.test.ts +165 -188
  258. package/lib/shared/interfaces/contentProvider.ts +1 -1
  259. package/lib/shared/itemTemplateUtils.test.ts +6 -12
  260. package/lib/shared/itemTemplateUtils.ts +19 -35
  261. package/lib/shared/jsonRepair.ts +8 -2
  262. package/lib/shared/libraryLoader.test.ts +15 -49
  263. package/lib/shared/libraryLoader.ts +7 -22
  264. package/lib/shared/netlifyLocale404.test.ts +179 -0
  265. package/lib/shared/netlifyLocale404.ts +110 -0
  266. package/lib/shared/nodeUtils.test.ts +24 -16
  267. package/lib/shared/nodeUtils.ts +49 -19
  268. package/lib/shared/pathArrayUtils.test.ts +1 -2
  269. package/lib/shared/pathArrayUtils.ts +1 -1
  270. package/lib/shared/pathSecurity.ts +1 -1
  271. package/lib/shared/pathUtils.test.ts +4 -6
  272. package/lib/shared/pathUtils.ts +42 -48
  273. package/lib/shared/paths/Path.test.ts +2 -2
  274. package/lib/shared/paths/Path.ts +0 -1
  275. package/lib/shared/paths/PathConverter.test.ts +1 -1
  276. package/lib/shared/paths/PathConverter.ts +14 -17
  277. package/lib/shared/paths/PathUtils.ts +9 -10
  278. package/lib/shared/paths/PathValidator.test.ts +2 -15
  279. package/lib/shared/paths/PathValidator.ts +11 -9
  280. package/lib/shared/paths/index.ts +1 -2
  281. package/lib/shared/propResolver.test.ts +240 -244
  282. package/lib/shared/propResolver.ts +14 -25
  283. package/lib/shared/pxToRem.test.ts +7 -6
  284. package/lib/shared/pxToRem.ts +2 -5
  285. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
  286. package/lib/shared/registry/ClientRegistry.ts +0 -1
  287. package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
  288. package/lib/shared/registry/ComponentRegistry.ts +9 -11
  289. package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
  290. package/lib/shared/registry/RegistryManager.ts +1 -2
  291. package/lib/shared/registry/SSRRegistry.ts +0 -1
  292. package/lib/shared/registry/createNodeType.ts +7 -9
  293. package/lib/shared/registry/defineNodeType.ts +2 -6
  294. package/lib/shared/registry/index.ts +0 -1
  295. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  296. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  297. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
  298. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  299. package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
  300. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  301. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
  302. package/lib/shared/registry/nodeTypes/index.ts +10 -2
  303. package/lib/shared/responsiveScaling.test.ts +15 -31
  304. package/lib/shared/responsiveScaling.ts +55 -37
  305. package/lib/shared/responsiveStyleUtils.ts +11 -13
  306. package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
  307. package/lib/shared/richtext/htmlToTiptap.ts +1 -3
  308. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  309. package/lib/shared/richtext/types.ts +1 -8
  310. package/lib/shared/slugTranslator.test.ts +13 -13
  311. package/lib/shared/slugTranslator.ts +12 -16
  312. package/lib/shared/slugify.ts +9 -15
  313. package/lib/shared/styleNodeUtils.test.ts +8 -8
  314. package/lib/shared/styleNodeUtils.ts +5 -10
  315. package/lib/shared/styleUtils.test.ts +87 -61
  316. package/lib/shared/styleUtils.ts +5 -6
  317. package/lib/shared/themeDefaults.test.ts +11 -11
  318. package/lib/shared/themeDefaults.ts +3 -4
  319. package/lib/shared/tree/PathBuilder.test.ts +62 -138
  320. package/lib/shared/tree/PathBuilder.ts +49 -39
  321. package/lib/shared/treePathUtils.test.ts +2 -10
  322. package/lib/shared/treePathUtils.ts +54 -59
  323. package/lib/shared/types/api.ts +1 -2
  324. package/lib/shared/types/cms.ts +25 -21
  325. package/lib/shared/types/comment.ts +49 -16
  326. package/lib/shared/types/components.ts +27 -25
  327. package/lib/shared/types/errors.test.ts +1 -6
  328. package/lib/shared/types/errors.ts +3 -7
  329. package/lib/shared/types/experiments.ts +28 -28
  330. package/lib/shared/types/index.ts +2 -2
  331. package/lib/shared/types/styles.ts +0 -1
  332. package/lib/shared/types/variables.test.ts +4 -13
  333. package/lib/shared/types/variables.ts +48 -27
  334. package/lib/shared/types.ts +1 -2
  335. package/lib/shared/utilityClassConfig.ts +648 -321
  336. package/lib/shared/utilityClassMapper.test.ts +203 -92
  337. package/lib/shared/utilityClassMapper.ts +188 -246
  338. package/lib/shared/utilityClassNames.ts +326 -0
  339. package/lib/shared/utils.test.ts +2 -10
  340. package/lib/shared/utils.ts +19 -10
  341. package/lib/shared/validation/cmsValidators.ts +2 -1
  342. package/lib/shared/validation/commentValidators.test.ts +53 -0
  343. package/lib/shared/validation/commentValidators.ts +12 -1
  344. package/lib/shared/validation/propValidator.test.ts +18 -20
  345. package/lib/shared/validation/propValidator.ts +12 -17
  346. package/lib/shared/validation/schemas.test.ts +24 -33
  347. package/lib/shared/validation/schemas.ts +469 -344
  348. package/lib/shared/validation/validators.test.ts +1 -6
  349. package/lib/shared/validation/validators.ts +89 -68
  350. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  351. package/lib/shared/viewportUnits.test.ts +11 -23
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
  354. package/lib/test-utils/factories/DomMockFactory.ts +7 -19
  355. package/lib/test-utils/factories/EventMockFactory.ts +7 -13
  356. package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
  357. package/lib/test-utils/factories/ServerMockFactory.ts +5 -9
  358. package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
  359. package/lib/test-utils/fixtures.ts +45 -45
  360. package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
  361. package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
  362. package/lib/test-utils/helpers.ts +1 -5
  363. package/lib/test-utils/index.ts +0 -4
  364. package/lib/test-utils/mockFactories.ts +12 -18
  365. package/lib/test-utils/mocks.ts +4 -2
  366. package/package.json +1 -1
  367. package/scripts/build-meno-filter.ts +1 -4
  368. package/vite.config.ts +4 -4
  369. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  370. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  371. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  372. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  373. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  374. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  375. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  376. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  377. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  378. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  379. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  380. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  381. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  382. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  383. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  384. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  385. /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
  386. /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
  387. /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
@@ -5,25 +5,35 @@ import type { ResponsiveScales } from './responsiveScaling';
5
5
 
6
6
  describe('utilityClassMapper', () => {
7
7
  describe('stylesToClasses', () => {
8
- test('converts padding to utility class', () => {
8
+ test('converts padding to arbitrary-value utility class', () => {
9
9
  const classes = stylesToClasses({ padding: '10px' });
10
- expect(classes).toContain('p-10px');
10
+ expect(classes).toContain('p-[10px]');
11
11
  });
12
12
 
13
- test('converts display flex to short form', () => {
13
+ test('converts display flex to Tailwind static utility', () => {
14
14
  const classes = stylesToClasses({ display: 'flex' });
15
- expect(classes).toContain('f');
15
+ expect(classes).toContain('flex');
16
+ });
17
+
18
+ test('converts display none to hidden', () => {
19
+ const classes = stylesToClasses({ display: 'none' });
20
+ expect(classes).toContain('hidden');
16
21
  });
17
22
 
18
23
  test('converts multiple properties', () => {
19
24
  const classes = stylesToClasses({ padding: '10px', margin: '20px' });
20
- expect(classes).toContain('p-10px');
21
- expect(classes).toContain('m-20px');
25
+ expect(classes).toContain('p-[10px]');
26
+ expect(classes).toContain('m-[20px]');
22
27
  });
23
28
 
24
29
  test('handles percentage values', () => {
25
30
  const classes = stylesToClasses({ width: '50%' });
26
- expect(classes).toContain('w-50p');
31
+ expect(classes).toContain('w-[50%]');
32
+ });
33
+
34
+ test('maps width 100% to w-full', () => {
35
+ const classes = stylesToClasses({ width: '100%' });
36
+ expect(classes).toContain('w-full');
27
37
  });
28
38
 
29
39
  test('handles auto values', () => {
@@ -31,9 +41,9 @@ describe('utilityClassMapper', () => {
31
41
  expect(classes).toContain('m-auto');
32
42
  });
33
43
 
34
- test('handles values with spaces', () => {
44
+ test('handles values with spaces (underscore encoding)', () => {
35
45
  const classes = stylesToClasses({ padding: '92px 0' });
36
- expect(classes).toContain('p-92px-0');
46
+ expect(classes).toContain('p-[92px_0]');
37
47
  });
38
48
 
39
49
  test('returns empty array for null', () => {
@@ -41,60 +51,110 @@ describe('utilityClassMapper', () => {
41
51
  expect(stylesToClasses(undefined)).toEqual([]);
42
52
  });
43
53
 
44
- test('handles color variables', () => {
54
+ test('handles color variables (Tailwind v4 var shorthand)', () => {
45
55
  const classes = stylesToClasses({ color: 'text' });
46
- expect(classes).toContain('c-text');
56
+ expect(classes).toContain('text-(--text)');
57
+ });
58
+
59
+ test('handles background color tokens', () => {
60
+ const classes = stylesToClasses({ backgroundColor: 'primary' });
61
+ expect(classes).toContain('bg-(--primary)');
47
62
  });
48
63
 
49
- test('converts shadow preset to sh- class', () => {
64
+ test('handles font-size with explicit value', () => {
65
+ const classes = stylesToClasses({ fontSize: '48px' });
66
+ expect(classes).toContain('text-[48px]');
67
+ });
68
+
69
+ test('handles font-size variables with length hint', () => {
70
+ const classes = stylesToClasses({ fontSize: 'var(--font-size-lg)' });
71
+ expect(classes).toContain('text-(length:--font-size-lg)');
72
+ });
73
+
74
+ test('font-weight var carries a number hint (not misread as font-family)', () => {
75
+ // `font-*` is shared by weight/family; without the hint a var binding resolves to
76
+ // font-family (`font-family: 450`), so the weight never applies and the family changes.
77
+ expect(stylesToClasses({ fontWeight: 'var(--h-fw)' })).toContain('font-(number:--h-fw)');
78
+ expect(stylesToClasses({ fontFamily: 'var(--l-ff)' })).toContain('font-(--l-ff)');
79
+ // round-trips back to the right property
80
+ expect(classToStyle('font-(number:--h-fw)')).toEqual({ prop: 'fontWeight', value: 'var(--h-fw)' });
81
+ expect(classToStyle('font-(--l-ff)')).toEqual({ prop: 'fontFamily', value: 'var(--l-ff)' });
82
+ });
83
+
84
+ test('converts shadow preset to shadow- class', () => {
50
85
  const classes = stylesToClasses({ boxShadow: '2' });
51
- expect(classes).toContain('sh-2');
86
+ expect(classes).toContain('shadow-2');
52
87
  });
53
88
 
54
- test('converts gradient preset to gr- class', () => {
89
+ test('converts gradient preset to bg-gradient- class', () => {
55
90
  const classes = stylesToClasses({ background: '1' });
56
- expect(classes).toContain('gr-1');
91
+ expect(classes).toContain('bg-gradient-1');
57
92
  });
58
93
 
59
- test('converts border preset to bd- class', () => {
94
+ test('converts border preset to border-preset- class', () => {
60
95
  const classes = stylesToClasses({ border: '0' });
61
- expect(classes).toContain('bd-0');
96
+ expect(classes).toContain('border-preset-0');
62
97
  });
63
98
 
64
99
  test('handles aspectRatio with slash value', () => {
65
100
  const classes = stylesToClasses({ aspectRatio: '1/1' });
66
- expect(classes).toContain('ar-1s1');
101
+ expect(classes).toContain('aspect-[1/1]');
102
+ });
103
+
104
+ test('handles CSS function values', () => {
105
+ const classes = stylesToClasses({ transform: 'translateY(-50%)' });
106
+ expect(classes).toContain('transform-[translateY(-50%)]');
107
+ });
108
+
109
+ test('handles grid template repeat()', () => {
110
+ const classes = stylesToClasses({ gridTemplateColumns: 'repeat(3, 1fr)' });
111
+ expect(classes).toContain('grid-cols-[repeat(3,_1fr)]');
112
+ });
113
+
114
+ test('handles z-index Tailwind-style bare number', () => {
115
+ const classes = stylesToClasses({ zIndex: 10 });
116
+ expect(classes).toContain('z-10');
117
+ });
118
+
119
+ test('handles opacity on Tailwind 0-100 scale', () => {
120
+ const classes = stylesToClasses({ opacity: 0.5 });
121
+ expect(classes).toContain('opacity-50');
122
+ });
123
+
124
+ test('handles properties without a Tailwind root via arbitrary properties', () => {
125
+ const classes = stylesToClasses({ textOverflow: 'ellipsis' });
126
+ expect(classes).toContain('[text-overflow:ellipsis]');
67
127
  });
68
128
 
69
129
  test('align-items stretch maps to a distinct class from flex-start', () => {
70
- // Regression: stretch used to share the `ai-s` class with flex-start,
71
- // so imported `align-items: stretch` silently rendered as flex-start
72
- // — exactly what made Finsweet's mobile CTA buttons line up to the
73
- // left instead of stretching to full width.
130
+ // Regression: stretch must never share a class with flex-start,
131
+ // otherwise imported `align-items: stretch` silently renders as
132
+ // flex-start — exactly what made Finsweet's mobile CTA buttons line
133
+ // up to the left instead of stretching to full width.
74
134
  const stretchClasses = stylesToClasses({ alignItems: 'stretch' });
75
135
  const flexStartClasses = stylesToClasses({ alignItems: 'flex-start' });
76
- expect(stretchClasses).toContain('ai-st');
77
- expect(flexStartClasses).toContain('ai-s');
78
- expect(stretchClasses).not.toContain('ai-s');
79
- expect(classToStyle('ai-st')).toEqual({ prop: 'alignItems', value: 'stretch' });
136
+ expect(stretchClasses).toContain('items-stretch');
137
+ expect(flexStartClasses).toContain('items-start');
138
+ expect(stretchClasses).not.toContain('items-start');
139
+ expect(classToStyle('items-stretch')).toEqual({ prop: 'alignItems', value: 'stretch' });
80
140
  });
81
141
 
82
142
  test('justify-content space-evenly maps to a distinct class from flex-end', () => {
83
- // Same bug shape as align-items: `space-evenly` previously aliased to
84
- // `jc-e` (flex-end), silently changing layout.
143
+ // Same bug shape as align-items: `space-evenly` must not alias to
144
+ // flex-end, silently changing layout.
85
145
  const evenly = stylesToClasses({ justifyContent: 'space-evenly' });
86
146
  const end = stylesToClasses({ justifyContent: 'flex-end' });
87
- expect(evenly).toContain('jc-ev');
88
- expect(end).toContain('jc-e');
89
- expect(evenly).not.toContain('jc-e');
90
- expect(classToStyle('jc-ev')).toEqual({ prop: 'justifyContent', value: 'space-evenly' });
147
+ expect(evenly).toContain('justify-evenly');
148
+ expect(end).toContain('justify-end');
149
+ expect(evenly).not.toContain('justify-end');
150
+ expect(classToStyle('justify-evenly')).toEqual({ prop: 'justifyContent', value: 'space-evenly' });
91
151
  });
92
152
  });
93
153
 
94
154
  describe('responsiveStylesToClasses', () => {
95
155
  test('handles flat style object', () => {
96
156
  const classes = responsiveStylesToClasses({ padding: '10px' });
97
- expect(classes).toContain('p-10px');
157
+ expect(classes).toContain('p-[10px]');
98
158
  });
99
159
 
100
160
  test('handles responsive styles with base', () => {
@@ -102,17 +162,17 @@ describe('utilityClassMapper', () => {
102
162
  base: { padding: '10px' },
103
163
  tablet: { padding: '20px' },
104
164
  });
105
- expect(classes).toContain('p-10px');
106
- expect(classes).toContain('t-p-20px');
165
+ expect(classes).toContain('p-[10px]');
166
+ expect(classes).toContain('tablet:p-[20px]');
107
167
  });
108
168
 
109
- test('handles mobile styles with mob prefix', () => {
169
+ test('handles mobile styles with mobile: variant prefix', () => {
110
170
  const classes = responsiveStylesToClasses({
111
171
  base: { padding: '10px' },
112
172
  mobile: { padding: '5px' },
113
173
  });
114
- expect(classes).toContain('p-10px');
115
- expect(classes).toContain('mob-p-5px');
174
+ expect(classes).toContain('p-[10px]');
175
+ expect(classes).toContain('mobile:p-[5px]');
116
176
  });
117
177
 
118
178
  test('returns empty array for null', () => {
@@ -130,39 +190,39 @@ describe('utilityClassMapper', () => {
130
190
  padding: { tablet: 0.75, mobile: 0.5 },
131
191
  };
132
192
 
133
- test('consumes scalable mobile override into base clamp; no mob- class emitted', () => {
193
+ test('consumes scalable mobile override into base clamp; no mobile: class emitted', () => {
134
194
  clearRegistry();
135
195
  const classes = responsiveStylesToClasses(
136
196
  { base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
137
- { fluidActive: true, responsiveScales: fluidScales }
197
+ { fluidActive: true, responsiveScales: fluidScales },
138
198
  );
139
- // base became a hashed class wrapping clamp(94px, ..., 150px)
140
- const hashed = classes.find((c) => /^fs-h[0-9a-z]+$/.test(c));
141
- expect(hashed).toBeDefined();
142
- expect(getStyleValue(hashed!)).toMatch(/^clamp\(94px,/);
143
- expect(getStyleValue(hashed!)).toContain(', 150px)');
144
- // no .mob-fs-* emitted
145
- expect(classes.some((c) => c.startsWith('mob-fs-'))).toBe(false);
199
+ // base became an arbitrary class wrapping clamp(94px, ..., 150px)
200
+ const clamped = classes.find((c) => c.startsWith('text-[clamp('));
201
+ expect(clamped).toBeDefined();
202
+ expect(String(getStyleValue(clamped!))).toMatch(/^clamp\(94px,/);
203
+ expect(String(getStyleValue(clamped!))).toContain(', 150px)');
204
+ // no mobile:text-* emitted
205
+ expect(classes.some((c) => c.startsWith('mobile:text-'))).toBe(false);
146
206
  });
147
207
 
148
- test('mobile-only non-scalable property (display) stays as mob- class', () => {
208
+ test('mobile-only non-scalable property (display) stays as mobile: class', () => {
149
209
  clearRegistry();
150
210
  const classes = responsiveStylesToClasses(
151
211
  {
152
212
  base: { fontSize: '150px' },
153
213
  mobile: { fontSize: '94px', display: 'none' },
154
214
  },
155
- { fluidActive: true, responsiveScales: fluidScales }
215
+ { fluidActive: true, responsiveScales: fluidScales },
156
216
  );
157
217
  // fontSize consumed into base clamp
158
- const hashed = classes.find((c) => /^fs-h[0-9a-z]+$/.test(c));
159
- expect(hashed).toBeDefined();
160
- expect(getStyleValue(hashed!)).toMatch(/^clamp\(94px,/);
218
+ const clamped = classes.find((c) => c.startsWith('text-[clamp('));
219
+ expect(clamped).toBeDefined();
220
+ expect(String(getStyleValue(clamped!))).toMatch(/^clamp\(94px,/);
161
221
  // display:none survives on mobile
162
- expect(classes).toContain('mob-h');
222
+ expect(classes).toContain('mobile:hidden');
163
223
  });
164
224
 
165
- test('tablet override stays as .t-* class (unchanged behavior)', () => {
225
+ test('tablet override stays as tablet: class (unchanged behavior)', () => {
166
226
  clearRegistry();
167
227
  const classes = responsiveStylesToClasses(
168
228
  {
@@ -170,71 +230,71 @@ describe('utilityClassMapper', () => {
170
230
  tablet: { fontSize: '120px' },
171
231
  mobile: { fontSize: '94px' },
172
232
  },
173
- { fluidActive: true, responsiveScales: fluidScales }
233
+ { fluidActive: true, responsiveScales: fluidScales },
174
234
  );
175
- expect(classes).toContain('t-fs-120px');
235
+ expect(classes).toContain('tablet:text-[120px]');
176
236
  // mobile consumed
177
- expect(classes.some((c) => c.startsWith('mob-fs-'))).toBe(false);
237
+ expect(classes.some((c) => c.startsWith('mobile:text-'))).toBe(false);
178
238
  });
179
239
 
180
240
  test('no mobile override → base passes through unchanged (no consumption)', () => {
181
241
  clearRegistry();
182
242
  const classes = responsiveStylesToClasses(
183
243
  { base: { fontSize: '150px' } },
184
- { fluidActive: true, responsiveScales: fluidScales }
244
+ { fluidActive: true, responsiveScales: fluidScales },
185
245
  );
186
- expect(classes).toContain('fs-150px');
187
- expect(classes.some((c) => /^fs-h/.test(c))).toBe(false);
246
+ expect(classes).toContain('text-[150px]');
247
+ expect(classes.some((c) => c.startsWith('text-[clamp('))).toBe(false);
188
248
  });
189
249
 
190
- test('mismatched units (rem vs px) → falls back, mob- class still emitted', () => {
250
+ test('mismatched units (rem vs px) → falls back, mobile: class still emitted', () => {
191
251
  clearRegistry();
192
252
  const classes = responsiveStylesToClasses(
193
253
  { base: { fontSize: '10rem' }, mobile: { fontSize: '94px' } },
194
- { fluidActive: true, responsiveScales: fluidScales }
254
+ { fluidActive: true, responsiveScales: fluidScales },
195
255
  );
196
- expect(classes).toContain('fs-10rem');
197
- expect(classes).toContain('mob-fs-94px');
256
+ expect(classes).toContain('text-[10rem]');
257
+ expect(classes).toContain('mobile:text-[94px]');
198
258
  });
199
259
 
200
260
  test('non-scalable mobile override (display) is not consumed', () => {
201
261
  clearRegistry();
202
262
  const classes = responsiveStylesToClasses(
203
263
  { base: { display: 'flex' }, mobile: { display: 'block' } },
204
- { fluidActive: true, responsiveScales: fluidScales }
264
+ { fluidActive: true, responsiveScales: fluidScales },
205
265
  );
206
- expect(classes).toContain('f');
207
- expect(classes).toContain('mob-b');
266
+ expect(classes).toContain('flex');
267
+ expect(classes).toContain('mobile:block');
208
268
  });
209
269
 
210
- test('fluidActive off → no consumption, mob- class emitted', () => {
270
+ test('fluidActive off → no consumption, mobile: class emitted', () => {
211
271
  clearRegistry();
212
272
  const classes = responsiveStylesToClasses(
213
273
  { base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
214
- { fluidActive: false, responsiveScales: fluidScales }
274
+ { fluidActive: false, responsiveScales: fluidScales },
215
275
  );
216
- expect(classes).toContain('fs-150px');
217
- expect(classes).toContain('mob-fs-94px');
276
+ expect(classes).toContain('text-[150px]');
277
+ expect(classes).toContain('mobile:text-[94px]');
218
278
  });
219
279
 
220
280
  test('fluidActive on but responsiveScales disabled → no consumption', () => {
221
281
  clearRegistry();
222
282
  const classes = responsiveStylesToClasses(
223
283
  { base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
224
- { fluidActive: true, responsiveScales: { ...fluidScales, enabled: false } }
284
+ { fluidActive: true, responsiveScales: { ...fluidScales, enabled: false } },
225
285
  );
226
- expect(classes).toContain('fs-150px');
227
- expect(classes).toContain('mob-fs-94px');
286
+ expect(classes).toContain('text-[150px]');
287
+ expect(classes).toContain('mobile:text-[94px]');
228
288
  });
229
289
 
230
290
  test('clamp math: MIN=94px, MAX=150px, fluidRange 320–1700', () => {
231
291
  clearRegistry();
232
292
  const classes = responsiveStylesToClasses(
233
293
  { base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
234
- { fluidActive: true, responsiveScales: fluidScales }
294
+ { fluidActive: true, responsiveScales: fluidScales },
235
295
  );
236
- const hashed = classes.find((c) => /^fs-h[0-9a-z]+$/.test(c))!;
237
- const value = String(getStyleValue(hashed));
296
+ const clamped = classes.find((c) => c.startsWith('text-[clamp('))!;
297
+ const value = String(getStyleValue(clamped));
238
298
  // slope = (150-94)/(1700-320) = 56/1380 ≈ 0.04058 px/px → slopeVw ≈ 4.058vw
239
299
  // intercept = 94 - 0.04058*320 = 94 - 12.9855 ≈ 81.0145px
240
300
  expect(value).toMatch(/clamp\(94px,\s*81\.0145px\s*\+\s*4\.058vw,\s*150px\)/);
@@ -244,22 +304,27 @@ describe('utilityClassMapper', () => {
244
304
 
245
305
  describe('classToStyle', () => {
246
306
  test('parses padding class', () => {
247
- const style = classToStyle('p-10px');
307
+ const style = classToStyle('p-[10px]');
248
308
  expect(style).toEqual({ prop: 'padding', value: '10px' });
249
309
  });
250
310
 
251
- test('parses special case flex', () => {
252
- const style = classToStyle('f');
311
+ test('parses static flex utility', () => {
312
+ const style = classToStyle('flex');
253
313
  expect(style).toEqual({ prop: 'display', value: 'flex' });
254
314
  });
255
315
 
256
- test('handles tablet prefix', () => {
257
- const style = classToStyle('t-p-20px');
316
+ test('parses hidden utility', () => {
317
+ const style = classToStyle('hidden');
318
+ expect(style).toEqual({ prop: 'display', value: 'none' });
319
+ });
320
+
321
+ test('handles tablet variant prefix', () => {
322
+ const style = classToStyle('tablet:p-[20px]');
258
323
  expect(style).toEqual({ prop: 'padding', value: '20px' });
259
324
  });
260
325
 
261
- test('handles mobile prefix', () => {
262
- const style = classToStyle('mob-p-5px');
326
+ test('handles mobile variant prefix', () => {
327
+ const style = classToStyle('mobile:p-[5px]');
263
328
  expect(style).toEqual({ prop: 'padding', value: '5px' });
264
329
  });
265
330
 
@@ -268,32 +333,69 @@ describe('utilityClassMapper', () => {
268
333
  expect(classToStyle('unknown-class')).toBeNull();
269
334
  });
270
335
 
336
+ test('decodes underscores back to spaces', () => {
337
+ const style = classToStyle('p-[92px_0]');
338
+ expect(style).toEqual({ prop: 'padding', value: '92px 0' });
339
+ });
340
+
341
+ test('decodes color variables to bare tokens', () => {
342
+ const style = classToStyle('bg-(--primary)');
343
+ expect(style).toEqual({ prop: 'backgroundColor', value: 'primary' });
344
+ });
345
+
346
+ test('decodes non-color variables to var() values', () => {
347
+ const style = classToStyle('w-(--site-width)');
348
+ expect(style).toEqual({ prop: 'width', value: 'var(--site-width)' });
349
+ });
350
+
351
+ test('disambiguates text- by value: font-size vs color', () => {
352
+ expect(classToStyle('text-[14px]')).toEqual({ prop: 'fontSize', value: '14px' });
353
+ expect(classToStyle('text-[#fff]')).toEqual({ prop: 'color', value: '#fff' });
354
+ expect(classToStyle('text-(length:--font-size-lg)')).toEqual({
355
+ prop: 'fontSize',
356
+ value: 'var(--font-size-lg)',
357
+ });
358
+ });
359
+
360
+ test('decodes keyword utilities', () => {
361
+ expect(classToStyle('w-full')).toEqual({ prop: 'width', value: '100%' });
362
+ expect(classToStyle('h-screen')).toEqual({ prop: 'height', value: '100vh' });
363
+ expect(classToStyle('rounded-full')).toEqual({ prop: 'borderRadius', value: '9999px' });
364
+ });
365
+
366
+ test('decodes arbitrary-property classes', () => {
367
+ expect(classToStyle('[text-overflow:ellipsis]')).toEqual({ prop: 'textOverflow', value: 'ellipsis' });
368
+ });
369
+
271
370
  test('parses shadow preset class', () => {
272
- const style = classToStyle('sh-2');
371
+ const style = classToStyle('shadow-2');
273
372
  expect(style).toEqual({ prop: 'boxShadow', value: '0 4px 12px rgba(0, 0, 0, 0.1)' });
274
373
  });
275
374
 
276
375
  test('parses gradient preset class', () => {
277
- const style = classToStyle('gr-1');
278
- expect(style).toEqual({ prop: 'background', value: 'linear-gradient(90deg, rgba(10,22,40,0.85) 0%, rgba(10,22,40,0.2) 100%)' });
376
+ const style = classToStyle('bg-gradient-1');
377
+ expect(style).toEqual({
378
+ prop: 'background',
379
+ value: 'linear-gradient(90deg, rgba(10,22,40,0.85) 0%, rgba(10,22,40,0.2) 100%)',
380
+ });
279
381
  });
280
382
 
281
383
  test('parses border preset class', () => {
282
- const style = classToStyle('bd-0');
384
+ const style = classToStyle('border-preset-0');
283
385
  expect(style).toEqual({ prop: 'border', value: '1px solid var(--border)' });
284
386
  });
285
387
  });
286
388
 
287
389
  describe('classesToStyles', () => {
288
390
  test('converts base classes to style object', () => {
289
- const styles = classesToStyles(['p-10px', 'm-20px']);
391
+ const styles = classesToStyles(['p-[10px]', 'm-[20px]']);
290
392
  expect(styles).toEqual({
291
393
  base: { padding: '10px', margin: '20px' },
292
394
  });
293
395
  });
294
396
 
295
397
  test('handles responsive classes', () => {
296
- const styles = classesToStyles(['p-10px', 't-p-20px']);
398
+ const styles = classesToStyles(['p-[10px]', 'tablet:p-[20px]']);
297
399
  expect(styles).toEqual({
298
400
  base: { padding: '10px' },
299
401
  tablet: { padding: '20px' },
@@ -301,7 +403,7 @@ describe('utilityClassMapper', () => {
301
403
  });
302
404
 
303
405
  test('handles mobile classes', () => {
304
- const styles = classesToStyles(['p-10px', 'mob-p-5px']);
406
+ const styles = classesToStyles(['p-[10px]', 'mobile:p-[5px]']);
305
407
  expect(styles).toEqual({
306
408
  base: { padding: '10px' },
307
409
  mobile: { padding: '5px' },
@@ -313,10 +415,19 @@ describe('utilityClassMapper', () => {
313
415
  });
314
416
 
315
417
  test('removes empty breakpoints', () => {
316
- const styles = classesToStyles(['p-10px']);
418
+ const styles = classesToStyles(['p-[10px]']);
317
419
  expect(styles).toEqual({ base: { padding: '10px' } });
318
420
  expect(styles?.tablet).toBeUndefined();
319
421
  expect(styles?.mobile).toBeUndefined();
320
422
  });
423
+
424
+ test('round-trips styles → classes → styles', () => {
425
+ const original = {
426
+ base: { padding: '92px 0', display: 'flex', color: 'primary', width: '100%' },
427
+ mobile: { padding: '12px 0' },
428
+ };
429
+ const classes = responsiveStylesToClasses(original);
430
+ expect(classesToStyles(classes)).toEqual(original);
431
+ });
321
432
  });
322
433
  });