meno-core 1.0.53 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (415) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +66 -261
  3. package/build-astro.ts +150 -128
  4. package/dist/bin/cli.js +42 -185
  5. package/dist/bin/cli.js.map +2 -2
  6. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  7. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  8. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-2IIQK7T3.js} +2624 -1389
  9. package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
  10. package/dist/chunks/chunk-4ZRU52J2.js +169 -0
  11. package/dist/chunks/chunk-4ZRU52J2.js.map +7 -0
  12. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-5X4HCN7J.js} +5 -7
  13. package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
  14. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-7CCVOL5M.js} +15 -19
  15. package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
  16. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-AR7LHIFY.js} +8 -2
  17. package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
  18. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-NUP7H7D3.js} +135 -41
  19. package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
  20. package/dist/chunks/{chunk-X754AHS5.js → chunk-QWTQZHG3.js} +194 -90
  21. package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
  22. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-XTKNX4FW.js} +216 -87
  23. package/dist/chunks/chunk-XTKNX4FW.js.map +7 -0
  24. package/dist/chunks/{fs-JGINUXGL.js → fs-G3ANWEFP.js} +2 -2
  25. package/dist/lib/client/index.js +1003 -475
  26. package/dist/lib/client/index.js.map +3 -3
  27. package/dist/lib/server/index.js +14393 -6371
  28. package/dist/lib/server/index.js.map +4 -4
  29. package/dist/lib/shared/index.js +342 -101
  30. package/dist/lib/shared/index.js.map +4 -4
  31. package/dist/lib/shared/richtext/index.js +1 -1
  32. package/dist/lib/test-utils/index.js +64 -80
  33. package/dist/lib/test-utils/index.js.map +2 -2
  34. package/entries/client-router.tsx +10 -8
  35. package/lib/client/ClientInitializer.ts +8 -8
  36. package/lib/client/ErrorBoundary.test.tsx +246 -186
  37. package/lib/client/ErrorBoundary.tsx +183 -132
  38. package/lib/client/componentRegistry.test.ts +96 -108
  39. package/lib/client/componentRegistry.ts +1 -3
  40. package/lib/client/contexts/ThemeContext.tsx +3 -2
  41. package/lib/client/core/ComponentBuilder.test.ts +663 -560
  42. package/lib/client/core/ComponentBuilder.ts +427 -155
  43. package/lib/client/core/ComponentRenderer.test.tsx +2 -4
  44. package/lib/client/core/ComponentRenderer.tsx +46 -33
  45. package/lib/client/core/builders/embedBuilder.ts +242 -54
  46. package/lib/client/core/builders/linkBuilder.ts +72 -45
  47. package/lib/client/core/builders/linkNodeBuilder.ts +79 -54
  48. package/lib/client/core/builders/listBuilder.ts +155 -89
  49. package/lib/client/core/builders/localeListBuilder.ts +97 -62
  50. package/lib/client/core/builders/types.ts +5 -5
  51. package/lib/client/core/cmsTemplateProcessor.ts +8 -9
  52. package/lib/client/elementRegistry.ts +3 -3
  53. package/lib/client/fontFamiliesService.test.ts +2 -10
  54. package/lib/client/fontFamiliesService.ts +3 -3
  55. package/lib/client/hmr/HMRManager.tsx +47 -30
  56. package/lib/client/hmrCssReload.ts +49 -14
  57. package/lib/client/hmrWebSocket.ts +14 -19
  58. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  59. package/lib/client/hooks/useColorVariables.ts +23 -18
  60. package/lib/client/hooks/usePropertyAutocomplete.ts +4 -6
  61. package/lib/client/hooks/useVariables.ts +6 -4
  62. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  63. package/lib/client/hydration/HydrationUtils.ts +3 -4
  64. package/lib/client/i18nConfigService.test.ts +2 -7
  65. package/lib/client/i18nConfigService.ts +2 -2
  66. package/lib/client/meno-filter/MenoFilter.test.ts +47 -49
  67. package/lib/client/meno-filter/MenoFilter.ts +14 -15
  68. package/lib/client/meno-filter/bindings.ts +18 -43
  69. package/lib/client/meno-filter/init.ts +4 -4
  70. package/lib/client/meno-filter/renderer.ts +30 -32
  71. package/lib/client/meno-filter/script.generated.ts +1 -3
  72. package/lib/client/meno-filter/ui.ts +4 -6
  73. package/lib/client/meno-filter/updates.ts +18 -23
  74. package/lib/client/meno-filter/utils.ts +1 -1
  75. package/lib/client/navigation.test.ts +174 -178
  76. package/lib/client/navigation.ts +0 -1
  77. package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
  78. package/lib/client/responsiveStyleResolver.test.ts +234 -232
  79. package/lib/client/responsiveStyleResolver.ts +74 -20
  80. package/lib/client/routing/RouteLoader.test.ts +24 -25
  81. package/lib/client/routing/RouteLoader.ts +27 -35
  82. package/lib/client/routing/Router.tsx +210 -153
  83. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  84. package/lib/client/scripts/ScriptExecutor.ts +18 -36
  85. package/lib/client/services/PrefetchService.test.ts +3 -3
  86. package/lib/client/services/PrefetchService.ts +23 -29
  87. package/lib/client/styleProcessor.test.ts +9 -9
  88. package/lib/client/styleProcessor.ts +24 -15
  89. package/lib/client/styles/StyleInjector.test.ts +122 -115
  90. package/lib/client/styles/StyleInjector.ts +10 -7
  91. package/lib/client/styles/UtilityClassCollector.ts +62 -28
  92. package/lib/client/styles/cspNonce.test.ts +2 -5
  93. package/lib/client/templateEngine.test.ts +554 -488
  94. package/lib/client/templateEngine.ts +203 -73
  95. package/lib/client/theme.test.ts +1 -1
  96. package/lib/client/theme.ts +0 -1
  97. package/lib/client/utils/toast.ts +0 -1
  98. package/lib/server/__integration__/api-routes.test.ts +8 -4
  99. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  100. package/lib/server/__integration__/server-lifecycle.test.ts +4 -5
  101. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  102. package/lib/server/__integration__/static-assets.test.ts +3 -3
  103. package/lib/server/__integration__/test-helpers.ts +87 -74
  104. package/lib/server/ab/generateFunctions.ts +12 -10
  105. package/lib/server/astro/cmsPageEmitter.ts +26 -42
  106. package/lib/server/astro/componentEmitter.ts +35 -43
  107. package/lib/server/astro/cssCollector.ts +10 -26
  108. package/lib/server/astro/nodeToAstro.test.ts +1754 -34
  109. package/lib/server/astro/nodeToAstro.ts +232 -216
  110. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +115 -119
  111. package/lib/server/astro/normalizeOrphanTemplateProps.ts +11 -18
  112. package/lib/server/astro/pageEmitter.ts +9 -13
  113. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  114. package/lib/server/astro/tailwindMapper.ts +33 -40
  115. package/lib/server/astro/templateTransformer.ts +17 -20
  116. package/lib/server/createServer.ts +7 -19
  117. package/lib/server/cssGenerator.test.ts +35 -44
  118. package/lib/server/cssGenerator.ts +8 -19
  119. package/lib/server/fileWatcher.test.ts +164 -13
  120. package/lib/server/fileWatcher.ts +209 -124
  121. package/lib/server/index.ts +14 -17
  122. package/lib/server/jsonLoader.test.ts +39 -2
  123. package/lib/server/jsonLoader.ts +39 -34
  124. package/lib/server/middleware/cors.test.ts +21 -21
  125. package/lib/server/middleware/cors.ts +28 -4
  126. package/lib/server/middleware/errorHandler.test.ts +7 -5
  127. package/lib/server/middleware/errorHandler.ts +3 -8
  128. package/lib/server/middleware/index.ts +0 -1
  129. package/lib/server/middleware/logger.test.ts +10 -8
  130. package/lib/server/middleware/logger.ts +17 -29
  131. package/lib/server/migrateTemplates.ts +3 -3
  132. package/lib/server/pageCache.test.ts +76 -77
  133. package/lib/server/pageCache.ts +0 -1
  134. package/lib/server/projectContext.ts +5 -4
  135. package/lib/server/providers/fileSystemCMSProvider.test.ts +142 -99
  136. package/lib/server/providers/fileSystemCMSProvider.ts +62 -38
  137. package/lib/server/routes/api/cms.test.ts +26 -14
  138. package/lib/server/routes/api/cms.ts +9 -14
  139. package/lib/server/routes/api/colors.test.ts +2 -2
  140. package/lib/server/routes/api/components.ts +31 -35
  141. package/lib/server/routes/api/config.ts +3 -4
  142. package/lib/server/routes/api/core-routes.ts +49 -76
  143. package/lib/server/routes/api/enums.test.ts +2 -2
  144. package/lib/server/routes/api/functions.ts +9 -17
  145. package/lib/server/routes/api/index.ts +3 -6
  146. package/lib/server/routes/api/pages.ts +8 -11
  147. package/lib/server/routes/api/shared.test.ts +1 -1
  148. package/lib/server/routes/api/shared.ts +12 -9
  149. package/lib/server/routes/api/variables.test.ts +3 -5
  150. package/lib/server/routes/api/variables.ts +1 -1
  151. package/lib/server/routes/index.ts +25 -28
  152. package/lib/server/routes/pages.ts +35 -31
  153. package/lib/server/routes/static.ts +17 -6
  154. package/lib/server/runtime/bundler.ts +98 -61
  155. package/lib/server/runtime/fs.ts +16 -26
  156. package/lib/server/runtime/httpServer.ts +8 -12
  157. package/lib/server/services/ColorService.test.ts +3 -3
  158. package/lib/server/services/ColorService.ts +37 -29
  159. package/lib/server/services/EnumService.test.ts +4 -8
  160. package/lib/server/services/EnumService.ts +11 -3
  161. package/lib/server/services/VariableService.test.ts +8 -12
  162. package/lib/server/services/VariableService.ts +11 -3
  163. package/lib/server/services/cmsService.test.ts +158 -120
  164. package/lib/server/services/cmsService.ts +36 -146
  165. package/lib/server/services/componentService.test.ts +125 -45
  166. package/lib/server/services/componentService.ts +70 -46
  167. package/lib/server/services/configService.test.ts +22 -45
  168. package/lib/server/services/configService.ts +50 -27
  169. package/lib/server/services/fileWatcherService.ts +42 -8
  170. package/lib/server/services/index.ts +0 -1
  171. package/lib/server/services/pageService.test.ts +8 -10
  172. package/lib/server/services/pageService.ts +32 -18
  173. package/lib/server/ssr/attributeBuilder.ts +18 -8
  174. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  175. package/lib/server/ssr/clientDataInjector.ts +7 -21
  176. package/lib/server/ssr/cmsSSRProcessor.ts +4 -8
  177. package/lib/server/ssr/cssCollector.ts +1 -1
  178. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  179. package/lib/server/ssr/errorOverlay.ts +3 -9
  180. package/lib/server/ssr/htmlGenerator.nonce.test.ts +9 -14
  181. package/lib/server/ssr/htmlGenerator.test.ts +120 -44
  182. package/lib/server/ssr/htmlGenerator.ts +126 -91
  183. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  184. package/lib/server/ssr/imageMetadata.ts +27 -21
  185. package/lib/server/ssr/jsCollector.test.ts +8 -18
  186. package/lib/server/ssr/jsCollector.ts +3 -8
  187. package/lib/server/ssr/liveReloadIntegration.test.ts +76 -36
  188. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  189. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  190. package/lib/server/ssr/ssrRenderer.test.ts +316 -256
  191. package/lib/server/ssr/ssrRenderer.ts +795 -243
  192. package/lib/server/ssrRenderer.test.ts +1048 -954
  193. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  194. package/lib/server/utils/jsonLineMapper.ts +7 -7
  195. package/lib/server/validateStyleCoverage.ts +18 -21
  196. package/lib/server/websocketManager.ts +28 -19
  197. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  198. package/lib/shared/attributeNodeUtils.ts +5 -12
  199. package/lib/shared/breakpoints.test.ts +5 -5
  200. package/lib/shared/breakpoints.ts +4 -11
  201. package/lib/shared/cmsQuery.test.ts +81 -0
  202. package/lib/shared/cmsQuery.ts +92 -0
  203. package/lib/shared/cmsQueryParser.test.ts +50 -42
  204. package/lib/shared/cmsQueryParser.ts +60 -38
  205. package/lib/shared/colorConversions.ts +9 -9
  206. package/lib/shared/colorVariableUtils.test.ts +27 -5
  207. package/lib/shared/colorVariableUtils.ts +21 -12
  208. package/lib/shared/componentRefs.ts +1 -5
  209. package/lib/shared/constants.test.ts +3 -3
  210. package/lib/shared/constants.ts +8 -8
  211. package/lib/shared/cssGeneration.test.ts +285 -144
  212. package/lib/shared/cssGeneration.ts +217 -531
  213. package/lib/shared/cssNamedColors.ts +152 -30
  214. package/lib/shared/cssProperties.test.ts +69 -70
  215. package/lib/shared/cssProperties.ts +298 -114
  216. package/lib/shared/elementClassName.test.ts +109 -109
  217. package/lib/shared/elementClassName.ts +3 -3
  218. package/lib/shared/elementUtils.ts +12 -16
  219. package/lib/shared/errorLogger.test.ts +12 -12
  220. package/lib/shared/errorLogger.ts +2 -10
  221. package/lib/shared/errors.test.ts +2 -13
  222. package/lib/shared/errors.ts +2 -8
  223. package/lib/shared/expressionEvaluator.test.ts +145 -0
  224. package/lib/shared/expressionEvaluator.ts +52 -24
  225. package/lib/shared/fontLoader.test.ts +32 -18
  226. package/lib/shared/fontLoader.ts +2 -2
  227. package/lib/shared/friendlyError.ts +2 -3
  228. package/lib/shared/gradientUtils.test.ts +3 -7
  229. package/lib/shared/gradientUtils.ts +17 -14
  230. package/lib/shared/hrefRefs.test.ts +2 -2
  231. package/lib/shared/hrefRefs.ts +2 -10
  232. package/lib/shared/i18n.test.ts +4 -4
  233. package/lib/shared/i18n.ts +18 -38
  234. package/lib/shared/index.ts +27 -0
  235. package/lib/shared/inlineSvgStyleRules.ts +14 -13
  236. package/lib/shared/interactiveStyleMappings.test.ts +32 -54
  237. package/lib/shared/interactiveStyleMappings.ts +11 -24
  238. package/lib/shared/interactiveStyles.test.ts +165 -188
  239. package/lib/shared/interfaces/contentProvider.ts +1 -1
  240. package/lib/shared/itemTemplateUtils.test.ts +11 -17
  241. package/lib/shared/itemTemplateUtils.ts +27 -39
  242. package/lib/shared/jsonRepair.ts +16 -10
  243. package/lib/shared/libraryLoader.test.ts +21 -55
  244. package/lib/shared/libraryLoader.ts +7 -22
  245. package/lib/shared/linkUtils.ts +1 -1
  246. package/lib/shared/logger.test.ts +66 -0
  247. package/lib/shared/logger.ts +94 -0
  248. package/lib/shared/markdown.ts +25 -0
  249. package/lib/shared/netlifyLocale404.test.ts +179 -0
  250. package/lib/shared/netlifyLocale404.ts +110 -0
  251. package/lib/shared/nodeUtils.test.ts +28 -16
  252. package/lib/shared/nodeUtils.ts +76 -23
  253. package/lib/shared/pathArrayUtils.test.ts +1 -2
  254. package/lib/shared/pathArrayUtils.ts +1 -1
  255. package/lib/shared/pathSecurity.ts +2 -2
  256. package/lib/shared/pathUtils.test.ts +4 -6
  257. package/lib/shared/pathUtils.ts +42 -49
  258. package/lib/shared/paths/Path.test.ts +2 -2
  259. package/lib/shared/paths/Path.ts +0 -1
  260. package/lib/shared/paths/PathConverter.test.ts +1 -1
  261. package/lib/shared/paths/PathConverter.ts +17 -20
  262. package/lib/shared/paths/PathUtils.ts +13 -13
  263. package/lib/shared/paths/PathValidator.test.ts +2 -15
  264. package/lib/shared/paths/PathValidator.ts +12 -10
  265. package/lib/shared/paths/index.ts +1 -2
  266. package/lib/shared/permissions.test.ts +168 -0
  267. package/lib/shared/permissions.ts +162 -0
  268. package/lib/shared/propResolver.test.ts +240 -244
  269. package/lib/shared/propResolver.ts +15 -26
  270. package/lib/shared/pxToRem.test.ts +7 -6
  271. package/lib/shared/pxToRem.ts +2 -5
  272. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +12 -6
  273. package/lib/shared/registry/ClientRegistry.ts +0 -2
  274. package/lib/shared/registry/ComponentRegistry.test.ts +46 -32
  275. package/lib/shared/registry/ComponentRegistry.ts +12 -9
  276. package/lib/shared/registry/NodeTypeDefinition.ts +16 -9
  277. package/lib/shared/registry/RegistryManager.ts +1 -2
  278. package/lib/shared/registry/SSRRegistry.ts +0 -2
  279. package/lib/shared/registry/createNodeType.ts +7 -9
  280. package/lib/shared/registry/defineNodeType.ts +2 -6
  281. package/lib/shared/registry/fieldPresets.ts +3 -1
  282. package/lib/shared/registry/index.ts +0 -1
  283. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  284. package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
  285. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  286. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +48 -19
  287. package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
  288. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  289. package/lib/shared/registry/nodeTypes/ListNodeType.ts +117 -77
  290. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  291. package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
  292. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +12 -8
  293. package/lib/shared/registry/nodeTypes/index.ts +26 -3
  294. package/lib/shared/responsiveScaling.test.ts +16 -34
  295. package/lib/shared/responsiveScaling.ts +61 -42
  296. package/lib/shared/responsiveStyleUtils.test.ts +0 -1
  297. package/lib/shared/responsiveStyleUtils.ts +11 -13
  298. package/lib/shared/richtext/htmlToTiptap.test.ts +139 -130
  299. package/lib/shared/richtext/htmlToTiptap.ts +2 -4
  300. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  301. package/lib/shared/richtext/types.ts +1 -8
  302. package/lib/shared/slugTranslator.test.ts +13 -13
  303. package/lib/shared/slugTranslator.ts +12 -16
  304. package/lib/shared/slugify.ts +9 -15
  305. package/lib/shared/styleNodeUtils.test.ts +8 -8
  306. package/lib/shared/styleNodeUtils.ts +18 -20
  307. package/lib/shared/styleUtils.test.ts +87 -61
  308. package/lib/shared/styleUtils.ts +5 -6
  309. package/lib/shared/themeDefaults.test.ts +11 -14
  310. package/lib/shared/themeDefaults.ts +3 -4
  311. package/lib/shared/tree/PathBuilder.test.ts +63 -145
  312. package/lib/shared/tree/PathBuilder.ts +59 -47
  313. package/lib/shared/treePathUtils.test.ts +51 -12
  314. package/lib/shared/treePathUtils.ts +91 -60
  315. package/lib/shared/types/api.ts +105 -8
  316. package/lib/shared/types/cms.test.ts +34 -0
  317. package/lib/shared/types/cms.ts +90 -34
  318. package/lib/shared/types/comment.ts +49 -16
  319. package/lib/shared/types/components.ts +51 -25
  320. package/lib/shared/types/errors.test.ts +1 -6
  321. package/lib/shared/types/errors.ts +3 -7
  322. package/lib/shared/types/experiments.ts +28 -28
  323. package/lib/shared/types/index.ts +25 -2
  324. package/lib/shared/types/permissions.ts +139 -0
  325. package/lib/shared/types/styles.ts +0 -1
  326. package/lib/shared/types/variables.test.ts +4 -13
  327. package/lib/shared/types/variables.ts +51 -30
  328. package/lib/shared/types.ts +1 -2
  329. package/lib/shared/utilityClassConfig.ts +751 -321
  330. package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
  331. package/lib/shared/utilityClassMapper.test.ts +433 -95
  332. package/lib/shared/utilityClassMapper.ts +322 -249
  333. package/lib/shared/utilityClassNames.test.ts +38 -0
  334. package/lib/shared/utilityClassNames.ts +458 -0
  335. package/lib/shared/utils.test.ts +2 -10
  336. package/lib/shared/utils.ts +19 -10
  337. package/lib/shared/validation/cmsValidators.ts +2 -1
  338. package/lib/shared/validation/commentValidators.test.ts +53 -0
  339. package/lib/shared/validation/commentValidators.ts +12 -1
  340. package/lib/shared/validation/index.ts +1 -0
  341. package/lib/shared/validation/permissionsValidators.test.ts +49 -0
  342. package/lib/shared/validation/permissionsValidators.ts +49 -0
  343. package/lib/shared/validation/propValidator.test.ts +19 -21
  344. package/lib/shared/validation/propValidator.ts +13 -18
  345. package/lib/shared/validation/schemas.test.ts +64 -32
  346. package/lib/shared/validation/schemas.ts +616 -345
  347. package/lib/shared/validation/validators.test.ts +3 -8
  348. package/lib/shared/validation/validators.ts +89 -68
  349. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  350. package/lib/shared/viewportUnits.test.ts +24 -23
  351. package/lib/shared/viewportUnits.ts +12 -5
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +10 -14
  354. package/lib/test-utils/factories/DomMockFactory.ts +17 -23
  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 +6 -10
  358. package/lib/test-utils/factories/StoreMockFactory.ts +18 -29
  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 +13 -19
  365. package/lib/test-utils/mocks.ts +6 -4
  366. package/package.json +3 -5
  367. package/scripts/build-for-publish.mjs +2 -4
  368. package/scripts/build-meno-filter.ts +4 -7
  369. package/tsconfig.json +1 -0
  370. package/vite.config.ts +4 -4
  371. package/build-next.ts +0 -1361
  372. package/build-static.test.ts +0 -424
  373. package/build-static.ts +0 -1074
  374. package/dist/build-static.js +0 -36
  375. package/dist/chunks/chunk-2MHDV5BF.js +0 -251
  376. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  377. package/dist/chunks/chunk-AZQYF6KE.js +0 -559
  378. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  379. package/dist/chunks/chunk-GZHGVVW3.js +0 -322
  380. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  381. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  382. package/dist/chunks/chunk-I7YIGZXT.js +0 -467
  383. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  384. package/dist/chunks/chunk-IGYR22T6.js +0 -6192
  385. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  386. package/dist/chunks/chunk-JGP5A3Y5.js +0 -1136
  387. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  388. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  389. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  390. package/dist/chunks/chunk-QB2LNO4W.js +0 -77
  391. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  392. package/dist/chunks/chunk-R6XHAFBF.js +0 -3821
  393. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  394. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  395. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  396. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  397. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  398. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  399. package/dist/chunks/configService-R3OGU2UD.js.map +0 -7
  400. package/dist/chunks/constants-STK2YBIW.js +0 -46
  401. package/dist/chunks/constants-STK2YBIW.js.map +0 -7
  402. package/dist/chunks/fs-JGINUXGL.js.map +0 -7
  403. package/dist/entries/server-router.js +0 -67
  404. package/dist/entries/server-router.js.map +0 -7
  405. package/entries/server-router.tsx +0 -81
  406. package/lib/server/providers/fileSystemPageProvider.ts +0 -160
  407. package/lib/server/webflow/buildWebflow.ts +0 -635
  408. package/lib/server/webflow/index.ts +0 -23
  409. package/lib/server/webflow/nodeToWebflow.test.ts +0 -3170
  410. package/lib/server/webflow/nodeToWebflow.ts +0 -2435
  411. package/lib/server/webflow/styleMapper.test.ts +0 -389
  412. package/lib/server/webflow/styleMapper.ts +0 -695
  413. package/lib/server/webflow/templateWrapper.ts +0 -49
  414. package/lib/server/webflow/types.ts +0 -396
  415. /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
