meno-core 1.0.53 → 1.0.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (387) hide show
  1. package/.claude/settings.local.json +1 -3
  2. package/bin/cli.ts +48 -57
  3. package/build-astro.ts +131 -113
  4. package/build-next.ts +109 -96
  5. package/build-static.test.ts +39 -10
  6. package/build-static.ts +120 -122
  7. package/dist/bin/cli.js +34 -38
  8. package/dist/bin/cli.js.map +2 -2
  9. package/dist/build-static.js +12 -11
  10. package/dist/chunks/chunk-2AR55GYH.js +42 -0
  11. package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
  12. package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
  13. package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
  14. package/dist/chunks/chunk-3XER4E5W.js +168 -0
  15. package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
  16. package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
  17. package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
  18. package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
  19. package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
  20. package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
  21. package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
  22. package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
  23. package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
  24. package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
  25. package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
  26. package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
  27. package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
  28. package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
  29. package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
  30. package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
  31. package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
  32. package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
  33. package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
  34. package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
  35. package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
  36. package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
  37. package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
  38. package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
  39. package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
  40. package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
  41. package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
  42. package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
  43. package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
  44. package/dist/chunks/configService-PRJZF7Y6.js +14 -0
  45. package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
  46. package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
  47. package/dist/entries/server-router.js +14 -19
  48. package/dist/entries/server-router.js.map +2 -2
  49. package/dist/lib/client/index.js +638 -332
  50. package/dist/lib/client/index.js.map +2 -2
  51. package/dist/lib/server/index.js +177 -235
  52. package/dist/lib/server/index.js.map +2 -2
  53. package/dist/lib/shared/index.js +80 -44
  54. package/dist/lib/shared/index.js.map +2 -2
  55. package/dist/lib/shared/richtext/index.js +1 -1
  56. package/dist/lib/test-utils/index.js +38 -60
  57. package/dist/lib/test-utils/index.js.map +2 -2
  58. package/entries/client-router.tsx +10 -8
  59. package/entries/server-router.tsx +1 -7
  60. package/lib/client/ClientInitializer.ts +8 -8
  61. package/lib/client/ErrorBoundary.test.tsx +146 -143
  62. package/lib/client/ErrorBoundary.tsx +175 -127
  63. package/lib/client/componentRegistry.test.ts +96 -108
  64. package/lib/client/componentRegistry.ts +1 -2
  65. package/lib/client/contexts/ThemeContext.tsx +3 -2
  66. package/lib/client/core/ComponentBuilder.test.ts +513 -560
  67. package/lib/client/core/ComponentBuilder.ts +318 -146
  68. package/lib/client/core/ComponentRenderer.test.tsx +1 -2
  69. package/lib/client/core/ComponentRenderer.tsx +46 -33
  70. package/lib/client/core/builders/embedBuilder.ts +241 -53
  71. package/lib/client/core/builders/linkBuilder.ts +71 -44
  72. package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
  73. package/lib/client/core/builders/listBuilder.ts +115 -86
  74. package/lib/client/core/builders/localeListBuilder.ts +95 -60
  75. package/lib/client/core/builders/types.ts +5 -5
  76. package/lib/client/core/cmsTemplateProcessor.ts +7 -7
  77. package/lib/client/elementRegistry.ts +3 -3
  78. package/lib/client/fontFamiliesService.test.ts +2 -10
  79. package/lib/client/fontFamiliesService.ts +3 -3
  80. package/lib/client/hmr/HMRManager.tsx +8 -0
  81. package/lib/client/hmrCssReload.ts +14 -8
  82. package/lib/client/hmrWebSocket.ts +9 -14
  83. package/lib/client/hooks/useColorVariables.test.ts +21 -21
  84. package/lib/client/hooks/useColorVariables.ts +12 -10
  85. package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
  86. package/lib/client/hooks/useVariables.ts +4 -4
  87. package/lib/client/hydration/HydrationUtils.test.ts +24 -25
  88. package/lib/client/hydration/HydrationUtils.ts +3 -4
  89. package/lib/client/i18nConfigService.test.ts +2 -7
  90. package/lib/client/i18nConfigService.ts +2 -2
  91. package/lib/client/meno-filter/MenoFilter.test.ts +19 -21
  92. package/lib/client/meno-filter/MenoFilter.ts +5 -9
  93. package/lib/client/meno-filter/bindings.ts +15 -40
  94. package/lib/client/meno-filter/init.ts +1 -1
  95. package/lib/client/meno-filter/renderer.ts +23 -29
  96. package/lib/client/meno-filter/script.generated.ts +1 -3
  97. package/lib/client/meno-filter/ui.ts +3 -5
  98. package/lib/client/meno-filter/updates.ts +15 -21
  99. package/lib/client/navigation.test.ts +159 -159
  100. package/lib/client/navigation.ts +0 -1
  101. package/lib/client/responsiveStyleResolver.test.ts +230 -228
  102. package/lib/client/responsiveStyleResolver.ts +13 -16
  103. package/lib/client/routing/RouteLoader.test.ts +23 -24
  104. package/lib/client/routing/RouteLoader.ts +22 -35
  105. package/lib/client/routing/Router.tsx +31 -3
  106. package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
  107. package/lib/client/scripts/ScriptExecutor.ts +15 -33
  108. package/lib/client/services/PrefetchService.test.ts +2 -2
  109. package/lib/client/services/PrefetchService.ts +10 -24
  110. package/lib/client/styleProcessor.test.ts +9 -9
  111. package/lib/client/styleProcessor.ts +18 -15
  112. package/lib/client/styles/StyleInjector.test.ts +122 -115
  113. package/lib/client/styles/StyleInjector.ts +9 -6
  114. package/lib/client/styles/UtilityClassCollector.ts +19 -26
  115. package/lib/client/styles/cspNonce.test.ts +2 -5
  116. package/lib/client/templateEngine.test.ts +554 -482
  117. package/lib/client/templateEngine.ts +200 -64
  118. package/lib/client/theme.ts +0 -1
  119. package/lib/client/utils/toast.ts +0 -1
  120. package/lib/server/__integration__/api-routes.test.ts +8 -4
  121. package/lib/server/__integration__/cms-integration.test.ts +1 -4
  122. package/lib/server/__integration__/server-lifecycle.test.ts +2 -5
  123. package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
  124. package/lib/server/__integration__/static-assets.test.ts +1 -1
  125. package/lib/server/__integration__/test-helpers.ts +84 -70
  126. package/lib/server/ab/generateFunctions.ts +12 -10
  127. package/lib/server/astro/cmsPageEmitter.ts +23 -38
  128. package/lib/server/astro/componentEmitter.ts +25 -36
  129. package/lib/server/astro/cssCollector.ts +10 -26
  130. package/lib/server/astro/nodeToAstro.test.ts +1750 -30
  131. package/lib/server/astro/nodeToAstro.ts +198 -187
  132. package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
  133. package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
  134. package/lib/server/astro/pageEmitter.ts +9 -13
  135. package/lib/server/astro/tailwindMapper.test.ts +10 -37
  136. package/lib/server/astro/tailwindMapper.ts +33 -40
  137. package/lib/server/astro/templateTransformer.ts +14 -17
  138. package/lib/server/createServer.ts +5 -17
  139. package/lib/server/cssGenerator.test.ts +35 -44
  140. package/lib/server/cssGenerator.ts +6 -17
  141. package/lib/server/fileWatcher.test.ts +124 -10
  142. package/lib/server/fileWatcher.ts +124 -118
  143. package/lib/server/index.ts +7 -1
  144. package/lib/server/jsonLoader.test.ts +39 -2
  145. package/lib/server/jsonLoader.ts +33 -31
  146. package/lib/server/middleware/cors.test.ts +20 -20
  147. package/lib/server/middleware/cors.ts +28 -4
  148. package/lib/server/middleware/errorHandler.test.ts +5 -3
  149. package/lib/server/middleware/errorHandler.ts +3 -8
  150. package/lib/server/middleware/index.ts +0 -1
  151. package/lib/server/middleware/logger.test.ts +7 -5
  152. package/lib/server/middleware/logger.ts +10 -22
  153. package/lib/server/pageCache.test.ts +76 -77
  154. package/lib/server/pageCache.ts +0 -1
  155. package/lib/server/projectContext.ts +4 -3
  156. package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
  157. package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
  158. package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
  159. package/lib/server/providers/fileSystemPageProvider.ts +31 -12
  160. package/lib/server/routes/api/cms.test.ts +26 -14
  161. package/lib/server/routes/api/cms.ts +9 -14
  162. package/lib/server/routes/api/components.ts +30 -34
  163. package/lib/server/routes/api/config.ts +0 -1
  164. package/lib/server/routes/api/core-routes.ts +47 -74
  165. package/lib/server/routes/api/functions.ts +8 -16
  166. package/lib/server/routes/api/index.ts +3 -6
  167. package/lib/server/routes/api/pages.ts +8 -11
  168. package/lib/server/routes/api/shared.test.ts +1 -1
  169. package/lib/server/routes/api/shared.ts +12 -9
  170. package/lib/server/routes/api/variables.test.ts +1 -3
  171. package/lib/server/routes/api/variables.ts +1 -1
  172. package/lib/server/routes/index.ts +23 -26
  173. package/lib/server/routes/pages.ts +34 -29
  174. package/lib/server/routes/static.ts +16 -4
  175. package/lib/server/runtime/bundler.ts +47 -32
  176. package/lib/server/runtime/fs.ts +3 -13
  177. package/lib/server/runtime/httpServer.ts +5 -9
  178. package/lib/server/services/ColorService.ts +32 -27
  179. package/lib/server/services/EnumService.test.ts +2 -6
  180. package/lib/server/services/EnumService.ts +7 -2
  181. package/lib/server/services/VariableService.test.ts +1 -5
  182. package/lib/server/services/VariableService.ts +6 -1
  183. package/lib/server/services/cmsService.test.ts +116 -78
  184. package/lib/server/services/cmsService.ts +24 -54
  185. package/lib/server/services/componentService.test.ts +122 -36
  186. package/lib/server/services/componentService.ts +54 -38
  187. package/lib/server/services/configService.test.ts +9 -31
  188. package/lib/server/services/configService.ts +20 -27
  189. package/lib/server/services/fileWatcherService.ts +40 -6
  190. package/lib/server/services/index.ts +0 -1
  191. package/lib/server/services/pageService.test.ts +1 -3
  192. package/lib/server/services/pageService.ts +14 -13
  193. package/lib/server/ssr/attributeBuilder.ts +16 -6
  194. package/lib/server/ssr/buildErrorOverlay.ts +11 -12
  195. package/lib/server/ssr/clientDataInjector.ts +7 -21
  196. package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
  197. package/lib/server/ssr/cssCollector.ts +1 -1
  198. package/lib/server/ssr/errorOverlay.test.ts +1 -1
  199. package/lib/server/ssr/errorOverlay.ts +3 -9
  200. package/lib/server/ssr/htmlGenerator.nonce.test.ts +3 -9
  201. package/lib/server/ssr/htmlGenerator.test.ts +120 -43
  202. package/lib/server/ssr/htmlGenerator.ts +120 -85
  203. package/lib/server/ssr/imageMetadata.test.ts +3 -1
  204. package/lib/server/ssr/imageMetadata.ts +25 -19
  205. package/lib/server/ssr/jsCollector.test.ts +3 -13
  206. package/lib/server/ssr/jsCollector.ts +3 -8
  207. package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
  208. package/lib/server/ssr/metaTagGenerator.ts +2 -2
  209. package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
  210. package/lib/server/ssr/ssrRenderer.test.ts +196 -246
  211. package/lib/server/ssr/ssrRenderer.ts +609 -225
  212. package/lib/server/ssrRenderer.test.ts +1044 -950
  213. package/lib/server/utils/jsonLineMapper.test.ts +28 -28
  214. package/lib/server/utils/jsonLineMapper.ts +1 -1
  215. package/lib/server/validateStyleCoverage.ts +18 -20
  216. package/lib/server/webflow/buildWebflow.ts +41 -53
  217. package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
  218. package/lib/server/webflow/nodeToWebflow.ts +195 -258
  219. package/lib/server/webflow/styleMapper.test.ts +15 -56
  220. package/lib/server/webflow/styleMapper.ts +33 -41
  221. package/lib/server/webflow/types.ts +1 -8
  222. package/lib/server/websocketManager.ts +16 -19
  223. package/lib/shared/attributeNodeUtils.test.ts +15 -15
  224. package/lib/shared/attributeNodeUtils.ts +5 -12
  225. package/lib/shared/breakpoints.ts +4 -11
  226. package/lib/shared/cmsQueryParser.test.ts +50 -42
  227. package/lib/shared/cmsQueryParser.ts +49 -31
  228. package/lib/shared/colorVariableUtils.test.ts +5 -5
  229. package/lib/shared/colorVariableUtils.ts +3 -8
  230. package/lib/shared/componentRefs.ts +1 -5
  231. package/lib/shared/constants.test.ts +3 -3
  232. package/lib/shared/constants.ts +4 -8
  233. package/lib/shared/cssGeneration.test.ts +262 -144
  234. package/lib/shared/cssGeneration.ts +189 -516
  235. package/lib/shared/cssNamedColors.ts +152 -30
  236. package/lib/shared/cssProperties.test.ts +5 -6
  237. package/lib/shared/cssProperties.ts +296 -112
  238. package/lib/shared/elementClassName.test.ts +109 -109
  239. package/lib/shared/elementClassName.ts +1 -1
  240. package/lib/shared/elementUtils.ts +12 -16
  241. package/lib/shared/errorLogger.ts +2 -10
  242. package/lib/shared/errors.test.ts +2 -13
  243. package/lib/shared/errors.ts +2 -8
  244. package/lib/shared/expressionEvaluator.test.ts +119 -0
  245. package/lib/shared/expressionEvaluator.ts +43 -24
  246. package/lib/shared/fontLoader.test.ts +19 -5
  247. package/lib/shared/friendlyError.ts +2 -3
  248. package/lib/shared/gradientUtils.test.ts +1 -5
  249. package/lib/shared/gradientUtils.ts +2 -6
  250. package/lib/shared/hrefRefs.ts +2 -10
  251. package/lib/shared/i18n.test.ts +1 -1
  252. package/lib/shared/i18n.ts +13 -34
  253. package/lib/shared/index.ts +4 -0
  254. package/lib/shared/inlineSvgStyleRules.ts +2 -4
  255. package/lib/shared/interactiveStyleMappings.test.ts +11 -33
  256. package/lib/shared/interactiveStyleMappings.ts +9 -16
  257. package/lib/shared/interactiveStyles.test.ts +165 -188
  258. package/lib/shared/interfaces/contentProvider.ts +1 -1
  259. package/lib/shared/itemTemplateUtils.test.ts +6 -12
  260. package/lib/shared/itemTemplateUtils.ts +19 -35
  261. package/lib/shared/jsonRepair.ts +8 -2
  262. package/lib/shared/libraryLoader.test.ts +15 -49
  263. package/lib/shared/libraryLoader.ts +7 -22
  264. package/lib/shared/netlifyLocale404.test.ts +179 -0
  265. package/lib/shared/netlifyLocale404.ts +110 -0
  266. package/lib/shared/nodeUtils.test.ts +24 -16
  267. package/lib/shared/nodeUtils.ts +49 -19
  268. package/lib/shared/pathArrayUtils.test.ts +1 -2
  269. package/lib/shared/pathArrayUtils.ts +1 -1
  270. package/lib/shared/pathSecurity.ts +1 -1
  271. package/lib/shared/pathUtils.test.ts +4 -6
  272. package/lib/shared/pathUtils.ts +42 -48
  273. package/lib/shared/paths/Path.test.ts +2 -2
  274. package/lib/shared/paths/Path.ts +0 -1
  275. package/lib/shared/paths/PathConverter.test.ts +1 -1
  276. package/lib/shared/paths/PathConverter.ts +14 -17
  277. package/lib/shared/paths/PathUtils.ts +9 -10
  278. package/lib/shared/paths/PathValidator.test.ts +2 -15
  279. package/lib/shared/paths/PathValidator.ts +11 -9
  280. package/lib/shared/paths/index.ts +1 -2
  281. package/lib/shared/propResolver.test.ts +240 -244
  282. package/lib/shared/propResolver.ts +14 -25
  283. package/lib/shared/pxToRem.test.ts +7 -6
  284. package/lib/shared/pxToRem.ts +2 -5
  285. package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
  286. package/lib/shared/registry/ClientRegistry.ts +0 -1
  287. package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
  288. package/lib/shared/registry/ComponentRegistry.ts +9 -11
  289. package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
  290. package/lib/shared/registry/RegistryManager.ts +1 -2
  291. package/lib/shared/registry/SSRRegistry.ts +0 -1
  292. package/lib/shared/registry/createNodeType.ts +7 -9
  293. package/lib/shared/registry/defineNodeType.ts +2 -6
  294. package/lib/shared/registry/index.ts +0 -1
  295. package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
  296. package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
  297. package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
  298. package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
  299. package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
  300. package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
  301. package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
  302. package/lib/shared/registry/nodeTypes/index.ts +10 -2
  303. package/lib/shared/responsiveScaling.test.ts +15 -31
  304. package/lib/shared/responsiveScaling.ts +55 -37
  305. package/lib/shared/responsiveStyleUtils.ts +11 -13
  306. package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
  307. package/lib/shared/richtext/htmlToTiptap.ts +1 -3
  308. package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
  309. package/lib/shared/richtext/types.ts +1 -8
  310. package/lib/shared/slugTranslator.test.ts +13 -13
  311. package/lib/shared/slugTranslator.ts +12 -16
  312. package/lib/shared/slugify.ts +9 -15
  313. package/lib/shared/styleNodeUtils.test.ts +8 -8
  314. package/lib/shared/styleNodeUtils.ts +5 -10
  315. package/lib/shared/styleUtils.test.ts +87 -61
  316. package/lib/shared/styleUtils.ts +5 -6
  317. package/lib/shared/themeDefaults.test.ts +11 -11
  318. package/lib/shared/themeDefaults.ts +3 -4
  319. package/lib/shared/tree/PathBuilder.test.ts +62 -138
  320. package/lib/shared/tree/PathBuilder.ts +49 -39
  321. package/lib/shared/treePathUtils.test.ts +2 -10
  322. package/lib/shared/treePathUtils.ts +54 -59
  323. package/lib/shared/types/api.ts +1 -2
  324. package/lib/shared/types/cms.ts +25 -21
  325. package/lib/shared/types/comment.ts +49 -16
  326. package/lib/shared/types/components.ts +27 -25
  327. package/lib/shared/types/errors.test.ts +1 -6
  328. package/lib/shared/types/errors.ts +3 -7
  329. package/lib/shared/types/experiments.ts +28 -28
  330. package/lib/shared/types/index.ts +2 -2
  331. package/lib/shared/types/styles.ts +0 -1
  332. package/lib/shared/types/variables.test.ts +4 -13
  333. package/lib/shared/types/variables.ts +48 -27
  334. package/lib/shared/types.ts +1 -2
  335. package/lib/shared/utilityClassConfig.ts +648 -321
  336. package/lib/shared/utilityClassMapper.test.ts +203 -92
  337. package/lib/shared/utilityClassMapper.ts +188 -246
  338. package/lib/shared/utilityClassNames.ts +326 -0
  339. package/lib/shared/utils.test.ts +2 -10
  340. package/lib/shared/utils.ts +19 -10
  341. package/lib/shared/validation/cmsValidators.ts +2 -1
  342. package/lib/shared/validation/commentValidators.test.ts +53 -0
  343. package/lib/shared/validation/commentValidators.ts +12 -1
  344. package/lib/shared/validation/propValidator.test.ts +18 -20
  345. package/lib/shared/validation/propValidator.ts +12 -17
  346. package/lib/shared/validation/schemas.test.ts +24 -33
  347. package/lib/shared/validation/schemas.ts +469 -344
  348. package/lib/shared/validation/validators.test.ts +1 -6
  349. package/lib/shared/validation/validators.ts +89 -68
  350. package/lib/shared/viewportUnits.integration.test.ts +14 -10
  351. package/lib/shared/viewportUnits.test.ts +11 -23
  352. package/lib/test-utils/dom-setup.ts +1 -1
  353. package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
  354. package/lib/test-utils/factories/DomMockFactory.ts +7 -19
  355. package/lib/test-utils/factories/EventMockFactory.ts +7 -13
  356. package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
  357. package/lib/test-utils/factories/ServerMockFactory.ts +5 -9
  358. package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
  359. package/lib/test-utils/fixtures.ts +45 -45
  360. package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
  361. package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
  362. package/lib/test-utils/helpers.ts +1 -5
  363. package/lib/test-utils/index.ts +0 -4
  364. package/lib/test-utils/mockFactories.ts +12 -18
  365. package/lib/test-utils/mocks.ts +4 -2
  366. package/package.json +1 -1
  367. package/scripts/build-meno-filter.ts +1 -4
  368. package/vite.config.ts +4 -4
  369. package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
  370. package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
  371. package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
  372. package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
  373. package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
  374. package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
  375. package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
  376. package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
  377. package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
  378. package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
  379. package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
  380. package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
  381. package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
  382. package/dist/chunks/chunk-X754AHS5.js.map +0 -7
  383. package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
  384. package/dist/chunks/configService-R3OGU2UD.js +0 -13
  385. /package/dist/chunks/{configService-R3OGU2UD.js.map → configService-PRJZF7Y6.js.map} +0 -0
  386. /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
  387. /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.js.map} +0 -0
