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
@@ -4,36 +4,60 @@
4
4
  * responsive styles, event handlers, and element registration.
5
5
  */
6
6
 
7
- import { createElement as h, Fragment } from "react";
8
- import type { ReactElement } from "react";
7
+ import { createElement as h, Fragment } from 'react';
8
+ import type { ReactElement } from 'react';
9
9
 
10
10
  // Component registry and utilities
11
- import { ComponentRegistry } from "../componentRegistry";
12
- import { processStructure } from "../templateEngine";
13
- import { NODE_TYPE } from "../../shared/constants";
14
- import { ErrorBoundary } from "../ErrorBoundary";
15
- import type { ComponentNode, StyleValue, StyleObject, ResponsiveStyleObject } from "../../shared/types";
16
- import { isComponentNode, extractNodeProperties, isSlotMarker, isEmbedNode, isLinkNode, isLocaleListNode, isListNode, markAsSlotContent, evaluateNodeIf, isBooleanMapping, hasIf, hasChildren, isSlotContent as isSlotContentNode } from "../../shared/nodeUtils";
17
- import { mergeNodeStyles } from "../../shared/styleNodeUtils";
18
- import { isCurrentLink } from "../../shared/linkUtils";
19
- import { extractAttributesFromNode, skipEmptyTemplateAttributes } from "../../shared/attributeNodeUtils";
20
- import { resolvePropsFromDefinition } from "../../shared/propResolver";
21
- import { ElementRegistry } from "../elementRegistry";
22
- import { InteractiveStylesRegistry } from "../InteractiveStylesRegistry";
23
- import type { Path } from "../../shared/pathArrayUtils";
24
- import type { I18nConfig } from "../../shared/types/components";
25
- import type { ItemContext, TemplateContext } from "../../shared/types/cms";
26
- import { processItemTemplate, processItemPropsTemplate, hasItemTemplates, type ValueResolver } from "../../shared/itemTemplateUtils";
27
- import { DEFAULT_I18N_CONFIG, isI18nValue, resolveI18nValue } from "../../shared/i18n";
28
- import { getChildPath, pathToString } from "../../shared/pathArrayUtils";
29
- import { responsiveStylesToClasses } from "../../shared/utilityClassMapper";
30
- import { getCachedResponsiveScalesConfig } from "../responsiveStyleResolver";
31
- import { UtilityClassCollector } from "../styles/UtilityClassCollector";
32
- import { processCMSTemplate, processCMSPropsTemplate, RAW_HTML_PREFIX } from "./cmsTemplateProcessor";
33
- import type { PrefetchService } from "../services/PrefetchService";
34
- import { generateElementClassName, type ElementClassContext } from "../../shared/elementClassName";
35
- import type { InteractiveStyles } from "../../shared/types/styles";
36
- import { extractInteractiveStyleMappings, resolveExtractedMappings, hasInteractiveStyleMappings } from "../../shared/interactiveStyleMappings";
11
+ import type { ComponentRegistry } from '../componentRegistry';
12
+ import { processStructure } from '../templateEngine';
13
+ import { NODE_TYPE } from '../../shared/constants';
14
+ import { ErrorBoundary } from '../ErrorBoundary';
15
+ import type { ComponentNode, StyleValue, StyleObject, ResponsiveStyleObject } from '../../shared/types';
16
+ import {
17
+ isComponentNode,
18
+ extractNodeProperties,
19
+ isSlotMarker,
20
+ isEmbedNode,
21
+ isLinkNode,
22
+ isLocaleListNode,
23
+ isListNode,
24
+ isValidNodeType,
25
+ markAsSlotContent,
26
+ evaluateNodeIf,
27
+ isBooleanMapping,
28
+ hasIf,
29
+ hasChildren,
30
+ isSlotContent as isSlotContentNode,
31
+ } from '../../shared/nodeUtils';
32
+ import { mergeNodeStyles } from '../../shared/styleNodeUtils';
33
+ import { isCurrentLink } from '../../shared/linkUtils';
34
+ import { extractAttributesFromNode, skipEmptyTemplateAttributes } from '../../shared/attributeNodeUtils';
35
+ import { resolvePropsFromDefinition } from '../../shared/propResolver';
36
+ import type { ElementRegistry } from '../elementRegistry';
37
+ import { InteractiveStylesRegistry } from '../InteractiveStylesRegistry';
38
+ import type { Path } from '../../shared/pathArrayUtils';
39
+ import type { I18nConfig } from '../../shared/types/components';
40
+ import type { ItemContext, TemplateContext } from '../../shared/types/cms';
41
+ import {
42
+ processItemTemplate,
43
+ processItemPropsTemplate,
44
+ hasItemTemplates,
45
+ type ValueResolver,
46
+ } from '../../shared/itemTemplateUtils';
47
+ import { DEFAULT_I18N_CONFIG, isI18nValue, resolveI18nValue } from '../../shared/i18n';
48
+ import { getChildPath, pathToString } from '../../shared/pathArrayUtils';
49
+ import { responsiveStylesToClasses } from '../../shared/utilityClassMapper';
50
+ import { getCachedResponsiveScalesConfig } from '../responsiveStyleResolver';
51
+ import { UtilityClassCollector } from '../styles/UtilityClassCollector';
52
+ import { processCMSTemplate, processCMSPropsTemplate, RAW_HTML_PREFIX } from './cmsTemplateProcessor';
53
+ import type { PrefetchService } from '../services/PrefetchService';
54
+ import { generateElementClassName, type ElementClassContext } from '../../shared/elementClassName';
55
+ import type { InteractiveStyles } from '../../shared/types/styles';
56
+ import {
57
+ extractInteractiveStyleMappings,
58
+ resolveExtractedMappings,
59
+ hasInteractiveStyleMappings,
60
+ } from '../../shared/interactiveStyleMappings';
37
61
 
