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
@@ -37,7 +37,13 @@ import { NODE_TYPE, RAW_HTML_PREFIX } from '../../shared/constants';
37
37
  import { extractInteractiveStyleMappings, hasInteractiveStyleMappings } from '../../shared/interactiveStyleMappings';
38
38
  import type { ImageMetadataMap } from '../ssr/imageMetadata';
39
39
  import { isI18nValue, resolveI18nValue, DEFAULT_I18N_CONFIG, buildLocalizedPath } from '../../shared/i18n';
40
- import { transformCMSTemplate, isTemplateExpression, transformItemTemplate, replaceItemMetaVars, rewriteItemVar } from './templateTransformer';
40
+ import {
41
+ transformCMSTemplate,
42
+ isTemplateExpression,
43
+ transformItemTemplate,
44
+ replaceItemMetaVars,
45
+ rewriteItemVar,
46
+ } from './templateTransformer';
41
47
  import type { SlugMap } from '../../shared/slugTranslator';
42
48
  import { buildSlugIndex, translatePath } from '../../shared/slugTranslator';
43
49
  import { stripRawHtmlPrefixDeep, astroComponentName } from './astroEmitHelpers';
@@ -177,20 +183,12 @@ function localizeHref(href: string, ctx: AstroEmitContext): string {
177
183
 
178
184
  function isStyleMapping(value: unknown): value is StyleMapping {
179
185
  return (
180
- typeof value === 'object' &&
181
- value !== null &&
182
- '_mapping' in value &&
183
- (value as StyleMapping)._mapping === true
186
+ typeof value === 'object' && value !== null && '_mapping' in value && (value as StyleMapping)._mapping === true
184
187
  );
185
188
  }
186
189
 
187
190
  function isLinkMapping(value: unknown): value is LinkMapping {
188
- return (
189
- typeof value === 'object' &&
190
- value !== null &&
191
- '_mapping' in value &&
192
- (value as LinkMapping)._mapping === true
193
- );
191
+ return typeof value === 'object' && value !== null && '_mapping' in value && (value as LinkMapping)._mapping === true;
194
192
  }
195
193
 
196
194
  /**
@@ -199,14 +197,12 @@ function isLinkMapping(value: unknown): value is LinkMapping {
199
197
  function emitAttrValue(key: string, value: string, ctx: AstroEmitContext): string {
200
198
  if (ctx.cmsMode && /\{\{cms\./.test(value)) {
201
199
  const b = ctx.cmsEntryBinding || 'entry';
202
- const w = (expr: string) => ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr;
200
+ const w = (expr: string) => (ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr);
203
201
  const fullMatch = value.match(/^\{\{cms\.([^}]+)\}\}$/);
204
202
  if (fullMatch) {
205
203
  return `${key}={${w(`${b}.data.${fullMatch[1].trim()}`)}}`;
206
204
  }
207
- const replaced = value.replace(/\{\{cms\.([^}]+)\}\}/g, (_, fp) =>
208
- `\${${w(`${b}.data.${fp.trim()}`)}}`
209
- );
205
+ const replaced = value.replace(/\{\{cms\.([^}]+)\}\}/g, (_, fp) => `\${${w(`${b}.data.${fp.trim()}`)}}`);
210
206
  return `${key}={\`${replaced}\`}`;
211
207
  }
212
208
  if (ctx.listItemBinding && /\{\{/.test(value)) {
@@ -229,17 +225,10 @@ function emitAttrValue(key: string, value: string, ctx: AstroEmitContext): strin
229
225
  }
230
226
 
231
227
  function isHtmlMapping(value: unknown): value is HtmlMapping {
232
- return (
233
- typeof value === 'object' &&
234
- value !== null &&
235
- '_mapping' in value &&
236
- (value as HtmlMapping)._mapping === true
237
- );
228
+ return typeof value === 'object' && value !== null && '_mapping' in value && (value as HtmlMapping)._mapping === true;
238
229
  }
239
230
 
240
- function isResponsiveStyle(
241
- style: StyleObject | ResponsiveStyleObject
242
- ): style is ResponsiveStyleObject {
231
+ function isResponsiveStyle(style: StyleObject | ResponsiveStyleObject): style is ResponsiveStyleObject {
243
232
  return 'base' in style || 'tablet' in style || 'mobile' in style;
244
233
  }
245
234
 
@@ -266,7 +255,7 @@ function escapeTemplateLiteral(s: string): string {
266
255
  * Get all style mappings from a style value, collecting { prop, property, values } tuples
267
256
  */
268
257
  function collectStyleMappings(
269
- style: StyleObject | ResponsiveStyleObject | undefined
258
+ style: StyleObject | ResponsiveStyleObject | undefined,
270
259
  ): Array<{ property: string; mapping: StyleMapping; breakpoint?: string }> {
271
260
  if (!style) return [];
272
261
  const result: Array<{ property: string; mapping: StyleMapping; breakpoint?: string }> = [];
@@ -299,14 +288,21 @@ function mappingToClassListEntries(
299
288
  mapping: StyleMapping,
300
289
  property: string,
301
290
  breakpointPrefix: string,
302
- ctx: AstroEmitContext
291
+ ctx: AstroEmitContext,
303
292
  ): string[] {
304
293
  const entries: string[] = [];
305
294
  const values = Object.entries(mapping.values);
306
295
  if (values.length === 0) return entries;
307
296
 
297
+ // Mappings reference component props by bare identifier. Those identifiers
298
+ // only exist inside a component definition — on a page the reference would
299
+ // be an undefined variable (an Astro build error). Mirror the runtime, where
300
+ // resolveStyleMapping() receives no props at page scope and drops the
301
+ // property: emit no conditional entries outside a component def.
302
+ if (!ctx.isComponentDef) return entries;
303
+
308
304
  // Generate utility class for each possible value
309
- const propRef = ctx.isComponentDef ? mapping.prop : mapping.prop;
305
+ const propRef = mapping.prop;
310
306
 
311
307
  if (values.length === 2) {
312
308
  const [[val1, css1], [val2, css2]] = values;
@@ -342,11 +338,7 @@ function coerceValue(val: string): string | boolean {
342
338
  /**
343
339
  * Generate a Tailwind class name for a single property:value pair
344
340
  */
345
- function getClassForValue(
346
- property: string,
347
- value: string | number,
348
- breakpointPrefix: string
349
- ): string | null {
341
+ function getClassForValue(property: string, value: string | number, breakpointPrefix: string): string | null {
350
342
  const twClass = propertyToTailwind(property, value);
351
343
  if (!twClass) return null;
352
344
  return breakpointPrefix ? `${breakpointPrefix}${twClass}` : twClass;
@@ -360,7 +352,7 @@ function buildClassAndStyleExpression(
360
352
  style: StyleObject | ResponsiveStyleObject | undefined,
361
353
  interactiveStyles: InteractiveStyles | undefined,
362
354
  elementClass: string | null,
363
- ctx: AstroEmitContext
355
+ ctx: AstroEmitContext,
364
356
  ): { classExpr: string; styleAttr: string } {
365
357
  // Static Tailwind classes from non-mapping styles
366
358
  const result = style
@@ -379,11 +371,12 @@ function buildClassAndStyleExpression(
379
371
  const mappings = collectStyleMappings(style);
380
372
 
381
373
  for (const { property, mapping, breakpoint } of mappings) {
382
- const bpValue = breakpoint === 'tablet'
383
- ? ctx.breakpoints.tablet?.breakpoint ?? 1024
384
- : breakpoint === 'mobile'
385
- ? ctx.breakpoints.mobile?.breakpoint ?? 540
386
- : 0;
374
+ const bpValue =
375
+ breakpoint === 'tablet'
376
+ ? (ctx.breakpoints.tablet?.breakpoint ?? 1024)
377
+ : breakpoint === 'mobile'
378
+ ? (ctx.breakpoints.mobile?.breakpoint ?? 540)
379
+ : 0;
387
380
  const prefix = bpValue ? `max-[${bpValue}px]:` : '';
388
381
  const entries = mappingToClassListEntries(mapping, property, prefix, ctx);
389
382
  conditionals.push(...entries);
@@ -392,17 +385,6 @@ function buildClassAndStyleExpression(
392
385
  // Build style attribute for dynamic (template expression) styles
393
386
  let styleAttr = '';
394
387
  if (Object.keys(dynamicStyles).length > 0 && ctx.isComponentDef) {
395
- const styleParts: string[] = [];
396
- for (const [cssProp, value] of Object.entries(dynamicStyles)) {
397
- // Convert {{propName}} to Astro expression in style
398
- const resolved = value.replace(/\{\{(.+?)\}\}/g, (_, expr) => {
399
- let trimmed = expr.trim();
400
- if (ctx.listItemBinding) trimmed = rewriteItemVar(trimmed, ctx.listItemBinding);
401
- if (ctx.listIndexVar) trimmed = replaceItemMetaVars(trimmed, ctx.listIndexVar, ctx.listSourceVar);
402
- return `\${${trimmed}}`;
403
- });
404
- styleParts.push(`${cssProp}: \${${resolved.includes('${') ? resolved.replace(/\$\{(.+?)\}/g, '$1') : `'${resolved}'`}}`);
405
- }
406
388
  // Build as template literal style attribute
407
389
  const entries: string[] = [];
408
390
  for (const [cssProp, value] of Object.entries(dynamicStyles)) {
@@ -418,7 +400,12 @@ function buildClassAndStyleExpression(
418
400
  }
419
401
 
420
402
  // Handle interactive style mappings: emit --is-N CSS variables as inline style
421
- if (interactiveStyles && interactiveStyles.length > 0 && ctx.isComponentDef && hasInteractiveStyleMappings(interactiveStyles)) {
403
+ if (
404
+ interactiveStyles &&
405
+ interactiveStyles.length > 0 &&
406
+ ctx.isComponentDef &&
407
+ hasInteractiveStyleMappings(interactiveStyles)
408
+ ) {
422
409
  const { mappings } = extractInteractiveStyleMappings(interactiveStyles);
423
410
  if (mappings.length > 0) {
424
411
  // Group mappings by prop for efficient ternary generation
@@ -430,7 +417,9 @@ function buildClassAndStyleExpression(
430
417
 
431
418
  if (entries.length === 2) {
432
419
  const [[val1, css1], [val2, css2]] = entries;
433
- varParts.push(`'${varName}': ${mapping.prop} === ${JSON.stringify(coerceValue(val1))} ? '${css1}' : '${css2}'`);
420
+ varParts.push(
421
+ `'${varName}': ${mapping.prop} === ${JSON.stringify(coerceValue(val1))} ? '${css1}' : '${css2}'`,
422
+ );
434
423
  } else {
435
424
  // Build a lookup object inline
436
425
  const lookupEntries = entries
@@ -443,9 +432,7 @@ function buildClassAndStyleExpression(
443
432
 
444
433
  // Merge with existing dynamic styles
445
434
  if (varParts.length > 0) {
446
- const existingStyleParts = styleAttr
447
- ? styleAttr.replace(/^ style=\{`/, '').replace(/`\}$/, '')
448
- : '';
435
+ const existingStyleParts = styleAttr ? styleAttr.replace(/^ style=\{`/, '').replace(/`\}$/, '') : '';
449
436
  const varStyleExpr = varParts.join(', ');
450
437
  if (existingStyleParts) {
451
438
  styleAttr = ` style={\`${existingStyleParts}; \${ Object.entries({${varStyleExpr}}).map(([k,v]) => \`\${k}:\${v}\`).join(';') }\`}`;
@@ -516,10 +503,7 @@ function hasTemplates(text: string): boolean {
516
503
  /**
517
504
  * Build element class name for interactive styles
518
505
  */
519
- function buildElementClass(
520
- ctx: AstroEmitContext,
521
- label: string | undefined
522
- ): string {
506
+ function buildElementClass(ctx: AstroEmitContext, label: string | undefined): string {
523
507
  return generateElementClassName({
524
508
  fileType: ctx.fileType,
525
509
  fileName: ctx.fileName,
@@ -528,9 +512,7 @@ function buildElementClass(
528
512
  });
529
513
  }
530
514
 
531
- function isResponsiveStyleObject(
532
- style: StyleObject | ResponsiveStyleObject
533
- ): style is ResponsiveStyleObject {
515
+ function isResponsiveStyleObject(style: StyleObject | ResponsiveStyleObject): style is ResponsiveStyleObject {
534
516
  return 'base' in style || 'tablet' in style || 'mobile' in style;
535
517
  }
536
518
 
@@ -539,14 +521,10 @@ function isResponsiveStyleObject(
539
521
  * link roots, or `props.style` for a component/link-instance root. Used to
540
522
  * replicate SSR's per-breakpoint merge of an instance override into the root.
541
523
  */
542
- function getComponentRootStyle(
543
- def: ComponentDefinition | undefined
544
- ): StyleObject | ResponsiveStyleObject | undefined {
545
- const root = def?.component?.structure as
546
- | { style?: unknown; props?: { style?: unknown } }
547
- | undefined;
524
+ function getComponentRootStyle(def: ComponentDefinition | undefined): StyleObject | ResponsiveStyleObject | undefined {
525
+ const root = def?.component?.structure as { style?: unknown; props?: { style?: unknown } } | undefined;
548
526
  if (!root || typeof root !== 'object') return undefined;
549
- const s = (root.style ?? root.props?.style);
527
+ const s = root.style ?? root.props?.style;
550
528
  return s && typeof s === 'object' ? (s as StyleObject | ResponsiveStyleObject) : undefined;
551
529
  }
552
530
 
@@ -559,7 +537,7 @@ function getComponentRootStyle(
559
537
  function hasNonEmptyStyle(style: StyleObject | ResponsiveStyleObject): boolean {
560
538
  if (isResponsiveStyleObject(style)) {
561
539
  return Object.values(style).some(
562
- (branch) => !!branch && typeof branch === 'object' && Object.keys(branch as object).length > 0
540
+ (branch) => !!branch && typeof branch === 'object' && Object.keys(branch as object).length > 0,
563
541
  );
564
542
  }
565
543
  return Object.keys(style).length > 0;
@@ -583,22 +561,19 @@ function stripTemplateExpressionStyle<T extends StyleObject | ResponsiveStyleObj
583
561
  if (isResponsiveStyleObject(style)) {
584
562
  const out: Record<string, unknown> = {};
585
563
  for (const [bp, branch] of Object.entries(style as Record<string, unknown>)) {
586
- out[bp] = branch && typeof branch === 'object'
587
- ? cleanFlat(branch as Record<string, unknown>)
588
- : branch;
564
+ out[bp] = branch && typeof branch === 'object' ? cleanFlat(branch as Record<string, unknown>) : branch;
589
565
  }
590
566
  return out as T;
591
567
  }
592
568
  return cleanFlat(style as Record<string, unknown>) as T;
593
569
  }
594
570
 
595
-
596
571
  /**
597
572
  * Build HTML attributes string from node attributes
598
573
  */
599
574
  function buildAttributesString(
600
575
  attributes: Record<string, string | number | boolean> | undefined,
601
- ctx: AstroEmitContext
576
+ ctx: AstroEmitContext,
602
577
  ): string {
603
578
  if (!attributes) return '';
604
579
  const parts: string[] = [];
@@ -651,7 +626,7 @@ function buildAttributesString(
651
626
  } else if (ctx.cmsMode && /\{\{cms\./.test(strVal)) {
652
627
  // CMS mode: transform {{cms.field}} in attributes
653
628
  const b = ctx.cmsEntryBinding || 'entry';
654
- const w = (expr: string) => ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr;
629
+ const w = (expr: string) => (ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr);
655
630
  const fullMatch = strVal.match(/^\{\{cms\.([^}]+)\}\}$/);
656
631
  if (fullMatch) {
657
632
  parts.push(`${key}={${w(`${b}.data.${fullMatch[1].trim()}`)}}`);
@@ -702,7 +677,7 @@ function resolveI18n(value: unknown, ctx: AstroEmitContext): unknown {
702
677
  */
703
678
  export function nodeToAstro(
704
679
  node: ComponentNode | ComponentNode[] | string | number | null | undefined,
705
- ctx: AstroEmitContext
680
+ ctx: AstroEmitContext,
706
681
  ): string {
707
682
  if (node === null || node === undefined) return '';
708
683
 
@@ -718,6 +693,18 @@ export function nodeToAstro(
718
693
  ctx.needsI18nResolver = true;
719
694
  return `${ind(ctx)}{r(${JSON.stringify(stripRawHtmlPrefixDeep(resolved))})}\n`;
720
695
  }
696
+ // Page context without a locale: resolve through the default locale
697
+ // (resolveTranslation falls back exact → default → first available →
698
+ // empty) instead of stringifying the i18n object to "[object Object]".
699
+ if (isI18nValue(resolved)) {
700
+ const config = ctx.i18nConfig ?? DEFAULT_I18N_CONFIG;
701
+ const fallbackLocale = ctx.defaultLocale ?? config.defaultLocale;
702
+ const fallbackResolved = resolveI18nValue(resolved, fallbackLocale, config);
703
+ if (typeof fallbackResolved === 'string') {
704
+ return nodeToAstro(fallbackResolved, ctx);
705
+ }
706
+ return `${ind(ctx)}${String(fallbackResolved ?? '')}\n`;
707
+ }
721
708
  // If resolution returned a non-string (e.g. array), stringify it
722
709
  return `${ind(ctx)}${String(resolved ?? '')}\n`;
723
710
  }
@@ -726,7 +713,12 @@ export function nodeToAstro(
726
713
  if (typeof node === 'string') {
727
714
  // CMS mode: transform {{cms.field}} expressions to entry data access
728
715
  if (ctx.cmsMode && isTemplateExpression(node) && /\{\{cms\./.test(node)) {
729
- const transformed = transformCMSTemplate(node, ctx.cmsEntryBinding || 'entry', ctx.cmsRichTextFields, ctx.cmsWrapFn);
716
+ const transformed = transformCMSTemplate(
717
+ node,
718
+ ctx.cmsEntryBinding || 'entry',
719
+ ctx.cmsRichTextFields,
720
+ ctx.cmsWrapFn,
721
+ );
730
722
  return `${ind(ctx)}${transformed}\n`;
731
723
  }
732
724
  // List item binding: transform {{item.field}} expressions
@@ -821,7 +813,7 @@ function splitImageClasses(allClasses: string[]): { pictureClasses: string[]; im
821
813
  for (const cls of allClasses) {
822
814
  // Strip responsive prefix (e.g., "md:object-cover" -> "object-cover") for matching
823
815
  const baseCls = cls.includes(':') ? cls.split(':').pop()! : cls;
824
- if (IMG_TAILWIND_PREFIXES.some(p => baseCls.startsWith(p)) || IMG_OPACITY_PATTERN.test(baseCls)) {
816
+ if (IMG_TAILWIND_PREFIXES.some((p) => baseCls.startsWith(p)) || IMG_OPACITY_PATTERN.test(baseCls)) {
825
817
  imgClasses.push(cls);
826
818
  } else {
827
819
  pictureClasses.push(cls);
@@ -852,7 +844,7 @@ function extractPxWidth(val: unknown): number | null {
852
844
  */
853
845
  function computeSizesAttribute(
854
846
  style: StyleObject | ResponsiveStyleObject | undefined,
855
- breakpoints: BreakpointConfig
847
+ breakpoints: BreakpointConfig,
856
848
  ): string {
857
849
  if (!style) return DEFAULT_SIZES;
858
850
 
@@ -868,9 +860,7 @@ function computeSizesAttribute(
868
860
 
869
861
  // Walk breakpoints from smallest to largest so the emitted media queries
870
862
  // are ordered from narrowest to widest, with the unconditional base last.
871
- const bpEntries = Object.entries(breakpoints).sort(
872
- (a, b) => a[1].breakpoint - b[1].breakpoint
873
- );
863
+ const bpEntries = Object.entries(breakpoints).sort((a, b) => a[1].breakpoint - b[1].breakpoint);
874
864
 
875
865
  const parts: string[] = [];
876
866
  // Track the "effective" width as we walk up: if a breakpoint doesn't set its
@@ -905,13 +895,34 @@ function computeSizesAttribute(
905
895
  }
906
896
 
907
897
  /**
908
- * Merge additional CSS declarations into an existing ` style="..."` attribute
909
- * string. If no style attribute exists yet, builds one. The inputs are raw CSS
910
- * already escaped for HTML attribute context.
898
+ * Merge additional CSS declarations into an existing ` style` attribute
899
+ * string. If no style attribute exists yet, builds a static one. Handles all
900
+ * three attr shapes the emitter produces: static `style="..."`, dynamic
901
+ * template-literal `style={\`...\`}`, and dynamic expression `style={...}`.
902
+ * The inputs are raw CSS already escaped for HTML attribute context.
911
903
  */
912
904
  function injectInlineStyle(styleAttr: string, extraCss: string): string {
913
905
  if (!extraCss) return styleAttr;
914
906
  if (!styleAttr) return ` style="${extraCss}"`;
907
+
908
+ // Dynamic template-literal style attr (` style={`...`}`): append the extra
909
+ // declarations inside the template literal so they survive the merge.
910
+ const dynLiteralMatch = styleAttr.match(/^ style=\{`([\s\S]*)`\}$/);
911
+ if (dynLiteralMatch) {
912
+ const existing = dynLiteralMatch[1];
913
+ const trimmed = existing.trimEnd();
914
+ const sep = trimmed.length > 0 && !trimmed.endsWith(';') ? ';' : '';
915
+ return ` style={\`${existing}${sep}${extraCss}\`}`;
916
+ }
917
+
918
+ // Dynamic expression style attr (` style={expr}`, e.g. the Object.entries()
919
+ // form for interactive style mappings): wrap the expression in a template
920
+ // literal and append the extra declarations after it.
921
+ const dynExprMatch = styleAttr.match(/^ style=\{([\s\S]*)\}$/);
922
+ if (dynExprMatch) {
923
+ return ` style={\`\${${dynExprMatch[1]}};${extraCss}\`}`;
924
+ }
925
+
915
926
  return styleAttr.replace(/style="([^"]*)"/, (_, existing: string) => {
916
927
  const trimmed = existing.trimEnd();
917
928
  const sep = trimmed.length > 0 && !trimmed.endsWith(';') ? ';' : '';
@@ -932,7 +943,10 @@ const PICTURE_WIDTHS = [500, 800, 1080, 1600, 2400];
932
943
  * identifier (`imgSubHeroPhoto`) for use as an ESM import variable.
933
944
  */
934
945
  function imagePathToVarName(srcPath: string): string {
935
- const stripped = srcPath.replace(/^\/+/, '').replace(/^images\//, '').replace(/\.[^.]+$/, '');
946
+ const stripped = srcPath
947
+ .replace(/^\/+/, '')
948
+ .replace(/^images\//, '')
949
+ .replace(/\.[^.]+$/, '');
936
950
  const parts = stripped.split(/[/_\-\s.]+/).filter(Boolean);
937
951
  if (parts.length === 0) return 'imgAsset';
938
952
  const camel = parts
@@ -1003,9 +1017,7 @@ function registerStaticImageImport(src: string, ctx: AstroEmitContext): string {
1003
1017
  * object-cover / opacity-* keep landing on the <img>. Returns null when the
1004
1018
  * class expression is dynamic (class:list) — callers fall back to div wrap.
1005
1019
  */
1006
- function splitStaticClassExpr(
1007
- classExpr: string
1008
- ): { outerClasses: string[]; innerClasses: string[] } | null {
1020
+ function splitStaticClassExpr(classExpr: string): { outerClasses: string[]; innerClasses: string[] } | null {
1009
1021
  if (classExpr.includes('class:list=')) return null;
1010
1022
  const classMatch = classExpr.match(/class="([^"]*)"/);
1011
1023
  if (!classMatch) return { outerClasses: [], innerClasses: [] };
@@ -1018,10 +1030,7 @@ function splitStaticClassExpr(
1018
1030
  * Format a static class+style pair as an Astro `pictureAttributes={{...}}`
1019
1031
  * prop. `classValue` comes pre-split (outer layout classes only).
1020
1032
  */
1021
- function formatPictureAttributesProp(
1022
- classValue: string,
1023
- styleAttr: string
1024
- ): string {
1033
+ function formatPictureAttributesProp(classValue: string, styleAttr: string): string {
1025
1034
  const styleMatch = styleAttr.match(/style="([^"]*)"/);
1026
1035
  const parts: string[] = [];
1027
1036
  if (classValue) parts.push(`class: ${JSON.stringify(classValue)}`);
@@ -1045,7 +1054,7 @@ function emitStaticPictureImage(
1045
1054
  ifExpr: string,
1046
1055
  ifClose: string,
1047
1056
  blurHash: string | undefined,
1048
- ctx: AstroEmitContext
1057
+ ctx: AstroEmitContext,
1049
1058
  ): string {
1050
1059
  const varName = registerStaticImageImport(src, ctx);
1051
1060
 
@@ -1093,9 +1102,7 @@ function emitStaticPictureImage(
1093
1102
  // on the <picture>, inner img-specific classes as `class` on <Picture>.
1094
1103
  if (split) {
1095
1104
  const outerClassValue = split.outerClasses.join(' ');
1096
- const innerClassAttr = split.innerClasses.length > 0
1097
- ? ` class="${split.innerClasses.join(' ')}"`
1098
- : '';
1105
+ const innerClassAttr = split.innerClasses.length > 0 ? ` class="${split.innerClasses.join(' ')}"` : '';
1099
1106
  const pictureAttrs = formatPictureAttributesProp(outerClassValue, styleAttr);
1100
1107
  return `${ifExpr}${ind(ctx)}<Picture${pictureAttrs} src={${varName}}${altAttr}${innerClassAttr} formats={['avif','webp']} widths={${widthsLiteral}} sizes="${escapeJSX(sizesValue)}"${loadingAttr}${fetchpriorityAttr} />\n${ifClose}`;
1101
1108
  }
@@ -1113,10 +1120,7 @@ function emitImageNode(node: HtmlNode, ctx: AstroEmitContext): string {
1113
1120
 
1114
1121
  // Element class for interactive styles
1115
1122
  let elementClass: string | null = null;
1116
- if (
1117
- (node.interactiveStyles && node.interactiveStyles.length > 0) ||
1118
- node.generateElementClass
1119
- ) {
1123
+ if ((node.interactiveStyles && node.interactiveStyles.length > 0) || node.generateElementClass) {
1120
1124
  elementClass = buildElementClass(ctx, node.label);
1121
1125
  if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1122
1126
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
@@ -1127,7 +1131,7 @@ function emitImageNode(node: HtmlNode, ctx: AstroEmitContext): string {
1127
1131
  style,
1128
1132
  node.interactiveStyles as InteractiveStyles | undefined,
1129
1133
  elementClass,
1130
- ctx
1134
+ ctx,
1131
1135
  );
1132
1136
 
1133
1137
  // Extract image-specific attributes
@@ -1166,7 +1170,7 @@ function emitImageNode(node: HtmlNode, ctx: AstroEmitContext): string {
1166
1170
  emitIfOpen(node, ctx),
1167
1171
  emitIfClose(node, ctx),
1168
1172
  metadata?.blurHash,
1169
- ctx
1173
+ ctx,
1170
1174
  );
1171
1175
  }
1172
1176
 
@@ -1198,12 +1202,8 @@ function emitImageNode(node: HtmlNode, ctx: AstroEmitContext): string {
1198
1202
  // is a replaced element and doesn't paint CSS backgrounds reliably).
1199
1203
  // Once the real image loads we clear the background on the wrapper so the
1200
1204
  // low-quality placeholder disappears.
1201
- const blurWrapperCss = hasBlur
1202
- ? `background-image:url(${escapeJSX(metadata!.blurHash!)});background-size:cover`
1203
- : '';
1204
- const blurOnload = hasBlur
1205
- ? ` onload="this.parentElement.style.backgroundImage=''"`
1206
- : '';
1205
+ const blurWrapperCss = hasBlur ? `background-image:url(${escapeJSX(metadata!.blurHash!)});background-size:cover` : '';
1206
+ const blurOnload = hasBlur ? ` onload="this.parentElement.style.backgroundImage=''"` : '';
1207
1207
 
1208
1208
  // Conditional rendering
1209
1209
  const ifExpr = emitIfOpen(node, ctx);
@@ -1302,17 +1302,19 @@ function emitHtmlNode(node: HtmlNode, ctx: AstroEmitContext): string {
1302
1302
 
1303
1303
  // Element class for interactive styles
1304
1304
  let elementClass: string | null = null;
1305
- if (
1306
- (node.interactiveStyles && node.interactiveStyles.length > 0) ||
1307
- node.generateElementClass
1308
- ) {
1305
+ if ((node.interactiveStyles && node.interactiveStyles.length > 0) || node.generateElementClass) {
1309
1306
  elementClass = buildElementClass(ctx, label);
1310
1307
  if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1311
1308
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
1312
1309
  }
1313
1310
  }
1314
1311
 
1315
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles as InteractiveStyles | undefined, elementClass, ctx);
1312
+ const { classExpr, styleAttr } = buildClassAndStyleExpression(
1313
+ style,
1314
+ node.interactiveStyles as InteractiveStyles | undefined,
1315
+ elementClass,
1316
+ ctx,
1317
+ );
1316
1318
  const attrs = buildAttributesString(node.attributes, ctx);
1317
1319
 
1318
1320
  // Dynamic tags use the variable name directly (capitalized, defined in frontmatter)
@@ -1369,7 +1371,7 @@ function emitComponentInstance(node: ComponentInstanceNode, ctx: AstroEmitContex
1369
1371
  }
1370
1372
  } else if (typeof value === 'string' && ctx.cmsMode && /\{\{cms\./.test(value)) {
1371
1373
  const b = ctx.cmsEntryBinding || 'entry';
1372
- const w = (expr: string) => ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr;
1374
+ const w = (expr: string) => (ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr);
1373
1375
  const fullMatch = value.match(/^\{\{cms\.([^}]+)\}\}$/);
1374
1376
  if (fullMatch) {
1375
1377
  propParts.push(`${key}={${w(`${b}.data.${fullMatch[1].trim()}`)}}`);
@@ -1433,9 +1435,9 @@ function emitComponentInstance(node: ComponentInstanceNode, ctx: AstroEmitContex
1433
1435
  // root are dropped downstream (styleObjectToCSS skips mappings) /
1434
1436
  // stripped here, falling back to the component's own emission.
1435
1437
  const rootStyle = getComponentRootStyle(ctx.globalComponents[node.component]);
1436
- const merged = (rootStyle
1437
- ? deepMergeStyles(rootStyle, instanceStyle!)
1438
- : instanceStyle!) as StyleObject | ResponsiveStyleObject;
1438
+ const merged = (rootStyle ? deepMergeStyles(rootStyle, instanceStyle!) : instanceStyle!) as
1439
+ | StyleObject
1440
+ | ResponsiveStyleObject;
1439
1441
  const cssSafe = stripTemplateExpressionStyle(merged);
1440
1442
  if (hasNonEmptyStyle(cssSafe)) rules.push({ style: cssSafe }); // no prefix/postfix → `.cls {…}`
1441
1443
  }
@@ -1460,22 +1462,14 @@ function emitComponentInstance(node: ComponentInstanceNode, ctx: AstroEmitContex
1460
1462
  return `${ifExpr}${ind(ctx)}<${name}${propsStr} />\n${emitIfClose(node, ctx)}`;
1461
1463
  }
1462
1464
 
1463
- return (
1464
- `${ifExpr}${ind(ctx)}<${name}${propsStr}>\n` +
1465
- children +
1466
- `${ind(ctx)}</${name}>\n${emitIfClose(node, ctx)}`
1467
- );
1465
+ return `${ifExpr}${ind(ctx)}<${name}${propsStr}>\n` + children + `${ind(ctx)}</${name}>\n${emitIfClose(node, ctx)}`;
1468
1466
  }
1469
1467
 
1470
1468
  function emitSlotMarker(node: SlotMarker, ctx: AstroEmitContext): string {
1471
1469
  if (node.default) {
1472
1470
  const defaultContent = emitChildren(node.default as (ComponentNode | string)[] | string | undefined, ctx);
1473
1471
  if (defaultContent.trim()) {
1474
- return (
1475
- `${ind(ctx)}<slot>\n` +
1476
- defaultContent +
1477
- `${ind(ctx)}</slot>\n`
1478
- );
1472
+ return `${ind(ctx)}<slot>\n` + defaultContent + `${ind(ctx)}</slot>\n`;
1479
1473
  }
1480
1474
  }
1481
1475
  return `${ind(ctx)}<slot />\n`;
@@ -1486,17 +1480,19 @@ function emitEmbedNode(node: EmbedNode, ctx: AstroEmitContext): string {
1486
1480
  const style = node.style as StyleObject | ResponsiveStyleObject | undefined;
1487
1481
 
1488
1482
  let elementClass: string | null = null;
1489
- if (
1490
- (node.interactiveStyles && node.interactiveStyles.length > 0) ||
1491
- node.generateElementClass
1492
- ) {
1483
+ if ((node.interactiveStyles && node.interactiveStyles.length > 0) || node.generateElementClass) {
1493
1484
  elementClass = buildElementClass(ctx, node.label);
1494
1485
  if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1495
1486
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
1496
1487
  }
1497
1488
  }
1498
1489
 
1499
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles as InteractiveStyles | undefined, elementClass, ctx);
1490
+ const { classExpr, styleAttr } = buildClassAndStyleExpression(
1491
+ style,
1492
+ node.interactiveStyles as InteractiveStyles | undefined,
1493
+ elementClass,
1494
+ ctx,
1495
+ );
1500
1496
  const attrs = buildAttributesString(node.attributes, ctx);
1501
1497
 
1502
1498
  // Handle HTML mapping (prop-dependent content)
@@ -1561,10 +1557,7 @@ function emitLinkNode(node: LinkNode, ctx: AstroEmitContext): string {
1561
1557
  const style = node.style as StyleObject | ResponsiveStyleObject | undefined;
1562
1558
 
1563
1559
  let elementClass: string | null = null;
1564
- if (
1565
- (node.interactiveStyles && node.interactiveStyles.length > 0) ||
1566
- node.generateElementClass
1567
- ) {
1560
+ if ((node.interactiveStyles && node.interactiveStyles.length > 0) || node.generateElementClass) {
1568
1561
  elementClass = buildElementClass(ctx, node.label);
1569
1562
  if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1570
1563
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
@@ -1572,7 +1565,12 @@ function emitLinkNode(node: LinkNode, ctx: AstroEmitContext): string {
1572
1565
  }
1573
1566
 
1574
1567
  // Build class expression with olink base class
1575
- const { classExpr, styleAttr } = buildClassAndStyleExpression(style, node.interactiveStyles as InteractiveStyles | undefined, elementClass, ctx);
1568
+ const { classExpr, styleAttr } = buildClassAndStyleExpression(
1569
+ style,
1570
+ node.interactiveStyles as InteractiveStyles | undefined,
1571
+ elementClass,
1572
+ ctx,
1573
+ );
1576
1574
  let finalClassExpr = classExpr;
1577
1575
  if (!classExpr.includes('olink')) {
1578
1576
  if (classExpr) {
@@ -1622,7 +1620,7 @@ function emitLinkNode(node: LinkNode, ctx: AstroEmitContext): string {
1622
1620
  }
1623
1621
  } else if (ctx.cmsMode && /\{\{cms\./.test(href)) {
1624
1622
  const b = ctx.cmsEntryBinding || 'entry';
1625
- const w = (expr: string) => ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr;
1623
+ const w = (expr: string) => (ctx.cmsWrapFn ? `${ctx.cmsWrapFn}(${expr})` : expr);
1626
1624
  const fullMatch = href.match(/^\{\{cms\.([^}]+)\}\}$/);
1627
1625
  if (fullMatch) {
1628
1626
  hrefAttr = ` href={${w(`${b}.data.${fullMatch[1].trim()}`)}}`;
@@ -1660,10 +1658,7 @@ function emitImageTypeNode(node: any, ctx: AstroEmitContext): string {
1660
1658
  const style = node.style as StyleObject | ResponsiveStyleObject | undefined;
1661
1659
 
1662
1660
  let elementClass: string | null = null;
1663
- if (
1664
- (node.interactiveStyles && node.interactiveStyles.length > 0) ||
1665
- node.generateElementClass
1666
- ) {
1661
+ if ((node.interactiveStyles && node.interactiveStyles.length > 0) || node.generateElementClass) {
1667
1662
  elementClass = buildElementClass(ctx, node.label);
1668
1663
  if (elementClass && node.interactiveStyles?.length && ctx.collectedInteractiveStyles) {
1669
1664
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
@@ -1674,7 +1669,7 @@ function emitImageTypeNode(node: any, ctx: AstroEmitContext): string {
1674
1669
  style,
1675
1670
  node.interactiveStyles as InteractiveStyles | undefined,
1676
1671
  elementClass,
1677
- ctx
1672
+ ctx,
1678
1673
  );
1679
1674
 
1680
1675
  const src = node.src as string | undefined;
@@ -1694,7 +1689,7 @@ function emitImageTypeNode(node: any, ctx: AstroEmitContext): string {
1694
1689
  '',
1695
1690
  '',
1696
1691
  staticMeta.blurHash,
1697
- ctx
1692
+ ctx,
1698
1693
  );
1699
1694
  }
1700
1695
 
@@ -1718,12 +1713,8 @@ function emitImageTypeNode(node: any, ctx: AstroEmitContext): string {
1718
1713
  const hasBlur = !!metadata.blurHash;
1719
1714
  const useWrapper = hasAvif || hasBlur;
1720
1715
 
1721
- const blurWrapperCss = hasBlur
1722
- ? `background-image:url(${escapeJSX(metadata.blurHash!)});background-size:cover`
1723
- : '';
1724
- const blurOnload = hasBlur
1725
- ? ` onload="this.parentElement.style.backgroundImage=''"`
1726
- : '';
1716
+ const blurWrapperCss = hasBlur ? `background-image:url(${escapeJSX(metadata.blurHash!)});background-size:cover` : '';
1717
+ const blurOnload = hasBlur ? ` onload="this.parentElement.style.backgroundImage=''"` : '';
1727
1718
 
1728
1719
  if (useWrapper) {
1729
1720
  const imgFillClasses = IMG_FILL_CLASSES.slice();
@@ -1812,19 +1803,17 @@ function emitListNode(node: ListNode, ctx: AstroEmitContext): string {
1812
1803
  };
1813
1804
 
1814
1805
  const children = node.children
1815
- ? (node.children as (ComponentNode | string)[]).map((child, i) => {
1816
- const childCtx = { ...innerCtx, elementPath: [...ctx.elementPath, i] };
1817
- const out = nodeToAstro(child, childCtx);
1818
- if (childCtx.needsI18nResolver) ctx.needsI18nResolver = true;
1819
- return out;
1820
- }).join('')
1806
+ ? (node.children as (ComponentNode | string)[])
1807
+ .map((child, i) => {
1808
+ const childCtx = { ...innerCtx, elementPath: [...ctx.elementPath, i] };
1809
+ const out = nodeToAstro(child, childCtx);
1810
+ if (childCtx.needsI18nResolver) ctx.needsI18nResolver = true;
1811
+ return out;
1812
+ })
1813
+ .join('')
1821
1814
  : '';
1822
1815
 
1823
- return (
1824
- `${ind(ctx)}{${mapSource}.map((${itemAs}, ${indexVar}) => (\n` +
1825
- children +
1826
- `${ind(ctx)}))}\n`
1827
- );
1816
+ return `${ind(ctx)}{${mapSource}.map((${itemAs}, ${indexVar}) => (\n` + children + `${ind(ctx)}))}\n`;
1828
1817
  }
1829
1818
 
1830
1819
  /**
@@ -1876,8 +1865,7 @@ function emitCollectionListNode(node: ListNode, ctx: AstroEmitContext): string {
1876
1865
  // shape the templates expect ({{item.field}} → item.field), and synthesize the
1877
1866
  // `_url`/`_id` meta the CMS data model exposes. Filter/sort above operate on the
1878
1867
  // raw entries (e.data.*); this map runs last so it sees the final set.
1879
- const urlExpr = ctx.collectionUrlExpr?.get(source)
1880
- ?? `\`/${source}/\${e.data.slug ?? e.id}\``;
1868
+ const urlExpr = ctx.collectionUrlExpr?.get(source) ?? `\`/${source}/\${e.data.slug ?? e.id}\``;
1881
1869
  queryChain += `.then(items => items.map((e) => ({ ...e.data, _id: e.id, _url: ${urlExpr} })))`;
1882
1870
 
1883
1871
  ctx.frontmatterLines.push(`const ${collectionVar} = ${queryChain};`);
@@ -1896,19 +1884,17 @@ function emitCollectionListNode(node: ListNode, ctx: AstroEmitContext): string {
1896
1884
  };
1897
1885
 
1898
1886
  const children = node.children
1899
- ? (node.children as (ComponentNode | string)[]).map((child, i) => {
1900
- const childCtx = { ...innerCtx, elementPath: [...ctx.elementPath, i] };
1901
- const out = nodeToAstro(child, childCtx);
1902
- if (childCtx.needsI18nResolver) ctx.needsI18nResolver = true;
1903
- return out;
1904
- }).join('')
1887
+ ? (node.children as (ComponentNode | string)[])
1888
+ .map((child, i) => {
1889
+ const childCtx = { ...innerCtx, elementPath: [...ctx.elementPath, i] };
1890
+ const out = nodeToAstro(child, childCtx);
1891
+ if (childCtx.needsI18nResolver) ctx.needsI18nResolver = true;
1892
+ return out;
1893
+ })
1894
+ .join('')
1905
1895
  : '';
1906
1896
 
1907
- return (
1908
- `${ind(ctx)}{${collectionVar}.map((${itemAs}, ${indexVar}) => (\n` +
1909
- children +
1910
- `${ind(ctx)}))}\n`
1911
- );
1897
+ return `${ind(ctx)}{${collectionVar}.map((${itemAs}, ${indexVar}) => (\n` + children + `${ind(ctx)}))}\n`;
1912
1898
  }
1913
1899
 
1914
1900
  /**
@@ -1932,7 +1918,10 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
1932
1918
  const style = node.style as StyleObject | ResponsiveStyleObject | undefined;
1933
1919
 
1934
1920
  let elementClass: string | null = null;
1935
- if ((node.interactiveStyles && (node.interactiveStyles as InteractiveStyles).length > 0) || node.generateElementClass) {
1921
+ if (
1922
+ (node.interactiveStyles && (node.interactiveStyles as InteractiveStyles).length > 0) ||
1923
+ node.generateElementClass
1924
+ ) {
1936
1925
  elementClass = buildElementClass(ctx, node.label);
1937
1926
  if (elementClass && (node.interactiveStyles as InteractiveStyles)?.length && ctx.collectedInteractiveStyles) {
1938
1927
  ctx.collectedInteractiveStyles.set(elementClass, node.interactiveStyles as InteractiveStyles);
@@ -1943,22 +1932,28 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
1943
1932
  style,
1944
1933
  node.interactiveStyles as InteractiveStyles | undefined,
1945
1934
  elementClass,
1946
- ctx
1935
+ ctx,
1947
1936
  );
1948
1937
 
1949
1938
  // Build item classes
1950
1939
  const itemStyle = node.itemStyle as StyleObject | ResponsiveStyleObject | undefined;
1951
- const itemResult = itemStyle ? responsiveStylesToTailwind(itemStyle, ctx.breakpoints, ctx.responsiveScales) : { classes: [], dynamicStyles: {} };
1940
+ const itemResult = itemStyle
1941
+ ? responsiveStylesToTailwind(itemStyle, ctx.breakpoints, ctx.responsiveScales)
1942
+ : { classes: [], dynamicStyles: {} };
1952
1943
  const itemClasses = itemResult.classes;
1953
1944
 
1954
1945
  // Build active item classes (item + active combined)
1955
1946
  const activeItemStyle = node.activeItemStyle as StyleObject | ResponsiveStyleObject | undefined;
1956
- const activeResult = activeItemStyle ? responsiveStylesToTailwind(activeItemStyle, ctx.breakpoints, ctx.responsiveScales) : { classes: [], dynamicStyles: {} };
1947
+ const activeResult = activeItemStyle
1948
+ ? responsiveStylesToTailwind(activeItemStyle, ctx.breakpoints, ctx.responsiveScales)
1949
+ : { classes: [], dynamicStyles: {} };
1957
1950
  const activeItemClasses = [...itemClasses, ...activeResult.classes];
1958
1951
 
1959
1952
  // Build separator classes
1960
1953
  const separatorStyle = node.separatorStyle as StyleObject | ResponsiveStyleObject | undefined;
1961
- const sepResult = separatorStyle ? responsiveStylesToTailwind(separatorStyle, ctx.breakpoints, ctx.responsiveScales) : { classes: [], dynamicStyles: {} };
1954
+ const sepResult = separatorStyle
1955
+ ? responsiveStylesToTailwind(separatorStyle, ctx.breakpoints, ctx.responsiveScales)
1956
+ : { classes: [], dynamicStyles: {} };
1962
1957
  const separatorClasses = sepResult.classes;
1963
1958
 
1964
1959
  // Build locale icon map
@@ -1971,7 +1966,9 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
1971
1966
 
1972
1967
  // Flag classes
1973
1968
  const flagStyle = node.flagStyle as StyleObject | ResponsiveStyleObject | undefined;
1974
- const flagResult = flagStyle ? responsiveStylesToTailwind(flagStyle, ctx.breakpoints, ctx.responsiveScales) : { classes: [], dynamicStyles: {} };
1969
+ const flagResult = flagStyle
1970
+ ? responsiveStylesToTailwind(flagStyle, ctx.breakpoints, ctx.responsiveScales)
1971
+ : { classes: [], dynamicStyles: {} };
1975
1972
  const flagClasses = flagResult.classes;
1976
1973
 
1977
1974
  // Build links
@@ -1993,9 +1990,16 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
1993
1990
  // Display text
1994
1991
  let displayText: string;
1995
1992
  switch (displayType) {
1996
- case 'code': displayText = code.toUpperCase(); break;
1997
- case 'name': displayText = localeConfig.name; break;
1998
- case 'nativeName': default: displayText = localeConfig.nativeName; break;
1993
+ case 'code':
1994
+ displayText = code.toUpperCase();
1995
+ break;
1996
+ case 'name':
1997
+ displayText = localeConfig.name;
1998
+ break;
1999
+ case 'nativeName':
2000
+ default:
2001
+ displayText = localeConfig.nativeName;
2002
+ break;
1999
2003
  }
2000
2004
 
2001
2005
  // Build link content
@@ -2007,7 +2011,9 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
2007
2011
  }
2008
2012
  linkContent += `<div>${escapeJSX(displayText)}</div>`;
2009
2013
 
2010
- links.push(`${ind(ctx)} <a href="${escapeJSX(path)}"${hreflangAttr}${currentAttr} data-locale="${escapeJSX(code)}"${classAttr}>${linkContent}</a>`);
2014
+ links.push(
2015
+ `${ind(ctx)} <a href="${escapeJSX(path)}"${hreflangAttr}${currentAttr} data-locale="${escapeJSX(code)}"${classAttr}>${linkContent}</a>`,
2016
+ );
2011
2017
  }
2012
2018
 
2013
2019
  // Join with separator
@@ -2023,7 +2029,8 @@ function emitLocaleListNode(node: LocaleListNode, ctx: AstroEmitContext): string
2023
2029
 
2024
2030
  return (
2025
2031
  `${ind(ctx)}<div data-locale-list="true"${containerClassExpr}${containerStyleAttr}${attrs}>\n` +
2026
- linksContent + '\n' +
2032
+ linksContent +
2033
+ '\n' +
2027
2034
  `${ind(ctx)}</div>\n`
2028
2035
  );
2029
2036
  }
@@ -2111,6 +2118,10 @@ function emitIfClose(node: ComponentNode, ctx: AstroEmitContext): string {
2111
2118
  if (typeof ifValue === 'boolean') return '';
2112
2119
 
2113
2120
  if (typeof ifValue === 'object' && ifValue._mapping && ctx.isComponentDef) {
2121
+ // Degenerate mapping (no true values): emitIfOpen emitted only a
2122
+ // `{/* hidden */}` comment, not a `{cond && (` opener — close nothing.
2123
+ const hasTrueValue = Object.values(ifValue.values).some((v) => v === true);
2124
+ if (!hasTrueValue) return '';
2114
2125
  return `${ind(ctx)})}\n`;
2115
2126
  }
2116
2127
 
@@ -2139,7 +2150,7 @@ function emitIfClose(node: ComponentNode, ctx: AstroEmitContext): string {
2139
2150
 
2140
2151
  function emitChildren(
2141
2152
  children: (ComponentNode | string)[] | string | ComponentNode | null | undefined,
2142
- ctx: AstroEmitContext
2153
+ ctx: AstroEmitContext,
2143
2154
  ): string {
2144
2155
  if (!children) return '';
2145
2156