package/build-next.ts CHANGED
@@ -11,37 +11,46 @@
11
11
  * browser parses the statically exported `.html` file.
12
12
  */
13
13
 
14
- import { existsSync, readdirSync, mkdirSync, rmSync, statSync, copyFileSync, writeFileSync } from "fs";
15
- import { writeFile, readFile } from "fs/promises";
16
- import { join } from "path";
17
- import { createHash } from "crypto";
14
+ import { existsSync, readdirSync, mkdirSync, rmSync, statSync, copyFileSync, writeFileSync } from 'fs';
15
+ import { writeFile, readFile } from 'fs/promises';
16
+ import { join } from 'path';
17
+ import { createHash } from 'crypto';
18
18
  import {
19
19
  loadJSONFile,
20
20
  loadComponentDirectory,
21
21
  mapPageNameToPath,
22
22
  parseJSON,
23
23
  loadI18nConfig,
24
- } from "./lib/server/jsonLoader";
25
- import { projectPaths } from "./lib/server/projectContext";
26
- import { loadProjectConfig, generateFontCSS, generateFontPreloadTags } from "./lib/shared/fontLoader";
27
- import { FileSystemCMSProvider } from "./lib/server/providers/fileSystemCMSProvider";
28
- import { CMSService } from "./lib/server/services/cmsService";
29
- import { isI18nValue, resolveI18nValue } from "./lib/shared/i18n";
30
- import type { ComponentDefinition, JSONPage, CMSSchema, CMSItem, I18nConfig } from "./lib/shared/types";
31
- import { isItemDraftForLocale } from "./lib/shared/types";
32
- import type { SlugMap } from "./lib/shared/slugTranslator";
33
- import { renderPageSSR } from "./lib/server/ssr/ssrRenderer";
34
- import { generateThemeColorVariablesCSS, generateVariablesCSS } from "./lib/server/cssGenerator";
35
- import { colorService } from "./lib/server/services/ColorService";
36
- import { variableService } from "./lib/server/services/VariableService";
37
- import { configService } from "./lib/server/services/configService";
38
- import { loadBreakpointConfig, loadIconsConfig } from "./lib/server/jsonLoader";
39
- import type { InteractiveStyles } from "./lib/shared/types/styles";
40
- import { collectComponentLibraries, filterLibrariesByContext, mergeLibraries, generateLibraryTags } from "./lib/shared/libraryLoader";
41
- import { migrateTemplatesDirectory } from "./lib/server/migrateTemplates";
42
- import { collectAllMappingClasses } from "./lib/server/astro/cssCollector";
43
- import { generateAllInteractiveCSS, generateUtilityCSS, extractUtilityClassesFromHTML } from "./lib/shared/cssGeneration";
44
- import { needsFormHandler, formHandlerScript } from "./lib/client/scripts/formHandler";
24
+ } from './lib/server/jsonLoader';
25
+ import { projectPaths } from './lib/server/projectContext';
26
+ import { loadProjectConfig, generateFontCSS, generateFontPreloadTags } from './lib/shared/fontLoader';
27
+ import { FileSystemCMSProvider } from './lib/server/providers/fileSystemCMSProvider';
28
+ import { CMSService } from './lib/server/services/cmsService';
29
+ import { isI18nValue, resolveI18nValue } from './lib/shared/i18n';
30
+ import type { ComponentDefinition, JSONPage, CMSSchema, CMSItem, I18nConfig } from './lib/shared/types';
31
+ import { isItemDraftForLocale } from './lib/shared/types';
32
+ import type { SlugMap } from './lib/shared/slugTranslator';
33
+ import { renderPageSSR } from './lib/server/ssr/ssrRenderer';
34
+ import { generateThemeColorVariablesCSS, generateVariablesCSS } from './lib/server/cssGenerator';
35
+ import { colorService } from './lib/server/services/ColorService';
36
+ import { variableService } from './lib/server/services/VariableService';
37
+ import { configService } from './lib/server/services/configService';
38
+ import { loadBreakpointConfig, loadIconsConfig } from './lib/server/jsonLoader';
39
+ import type { InteractiveStyles } from './lib/shared/types/styles';
40
+ import {
41
+ collectComponentLibraries,
42
+ filterLibrariesByContext,
43
+ mergeLibraries,
44
+ generateLibraryTags,
45
+ } from './lib/shared/libraryLoader';
46
+ import { migrateTemplatesDirectory } from './lib/server/migrateTemplates';
47
+ import { collectAllMappingClasses } from './lib/server/astro/cssCollector';
48
+ import {
49
+ generateAllInteractiveCSS,
50
+ generateUtilityCSS,
51
+ extractUtilityClassesFromHTML,
52
+ } from './lib/shared/cssGeneration';
53
+ import { needsFormHandler, formHandlerScript } from './lib/client/scripts/formHandler';
45
54
 