38
62
  // Extracted builders
39
63
  import {
@@ -46,10 +70,20 @@ import {
46
70
  buildLocaleList,
47
71
  buildList,
48
72
  buildLink,
49
- } from "./builders";
73
+ } from './builders';
50
74
 
51
75
  type ComponentProps = Record<string, unknown>;
52
76
 
77
+ /**
78
+ * Narrow processStructure's object result to a renderable node.
79
+ * processStructure returns a plain Record<string, unknown> when the input was
80
+ * not a node (e.g. a props object) — real nodes always carry a valid `type`
81
+ * discriminant. Non-node objects can't be rendered, so callers bail out.
82
+ */
83
+ function isProcessedComponentNode(value: ComponentNode | Record<string, unknown>): value is ComponentNode {
84
+ return typeof value.type === 'string' && isValidNodeType(value.type);
85
+ }
86
+
53
87
  // Document-structure tags can't nest inside <div id="root">: the HTML5 parser
54
88
  // hoists a nested <body>'s attributes onto the outer <body> and drops the tag,
55
89
  // so SSR and client-side (React createElement) end up with divergent DOMs.
@@ -101,12 +135,11 @@ export class ComponentBuilder {
101
135
  let cached = this.styleClassCache.get(style);
102
136
  if (!cached) {
103
137
  const scales = getCachedResponsiveScalesConfig();
104
- const fluidActive =
105
- scales?.enabled === true && scales?.mode === 'fluid';
106
- cached = responsiveStylesToClasses(
107
- style as StyleObject | ResponsiveStyleObject,
108
- { fluidActive, responsiveScales: scales ?? undefined }
109
- );
138
+ const fluidActive = scales?.enabled === true && scales?.mode === 'fluid';
139
+ cached = responsiveStylesToClasses(style as StyleObject | ResponsiveStyleObject, {
140
+ fluidActive,
141
+ responsiveScales: scales ?? undefined,
142
+ });
110
143
  this.styleClassCache.set(style, cached);
111
144
  }
112
145
  UtilityClassCollector.collect(cached);
@@ -128,7 +161,7 @@ export class ComponentBuilder {
128
161
  private getParentComponentNameForNestedComponent(
129
162
  componentContext: string | null,
130
163
  parentComponentName: string | null,
131
- componentName: string | null
164
+ componentName: string | null,
132
165
  ): string | null {
133
166
  return componentContext || parentComponentName || componentName || null;
134
167
  }
@@ -138,7 +171,7 @@ export class ComponentBuilder {
138
171
  */
139
172
  private getEffectiveParentComponentName(
140
173
  componentContext: string | null,
141
- parentComponentName: string | null
174
+ parentComponentName: string | null,
142
175
  ): string | null {
143
176
  return componentContext || parentComponentName;
144
177
  }
@@ -235,12 +268,24 @@ export class ComponentBuilder {
235
268
  */
236
269
  buildChildren(
237
270
  children: (string | ComponentNode)[] | string | ComponentNode | undefined,
238
- ctx: BuildChildrenContext
271
+ ctx: BuildChildrenContext,
239
272
  ): ReactElement | ReactElement[] | string | number | null {
240
273
  const {
241
- elementPath, parentComponentName, viewportWidth, componentContext, componentRootPath,
242
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap,
243
- itemContext, cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
274
+ elementPath,
275
+ parentComponentName,
276
+ viewportWidth,
277
+ componentContext,
278
+ componentRootPath,
279
+ locale,
280
+ i18nConfig,
281
+ cmsContext,
282
+ cmsLocale,
283
+ collectionItemsMap,
284
+ itemContext,
285
+ cmsItemIndexPath,
286
+ cmsListPaths,
287
+ templateContext,
288
+ componentResolvedProps,
244
289
  } = ctx;
245
290
 
246
291
  if (Array.isArray(children)) {
@@ -264,8 +309,8 @@ export class ComponentBuilder {
264
309
  cmsItemIndexPath,
265
310
  cmsListPaths,
266
311
  templateContext,
267
- componentResolvedProps
268
- })
312
+ componentResolvedProps,
313
+ }),
269
314
  )
270
315
  .filter((item): item is ReactElement | string | number => item !== null) as ReactElement[];
271
316
  }
@@ -287,7 +332,7 @@ export class ComponentBuilder {
287
332
  cmsItemIndexPath,
288
333
  cmsListPaths,
289
334
  templateContext,
290
- componentResolvedProps
335
+ componentResolvedProps,
291
336
  });