@@ -4,11 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { ComponentNode } from '../types/nodes';
7
- import type {
8
- NodeTypeDefinition,
9
- NodeTypeDefinitionInput,
10
- NodeCapabilities,
11
- } from './NodeTypeDefinition';
7
+ import type { NodeTypeDefinition, NodeTypeDefinitionInput, NodeCapabilities } from './NodeTypeDefinition';
12
8
 
13
9
  /**
14
10
  * Default capabilities for node types
@@ -26,7 +22,7 @@ const DEFAULT_CAPABILITIES: NodeCapabilities = {
26
22
  * Provides type safety and validation at definition time
27
23
  */
28
24
  export function defineNodeType<TNode extends ComponentNode>(
29
- input: NodeTypeDefinitionInput<TNode>
25
+ input: NodeTypeDefinitionInput<TNode>,
30
26
  ): NodeTypeDefinition<TNode> {
31
27
  // Validate required fields
32
28
  if (!input.type) {
@@ -33,7 +33,9 @@ interface TextFieldConfig extends BaseFieldConfig {
33
33
  }
34
34
 
35
35
  interface SelectFieldConfig extends BaseFieldConfig {
36
- options: string[];
36
+ // Bare strings are value-is-label options; the object form carries a distinct
37
+ // display label (e.g. an empty-value `{ value: '', label: 'None' }` entry).
38
+ options: (string | { value: string | boolean; label: string })[];
37
39
  }
38
40
 
39
41
  // ============================================
@@ -48,4 +48,3 @@ export {
48
48
  LinkNodeType,
49
49
  LocaleListNodeType,
50
50
  } from './nodeTypes';
51
-
@@ -12,21 +12,20 @@ import { createNodeType } from '../createNodeType';
12
12
  const ComponentNodeSchemaRef: z.ZodType<any> = z.lazy(() => z.any());
13
13
 
14
14
  // Schema is the SINGLE source of truth
15
- const ComponentInstanceNodeSchemaInternal = z.object({
16
- type: z.literal('component'),
17
- component: z.string(),
18
- label: z.string().optional(), // Custom label displayed in structure tree
19
- if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
20
- props: z.record(z.string(), z.any()).optional(),
21
- style: StyleValueSchema.optional(),
22
- interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
23
- generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
24
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
25
- children: z.union([
26
- z.array(z.union([ComponentNodeSchemaRef, z.string()])),
27
- z.string(),
28
- ]).optional(),
29
- }).passthrough();
15
+ const ComponentInstanceNodeSchemaInternal = z
16
+ .object({
17
+ type: z.literal('component'),
18
+ component: z.string(),
19
+ label: z.string().optional(), // Custom label displayed in structure tree
20
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
21
+ props: z.record(z.string(), z.any()).optional(),
22
+ style: StyleValueSchema.optional(),
23
+ interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
24
+ generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
25
+ attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
26
+ children: z.union([z.array(z.union([ComponentNodeSchemaRef, z.string()])), z.string()]).optional(),
27
+ })
28
+ .passthrough();
30
29
 
31
30
  // TypeScript type inferred from schema
32
31
  export type ComponentInstanceNode = z.infer<typeof ComponentInstanceNodeSchemaInternal>;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Custom Node Type Definition
3
+ *
4
+ * Represents a hand-authored, opaque `.astro` component (under `src/custom/`) that Meno
5
+ * treats as a BLACK BOX: it renders server-side in a real `astro build` / play preview
6
+ * (the meno-astro codec emits `<Fancy … />` + the import), but Meno doesn't model its
7
+ * internals — you can place it, pass props, and slot children, but not edit what's inside.
8
+ * The native-Astro sibling of an island (no framework renderer, no hydration).
9
+ *
10
+ * meno-core can't execute a foreign `.astro` file, so the editor canvas renders a quiet
11
+ * placeholder (its slotted children, or an inline marker) — real rendering is play/build
12
+ * only. The on-disk `.astro` form and the round-trip codec live in `meno-astro`; this file
13
+ * only models the node so it validates, persists, and renders a placeholder.
14
+ */
15
+
16
+ import { z } from 'zod';
17
+ import { createElement as h } from 'react';
18
+ import type { ComponentNode } from '../../types/nodes';
19
+ import { IfConditionSchema } from '../../validation/schemas';
20
+ import { createNodeType } from '../createNodeType';
21
+ import { textField } from '../fieldPresets';
22
+
23
+ // Schema is the SINGLE source of truth
24
+ const CustomNodeSchemaInternal = z
25
+ .object({
26
+ type: z.literal('custom'),
27
+ // Path to the hand-authored .astro component relative to `src/custom/`
28
+ // (e.g. "Fancy.astro", "widgets/Banner.astro").
29
+ src: z.string(),
30
+ props: z.record(z.string(), z.any()).optional(),
31
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
32
+ // Slotted children (rendered server-side into the component's default <slot/>).
33
+ children: z.union([z.array(z.any()), z.string()]).optional(),
34
+ })
35
+ .passthrough();
36
+
37
+ // TypeScript type inferred from schema
38
+ export type CustomNode = z.infer<typeof CustomNodeSchemaInternal>;
39
+
40
+ // Export schema for validation/schemas.ts
41
+ export const CustomNodeSchema = CustomNodeSchemaInternal;
42
+
43
+ /** Basename of the custom source (drops directory + `.astro` extension) for display. */
44
+ function customName(src: string): string {
45
+ const base = src.split('/').pop() ?? src;
46
+ return base.replace(/\.astro$/i, '') || base;
47
+ }
48
+
49
+ /** Slotted children, normalized to the shape `buildChildren`/`renderChildren` accept. */
50
+ function customChildren(node: CustomNode): Array<ComponentNode | string> | string | undefined {
51
+ const { children } = node;
52
+ if (Array.isArray(children)) return children.length > 0 ? (children as Array<ComponentNode | string>) : undefined;
53
+ if (typeof children === 'string') return children.length > 0 ? children : undefined;
54
+ return undefined;
55
+ }
56
+
57
+ export const CustomNodeType = createNodeType({
58
+ type: 'custom',
59
+ displayName: 'Custom Component',
60
+ category: 'special',
61
+ schema: CustomNodeSchemaInternal,
62
+
63
+ defaultValues: {
64
+ src: '',
65
+ },
66
+
67
+ treeDisplay: {
68
+ icon: 'COMPONENT',
69
+ getLabel: (node) => `Custom: ${customName(node.src) || '?'}`,
70
+ },
71
+
72
+ editableFields: [textField({ name: 'src', label: 'Custom .astro file', required: true, placeholder: 'Fancy.astro' })],
73
+
74
+ // meno-core can't run a foreign `.astro` file. Render the slotted children as normal
75
+ // content and fall back to a quiet inline marker when there are none. The wrapper keeps
76
+ // `data-custom` for selection/identification only. Real rendering is play/build only.
77
+ clientRenderer: (node, context) => {
78
+ const children = customChildren(node);
79
+ const inner = children
80
+ ? context.buildChildren(children, context.elementPath)
81
+ : h('span', { style: { color: '#9ca3af', fontSize: '13px' } }, customName(node.src) || '?');
82
+ return h('div', { key: context.key, 'data-custom': node.src, style: { display: 'contents' } }, inner);
83
+ },
84
+
85
+ ssrRenderer: (node, context) => {
86
+ const children = customChildren(node);
87
+ const inner = children
88
+ ? context.renderChildren(children, context)
89
+ : `<span style="color:#9ca3af;font-size:13px">${escapeHtml(customName(node.src) || '?')}</span>`;
90
+ return `<div data-custom="${escapeHtml(node.src)}" style="display:contents">${inner}</div>`;
91
+ },
92
+
93
+ capabilities: {
94
+ canHaveChildren: true,
95
+ canHaveStyle: false,
96
+ canHaveAttributes: false,
97
+ requiresProps: ['src'],
98
+ },
99
+ });
100
+
101
+ function escapeHtml(value: string): string {
102
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
103
+ }
@@ -5,21 +5,28 @@
5
5
 
6
6
  import { z } from 'zod';
7
7
  import { createElement as h } from 'react';
8
- import { StyleValueSchema, InteractiveStylesSchema, IfConditionSchema, HtmlMappingSchema } from '../../validation/schemas';
8
+ import {
9
+ StyleValueSchema,
10
+ InteractiveStylesSchema,
11
+ IfConditionSchema,
12
+ HtmlMappingSchema,
13
+ } from '../../validation/schemas';
9
14
  import { createNodeType } from '../createNodeType';
10
15
  import { textareaField } from '../fieldPresets';
11
16
 
12
17
  // Schema is the SINGLE source of truth
13
- const EmbedNodeSchemaInternal = z.object({
14
- type: z.literal('embed'),
15
- html: z.union([z.string(), HtmlMappingSchema]),
16
- label: z.string().optional(), // Custom label displayed in structure tree
17
- if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
18
- style: StyleValueSchema.optional(),
19
- interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
20
- generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
21
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
22
- }).passthrough();
18
+ const EmbedNodeSchemaInternal = z
19
+ .object({
20
+ type: z.literal('embed'),
21
+ html: z.union([z.string(), HtmlMappingSchema]),
22
+ label: z.string().optional(), // Custom label displayed in structure tree
23
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
24
+ style: StyleValueSchema.optional(),
25
+ interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
26
+ generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
27
+ attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
28
+ })
29
+ .passthrough();
23
30
 
24
31
  // TypeScript type inferred from schema
25
32
  export type EmbedNode = z.infer<typeof EmbedNodeSchemaInternal>;
@@ -12,21 +12,20 @@ import { createNodeType } from '../createNodeType';
12
12
  const ComponentNodeSchemaRef: z.ZodType<any> = z.lazy(() => z.any());
13
13
 
14
14
  // Schema is the SINGLE source of truth
15
- const HtmlNodeSchemaInternal = z.object({
16
- type: z.literal('node'),
17
- tag: z.string(),
18
- label: z.string().optional(), // Custom label displayed in structure tree
19
- if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
20
- style: StyleValueSchema.optional(),
21
- interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
22
- generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
23
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
24
- props: z.record(z.string(), z.any()).optional(), // Backward compatibility
25
- children: z.union([
26
- z.array(z.union([ComponentNodeSchemaRef, z.string()])),
27
- z.string(),
28
- ]).optional(),
29
- }).passthrough();
15
+ const HtmlNodeSchemaInternal = z
16
+ .object({
17
+ type: z.literal('node'),
18
+ tag: z.string(),
19
+ label: z.string().optional(), // Custom label displayed in structure tree
20
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
21
+ style: StyleValueSchema.optional(),
22
+ interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
23
+ generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
24
+ attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
25
+ props: z.record(z.string(), z.any()).optional(), // Backward compatibility
26
+ children: z.union([z.array(z.union([ComponentNodeSchemaRef, z.string()])), z.string()]).optional(),
27
+ })
28
+ .passthrough();
30
29
 
31
30
  // TypeScript type inferred from schema
32
31
  export type HtmlNode = z.infer<typeof HtmlNodeSchemaInternal>;
@@ -52,7 +51,7 @@ export const HtmlNodeType = createNodeType({
52
51
  },
53
52
 
54
53
  clientRenderer: (node, context) => {
55
- const props: Record<string, any> = { key: context.key };
54
+ const props: Record<string, unknown> = { key: context.key };
56
55
 
57
56
  // Prevent form submission in editor
58
57
  if (node.tag === 'form') {
@@ -60,7 +59,22 @@ export const HtmlNodeType = createNodeType({
60
59
  }
61
60
 
62
61
  // Void elements cannot have children
63
- const voidElements = ['img', 'br', 'hr', 'input', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr'];
62
+ const voidElements = [
63
+ 'img',
64
+ 'br',
65
+ 'hr',
66
+ 'input',
67
+ 'meta',
68
+ 'link',
69
+ 'area',
70
+ 'base',
71
+ 'col',
72
+ 'embed',
73
+ 'param',
74
+ 'source',
75
+ 'track',
76
+ 'wbr',
77
+ ];
64
78
  if (voidElements.includes(node.tag.toLowerCase())) {
65
79
  return h(node.tag, props);
66
80
  }
@@ -69,7 +83,7 @@ export const HtmlNodeType = createNodeType({
69
83
  node.children as any,
70
84
  context.elementPath,
71
85
  context.parentComponentName,
72
- context.componentContext
86
+ context.componentContext,
73
87
  );
74
88
 
75
89
  return h(node.tag, props, children);
@@ -78,7 +92,22 @@ export const HtmlNodeType = createNodeType({
78
92
  ssrRenderer: (node, context) => {
79
93
  const childrenHTML = context.renderChildren(node.children as any, context);
80
94
  // Void elements don't have closing tags
81
- const voidElements = ['img', 'br', 'hr', 'input', 'meta', 'link', 'area', 'base', 'col', 'embed', 'param', 'source', 'track', 'wbr'];
95
+ const voidElements = [
96
+ 'img',
97
+ 'br',
98
+ 'hr',
99
+ 'input',
100
+ 'meta',
101
+ 'link',
102
+ 'area',
103
+ 'base',
104
+ 'col',
105
+ 'embed',
106
+ 'param',
107
+ 'source',
108
+ 'track',
109
+ 'wbr',
110
+ ];
82
111
  if (voidElements.includes(node.tag.toLowerCase())) {
83
112
  return `<${node.tag} />`;
84
113
  }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Island Node Type Definition
3
+ *
4
+ * Represents a BYO framework component (React/Preact/Vue/Svelte) rendered as an
5
+ * Astro island. The actual hydration only happens in a real `astro build` / play
6
+ * preview (the meno-astro codec emits `<Counter client:visible … />`). meno-core
7
+ * itself can't run a framework component, so the editor canvas renders a visible
8
+ * placeholder — design-time positioning works, interactivity is play/build only.
9
+ *
10
+ * The on-disk `.astro` form and the round-trip codec live in `meno-astro`; this
11
+ * file only models the node so it validates, persists, and renders a placeholder.
12
+ */
13
+
14
+ import { z } from 'zod';
15
+ import { createElement as h } from 'react';
16
+ import type { ComponentNode } from '../../types/nodes';
17
+ import { IfConditionSchema } from '../../validation/schemas';
18
+ import { createNodeType } from '../createNodeType';
19
+ import { textField, selectField } from '../fieldPresets';
20
+
21
+ /**
22
+ * Client (hydration) directive for an island.
23
+ * `directive` maps to Astro's `client:*` directive; `value` carries the media
24
+ * query (`client:media="(max-width: 50em)"`) or the framework name
25
+ * (`client:only="react"`). When omitted entirely, the island is server-rendered
26
+ * with no JS (a valid, zero-hydration Astro island).
27
+ */
28
+ export const IslandClientDirectiveSchema = z
29
+ .object({
30
+ directive: z.enum(['load', 'idle', 'visible', 'media', 'only']),
31
+ value: z.string().optional(),
32
+ })
33
+ .passthrough();
34
+
35
+ // Schema is the SINGLE source of truth
36
+ const IslandNodeSchemaInternal = z
37
+ .object({
38
+ type: z.literal('island'),
39
+ // Path to the framework component file relative to `src/islands/`
40
+ // (e.g. "Counter.tsx", "widgets/Chart.vue"). The extension derives the
41
+ // framework, which drives the Astro integration that gets provisioned.
42
+ src: z.string(),
43
+ client: IslandClientDirectiveSchema.optional(),
44
+ props: z.record(z.string(), z.any()).optional(),
45
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
46
+ // Slotted children (rendered server-side into the island's <slot/>).
47
+ children: z.union([z.array(z.any()), z.string()]).optional(),
48
+ })
49
+ .passthrough();
50
+
51
+ // TypeScript type inferred from schema
52
+ export type IslandNode = z.infer<typeof IslandNodeSchemaInternal>;
53
+
54
+ // Export schema for validation/schemas.ts
55
+ export const IslandNodeSchema = IslandNodeSchemaInternal;
56
+
57
+ /** Basename of the island source (drops directory + extension) for display. */
58
+ function islandName(src: string): string {
59
+ const base = src.split('/').pop() ?? src;
60
+ return base.replace(/\.(tsx|jsx|vue|svelte)$/i, '') || base;
61
+ }
62
+
63
+ /** Slotted children, normalized to the shape `buildChildren`/`renderChildren` accept. */
64
+ function islandChildren(node: IslandNode): Array<ComponentNode | string> | string | undefined {
65
+ const { children } = node;
66
+ if (Array.isArray(children)) return children.length > 0 ? (children as Array<ComponentNode | string>) : undefined;
67
+ if (typeof children === 'string') return children.length > 0 ? children : undefined;
68
+ return undefined;
69
+ }
70
+
71
+ export const IslandNodeType = createNodeType({
72
+ type: 'island',
73
+ displayName: 'Island',
74
+ category: 'special',
75
+ schema: IslandNodeSchemaInternal,
76
+
77
+ defaultValues: {
78
+ src: '',
79
+ client: { directive: 'load' },
80
+ },
81
+
82
+ treeDisplay: {
83
+ icon: 'COMPONENT',
84
+ getLabel: (node) => `Island: ${islandName(node.src) || '?'}`,
85
+ },
86
+
87
+ editableFields: [
88
+ textField({ name: 'src', label: 'Island file', required: true, placeholder: 'Counter.tsx' }),
89
+ selectField({
90
+ name: 'client.directive',
91
+ label: 'Hydration',
92
+ // The empty-value entry clears `client` entirely → a server-rendered island
93
+ // with zero JS (a valid Astro pattern); the rest map to `client:<directive>`.
94
+ options: [{ value: '', label: 'None (server-only, no JS)' }, 'load', 'idle', 'visible', 'media', 'only'],
95
+ }),
96
+ ],
97
+
98
+ // meno-core can't run a framework component. Instead of a loud highlight box
99
+ // (which made island-heavy pages unreadable), render the island's slotted
100
+ // children as normal content and fall back to a quiet inline marker when there
101
+ // are none. The wrapper keeps `data-island` for selection/identification only.
102
+ // Real hydration is play/build only.
103
+ clientRenderer: (node, context) => {
104
+ const children = islandChildren(node);
105
+ const inner = children
106
+ ? context.buildChildren(children, context.elementPath)
107
+ : h('span', { style: { color: '#9ca3af', fontSize: '13px' } }, islandName(node.src) || '?');
108
+ return h('div', { key: context.key, 'data-island': node.src, style: { display: 'contents' } }, inner);
109
+ },
110
+
111
+ ssrRenderer: (node, context) => {
112
+ const children = islandChildren(node);
113
+ const inner = children
114
+ ? context.renderChildren(children, context)
115
+ : `<span style="color:#9ca3af;font-size:13px">${escapeHtml(islandName(node.src) || '?')}</span>`;
116
+ return `<div data-island="${escapeHtml(node.src)}" style="display:contents">${inner}</div>`;
117
+ },
118
+
119
+ capabilities: {
120
+ canHaveChildren: true,
121
+ canHaveStyle: false,
122
+ canHaveAttributes: false,
123
+ requiresProps: ['src'],
124
+ },
125
+ });
126
+
127
+ function escapeHtml(value: string): string {
128
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
129
+ }
@@ -5,7 +5,12 @@
5
5
 
6
6
  import { z } from 'zod';
7
7
  import { createElement as h } from 'react';
8
- import { StyleValueSchema, InteractiveStylesSchema, LinkMappingSchema, IfConditionSchema } from '../../validation/schemas';
8
+ import {
9
+ StyleValueSchema,
10
+ InteractiveStylesSchema,
11
+ LinkMappingSchema,
12
+ IfConditionSchema,
13
+ } from '../../validation/schemas';
9
14
  import { createNodeType } from '../createNodeType';
10
15
  import { urlField } from '../fieldPresets';
11
16
 
@@ -14,20 +19,19 @@ import { urlField } from '../fieldPresets';
14
19
  const ComponentNodeSchemaRef: z.ZodType<any> = z.lazy(() => z.any());
15
20
 
16
21
  // Schema is the SINGLE source of truth
17
- const LinkNodeSchemaInternal = z.object({
18
- type: z.literal('link'),
19
- href: z.union([z.string(), LinkMappingSchema]),
20
- label: z.string().optional(), // Custom label displayed in structure tree
21
- if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
22
- style: StyleValueSchema.optional(),
23
- interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
24
- generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
25
- attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
26
- children: z.union([
27
- z.array(z.union([ComponentNodeSchemaRef, z.string()])),
28
- z.string(),
29
- ]).optional(),
30
- }).passthrough();
22
+ const LinkNodeSchemaInternal = z
23
+ .object({
24
+ type: z.literal('link'),
25
+ href: z.union([z.string(), LinkMappingSchema]),
26
+ label: z.string().optional(), // Custom label displayed in structure tree
27
+ if: IfConditionSchema.optional(), // Conditional rendering - skip node when false
28
+ style: StyleValueSchema.optional(),
29
+ interactiveStyles: InteractiveStylesSchema.optional(), // Interactive CSS rules (hover, active, etc.)
30
+ generateElementClass: z.boolean().optional(), // Generate element class without styles (for custom CSS)
31
+ attributes: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
32
+ children: z.union([z.array(z.union([ComponentNodeSchemaRef, z.string()])), z.string()]).optional(),
33
+ })
34
+ .passthrough();
31
35
 
32
36
  // TypeScript type inferred from schema
33
37
  export type LinkNode = z.infer<typeof LinkNodeSchemaInternal>;
@@ -52,16 +56,14 @@ export const LinkNodeType = createNodeType({
52
56
  getLabel: () => 'Link',
53
57
  },
54
58
 
55
- editableFields: [
56
- urlField({ name: 'href', label: 'Link URL', required: true }),
57
- ],
59
+ editableFields: [urlField({ name: 'href', label: 'Link URL', required: true })],
58
60
 
59
61
  clientRenderer: (node, context) => {
60
62
  const children = context.buildChildren(
61
63
  node.children as any,
62
64
  context.elementPath,
63
65
  context.parentComponentName,
64
- context.componentContext
66
+ context.componentContext,
65
67
  );
66
68
  return h('div', { key: context.key }, children);
67
69
  },