46
55
  // ---------------------------------------------------------------------------
47
56
  // Helpers
@@ -65,11 +74,7 @@ function writePageScript(javascript: string | undefined, scriptsDir: string): st
65
74
  return [`/_scripts/${scriptFile}`];
66
75
  }
67
76
 
68
- function copyDirectory(
69
- src: string,
70
- dest: string,
71
- filter?: (filename: string) => boolean,
72
- ): void {
77
+ function copyDirectory(src: string, dest: string, filter?: (filename: string) => boolean): void {
73
78
  if (!existsSync(src)) return;
74
79
  if (!existsSync(dest)) mkdirSync(dest, { recursive: true });
75
80
  const files = readdirSync(src);
@@ -95,7 +100,7 @@ function buildCMSItemPath(
95
100
  item: CMSItem,
96
101
  slugField: string,
97
102
  locale: string,
98
- i18nConfig: I18nConfig
103
+ i18nConfig: I18nConfig,
99
104
  ): string {
100
105
  let slug = item[slugField] ?? item._slug ?? item._id;
101
106
  if (isI18nValue(slug)) {
@@ -212,7 +217,10 @@ interface NextBuildStats {
212
217
  */
213
218
  function metaHtmlToJSX(metaHtml: string): string {
214
219
  if (!metaHtml || !metaHtml.trim()) return '';
215
- const lines = metaHtml.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
220
+ const lines = metaHtml
221
+ .split(/\r?\n/)
222
+ .map((l) => l.trim())
223
+ .filter(Boolean);
216
224
 
217
225
  const elements: string[] = [];
218
226
  for (const line of lines) {
@@ -232,7 +240,6 @@ function metaHtmlToJSX(metaHtml: string): string {
232
240
  .map(([k, v]) => `${jsxAttrName(k)}=${JSON.stringify(v)}`)
233
241
  .join(' ');
234
242
  elements.push(`<${tagName} ${attrParts} />`);
235
- continue;
236
243
  }
237
244
  // Unknown tag — skip rather than crash the build.
238
245
  }
@@ -246,15 +253,15 @@ function metaHtmlToJSX(metaHtml: string): string {
246
253
  */
247
254
  const JSX_ATTR_NAMES: Record<string, string> = {
248
255
  'http-equiv': 'httpEquiv',
249
- 'hreflang': 'hrefLang',
250
- 'crossorigin': 'crossOrigin',
251
- 'referrerpolicy': 'referrerPolicy',
252
- 'imagesrcset': 'imageSrcSet',
253
- 'imagesizes': 'imageSizes',
254
- 'fetchpriority': 'fetchPriority',
255
- 'class': 'className',
256
- 'for': 'htmlFor',
257
- 'charset': 'charSet',
256
+ hreflang: 'hrefLang',
257
+ crossorigin: 'crossOrigin',
258
+ referrerpolicy: 'referrerPolicy',
259
+ imagesrcset: 'imageSrcSet',
260
+ imagesizes: 'imageSizes',
261
+ fetchpriority: 'fetchPriority',
262
+ class: 'className',
263
+ for: 'htmlFor',
264
+ charset: 'charSet',
258
265
  };
259
266
 
260
267
  function jsxAttrName(name: string): string {
@@ -395,12 +402,15 @@ export default function Page() {
395
402
  */
396
403
  function emitNextCMSPage(options: {
397
404
  slugs: string[];
398
- perSlugData: Record<string, {
399
- html: string;
400
- meta: string;
401
- title: string;
402
- scriptPaths: string[];
403
- }>;
405
+ perSlugData: Record<
406
+ string,
407
+ {
408
+ html: string;
409
+ meta: string;
410
+ title: string;
411
+ scriptPaths: string[];
412
+ }
413
+ >;
404
414
  locale: string;
405
415
  theme: string;
406
416
  fontPreloads: string;
@@ -409,17 +419,8 @@ function emitNextCMSPage(options: {
409
419
  iconTagsHtml: string;
410
420
  formHandlerNeeded: boolean;
411
421
  }): string {
412
- const {
413
- slugs,
414
- perSlugData,
415
- locale,
416
- theme,
417
- fontPreloads,
418
- libraryTags,
419
- customCode,
420
- iconTagsHtml,
421
- formHandlerNeeded,
422
- } = options;
422
+ const { slugs, perSlugData, locale, theme, fontPreloads, libraryTags, customCode, iconTagsHtml, formHandlerNeeded } =
423
+ options;
423
424
 
424
425
  const headHtmlBlocks: string[] = [];
425
426
  if (iconTagsHtml) headHtmlBlocks.push(iconTagsHtml);
@@ -451,7 +452,7 @@ function emitNextCMSPage(options: {
451
452
  metaHtml: \`${escapeTemplateLiteral(data.meta)}\`,
452
453
  html: \`${escapeTemplateLiteral(data.html)}\`,
453
454
  bodyEndHtml: \`${escapeTemplateLiteral(bodyEndHtml)}\`,
454
- }`
455
+ }`,
455
456
  );
456
457
  }
457
458
 
@@ -502,7 +503,9 @@ export default async function Page({ params }: { params: Promise<{ slug: string
502
503
  `;
503
504
  }
504
505
 
505
- function cmsRawHeadImport(_options: { /* keep for future depth handling */ }): string {
506
+ function cmsRawHeadImport(_options: {
507
+ /* keep for future depth handling */
508
+ }): string {
506
509
  // CMS files live two levels under app/ (e.g. app/blog/[slug]/page.tsx), so
507
510
  // the relative path to app/components/RawHead.tsx is "../../components/RawHead".
508
511
  // When the URL pattern's path prefix is empty (e.g. "/[slug]"), they live at
@@ -512,7 +515,9 @@ function cmsRawHeadImport(_options: { /* keep for future depth handling */ }): s
512
515
  return '../../components/RawHead';
513
516
  }
514
517
 
515
- function cmsMetaTagsImport(_options: { /* placeholder */ }): string {
518
+ function cmsMetaTagsImport(_options: {
519
+ /* placeholder */
520
+ }): string {
516
521
  return '../../components/MetaTags';
517
522
  }
518
523
 
@@ -520,10 +525,7 @@ function cmsMetaTagsImport(_options: { /* placeholder */ }): string {
520
525
  // Main export
521
526
  // ---------------------------------------------------------------------------
522
527
 
523
- export async function buildNextProject(
524
- projectRoot?: string,
525
- outputDir?: string
526
- ): Promise<NextBuildStats> {
528
+ export async function buildNextProject(projectRoot?: string, outputDir?: string): Promise<NextBuildStats> {
527
529
  // ----------------------------------------------------------
528
530
  // 1. Setup: load project configuration
529
531
  // ----------------------------------------------------------
@@ -538,7 +540,9 @@ export async function buildNextProject(
538
540
 
539
541
  const { components, warnings, errors: compErrors } = await loadComponentDirectory(projectPaths.components());
540
542
  const globalComponents: Record<string, ComponentDefinition> = {};
541
- components.forEach((value, key) => { globalComponents[key] = value; });
543
+ components.forEach((value, key) => {
544
+ globalComponents[key] = value;
545
+ });
542
546
  for (const w of warnings) console.warn(` Warning: ${w}`);
543
547
  for (const e of compErrors) console.error(` Error: ${e}`);
544
548
 
@@ -600,7 +604,9 @@ export async function buildNextProject(
600
604
  const pageId = basePath === '/' ? 'index' : basePath.substring(1);
601
605
  slugMappings.push({ pageId, slugs: pageData.meta.slugs });
602
606
  }
603
- } catch { /* ignore parse errors in first pass */ }
607
+ } catch {
608
+ /* ignore parse errors in first pass */
609
+ }
604
610
  }
605
611
 
606
612
  // ----------------------------------------------------------
@@ -623,7 +629,15 @@ export async function buildNextProject(
623
629
  }
624
630
 
625
631
  function recordRender(
626
- result: { html: string; meta: string; title: string; javascript: string; componentCSS?: string; locale: string; interactiveStylesMap: Map<string, InteractiveStyles> },
632
+ result: {
633
+ html: string;
634
+ meta: string;
635
+ title: string;
636
+ javascript: string;
637
+ componentCSS?: string;
638
+ locale: string;
639
+ interactiveStylesMap: Map<string, InteractiveStyles>;
640
+ },
627
641
  urlPath: string,
628
642
  pageData?: JSONPage,
629
643
  pageName?: string,
@@ -691,8 +705,12 @@ export async function buildNextProject(
691
705
  }
692
706
 
693
707
  const urlPath = isDefault
694
- ? (slug === '' ? '/' : `/${slug}`)
695
- : (slug === '' ? `/${locale}` : `/${locale}/${slug}`);
708
+ ? slug === ''
709
+ ? '/'
710
+ : `/${slug}`
711
+ : slug === ''
712
+ ? `/${locale}`
713
+ : `/${locale}/${slug}`;
696
714
 
697
715
  const result = await renderPageSSR(
698
716
  pageData,
@@ -704,7 +722,7 @@ export async function buildNextProject(
704
722
  slugMappings,
705
723
  undefined,
706
724
  cmsService,
707
- true
725
+ true,
708
726
  );
709
727
 
710
728
  recordRender(result, urlPath, pageData, pageName);
@@ -785,7 +803,7 @@ export async function buildNextProject(
785
803
  const cmsEmissions: CMSEmission[] = [];
786
804
 
787
805
  if (existsSync(templatesDir)) {
788
- const templateFiles = readdirSync(templatesDir).filter(f => f.endsWith('.json'));
806
+ const templateFiles = readdirSync(templatesDir).filter((f) => f.endsWith('.json'));
789
807
 
790
808
  for (const file of templateFiles) {
791
809
  const templateContent = await loadJSONFile(join(templatesDir, file));
@@ -811,9 +829,8 @@ export async function buildNextProject(
811
829
  const items = await cmsService.queryItems({ collection: cmsSchema.id });
812
830
  const urlPatternWithoutSlash = cmsSchema.urlPattern.replace(/^\//, '');
813
831
  const slugPlaceholderIdx = urlPatternWithoutSlash.indexOf('{{');
814
- const pathPrefix = slugPlaceholderIdx > 0
815
- ? urlPatternWithoutSlash.substring(0, slugPlaceholderIdx).replace(/\/$/, '')
816
- : '';
832
+ const pathPrefix =
833
+ slugPlaceholderIdx > 0 ? urlPatternWithoutSlash.substring(0, slugPlaceholderIdx).replace(/\/$/, '') : '';
817
834
 
818
835
  for (const localeEntry of i18nConfig.locales) {
819
836
  const localeCode = localeEntry.code;
@@ -839,7 +856,7 @@ export async function buildNextProject(
839
856
  slugMappings,
840
857
  { cms: itemWithUrl },
841
858
  cmsService,
842
- true
859
+ true,
843
860
  );
844
861
 
845
862
  mergeInteractiveStyles(result.interactiveStylesMap);
@@ -912,12 +929,14 @@ export async function buildNextProject(
912
929
  const themeColorCSS = generateThemeColorVariablesCSS(themeConfig);
913
930
  const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
914
931
  const componentCSSCombined = Array.from(allComponentCSS).join('\n');
915
- const utilityCSS = allUtilityClasses.size > 0
916
- ? generateUtilityCSS(allUtilityClasses, breakpoints, responsiveScales, remConversionConfig)
917
- : '';
918
- const interactiveStylesCSS = allInteractiveStyles.size > 0
919
- ? generateAllInteractiveCSS(allInteractiveStyles, breakpoints, remConversionConfig, responsiveScales)
920
- : '';
932
+ const utilityCSS =
933
+ allUtilityClasses.size > 0
934
+ ? generateUtilityCSS(allUtilityClasses, breakpoints, responsiveScales, remConversionConfig)
935
+ : '';
936
+ const interactiveStylesCSS =
937
+ allInteractiveStyles.size > 0
938
+ ? generateAllInteractiveCSS(allInteractiveStyles, breakpoints, remConversionConfig, responsiveScales)
939
+ : '';
921
940
 
922
941
  const baseCSS = `@layer base {
923
942
  * { margin: 0; padding: 0; box-sizing: border-box; }
@@ -948,7 +967,9 @@ export async function buildNextProject(
948
967
  utilityCSS,
949
968
  componentCSSCombined,
950
969
  interactiveStylesCSS,
951
- ].filter(Boolean).join('\n\n');
970
+ ]
971
+ .filter(Boolean)
972
+ .join('\n\n');
952
973
 
953
974
  await writeFile(join(appDir, 'globals.css'), globalCSS, 'utf-8');
954
975
 
@@ -1091,9 +1112,7 @@ export default function MetaTags({ html }: { html: string }) {
1091
1112
  // 10. Emit regular page files (app/<path>/page.tsx)
1092
1113
  // ----------------------------------------------------------
1093
1114
  for (const result of allResults) {
1094
- const scriptPaths: string[] = result.javascript
1095
- ? [`/_scripts/${hashContent(result.javascript)}.js`]
1096
- : [];
1115
+ const scriptPaths: string[] = result.javascript ? [`/_scripts/${hashContent(result.javascript)}.js`] : [];
1097
1116
 
1098
1117
  const route = urlPathToAppRoute(result.urlPath);
1099
1118
  const targetDir = route.isRoot ? appDir : join(appDir, route.dir);
@@ -1123,7 +1142,7 @@ export default function MetaTags({ html }: { html: string }) {
1123
1142
  });
1124
1143
  content = content.replace(
1125
1144
  "import RawHead from '../components/RawHead';",
1126
- `import RawHead from '${rawHeadImportPath}';`
1145
+ `import RawHead from '${rawHeadImportPath}';`,
1127
1146
  );
1128
1147
 
1129
1148
  await writeFile(pageFilePath, content, 'utf-8');
@@ -1298,17 +1317,11 @@ export default nextConfig;
1298
1317
  await writeFile(
1299
1318
  join(outDir, 'next-env.d.ts'),
1300
1319
  `/// <reference types="next" />\n/// <reference types="next/image-types/global" />\n`,
1301
- 'utf-8'
1320
+ 'utf-8',
1302
1321
  );
1303
1322
 
1304
1323
  // .gitignore — keep the exported project clean when committed.
1305
- const gitignore = [
1306
- 'node_modules',
1307
- '.next',
1308
- 'out',
1309
- '.DS_Store',
1310
- '*.log',
1311
- ].join('\n') + '\n';
1324
+ const gitignore = ['node_modules', '.next', 'out', '.DS_Store', '*.log'].join('\n') + '\n';
1312
1325
  await writeFile(join(outDir, '.gitignore'), gitignore, 'utf-8');
1313
1326
 
1314
1327
  // README — quick start, mirrors what Astro export ships.
@@ -71,7 +71,7 @@ describe('formatBunLog', () => {
71
71
  test('formats log with position info', () => {
72
72
  const log = {
73
73
  position: { file: 'src/app.ts', line: 42, column: 10, lineText: 'const x = y;' },
74
- message: 'Variable not found'
74
+ message: 'Variable not found',
75
75
  };
76
76
  const result = formatBunLog(log);
77
77
  expect(result).toContain('File: src/app.ts');
@@ -110,7 +110,7 @@ describe('formatBunLog', () => {
110
110
  test('handles position without lineText', () => {
111
111
  const log = {
112
112
  position: { file: 'index.ts', line: 1 },
113
- message: 'Error'
113
+ message: 'Error',
114
114
  };
115
115
  const result = formatBunLog(log);
116
116
  expect(result).toContain('File: index.ts');
@@ -160,6 +160,21 @@ describe('getLocalizedOutputPath', () => {
160
160
  test('deep nested path with default locale', () => {
161
161
  expect(getLocalizedOutputPath('/blog/post', 'en', 'en', dist)).toBe('/project/dist/blog/post.html');
162
162
  });
163
+
164
+ test('normalizes "/" slug to index.html (path-like slug from editor)', () => {
165
+ const slugs = { en: '/' };
166
+ expect(getLocalizedOutputPath('/', 'en', 'en', dist, slugs)).toBe('/project/dist/index.html');
167
+ });
168
+
169
+ test('normalizes leading-slash slug', () => {
170
+ const slugs = { en: '/privacy' };
171
+ expect(getLocalizedOutputPath('/privacy', 'en', 'en', dist, slugs)).toBe('/project/dist/privacy.html');
172
+ });
173
+
174
+ test('normalizes trailing-slash slug', () => {
175
+ const slugs = { en: 'privacy/' };
176
+ expect(getLocalizedOutputPath('/privacy', 'en', 'en', dist, slugs)).toBe('/project/dist/privacy.html');
177
+ });
163
178
  });
164
179
 
165
180
  // ============================================================================
@@ -197,6 +212,16 @@ describe('getDisplayPath', () => {
197
212
  const slugs = { en: 'about' };
198
213
  expect(getDisplayPath('/about', 'de', 'en', slugs)).toBe('/de/about');
199
214
  });
215
+
216
+ test('normalizes "/" slug to root path', () => {
217
+ const slugs = { en: '/' };
218
+ expect(getDisplayPath('/', 'en', 'en', slugs)).toBe('/');
219
+ });
220
+
221
+ test('normalizes leading-slash slug', () => {
222
+ const slugs = { en: '/privacy' };
223
+ expect(getDisplayPath('/privacy', 'en', 'en', slugs)).toBe('/privacy');
224
+ });
200
225
  });
201
226
 
202
227
  // ============================================================================
@@ -208,9 +233,9 @@ describe('isCMSPage', () => {
208
233
  const page = {
209
234
  meta: {
210
235
  source: 'cms',
211
- cms: { id: 'blog', urlPattern: '/blog/{{slug}}', slugField: 'slug' }
236
+ cms: { id: 'blog', urlPattern: '/blog/{{slug}}', slugField: 'slug' },
212
237
  },
213
- nodes: []
238
+ nodes: [],
214
239
  } as unknown as JSONPage;
215
240
  expect(isCMSPage(page)).toBe(true);
216
241
  });
@@ -218,7 +243,7 @@ describe('isCMSPage', () => {
218
243
  test('returns false when source is not cms', () => {
219
244
  const page = {
220
245
  meta: { title: 'About' },
221
- nodes: []
246
+ nodes: [],
222
247
  } as unknown as JSONPage;
223
248
  expect(isCMSPage(page)).toBe(false);
224
249
  });
@@ -226,7 +251,7 @@ describe('isCMSPage', () => {
226
251
  test('returns false when source is cms but cms config is missing', () => {
227
252
  const page = {
228
253
  meta: { source: 'cms' },
229
- nodes: []
254
+ nodes: [],
230
255
  } as unknown as JSONPage;
231
256
  expect(isCMSPage(page)).toBe(false);
232
257
  });
@@ -249,7 +274,10 @@ describe('isCMSPage', () => {
249
274
  describe('buildCMSItemPath', () => {
250
275
  const defaultI18nConfig: I18nConfig = {
251
276
  defaultLocale: 'en',
252
- locales: [{ code: 'en', name: 'English' }, { code: 'fr', name: 'French' }]
277
+ locales: [
278
+ { code: 'en', name: 'English' },
279
+ { code: 'fr', name: 'French' },
280
+ ],
253
281
  };
254
282
 
255
283
  test('replaces {{slug}} with item slug field', () => {
@@ -280,7 +308,7 @@ describe('buildCMSItemPath', () => {
280
308
  const item: CMSItem = {
281
309
  _id: '1',
282
310
  _slug: { _i18n: true, en: 'hello', fr: 'bonjour' },
283
- title: 'Test'
311
+ title: 'Test',
284
312
  };
285
313
  const result = buildCMSItemPath('/page/{{slug}}', item, '_slug', 'fr', defaultI18nConfig);
286
314
  expect(result).toBe('/page/bonjour');
@@ -290,7 +318,7 @@ describe('buildCMSItemPath', () => {
290
318
  const item: CMSItem = {
291
319
  _id: '1',
292
320
  _slug: { _i18n: true, en: 'hello', fr: 'bonjour' },
293
- title: 'Test'
321
+ title: 'Test',
294
322
  };
295
323
  const result = buildCMSItemPath('/page/{{slug}}', item, '_slug', 'en', defaultI18nConfig);
296
324
  expect(result).toBe('/page/hello');
@@ -316,7 +344,8 @@ describe('injectTrackingScript', () => {
316
344
  });
317
345
 
318
346
  test('preserves existing head content', () => {
319
- const html = '<html><head><meta charset="utf-8"><link rel="stylesheet" href="style.css"></head><body></body></html>';
347
+ const html =
348
+ '<html><head><meta charset="utf-8"><link rel="stylesheet" href="style.css"></head><body></body></html>';
320
349
  const script = 'track()';
321
350
  const result = injectTrackingScript(html, script);
322
351
  expect(result).toContain('<meta charset="utf-8">');