292
337
  }
293
338
 
@@ -313,7 +358,7 @@ export class ComponentBuilder {
313
358
  templateContext = null,
314
359
  cmsItemIndexPath = null,
315
360
  cmsListPaths = null,
316
- componentResolvedProps = null
361
+ componentResolvedProps = null,
317
362
  } = options;
318
363
 
319
364
  if (!node) return null;
@@ -340,9 +385,22 @@ export class ComponentBuilder {
340
385
 
341
386
  // Build context for specialized builders (needed for if condition evaluation)
342
387
  const ctx: BuilderContext = {
343
- key, elementPath, parentComponentName, viewportWidth, componentContext, componentRootPath,
344
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap,
345
- itemContext, cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
388
+ key,
389
+ elementPath,
390
+ parentComponentName,
391
+ viewportWidth,
392
+ componentContext,
393
+ componentRootPath,
394
+ locale,
395
+ i18nConfig,
396
+ cmsContext,
397
+ cmsLocale,
398
+ collectionItemsMap,
399
+ itemContext,
400
+ cmsItemIndexPath,
401
+ cmsListPaths,
402
+ templateContext,
403
+ componentResolvedProps,
346
404
  };
347
405
 
348
406
  // Dependencies for specialized builders
@@ -380,7 +438,7 @@ export class ComponentBuilder {
380
438
 
381
439
  // Get node type
382
440
  const nodeType = typeof node === 'object' && node !== null && 'type' in node ? node.type : undefined;
383
- const children = typeof node === 'object' && node !== null && 'children' in node ? (node.children || []) : [];
441
+ const children = typeof node === 'object' && node !== null && 'children' in node ? node.children || [] : [];
384
442
 
385
443
  // Handle specialized node types using extracted builders
386
444
  if (nodeType === NODE_TYPE.EMBED && isEmbedNode(node)) {
@@ -429,23 +487,27 @@ export class ComponentBuilder {
429
487
  if (process.env.NODE_ENV !== 'production') {
430
488
  console.warn(
431
489
  `[Meno] Component "${componentName}" not found in registry. ` +
432
- `Registered: [${this.componentRegistry.getNames().join(', ')}]`
490
+ `Registered: [${this.componentRegistry.getNames().join(', ')}]`,
433
491
  );
434
492
  }
435
- return h('div', {
436
- key,
437
- style: {
438
- border: '1px dashed #e5a00d',
439
- padding: '8px 12px',
440
- margin: '4px 0',
441
- borderRadius: '4px',
442
- color: '#b8860b',
443
- fontSize: '13px',
444
- fontFamily: 'monospace',
445
- background: '#fffbe6',
493
+ return h(
494
+ 'div',
495
+ {
496
+ key,
497
+ style: {
498
+ border: '1px dashed #e5a00d',
499
+ padding: '8px 12px',
500
+ margin: '4px 0',
501
+ borderRadius: '4px',
502
+ color: '#b8860b',
503
+ fontSize: '13px',
504
+ fontFamily: 'monospace',
505
+ background: '#fffbe6',
506
+ },
507
+ 'data-missing-component': componentName,
446
508
  },
447
- 'data-missing-component': componentName,
448
- }, `Component not found: ${componentName}`);
509
+ `Component not found: ${componentName}`,
510
+ );
449
511
  }
450
512
 
451
513
  // Handle Link component
@@ -536,7 +598,9 @@ export class ComponentBuilder {
536
598
 
537
599
  // Add remaining HTML
538
600
  if (lastIndex < html.length) {
539
- segments.push(h('span', { key: `rt-${segmentIndex}`, dangerouslySetInnerHTML: { __html: html.slice(lastIndex) } }));
601
+ segments.push(
602
+ h('span', { key: `rt-${segmentIndex}`, dangerouslySetInnerHTML: { __html: html.slice(lastIndex) } }),
603
+ );
540
604
  }
541
605
 
542
606
  return h(Fragment, null, ...segments);
@@ -580,34 +644,47 @@ export class ComponentBuilder {
580
644
  */
581
645
  private buildNodeArray(nodes: ComponentNode[], options: BuildComponentOptions): ReactElement[] {
582
646
  const {
583
- customProps, elementPath = [0], parentComponentName = null, viewportWidth = 1920,
584
- componentContext = null, locale, i18nConfig, cmsContext = null, cmsLocale = null,
585
- collectionItemsMap = {}, itemContext = null, cmsItemIndexPath = null,
586
- cmsListPaths = null, templateContext = null, componentResolvedProps = null
647
+ customProps,
648
+ elementPath = [0],
649
+ parentComponentName = null,
650
+ viewportWidth = 1920,
651
+ componentContext = null,
652
+ locale,
653
+ i18nConfig,
654
+ cmsContext = null,
655
+ cmsLocale = null,
656
+ collectionItemsMap = {},
657
+ itemContext = null,
658
+ cmsItemIndexPath = null,
659
+ cmsListPaths = null,
660
+ templateContext = null,
661
+ componentResolvedProps = null,
587
662
  } = options;
588
663
 
589
- return nodes.map((child, index) => {
590
- const childPath = getChildPath(elementPath, index);
591
- return this.buildComponent({
592
- node: child,
593
- key: index,
594
- customProps,
595
- elementPath: childPath,
596
- parentComponentName,
597
- viewportWidth,
598
- componentContext,
599
- locale,
600
- i18nConfig,
601
- cmsContext,
602
- cmsLocale,
603
- collectionItemsMap,
604
- itemContext,
605
- cmsItemIndexPath,
606
- cmsListPaths,
607
- templateContext,
608
- componentResolvedProps
609
- });
610
- }).filter((item): item is ReactElement => item !== null && typeof item === 'object');
664
+ return nodes
665
+ .map((child, index) => {
666
+ const childPath = getChildPath(elementPath, index);
667
+ return this.buildComponent({
668
+ node: child,
669
+ key: index,
670
+ customProps,
671
+ elementPath: childPath,
672
+ parentComponentName,
673
+ viewportWidth,
674
+ componentContext,
675
+ locale,
676
+ i18nConfig,
677
+ cmsContext,
678
+ cmsLocale,
679
+ collectionItemsMap,
680
+ itemContext,
681
+ cmsItemIndexPath,
682
+ cmsListPaths,
683
+ templateContext,
684
+ componentResolvedProps,
685
+ });
686
+ })
687
+ .filter((item): item is ReactElement => item !== null && typeof item === 'object');
611
688
  }
612
689
 
613
690
  /**
@@ -659,7 +736,11 @@ export class ComponentBuilder {
659
736
  /**
660
737
  * Apply style classes to props
661
738
  */
662
- private applyStyleClasses(props: Record<string, unknown>, node: ComponentNode, ctx: BuilderContext): Record<string, unknown> {
739
+ private applyStyleClasses(
740
+ props: Record<string, unknown>,
741
+ node: ComponentNode,
742
+ ctx: BuilderContext,
743
+ ): Record<string, unknown> {
663
744
  const nodeStyle = node.style || (isComponentNode(node) ? node.props?.style : undefined);
664
745
 
665
746
  if (nodeStyle && typeof nodeStyle === 'object') {
@@ -678,7 +759,11 @@ export class ComponentBuilder {
678
759
  if (utilityClasses.length > 0) {
679
760
  const existingClassName = (props.className || '') as string;
680
761
  const classArray = existingClassName ? existingClassName.split(/\s+/) : [];
681
- return { ...props, className: [...classArray, ...utilityClasses].filter(Boolean).join(' '), style: processedStyle };
762
+ return {
763
+ ...props,
764
+ className: [...classArray, ...utilityClasses].filter(Boolean).join(' '),
765
+ style: processedStyle,
766
+ };
682
767
  }
683
768
  }
684
769
 
@@ -688,10 +773,16 @@ export class ComponentBuilder {
688
773
  /**
689
774
  * Apply interactive styles and generate element class
690
775
  */
691
- private applyInteractiveStyles(props: Record<string, unknown>, node: ComponentNode, ctx: BuilderContext): Record<string, unknown> {
692
- const nodeInteractiveStyles = 'interactiveStyles' in node ? node.interactiveStyles as InteractiveStyles | undefined : undefined;
693
- const nodeGenerateElementClass = 'generateElementClass' in node ? node.generateElementClass as boolean | undefined : undefined;
694
- const nodeLabel = 'label' in node ? node.label as string | undefined : undefined;
776
+ private applyInteractiveStyles(
777
+ props: Record<string, unknown>,
778
+ node: ComponentNode,
779
+ ctx: BuilderContext,
780
+ ): Record<string, unknown> {
781
+ const nodeInteractiveStyles =
782
+ 'interactiveStyles' in node ? (node.interactiveStyles as InteractiveStyles | undefined) : undefined;
783
+ const nodeGenerateElementClass =
784
+ 'generateElementClass' in node ? (node.generateElementClass as boolean | undefined) : undefined;
785
+ const nodeLabel = 'label' in node ? (node.label as string | undefined) : undefined;
695
786
 
696
787
  if (!((nodeInteractiveStyles && nodeInteractiveStyles.length > 0) || nodeGenerateElementClass)) {
697
788
  return props;
@@ -700,11 +791,16 @@ export class ComponentBuilder {
700
791
  // Check if slot content
701
792
  const isSlotContent = isSlotContentNode(node);
702
793
  const useComponentContext = ctx.componentContext && !isSlotContent;
703
- const fileType = useComponentContext ? 'component' : (this.getCurrentFileType?.() || 'page');
704
- const fileName = useComponentContext ? ctx.componentContext! : (this.getCurrentPageName ? this.getCurrentPageName() : 'page');
705
- const pathForClass = useComponentContext && ctx.componentRootPath
706
- ? ctx.elementPath.slice(ctx.componentRootPath.length)
707
- : ctx.elementPath;
794
+ const fileType = useComponentContext ? 'component' : this.getCurrentFileType?.() || 'page';
795
+ const fileName = useComponentContext
796
+ ? ctx.componentContext!
797
+ : this.getCurrentPageName
798
+ ? this.getCurrentPageName()
799
+ : 'page';
800
+ const pathForClass =
801
+ useComponentContext && ctx.componentRootPath
802
+ ? ctx.elementPath.slice(ctx.componentRootPath.length)
803
+ : ctx.elementPath;
708
804
 
709
805
  const elementClassCtx: ElementClassContext = {
710
806
  fileType,
@@ -755,7 +851,11 @@ export class ComponentBuilder {
755
851
  * objects on attribute values to the active locale's string before
756
852
  * downstream template / CMS processing, mirroring the SSR path.
757
853
  */
758
- private mergeAttributes(props: Record<string, unknown>, node: ComponentNode, ctx: BuilderContext): Record<string, unknown> {
854
+ private mergeAttributes(
855
+ props: Record<string, unknown>,
856
+ node: ComponentNode,
857
+ ctx: BuilderContext,
858
+ ): Record<string, unknown> {
759
859
  let extractedAttributes = extractAttributesFromNode(node) as Record<string, unknown>;
760
860
  const attrI18nConfig = ctx.i18nConfig ?? DEFAULT_I18N_CONFIG;
761
861
  const attrEffectiveLocale = ctx.locale || attrI18nConfig.defaultLocale;
@@ -772,7 +872,7 @@ export class ComponentBuilder {
772
872
  extractedAttributes = processCMSPropsTemplate(
773
873
  extractedAttributes as Record<string, unknown>,
774
874
  ctx.cmsContext,
775
- ctx.cmsLocale || ctx.locale
875
+ ctx.cmsLocale || ctx.locale,
776
876
  ) as Record<string, string | number | boolean>;
777
877
  }
778
878
 
@@ -784,19 +884,25 @@ export class ComponentBuilder {
784
884
  const i18nResolver: ValueResolver | undefined = effectiveLocale
785
885
  ? (value: unknown) => resolveI18nValue(value, effectiveLocale, config)
786
886
  : undefined;
787
- extractedAttributes = processItemPropsTemplate(extractedAttributes, effectiveItemContext, i18nResolver) as Record<string, string | number | boolean>;
887
+ extractedAttributes = processItemPropsTemplate(extractedAttributes, effectiveItemContext, i18nResolver) as Record<
888
+ string,
889
+ string | number | boolean
890
+ >;
788
891
  }
789
892
 
790
893
  // Auto-skip attributes that were entirely template expressions and resolved to ""
791
894
  if (Object.keys(extractedAttributes).length > 0) {
792
- extractedAttributes = skipEmptyTemplateAttributes(originalAttributes, extractedAttributes) as Record<string, string | number | boolean>;
895
+ extractedAttributes = skipEmptyTemplateAttributes(originalAttributes, extractedAttributes) as Record<
896
+ string,
897
+ string | number | boolean
898
+ >;
793
899
  }
794
900
 
795
901
  if (Object.keys(extractedAttributes).length === 0) {
796
902
  return props;
797
903
  }
798
904
 
799
- let result = { ...props };
905
+ const result = { ...props };
800
906
 
801
907
  // Handle class/className specially - check key existence, not truthiness (empty string is valid)
802
908
  if ('class' in extractedAttributes || 'className' in extractedAttributes) {
@@ -826,18 +932,28 @@ export class ComponentBuilder {
826
932
  nodeProps: Record<string, unknown>,
827
933
  children: unknown,
828
934
  finalProps: Record<string, unknown>,
829
- options: BuildComponentOptions
935
+ options: BuildComponentOptions,
830
936
  ): ReactElement | null {
831
937
  const {
832
- key = 0, elementPath = [0], parentComponentName = null, viewportWidth = 1920,
833
- componentContext = null, locale, i18nConfig, cmsContext = null, cmsLocale = null,
834
- collectionItemsMap = {}, itemContext = null, cmsItemIndexPath = null,
835
- cmsListPaths = null, templateContext = null,
938
+ key = 0,
939
+ elementPath = [0],
940
+ parentComponentName = null,
941
+ viewportWidth = 1920,
942
+ componentContext = null,
943
+ locale,
944
+ i18nConfig,
945
+ cmsContext = null,
946
+ cmsLocale = null,
947
+ collectionItemsMap = {},
948
+ itemContext = null,
949
+ cmsItemIndexPath = null,
950
+ cmsListPaths = null,
951
+ templateContext = null,
836
952
  // The host (outer) component's resolved props. Forwarded to
837
953
  // `processStructure` as `parentProps` so a `{{x}}` in this
838
954
  // component's structure falls back to the host's `x` when this
839
955
  // component's interface doesn't declare it.
840
- componentResolvedProps: parentResolvedProps = null
956
+ componentResolvedProps: parentResolvedProps = null,
841
957
  } = options;
842
958
 
843
959
  const componentDef = this.componentRegistry.get(componentName);
@@ -870,12 +986,12 @@ export class ComponentBuilder {
870
986
  }
871
987
 
872
988
  // Resolve props (validate, coerce types, apply defaults) AFTER templates are resolved
873
- let resolvedProps = resolvePropsFromDefinition(
989
+ const resolvedProps = resolvePropsFromDefinition(
874
990
  structuredComponentDef,
875
991
  propsToResolve,
876
992
  children as ComponentNode | string | (ComponentNode | string)[] | null | undefined,
877
993
  locale,
878
- i18nConfig
994
+ i18nConfig,
879
995
  );
880
996
 
881
997
  // Process structure
@@ -889,10 +1005,16 @@ export class ComponentBuilder {
889
1005
  parentProps: parentResolvedProps ?? undefined,
890
1006
  },
891
1007
  viewportWidth,
892
- markedChildren
1008
+ markedChildren,
893
1009
  );
894
1010
 
895
- if (!processedStructure || typeof processedStructure === 'string' || typeof processedStructure === 'number' || Array.isArray(processedStructure)) {
1011
+ if (
1012
+ !processedStructure ||
1013
+ typeof processedStructure === 'string' ||
1014
+ typeof processedStructure === 'number' ||
1015
+ Array.isArray(processedStructure) ||
1016
+ !isProcessedComponentNode(processedStructure)
1017
+ ) {
896
1018
  return null;
897
1019
  }
898
1020
 
@@ -910,13 +1032,13 @@ export class ComponentBuilder {
910
1032
  const nestedParentComponentName = this.getParentComponentNameForNestedComponent(
911
1033
  componentContext,
912
1034
  parentComponentName,
913
- componentName
1035
+ componentName,
914
1036
  );
915
1037
 
916
1038
  const componentElement = this.buildComponent({
917
1039
  node: processedStructure,
918
1040
  key,
919
- customProps: { '__componentProps': resolvedProps },
1041
+ customProps: { __componentProps: resolvedProps },
920
1042
  elementPath,
921
1043
  parentComponentName: nestedParentComponentName,
922
1044
  viewportWidth,
@@ -931,11 +1053,10 @@ export class ComponentBuilder {
931
1053
  cmsItemIndexPath,
932
1054
  cmsListPaths,
933
1055
  templateContext,
934
- componentResolvedProps: resolvedProps
1056
+ componentResolvedProps: resolvedProps,
935
1057
  });
936
1058
 
937
1059
  return h(ErrorBoundary, { key, componentName, level: 'component' }, componentElement);
938
-
939
1060
  } catch (error) {
940
1061
  return h(ErrorBoundary, { key, componentName, level: 'component' }, null);
941
1062
  }
@@ -949,19 +1070,30 @@ export class ComponentBuilder {
949
1070
  props: Record<string, unknown>,
950
1071
  children: unknown,
951
1072
  customProps: ComponentProps,
952
- options: BuildComponentOptions
1073
+ options: BuildComponentOptions,
953
1074
  ): ReactElement | null {
954
1075
  const {
955
- key = 0, elementPath = [0], parentComponentName = null, viewportWidth = 1920,
956
- componentContext = null, componentRootPath, locale, i18nConfig,
957
- cmsContext = null, cmsLocale = null, collectionItemsMap = {},
958
- itemContext = null, cmsItemIndexPath = null, cmsListPaths = null,
959
- templateContext = null, componentResolvedProps = null,
960
- isSlotContent = false
1076
+ key = 0,
1077
+ elementPath = [0],
1078
+ parentComponentName = null,
1079
+ viewportWidth = 1920,
1080
+ componentContext = null,
1081
+ componentRootPath,
1082
+ locale,
1083
+ i18nConfig,
1084
+ cmsContext = null,
1085
+ cmsLocale = null,
1086
+ collectionItemsMap = {},
1087
+ itemContext = null,
1088
+ cmsItemIndexPath = null,
1089
+ cmsListPaths = null,
1090
+ templateContext = null,
1091
+ componentResolvedProps = null,
1092
+ isSlotContent = false,
961
1093
  } = options;
962
1094
 
963
1095
  const requestedTag = (tag || 'div').toLowerCase();
964
- const finalTag = DOCUMENT_STRUCTURE_TAGS.has(requestedTag) ? 'div' : (tag || 'div');
1096
+ const finalTag = DOCUMENT_STRUCTURE_TAGS.has(requestedTag) ? 'div' : tag || 'div';
965
1097
  const effectiveParentComponentName = this.getEffectiveParentComponentName(componentContext, parentComponentName);
966
1098
 
967
1099
  // Remove __componentProps from customProps before spreading
@@ -975,8 +1107,11 @@ export class ComponentBuilder {
975
1107
  // (buildCustomComponent reads finalProps.style via mergeNodeStyles).
976
1108
  let propsForElement = props;
977
1109
  const nodeStyle = props.style;
978
- if (nodeStyle && typeof nodeStyle === 'object' &&
979
- ('base' in (nodeStyle as object) || 'tablet' in (nodeStyle as object) || 'mobile' in (nodeStyle as object))) {
1110
+ if (
1111
+ nodeStyle &&
1112
+ typeof nodeStyle === 'object' &&
1113
+ ('base' in (nodeStyle as object) || 'tablet' in (nodeStyle as object) || 'mobile' in (nodeStyle as object))
1114
+ ) {
980
1115
  const { style: _, ...rest } = props;
981
1116
  propsForElement = rest;
982
1117
  }
@@ -984,7 +1119,7 @@ export class ComponentBuilder {
984
1119
  const processedProps: Record<string, unknown> = {
985
1120
  ...propsForElement,
986
1121
  ...customPropsForDOM,
987
- key
1122
+ key,
988
1123
  };
989
1124
 
990
1125
  // Add is-current class for <a> tags when href matches current page path
@@ -1004,10 +1139,13 @@ export class ComponentBuilder {
1004
1139
  el.setAttribute('data-cms-item-index', cmsItemIndexPath.join('.'));
1005
1140
  // Set full CMS context as JSON for nested CMS list support
1006
1141
  if (cmsListPaths && cmsListPaths.length === cmsItemIndexPath.length) {
1007
- el.setAttribute('data-cms-context', JSON.stringify({
1008
- itemIndexPath: cmsItemIndexPath,
1009
- listPaths: cmsListPaths
1010
- }));
1142
+ el.setAttribute(
1143
+ 'data-cms-context',
1144
+ JSON.stringify({
1145
+ itemIndexPath: cmsItemIndexPath,
1146
+ listPaths: cmsListPaths,
1147
+ }),
1148
+ );
1011
1149
  }
1012
1150
  }
1013
1151
  if (isComponentRoot) el.setAttribute('data-component-root', 'true');
@@ -1021,9 +1159,7 @@ export class ComponentBuilder {
1021
1159
  const componentDef = this.componentRegistry.get(componentContext);
1022
1160
  const defineVars = componentDef?.component?.defineVars;
1023
1161
  if (defineVars && customProps.__componentProps) {
1024
- const varsToExpose = defineVars === true
1025
- ? Object.keys(componentDef.component.interface || {})
1026
- : defineVars;
1162
+ const varsToExpose = defineVars === true ? Object.keys(componentDef.component.interface || {}) : defineVars;
1027
1163
 
1028
1164
  const propsForJS: Record<string, unknown> = {};
1029
1165
  const resolvedProps = customProps.__componentProps as Record<string, unknown>;
@@ -1047,17 +1183,25 @@ export class ComponentBuilder {
1047
1183
  }
1048
1184
  }
1049
1185
 
1050
- const componentProps = isComponentRoot && customProps.__componentProps
1051
- ? customProps.__componentProps as Record<string, unknown>
1052
- : undefined;
1186
+ const componentProps =
1187
+ isComponentRoot && customProps.__componentProps
1188
+ ? (customProps.__componentProps as Record<string, unknown>)
1189
+ : undefined;
1053
1190
  const componentNameForRegistry = isComponentRoot ? componentContext : parentComponentName;
1054
1191
 
1055
1192
  const metadata = {
1056
1193
  parentComponentName: parentComponentName ?? null,
1057
- componentContext: componentContext ?? null
1194
+ componentContext: componentContext ?? null,
1058
1195
  };
1059
1196
 
1060
- this.elementRegistry.register(elementPath, el, componentNameForRegistry, isComponentRoot, componentProps, metadata);
1197
+ this.elementRegistry.register(
1198
+ elementPath,
1199
+ el,
1200
+ componentNameForRegistry,
1201
+ isComponentRoot,
1202
+ componentProps,
1203
+ metadata,
1204
+ );
1061
1205
  };
1062
1206
 
1063
1207
  // Add onClick handler
@@ -1066,7 +1210,22 @@ export class ComponentBuilder {
1066
1210
  }
1067
1211
 
1068
1212
  // HTML void elements
1069
- const VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
1213
+ const VOID_ELEMENTS = [
1214
+ 'area',
1215
+ 'base',
1216
+ 'br',
1217
+ 'col',
1218
+ 'embed',
1219
+ 'hr',
1220
+ 'img',
1221
+ 'input',
1222
+ 'link',
1223
+ 'meta',
1224
+ 'param',
1225
+ 'source',
1226
+ 'track',
1227
+ 'wbr',
1228
+ ];
1070
1229
  if (VOID_ELEMENTS.includes(finalTag.toLowerCase())) {
1071
1230
  const { children: _ignored, ...voidElementProps } = processedProps;
1072
1231
  return h(finalTag, voidElementProps);
@@ -1074,8 +1233,21 @@ export class ComponentBuilder {
1074
1233
 
1075
1234
  // Build children
1076
1235
  const childElements = this.buildChildren(children as (ComponentNode | string)[], {
1077
- elementPath, parentComponentName: effectiveParentComponentName, viewportWidth, componentContext, componentRootPath,
1078
- locale, i18nConfig, cmsContext, cmsLocale, collectionItemsMap, itemContext, cmsItemIndexPath, cmsListPaths, templateContext, componentResolvedProps
1236
+ elementPath,
1237
+ parentComponentName: effectiveParentComponentName,
1238
+ viewportWidth,
1239
+ componentContext,
1240
+ componentRootPath,
1241
+ locale,
1242
+ i18nConfig,
1243
+ cmsContext,
1244
+ cmsLocale,
1245
+ collectionItemsMap,
1246
+ itemContext,
1247
+ cmsItemIndexPath,
1248
+ cmsListPaths,
1249
+ templateContext,
1250
+ componentResolvedProps,
1079
1251
  });
1080
1252
 
1081
1253
  return h(finalTag, processedProps, childElements);