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
@@ -3,25 +3,29 @@
3
3
  * Handles rendering of locale/language switcher nodes
4
4
  */
5
5
 
6
- import { createElement as h } from "react";
7
- import type { ReactElement } from "react";
8
- import type { LocaleListNode, StyleObject, ResponsiveStyleObject, InteractiveStyles } from "../../../shared/types";
9
- import { extractAttributesFromNode } from "../../../shared/attributeNodeUtils";
10
- import { responsiveStylesToClasses } from "../../../shared/utilityClassMapper";
11
- import { getCachedResponsiveScalesConfig } from "../../responsiveStyleResolver";
12
- import { pathToString } from "../../../shared/pathArrayUtils";
13
- import { generateElementClassName, type ElementClassContext } from "../../../shared/elementClassName";
14
- import { InteractiveStylesRegistry } from "../../InteractiveStylesRegistry";
15
- import { hasInteractiveStyleMappings, extractInteractiveStyleMappings, resolveExtractedMappings } from "../../../shared/interactiveStyleMappings";
16
- import { UtilityClassCollector } from "../../styles/UtilityClassCollector";
17
- import type { ElementRegistry } from "../../elementRegistry";
18
- import type { BuilderContext } from "./types";
6
+ import { createElement as h } from 'react';
7
+ import type { ReactElement } from 'react';
8
+ import type { LocaleListNode, StyleObject, ResponsiveStyleObject, InteractiveStyles } from '../../../shared/types';
9
+ import { extractAttributesFromNode } from '../../../shared/attributeNodeUtils';
10
+ import { responsiveStylesToClasses } from '../../../shared/utilityClassMapper';
11
+ import { getCachedResponsiveScalesConfig } from '../../responsiveStyleResolver';
12
+ import { pathToString } from '../../../shared/pathArrayUtils';
13
+ import { generateElementClassName, type ElementClassContext } from '../../../shared/elementClassName';
14
+ import { InteractiveStylesRegistry } from '../../InteractiveStylesRegistry';
15
+ import {
16
+ hasInteractiveStyleMappings,
17
+ extractInteractiveStyleMappings,
18
+ resolveExtractedMappings,
19
+ } from '../../../shared/interactiveStyleMappings';
20
+ import { UtilityClassCollector } from '../../styles/UtilityClassCollector';
21
+ import type { ElementRegistry } from '../../elementRegistry';
22
+ import type { BuilderContext } from './types';
19
23
 
20
24
  export interface LocaleListBuilderDeps {
21
25
  elementRegistry: ElementRegistry;
22
26
  getEffectiveParentComponentName: (
23
27
  componentContext: string | null,
24
- parentComponentName: string | null
28
+ parentComponentName: string | null,
25
29
  ) => string | null;
26
30
  /** Optional getter for current page name (for interactive styles class generation) */
27
31
  getCurrentPageName?: () => string;
@@ -32,14 +36,16 @@ export interface LocaleListBuilderDeps {
32
36
  /**
33
37
  * Build a locale list node (language switcher)
34
38
  */
35
- export function buildLocaleList(
36
- node: LocaleListNode,
37
- ctx: BuilderContext,
38
- deps: LocaleListBuilderDeps
39
- ): ReactElement {
39
+ export function buildLocaleList(node: LocaleListNode, ctx: BuilderContext, deps: LocaleListBuilderDeps): ReactElement {
40
40
  const {
41
- key, elementPath, parentComponentName, componentContext, componentRootPath,
42
- locale, i18nConfig, cmsItemIndexPath
41
+ key,
42
+ elementPath,
43
+ parentComponentName,
44
+ componentContext,
45
+ componentRootPath,
46
+ locale,
47
+ i18nConfig,
48
+ cmsItemIndexPath,
43
49
  } = ctx;
44
50
 
45
51
  const effectiveParentComponentName = deps.getEffectiveParentComponentName(componentContext, parentComponentName);
@@ -48,7 +54,7 @@ export function buildLocaleList(
48
54
  const extractedAttributes = extractAttributesFromNode(node as any);
49
55
 
50
56
  // Build locale list props
51
- const localeListProps: Record<string, any> = {
57
+ const localeListProps: Record<string, unknown> = {
52
58
  key,
53
59
  'data-element-path': pathToString(elementPath),
54
60
  'data-locale-list': 'true',
@@ -63,7 +69,7 @@ export function buildLocaleList(
63
69
  }
64
70
  }
65
71
  }
66
- }
72
+ },
67
73
  };
68
74
 
69
75
  // Add CMS item index path for elements inside CMS lists
@@ -72,7 +78,7 @@ export function buildLocaleList(
72
78
  }
73
79
 
74
80
  // Start building className
75
- let classNames: string[] = [];
81
+ const classNames: string[] = [];
76
82
 
77
83
  // Resolve fluid mode once for all conversions in this builder
78
84
  const scales = getCachedResponsiveScalesConfig();
@@ -80,10 +86,10 @@ export function buildLocaleList(
80
86
 
81
87
  // Convert container styles to utility classes
82
88
  if (node.style) {
83
- const utilityClasses = responsiveStylesToClasses(
84
- node.style as StyleObject | ResponsiveStyleObject,
85
- { fluidActive, responsiveScales: scales ?? undefined }
86
- );
89
+ const utilityClasses = responsiveStylesToClasses(node.style as StyleObject | ResponsiveStyleObject, {
90
+ fluidActive,
91
+ responsiveScales: scales ?? undefined,
92
+ });
87
93
  UtilityClassCollector.collect(utilityClasses);
88
94
  classNames.push(...utilityClasses);
89
95
  }
@@ -96,11 +102,10 @@ export function buildLocaleList(
96
102
  if ((nodeInteractiveStyles && nodeInteractiveStyles.length > 0) || nodeGenerateElementClass) {
97
103
  // Determine file type and name for element class generation
98
104
  const useComponentContext = Boolean(componentContext);
99
- const fileType = useComponentContext ? 'component' : (deps.getCurrentFileType?.() || 'page');
100
- const fileName = useComponentContext ? componentContext! : (deps.getCurrentPageName?.() || 'page');
101
- const pathForClass = useComponentContext && componentRootPath
102
- ? elementPath.slice(componentRootPath.length)
103
- : elementPath;
105
+ const fileType = useComponentContext ? 'component' : deps.getCurrentFileType?.() || 'page';
106
+ const fileName = useComponentContext ? componentContext! : deps.getCurrentPageName?.() || 'page';
107
+ const pathForClass =
108
+ useComponentContext && componentRootPath ? elementPath.slice(componentRootPath.length) : elementPath;
104
109
 
105
110
  const elementClassCtx: ElementClassContext = {
106
111
  fileType,
@@ -131,10 +136,10 @@ export function buildLocaleList(
131
136
  const previewClasses: string[] = [];
132
137
  for (const rule of nodeInteractiveStyles) {
133
138
  if (rule.previewProp && ctx.componentResolvedProps[rule.previewProp] === true) {
134
- const styleClasses = responsiveStylesToClasses(
135
- rule.style as StyleObject | ResponsiveStyleObject,
136
- { fluidActive, responsiveScales: scales ?? undefined }
137
- );
139
+ const styleClasses = responsiveStylesToClasses(rule.style as StyleObject | ResponsiveStyleObject, {
140
+ fluidActive,
141
+ responsiveScales: scales ?? undefined,
142
+ });
138
143
  UtilityClassCollector.collect(styleClasses);
139
144
  previewClasses.push(...styleClasses);
140
145
  }
@@ -151,9 +156,9 @@ export function buildLocaleList(
151
156
  // would be spread onto the React element below and override the className that
152
157
  // holds the utility classes derived from node.style.
153
158
  if ('class' in extractedAttributes || 'className' in extractedAttributes) {
154
- const attrClass = ((extractedAttributes as Record<string, unknown>).class
155
- ?? (extractedAttributes as Record<string, unknown>).className
156
- ?? '') as string;
159
+ const attrClass = ((extractedAttributes as Record<string, unknown>).class ??
160
+ (extractedAttributes as Record<string, unknown>).className ??
161
+ '') as string;
157
162
  if (attrClass) {
158
163
  classNames.push(...attrClass.split(/\s+/));
159
164
  }
@@ -188,10 +193,30 @@ export function buildLocaleList(
188
193
  const currentLocaleCode = locale || i18nConfig?.defaultLocale || 'en';
189
194
 
190
195
  // Convert item styles, active item styles, separator styles, and flag styles to utility classes
191
- const itemClasses = node.itemStyle ? responsiveStylesToClasses(node.itemStyle as StyleObject | ResponsiveStyleObject, { fluidActive, responsiveScales: scales ?? undefined }) : [];
192
- const activeItemClasses = node.activeItemStyle ? responsiveStylesToClasses(node.activeItemStyle as StyleObject | ResponsiveStyleObject, { fluidActive, responsiveScales: scales ?? undefined }) : [];
193
- const separatorClasses = node.separatorStyle ? responsiveStylesToClasses(node.separatorStyle as StyleObject | ResponsiveStyleObject, { fluidActive, responsiveScales: scales ?? undefined }) : [];
194
- const flagClasses = node.flagStyle ? responsiveStylesToClasses(node.flagStyle as StyleObject | ResponsiveStyleObject, { fluidActive, responsiveScales: scales ?? undefined }) : [];
196
+ const itemClasses = node.itemStyle
197
+ ? responsiveStylesToClasses(node.itemStyle as StyleObject | ResponsiveStyleObject, {
198
+ fluidActive,
199
+ responsiveScales: scales ?? undefined,
200
+ })
201
+ : [];
202
+ const activeItemClasses = node.activeItemStyle
203
+ ? responsiveStylesToClasses(node.activeItemStyle as StyleObject | ResponsiveStyleObject, {
204
+ fluidActive,
205
+ responsiveScales: scales ?? undefined,
206
+ })
207
+ : [];
208
+ const separatorClasses = node.separatorStyle
209
+ ? responsiveStylesToClasses(node.separatorStyle as StyleObject | ResponsiveStyleObject, {
210
+ fluidActive,
211
+ responsiveScales: scales ?? undefined,
212
+ })
213
+ : [];
214
+ const flagClasses = node.flagStyle
215
+ ? responsiveStylesToClasses(node.flagStyle as StyleObject | ResponsiveStyleObject, {
216
+ fluidActive,
217
+ responsiveScales: scales ?? undefined,
218
+ })
219
+ : [];
195
220
  UtilityClassCollector.collect(itemClasses);
196
221
  UtilityClassCollector.collect(activeItemClasses);
197
222
  UtilityClassCollector.collect(separatorClasses);
@@ -201,26 +226,31 @@ export function buildLocaleList(
201
226
  const linkElements: ReactElement[] = [];
202
227
  for (let i = 0; i < configLocales.length; i++) {
203
228
  const localeConfig = configLocales[i];
229
+ if (!localeConfig) continue;
204
230
  const isCurrent = localeConfig.code === currentLocaleCode;
205
231
  if (!showCurrent && isCurrent) continue;
206
232
 
207
233
  // Add separator between links (empty span styled via separatorStyle)
208
234
  if (showSeparator && linkElements.length > 0) {
209
- linkElements.push(h('span', {
210
- key: `sep-${i}`,
211
- className: separatorClasses.length > 0 ? separatorClasses.join(' ') : undefined
212
- }));
235
+ linkElements.push(
236
+ h('span', {
237
+ key: `sep-${i}`,
238
+ className: separatorClasses.length > 0 ? separatorClasses.join(' ') : undefined,
239
+ }),
240
+ );
213
241
  }
214
242
 
215
243
  // Build link element content - flag icon (if enabled and exists) + nativeName in span
216
244
  const linkContent: (ReactElement | string)[] = [];
217
245
  if (showFlag && localeConfig.icon) {
218
- linkContent.push(h('img', {
219
- key: 'flag',
220
- src: localeConfig.icon,
221
- alt: `${localeConfig.name} flag`,
222
- className: flagClasses.length > 0 ? flagClasses.join(' ') : undefined
223
- }));
246
+ linkContent.push(
247
+ h('img', {
248
+ key: 'flag',
249
+ src: localeConfig.icon,
250
+ alt: `${localeConfig.name} flag`,
251
+ className: flagClasses.length > 0 ? flagClasses.join(' ') : undefined,
252
+ }),
253
+ );
224
254
  }
225
255
  // Get display text based on displayType (mirrors SSR logic)
226
256
  const displayType = node.displayType || 'nativeName';
@@ -232,7 +262,6 @@ export function buildLocaleList(
232
262
  case 'name':
233
263
  displayText = localeConfig.name;
234
264
  break;
235
- case 'nativeName':
236
265
  default:
237
266
  displayText = localeConfig.nativeName;
238
267
  break;
@@ -241,13 +270,19 @@ export function buildLocaleList(
241
270
 
242
271
  // Current item gets both itemClasses + activeItemClasses (additive/override)
243
272
  const linkClasses = isCurrent ? [...itemClasses, ...activeItemClasses] : itemClasses;
244
- linkElements.push(h('div', {
245
- key: `locale-${localeConfig.code}`,
246
- 'data-current': isCurrent ? 'true' : 'false',
247
- 'data-locale': localeConfig.code,
248
- className: linkClasses.length > 0 ? linkClasses.join(' ') : undefined,
249
- style: { cursor: 'pointer' }
250
- }, ...linkContent));
273
+ linkElements.push(
274
+ h(
275
+ 'div',
276
+ {
277
+ key: `locale-${localeConfig.code}`,
278
+ 'data-current': isCurrent ? 'true' : 'false',
279
+ 'data-locale': localeConfig.code,
280
+ className: linkClasses.length > 0 ? linkClasses.join(' ') : undefined,
281
+ style: { cursor: 'pointer' },
282
+ },
283
+ ...linkContent,
284
+ ),
285
+ );
251
286
  }
252
287
 
253
288
  return h('div', localeListProps, linkElements);
@@ -2,11 +2,11 @@
2
2
  * Shared types for ComponentBuilder modules
3
3
  */
4
4
 
5
- import type { ReactElement } from "react";
6
- import type { ComponentNode } from "../../../shared/types";
7
- import type { Path } from "../../../shared/pathArrayUtils";
8
- import type { I18nConfig } from "../../../shared/types/components";
9
- import type { CMSItem, ItemContext, TemplateContext } from "../../../shared/types/cms";
5
+ import type { ReactElement } from 'react';
6
+ import type { ComponentNode } from '../../../shared/types';
7
+ import type { Path } from '../../../shared/pathArrayUtils';
8
+ import type { I18nConfig } from '../../../shared/types/components';
9
+ import type { CMSItem, ItemContext, TemplateContext } from '../../../shared/types/cms';
10
10
 
11
11
  export type ComponentProps = Record<string, unknown>;
12
12
 
@@ -7,7 +7,6 @@
7
7
 
8
8
  import type { I18nValue, I18nConfig } from '../../shared/types';
9
9
  import { getI18nConfig } from '../i18nConfigService';
10
- import { isRichTextMarker, richTextMarkerToHtml } from '../../shared/propResolver';
11
10
  import { tiptapToHtml } from '../../shared/richtext/tiptapToHtml';
12
11
  import { isTiptapDocument } from '../../shared/richtext/types';
13
12
  import { RAW_HTML_PREFIX } from '../../shared/constants';
@@ -60,15 +59,11 @@ function resolveI18nValue(value: I18nValue, locale: string, config: I18nConfig):
60
59
  * @param locale - Optional locale for i18n resolution (defaults to config's defaultLocale)
61
60
  * @returns Processed string with values filled in
62
61
  */
63
- export function processCMSTemplate(
64
- template: string,
65
- cmsItem: Record<string, unknown>,
66
- locale?: string
67
- ): string {
62
+ export function processCMSTemplate(template: string, cmsItem: Record<string, unknown>, locale?: string): string {
68
63
  const config = getI18nConfig();
69
64
  const effectiveLocale = locale || config.defaultLocale;
70
65
 
71
- return template.replace(/\{\{cms\.([^}]+)\}\}/g, (match, fieldPath) => {
66
+ return template.replace(/\{\{cms\.([^}]+)\}\}/g, (_match, fieldPath) => {
72
67
  // Support nested paths like cms.author.name
73
68
  const parts = fieldPath.trim().split('.');
74
69
  let value: unknown = cmsItem;
@@ -94,7 +89,11 @@ export function processCMSTemplate(
94
89
  }
95
90
  // Handle rich-text markers - extract HTML content for interpolation
96
91
  // Mark with RAW_HTML_PREFIX so renderer knows not to escape
97
- if (typeof value === 'object' && '__richtext__' in value && typeof (value as unknown as { html: string }).html === 'string') {
92
+ if (
93
+ typeof value === 'object' &&
94
+ '__richtext__' in value &&
95
+ typeof (value as unknown as { html: string }).html === 'string'
96
+ ) {
98
97
  return RAW_HTML_PREFIX + (value as unknown as { html: string }).html;
99
98
  }
100
99
  // Handle raw TipTap documents (fallback if not preprocessed)
@@ -118,7 +117,7 @@ export function processCMSTemplate(
118
117
  export function processCMSPropsTemplate(
119
118
  props: Record<string, unknown>,
120
119
  cmsItem: Record<string, unknown>,
121
- locale?: string
120
+ locale?: string,
122
121
  ): Record<string, unknown> {
123
122
  const result: Record<string, unknown> = {};
124
123
 
@@ -27,7 +27,7 @@ export class ElementRegistry {
27
27
  _componentName?: string | null,
28
28
  _isComponentRoot?: boolean,
29
29
  _props?: Record<string, unknown>,
30
- _metadata?: Partial<ElementMetadata>
30
+ _metadata?: Partial<ElementMetadata>,
31
31
  ): void {
32
32
  // No-op in core - element tracking is editor-only
33
33
  }
@@ -81,7 +81,7 @@ export class ElementRegistry {
81
81
  _path: Path | string,
82
82
  _itemIndex: number,
83
83
  _element: HTMLElement | null,
84
- _metadata?: Partial<ElementMetadata>
84
+ _metadata?: Partial<ElementMetadata>,
85
85
  ): void {
86
86
  // No-op in core - element tracking is editor-only
87
87
  }
@@ -105,7 +105,7 @@ let _elementRegistry: ElementRegistry = new ElementRegistry();
105
105
  export const elementRegistry: ElementRegistry = new Proxy({} as ElementRegistry, {
106
106
  get(_target, prop) {
107
107
  return (_elementRegistry as any)[prop];
108
- }
108
+ },
109
109
  });
110
110
 
111
111
  /**
@@ -1,9 +1,5 @@
1
1
  import { describe, test, expect, beforeEach, afterEach, mock } from 'bun:test';
2
- import {
3
- fetchFontFamilies,
4
- getCachedFontFamilies,
5
- invalidateFontFamilies,
6
- } from './fontFamiliesService';
2
+ import { fetchFontFamilies, getCachedFontFamilies, invalidateFontFamilies } from './fontFamiliesService';
7
3
 
8
4
  const realFetch = globalThis.fetch;
9
5
 
@@ -36,11 +32,7 @@ describe('fontFamiliesService', () => {
36
32
 
37
33
  test('fetchFontFamilies dedupes families that repeat across weights/unicode-ranges', async () => {
38
34
  mockFetchOnce({
39
- fonts: [
40
- { family: 'Fraunces', weight: 400 },
41
- { family: 'Fraunces', weight: 700 },
42
- { family: 'Inter' },
43
- ],
35
+ fonts: [{ family: 'Fraunces', weight: 400 }, { family: 'Fraunces', weight: 700 }, { family: 'Inter' }],
44
36
  });
45
37
  const families = await fetchFontFamilies();
46
38
  expect(families).toEqual(['Fraunces', 'Inter']);
@@ -15,7 +15,7 @@ function extractFamilyFromPath(path: string): string {
15
15
  const name = filename.replace(/\.(ttf|woff2?|otf)$/i, '');
16
16
  return name
17
17
  .split('-')
18
- .map(part => part.charAt(0).toUpperCase() + part.slice(1))
18
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
19
19
  .join(' ');
20
20
  }
21
21
 
@@ -38,8 +38,8 @@ export async function fetchFontFamilies(): Promise<string[]> {
38
38
  if (inFlight) return inFlight;
39
39
 
40
40
  inFlight = fetch('/api/config')
41
- .then(res => (res.ok ? res.json() : { fonts: [] }))
42
- .then(config => {
41
+ .then((res) => (res.ok ? res.json() : { fonts: [] }))
42
+ .then((config) => {
43
43
  cachedFamilies = extractFamilies(config.fonts);
44
44
  return cachedFamilies;
45
45
  })
@@ -3,9 +3,9 @@
3
3
  * Manages Hot Module Replacement WebSocket connection, status tracking, and visual indicators.
4
4
  */
5
5
 
6
- import { createElement as h, useState, useEffect, useRef } from "react";
7
- import type { ReactElement } from "react";
8
- import { HMRWebSocket } from "../hmrWebSocket";
6
+ import { createElement as h, useState, useEffect, useRef } from 'react';
7
+ import type { ReactElement } from 'react';
8
+ import { HMRWebSocket } from '../hmrWebSocket';
9
9
 
10
10
  export interface HMRManagerProps {
11
11
  /**
@@ -46,7 +46,7 @@ export function HMRManager({
46
46
  onStatusChange,
47
47
  currentPath = typeof window !== 'undefined' ? window.location.pathname : '/',
48
48
  onReload,
49
- onCMSUpdate
49
+ onCMSUpdate,
50
50
  }: HMRManagerProps): ReactElement | null {
51
51
  const [status, setStatus] = useState<'connecting' | 'connected' | 'disconnected' | 'error'>('connecting');
52
52
 
@@ -63,7 +63,9 @@ export function HMRManager({
63
63
  onCMSUpdateRef.current = onCMSUpdate;
64
64
  }, [onCMSUpdate]);
65
65
 
66
- useEffect(() => { onReloadRef.current = onReload; }, [onReload]);
66
+ useEffect(() => {
67
+ onReloadRef.current = onReload;
68
+ }, [onReload]);
67
69
 
68
70
  // Define message handler - reads from refs to always get latest values
69
71
  const createMessageHandler = () => (data: any) => {
@@ -95,6 +97,19 @@ export function HMRManager({
95
97
  } else if (data.type === 'hmr:libraries-update') {
96
98
  // Dispatch custom event to trigger full page reload for library changes
97
99
  document.dispatchEvent(new CustomEvent('hmr-libraries-update'));
100
+ } else if (data.type === 'hmr:config-update') {
101
+ // project.config.json changed (e.g. a locale was added in the editor).
102
+ // Dispatch custom event so the Router refetches the i18n config and
103
+ // re-resolves the current route — without this, the canvas keeps the
104
+ // stale locale set and a freshly added locale's paths (/pl) 404 with
105
+ // "Page not found" until a full canvas reload. The studio frame has its
106
+ // own handler for the same WS message (editor.tsx); this is the canvas's.
107
+ document.dispatchEvent(new CustomEvent('hmr-config-update'));
108
+ } else if (data.type === 'hmr:stylesheet-update') {
109
+ // An external project stylesheet changed (e.g. a mirror-import class edit
110
+ // via /api/element-styles/update — outside the file watcher's categories).
111
+ // Dispatch so the canvas cache-busts its <link> stylesheets and repaints.
112
+ document.dispatchEvent(new CustomEvent('hmr-stylesheet-update'));
98
113
  }
99
114
  };
100
115
 
@@ -150,17 +165,16 @@ export function HMRManager({
150
165
  useEffect(() => {
151
166
  if (import.meta.hot) {
152
167
  import.meta.hot.accept();
153
-
168
+
154
169
  import.meta.hot.on('bun:beforeUpdate', () => {
155
170
  // HMR update starting
156
171
  });
157
-
172
+
158
173
  import.meta.hot.on('bun:afterUpdate', () => {
159
174
  // HMR update complete
160
175
  });
161
-
162
- import.meta.hot.on('bun:error', () => {
163
- });
176
+
177
+ import.meta.hot.on('bun:error', () => {});
164
178
  }
165
179
  }, []);
166
180
 
@@ -182,24 +196,27 @@ export function HMRIndicator({ status }: { status: 'connecting' | 'connected' |
182
196
 
183
197
  const config = statusConfig[status];
184
198
 
185
- return h('div', {
186
- id: 'hmr-indicator',
187
- style: {
188
- position: 'fixed',
189
- top: '20px',
190
- right: '20px',
191
- background: config.bg,
192
- color: 'white',
193
- padding: '12px 20px',
194
- borderRadius: '8px',
195
- fontSize: '14px',
196
- fontWeight: '600',
197
- display: config.show ? 'block' : 'none',
198
- boxShadow: `0 4px 12px ${config.bg}40`,
199
- animation: 'slideIn 0.3s ease-out',
200
- zIndex: 9999,
201
- transition: 'all 0.3s ease',
202
- }
203
- }, config.text);
199
+ return h(
200
+ 'div',
201
+ {
202
+ id: 'hmr-indicator',
203
+ style: {
204
+ position: 'fixed',
205
+ top: '20px',
206
+ right: '20px',
207
+ background: config.bg,
208
+ color: 'white',
209
+ padding: '12px 20px',
210
+ borderRadius: '8px',
211
+ fontSize: '14px',
212
+ fontWeight: '600',
213
+ display: config.show ? 'block' : 'none',
214
+ boxShadow: `0 4px 12px ${config.bg}40`,
215
+ animation: 'slideIn 0.3s ease-out',
216
+ zIndex: 9999,
217
+ transition: 'all 0.3s ease',
218
+ },
219
+ },
220
+ config.text,
221
+ );
204
222
  }
205
-
@@ -23,6 +23,27 @@ declare global {
23
23
  __hmrVariablesCSSInitialized?: boolean;
24
24
  __hmrFontsCSSInitialized?: boolean;
25
25
  __hmrLibrariesInitialized?: boolean;
26
+ __hmrStylesheetInitialized?: boolean;
27
+ }
28
+ }
29
+
30
+ // Cache-bust same-origin `<link rel="stylesheet">` hrefs so the canvas repaints
31
+ // after a project CSS file changes (e.g. a mirror-import class edit). The old
32
+ // stylesheet stays applied until the new one loads, so there's no flash. Skips
33
+ // cross-origin (CDN) links and data: URIs.
34
+ function reloadExternalStylesheets(): void {
35
+ const links = document.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"]');
36
+ for (const link of links) {
37
+ const href = link.getAttribute('href');
38
+ if (!href || href.startsWith('data:')) continue;
39
+ try {
40
+ const url = new URL(href, location.href);
41
+ if (url.origin !== location.origin) continue;
42
+ url.searchParams.set('hmr', String(Date.now()));
43
+ link.href = url.href;
44
+ } catch {
45
+ // ignore unparseable href
46
+ }
26
47
  }
27
48
  }
28
49
 
@@ -32,17 +53,15 @@ async function injectUpdatedThemeCSS(): Promise<void> {
32
53
  const themesResponse = await fetch('/api/themes');
33
54
  if (!themesResponse.ok) return;
34
55
 
35
- const themesData = await themesResponse.json() as {
56
+ const themesData = (await themesResponse.json()) as {
36
57
  themes: Array<{ name: string; label: string }>;
37
58
  default: string;
38
59
  };
39
60
 
40
61
  // Fetch colors for each theme
41
- const themeColors: Record<string, any> = {};
62
+ const themeColors: Record<string, Record<string, string>> = {};
42
63
  for (const theme of themesData.themes) {
43
- const colorResponse = await fetch(
44
- `/api/colors?theme=${encodeURIComponent(theme.name)}`
45
- );
64
+ const colorResponse = await fetch(`/api/colors?theme=${encodeURIComponent(theme.name)}`);
46
65
  if (colorResponse.ok) {
47
66
  themeColors[theme.name] = (await colorResponse.json()).colors;
48
67
  }
@@ -51,20 +70,21 @@ async function injectUpdatedThemeCSS(): Promise<void> {
51
70
  // Also get default theme colors
52
71
  const defaultResponse = await fetch('/api/colors');
53
72
  if (defaultResponse.ok) {
54
- themeColors['default'] = (await defaultResponse.json()).colors;
73
+ themeColors.default = (await defaultResponse.json()).colors;
55
74
  }
56
75
 
57
76
  // Generate CSS — default theme in :root, each named theme in [theme="..."]
58
77
  let css = '';
59
- if (themeColors['default']) {
60
- const vars = Object.entries(themeColors['default'])
78
+ if (themeColors.default) {
79
+ const vars = Object.entries(themeColors.default)
61
80
  .map(([name, value]) => ` --${name}: ${value};`)
62
81
  .join('\n');
63
82
  css += `:root {\n${vars}\n}\n\n`;
64
83
  }
65
84
  for (const theme of themesData.themes) {
66
- if (themeColors[theme.name]) {
67
- const vars = Object.entries(themeColors[theme.name])
85
+ const themeVars = themeColors[theme.name];
86
+ if (themeVars) {
87
+ const vars = Object.entries(themeVars)
68
88
  .map(([name, value]) => ` --${name}: ${value};`)
69
89
  .join('\n');
70
90
  css += `[theme="${theme.name}"] {\n${vars}\n}\n\n`;
@@ -140,21 +160,36 @@ export function setupCssHmrListeners(): void {
140
160
 
141
161
  if (!window.__hmrColorsInitialized) {
142
162
  window.__hmrColorsInitialized = true;
143
- document.addEventListener('hmr-colors-update', () => { void injectUpdatedThemeCSS(); });
163
+ document.addEventListener('hmr-colors-update', () => {
164
+ void injectUpdatedThemeCSS();
165
+ });
144
166
  }
145
167
 
146
168
  if (!window.__hmrVariablesCSSInitialized) {
147
169
  window.__hmrVariablesCSSInitialized = true;
148
- document.addEventListener('hmr-variables-update', () => { void injectUpdatedVariablesCSS(); });
170
+ document.addEventListener('hmr-variables-update', () => {
171
+ void injectUpdatedVariablesCSS();
172
+ });
149
173
  }
150
174
 
151
175
  if (!window.__hmrFontsCSSInitialized) {
152
176
  window.__hmrFontsCSSInitialized = true;
153
- document.addEventListener('hmr-fonts-update', () => { void injectUpdatedFontsCSS(); });
177
+ document.addEventListener('hmr-fonts-update', () => {
178
+ void injectUpdatedFontsCSS();
179
+ });
154
180
  }
155
181
 
156
182
  if (!window.__hmrLibrariesInitialized) {
157
183
  window.__hmrLibrariesInitialized = true;
158
- document.addEventListener('hmr-libraries-update', () => { location.reload(); });
184
+ document.addEventListener('hmr-libraries-update', () => {
185
+ location.reload();
186
+ });
187
+ }
188
+
189
+ if (!window.__hmrStylesheetInitialized) {
190
+ window.__hmrStylesheetInitialized = true;
191
+ document.addEventListener('hmr-stylesheet-update', () => {
192
+ reloadExternalStylesheets();
193
+ });
159
194
  }
160
195
  }