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.
- package/.claude/settings.local.json +1 -3
- package/bin/cli.ts +66 -261
- package/build-astro.ts +150 -128
- package/dist/bin/cli.js +42 -185
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/chunk-2AR55GYH.js +42 -0
- package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
- package/dist/chunks/{chunk-JGWFTO6P.js → chunk-2IIQK7T3.js} +2624 -1389
- package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
- package/dist/chunks/chunk-4ZRU52J2.js +169 -0
- package/dist/chunks/chunk-4ZRU52J2.js.map +7 -0
- package/dist/chunks/{chunk-UB44F4Z2.js → chunk-5X4HCN7J.js} +5 -7
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-WQFG7PAH.js → chunk-7CCVOL5M.js} +15 -19
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-YBLHKYFF.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-O3NAGJP4.js → chunk-NUP7H7D3.js} +135 -41
- package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
- package/dist/chunks/{chunk-X754AHS5.js → chunk-QWTQZHG3.js} +194 -90
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-XTKNX4FW.js} +216 -87
- package/dist/chunks/chunk-XTKNX4FW.js.map +7 -0
- package/dist/chunks/{fs-JGINUXGL.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +1003 -475
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14393 -6371
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +342 -101
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +64 -80
- package/dist/lib/test-utils/index.js.map +2 -2
- package/entries/client-router.tsx +10 -8
- package/lib/client/ClientInitializer.ts +8 -8
- package/lib/client/ErrorBoundary.test.tsx +246 -186
- package/lib/client/ErrorBoundary.tsx +183 -132
- package/lib/client/componentRegistry.test.ts +96 -108
- package/lib/client/componentRegistry.ts +1 -3
- package/lib/client/contexts/ThemeContext.tsx +3 -2
- package/lib/client/core/ComponentBuilder.test.ts +663 -560
- package/lib/client/core/ComponentBuilder.ts +427 -155
- package/lib/client/core/ComponentRenderer.test.tsx +2 -4
- package/lib/client/core/ComponentRenderer.tsx +46 -33
- package/lib/client/core/builders/embedBuilder.ts +242 -54
- package/lib/client/core/builders/linkBuilder.ts +72 -45
- package/lib/client/core/builders/linkNodeBuilder.ts +79 -54
- package/lib/client/core/builders/listBuilder.ts +155 -89
- package/lib/client/core/builders/localeListBuilder.ts +97 -62
- package/lib/client/core/builders/types.ts +5 -5
- package/lib/client/core/cmsTemplateProcessor.ts +8 -9
- package/lib/client/elementRegistry.ts +3 -3
- package/lib/client/fontFamiliesService.test.ts +2 -10
- package/lib/client/fontFamiliesService.ts +3 -3
- package/lib/client/hmr/HMRManager.tsx +47 -30
- package/lib/client/hmrCssReload.ts +49 -14
- package/lib/client/hmrWebSocket.ts +14 -19
- package/lib/client/hooks/useColorVariables.test.ts +21 -21
- package/lib/client/hooks/useColorVariables.ts +23 -18
- package/lib/client/hooks/usePropertyAutocomplete.ts +4 -6
- package/lib/client/hooks/useVariables.ts +6 -4
- package/lib/client/hydration/HydrationUtils.test.ts +24 -25
- package/lib/client/hydration/HydrationUtils.ts +3 -4
- package/lib/client/i18nConfigService.test.ts +2 -7
- package/lib/client/i18nConfigService.ts +2 -2
- package/lib/client/meno-filter/MenoFilter.test.ts +47 -49
- package/lib/client/meno-filter/MenoFilter.ts +14 -15
- package/lib/client/meno-filter/bindings.ts +18 -43
- package/lib/client/meno-filter/init.ts +4 -4
- package/lib/client/meno-filter/renderer.ts +30 -32
- package/lib/client/meno-filter/script.generated.ts +1 -3
- package/lib/client/meno-filter/ui.ts +4 -6
- package/lib/client/meno-filter/updates.ts +18 -23
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +174 -178
- package/lib/client/navigation.ts +0 -1
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +234 -232
- package/lib/client/responsiveStyleResolver.ts +74 -20
- package/lib/client/routing/RouteLoader.test.ts +24 -25
- package/lib/client/routing/RouteLoader.ts +27 -35
- package/lib/client/routing/Router.tsx +210 -153
- package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
- package/lib/client/scripts/ScriptExecutor.ts +18 -36
- package/lib/client/services/PrefetchService.test.ts +3 -3
- package/lib/client/services/PrefetchService.ts +23 -29
- package/lib/client/styleProcessor.test.ts +9 -9
- package/lib/client/styleProcessor.ts +24 -15
- package/lib/client/styles/StyleInjector.test.ts +122 -115
- package/lib/client/styles/StyleInjector.ts +10 -7
- package/lib/client/styles/UtilityClassCollector.ts +62 -28
- package/lib/client/styles/cspNonce.test.ts +2 -5
- package/lib/client/templateEngine.test.ts +554 -488
- package/lib/client/templateEngine.ts +203 -73
- package/lib/client/theme.test.ts +1 -1
- package/lib/client/theme.ts +0 -1
- package/lib/client/utils/toast.ts +0 -1
- package/lib/server/__integration__/api-routes.test.ts +8 -4
- package/lib/server/__integration__/cms-integration.test.ts +1 -4
- package/lib/server/__integration__/server-lifecycle.test.ts +4 -5
- package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
- package/lib/server/__integration__/static-assets.test.ts +3 -3
- package/lib/server/__integration__/test-helpers.ts +87 -74
- package/lib/server/ab/generateFunctions.ts +12 -10
- package/lib/server/astro/cmsPageEmitter.ts +26 -42
- package/lib/server/astro/componentEmitter.ts +35 -43
- package/lib/server/astro/cssCollector.ts +10 -26
- package/lib/server/astro/nodeToAstro.test.ts +1754 -34
- package/lib/server/astro/nodeToAstro.ts +232 -216
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +115 -119
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +11 -18
- package/lib/server/astro/pageEmitter.ts +9 -13
- package/lib/server/astro/tailwindMapper.test.ts +10 -37
- package/lib/server/astro/tailwindMapper.ts +33 -40
- package/lib/server/astro/templateTransformer.ts +17 -20
- package/lib/server/createServer.ts +7 -19
- package/lib/server/cssGenerator.test.ts +35 -44
- package/lib/server/cssGenerator.ts +8 -19
- package/lib/server/fileWatcher.test.ts +164 -13
- package/lib/server/fileWatcher.ts +209 -124
- package/lib/server/index.ts +14 -17
- package/lib/server/jsonLoader.test.ts +39 -2
- package/lib/server/jsonLoader.ts +39 -34
- package/lib/server/middleware/cors.test.ts +21 -21
- package/lib/server/middleware/cors.ts +28 -4
- package/lib/server/middleware/errorHandler.test.ts +7 -5
- package/lib/server/middleware/errorHandler.ts +3 -8
- package/lib/server/middleware/index.ts +0 -1
- package/lib/server/middleware/logger.test.ts +10 -8
- package/lib/server/middleware/logger.ts +17 -29
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/pageCache.test.ts +76 -77
- package/lib/server/pageCache.ts +0 -1
- package/lib/server/projectContext.ts +5 -4
- package/lib/server/providers/fileSystemCMSProvider.test.ts +142 -99
- package/lib/server/providers/fileSystemCMSProvider.ts +62 -38
- package/lib/server/routes/api/cms.test.ts +26 -14
- package/lib/server/routes/api/cms.ts +9 -14
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +31 -35
- package/lib/server/routes/api/config.ts +3 -4
- package/lib/server/routes/api/core-routes.ts +49 -76
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +9 -17
- package/lib/server/routes/api/index.ts +3 -6
- package/lib/server/routes/api/pages.ts +8 -11
- package/lib/server/routes/api/shared.test.ts +1 -1
- package/lib/server/routes/api/shared.ts +12 -9
- package/lib/server/routes/api/variables.test.ts +3 -5
- package/lib/server/routes/api/variables.ts +1 -1
- package/lib/server/routes/index.ts +25 -28
- package/lib/server/routes/pages.ts +35 -31
- package/lib/server/routes/static.ts +17 -6
- package/lib/server/runtime/bundler.ts +98 -61
- package/lib/server/runtime/fs.ts +16 -26
- package/lib/server/runtime/httpServer.ts +8 -12
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +37 -29
- package/lib/server/services/EnumService.test.ts +4 -8
- package/lib/server/services/EnumService.ts +11 -3
- package/lib/server/services/VariableService.test.ts +8 -12
- package/lib/server/services/VariableService.ts +11 -3
- package/lib/server/services/cmsService.test.ts +158 -120
- package/lib/server/services/cmsService.ts +36 -146
- package/lib/server/services/componentService.test.ts +125 -45
- package/lib/server/services/componentService.ts +70 -46
- package/lib/server/services/configService.test.ts +22 -45
- package/lib/server/services/configService.ts +50 -27
- package/lib/server/services/fileWatcherService.ts +42 -8
- package/lib/server/services/index.ts +0 -1
- package/lib/server/services/pageService.test.ts +8 -10
- package/lib/server/services/pageService.ts +32 -18
- package/lib/server/ssr/attributeBuilder.ts +18 -8
- package/lib/server/ssr/buildErrorOverlay.ts +11 -12
- package/lib/server/ssr/clientDataInjector.ts +7 -21
- package/lib/server/ssr/cmsSSRProcessor.ts +4 -8
- package/lib/server/ssr/cssCollector.ts +1 -1
- package/lib/server/ssr/errorOverlay.test.ts +1 -1
- package/lib/server/ssr/errorOverlay.ts +3 -9
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +9 -14
- package/lib/server/ssr/htmlGenerator.test.ts +120 -44
- package/lib/server/ssr/htmlGenerator.ts +126 -91
- package/lib/server/ssr/imageMetadata.test.ts +3 -1
- package/lib/server/ssr/imageMetadata.ts +27 -21
- package/lib/server/ssr/jsCollector.test.ts +8 -18
- package/lib/server/ssr/jsCollector.ts +3 -8
- package/lib/server/ssr/liveReloadIntegration.test.ts +76 -36
- package/lib/server/ssr/metaTagGenerator.ts +2 -2
- package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
- package/lib/server/ssr/ssrRenderer.test.ts +316 -256
- package/lib/server/ssr/ssrRenderer.ts +795 -243
- package/lib/server/ssrRenderer.test.ts +1048 -954
- package/lib/server/utils/jsonLineMapper.test.ts +28 -28
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +18 -21
- package/lib/server/websocketManager.ts +28 -19
- package/lib/shared/attributeNodeUtils.test.ts +15 -15
- package/lib/shared/attributeNodeUtils.ts +5 -12
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/breakpoints.ts +4 -11
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.test.ts +50 -42
- package/lib/shared/cmsQueryParser.ts +60 -38
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +27 -5
- package/lib/shared/colorVariableUtils.ts +21 -12
- package/lib/shared/componentRefs.ts +1 -5
- package/lib/shared/constants.test.ts +3 -3
- package/lib/shared/constants.ts +8 -8
- package/lib/shared/cssGeneration.test.ts +285 -144
- package/lib/shared/cssGeneration.ts +217 -531
- package/lib/shared/cssNamedColors.ts +152 -30
- package/lib/shared/cssProperties.test.ts +69 -70
- package/lib/shared/cssProperties.ts +298 -114
- package/lib/shared/elementClassName.test.ts +109 -109
- package/lib/shared/elementClassName.ts +3 -3
- package/lib/shared/elementUtils.ts +12 -16
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/errorLogger.ts +2 -10
- package/lib/shared/errors.test.ts +2 -13
- package/lib/shared/errors.ts +2 -8
- package/lib/shared/expressionEvaluator.test.ts +145 -0
- package/lib/shared/expressionEvaluator.ts +52 -24
- package/lib/shared/fontLoader.test.ts +32 -18
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/friendlyError.ts +2 -3
- package/lib/shared/gradientUtils.test.ts +3 -7
- package/lib/shared/gradientUtils.ts +17 -14
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +2 -10
- package/lib/shared/i18n.test.ts +4 -4
- package/lib/shared/i18n.ts +18 -38
- package/lib/shared/index.ts +27 -0
- package/lib/shared/inlineSvgStyleRules.ts +14 -13
- package/lib/shared/interactiveStyleMappings.test.ts +32 -54
- package/lib/shared/interactiveStyleMappings.ts +11 -24
- package/lib/shared/interactiveStyles.test.ts +165 -188
- package/lib/shared/interfaces/contentProvider.ts +1 -1
- package/lib/shared/itemTemplateUtils.test.ts +11 -17
- package/lib/shared/itemTemplateUtils.ts +27 -39
- package/lib/shared/jsonRepair.ts +16 -10
- package/lib/shared/libraryLoader.test.ts +21 -55
- package/lib/shared/libraryLoader.ts +7 -22
- package/lib/shared/linkUtils.ts +1 -1
- package/lib/shared/logger.test.ts +66 -0
- package/lib/shared/logger.ts +94 -0
- package/lib/shared/markdown.ts +25 -0
- package/lib/shared/netlifyLocale404.test.ts +179 -0
- package/lib/shared/netlifyLocale404.ts +110 -0
- package/lib/shared/nodeUtils.test.ts +28 -16
- package/lib/shared/nodeUtils.ts +76 -23
- package/lib/shared/pathArrayUtils.test.ts +1 -2
- package/lib/shared/pathArrayUtils.ts +1 -1
- package/lib/shared/pathSecurity.ts +2 -2
- package/lib/shared/pathUtils.test.ts +4 -6
- package/lib/shared/pathUtils.ts +42 -49
- package/lib/shared/paths/Path.test.ts +2 -2
- package/lib/shared/paths/Path.ts +0 -1
- package/lib/shared/paths/PathConverter.test.ts +1 -1
- package/lib/shared/paths/PathConverter.ts +17 -20
- package/lib/shared/paths/PathUtils.ts +13 -13
- package/lib/shared/paths/PathValidator.test.ts +2 -15
- package/lib/shared/paths/PathValidator.ts +12 -10
- package/lib/shared/paths/index.ts +1 -2
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +240 -244
- package/lib/shared/propResolver.ts +15 -26
- package/lib/shared/pxToRem.test.ts +7 -6
- package/lib/shared/pxToRem.ts +2 -5
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +12 -6
- package/lib/shared/registry/ClientRegistry.ts +0 -2
- package/lib/shared/registry/ComponentRegistry.test.ts +46 -32
- package/lib/shared/registry/ComponentRegistry.ts +12 -9
- package/lib/shared/registry/NodeTypeDefinition.ts +16 -9
- package/lib/shared/registry/RegistryManager.ts +1 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -2
- package/lib/shared/registry/createNodeType.ts +7 -9
- package/lib/shared/registry/defineNodeType.ts +2 -6
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/index.ts +0 -1
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +48 -19
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +117 -77
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +12 -8
- package/lib/shared/registry/nodeTypes/index.ts +26 -3
- package/lib/shared/responsiveScaling.test.ts +16 -34
- package/lib/shared/responsiveScaling.ts +61 -42
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/responsiveStyleUtils.ts +11 -13
- package/lib/shared/richtext/htmlToTiptap.test.ts +139 -130
- package/lib/shared/richtext/htmlToTiptap.ts +2 -4
- package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
- package/lib/shared/richtext/types.ts +1 -8
- package/lib/shared/slugTranslator.test.ts +13 -13
- package/lib/shared/slugTranslator.ts +12 -16
- package/lib/shared/slugify.ts +9 -15
- package/lib/shared/styleNodeUtils.test.ts +8 -8
- package/lib/shared/styleNodeUtils.ts +18 -20
- package/lib/shared/styleUtils.test.ts +87 -61
- package/lib/shared/styleUtils.ts +5 -6
- package/lib/shared/themeDefaults.test.ts +11 -14
- package/lib/shared/themeDefaults.ts +3 -4
- package/lib/shared/tree/PathBuilder.test.ts +63 -145
- package/lib/shared/tree/PathBuilder.ts +59 -47
- package/lib/shared/treePathUtils.test.ts +51 -12
- package/lib/shared/treePathUtils.ts +91 -60
- package/lib/shared/types/api.ts +105 -8
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +90 -34
- package/lib/shared/types/comment.ts +49 -16
- package/lib/shared/types/components.ts +51 -25
- package/lib/shared/types/errors.test.ts +1 -6
- package/lib/shared/types/errors.ts +3 -7
- package/lib/shared/types/experiments.ts +28 -28
- package/lib/shared/types/index.ts +25 -2
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/styles.ts +0 -1
- package/lib/shared/types/variables.test.ts +4 -13
- package/lib/shared/types/variables.ts +51 -30
- package/lib/shared/types.ts +1 -2
- package/lib/shared/utilityClassConfig.ts +751 -321
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +433 -95
- package/lib/shared/utilityClassMapper.ts +322 -249
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +458 -0
- package/lib/shared/utils.test.ts +2 -10
- package/lib/shared/utils.ts +19 -10
- package/lib/shared/validation/cmsValidators.ts +2 -1
- package/lib/shared/validation/commentValidators.test.ts +53 -0
- package/lib/shared/validation/commentValidators.ts +12 -1
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/validation/permissionsValidators.test.ts +49 -0
- package/lib/shared/validation/permissionsValidators.ts +49 -0
- package/lib/shared/validation/propValidator.test.ts +19 -21
- package/lib/shared/validation/propValidator.ts +13 -18
- package/lib/shared/validation/schemas.test.ts +64 -32
- package/lib/shared/validation/schemas.ts +616 -345
- package/lib/shared/validation/validators.test.ts +3 -8
- package/lib/shared/validation/validators.ts +89 -68
- package/lib/shared/viewportUnits.integration.test.ts +14 -10
- package/lib/shared/viewportUnits.test.ts +24 -23
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/dom-setup.ts +1 -1
- package/lib/test-utils/factories/ConsoleMockFactory.ts +10 -14
- package/lib/test-utils/factories/DomMockFactory.ts +17 -23
- package/lib/test-utils/factories/EventMockFactory.ts +7 -13
- package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
- package/lib/test-utils/factories/ServerMockFactory.ts +6 -10
- package/lib/test-utils/factories/StoreMockFactory.ts +18 -29
- package/lib/test-utils/fixtures.ts +45 -45
- package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
- package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
- package/lib/test-utils/helpers.ts +1 -5
- package/lib/test-utils/index.ts +0 -4
- package/lib/test-utils/mockFactories.ts +13 -19
- package/lib/test-utils/mocks.ts +6 -4
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +4 -7
- package/tsconfig.json +1 -0
- package/vite.config.ts +4 -4
- package/build-next.ts +0 -1361
- package/build-static.test.ts +0 -424
- package/build-static.ts +0 -1074
- package/dist/build-static.js +0 -36
- package/dist/chunks/chunk-2MHDV5BF.js +0 -251
- package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
- package/dist/chunks/chunk-AZQYF6KE.js +0 -559
- package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
- package/dist/chunks/chunk-GZHGVVW3.js +0 -322
- package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
- package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
- package/dist/chunks/chunk-I7YIGZXT.js +0 -467
- package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
- package/dist/chunks/chunk-IGYR22T6.js +0 -6192
- package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
- package/dist/chunks/chunk-JGP5A3Y5.js +0 -1136
- package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
- package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
- package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
- package/dist/chunks/chunk-QB2LNO4W.js +0 -77
- package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
- package/dist/chunks/chunk-R6XHAFBF.js +0 -3821
- package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
- package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
- package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
- package/dist/chunks/chunk-X754AHS5.js.map +0 -7
- package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
- package/dist/chunks/configService-R3OGU2UD.js +0 -13
- package/dist/chunks/configService-R3OGU2UD.js.map +0 -7
- package/dist/chunks/constants-STK2YBIW.js +0 -46
- package/dist/chunks/constants-STK2YBIW.js.map +0 -7
- package/dist/chunks/fs-JGINUXGL.js.map +0 -7
- package/dist/entries/server-router.js +0 -67
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -81
- package/lib/server/providers/fileSystemPageProvider.ts +0 -160
- package/lib/server/webflow/buildWebflow.ts +0 -635
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3170
- package/lib/server/webflow/nodeToWebflow.ts +0 -2435
- package/lib/server/webflow/styleMapper.test.ts +0 -389
- package/lib/server/webflow/styleMapper.ts +0 -695
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -396
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
|
@@ -5,11 +5,33 @@
|
|
|
5
5
|
* - renderPageSSR
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { describe, test, expect, mock, beforeEach } from 'bun:test';
|
|
8
|
+
import { describe, test, expect, mock, beforeEach, afterAll } from 'bun:test';
|
|
9
9
|
|
|
10
10
|
// Mock modules that have side effects or need specific behavior
|
|
11
11
|
// These MUST be called before importing the module under test
|
|
12
12
|
|
|
13
|
+
// bun's mock.module() patches are PROCESS-GLOBAL and persist across test
|
|
14
|
+
// files in the same run. Snapshot the real module exports here (imports are
|
|
15
|
+
// evaluated before this module body runs, so these capture the originals)
|
|
16
|
+
// and restore them in afterAll so later test files (e.g.
|
|
17
|
+
// services/configService.test.ts) see the real implementations.
|
|
18
|
+
import * as realJsonLoader from '../jsonLoader';
|
|
19
|
+
import * as realImageMetadata from './imageMetadata';
|
|
20
|
+
import * as realConfigServiceModule from '../services/configService';
|
|
21
|
+
import * as realValidateStyleCoverage from '../validateStyleCoverage';
|
|
22
|
+
|
|
23
|
+
const REAL_JSON_LOADER = { ...realJsonLoader };
|
|
24
|
+
const REAL_IMAGE_METADATA = { ...realImageMetadata };
|
|
25
|
+
const REAL_CONFIG_SERVICE_MODULE = { ...realConfigServiceModule };
|
|
26
|
+
const REAL_VALIDATE_STYLE_COVERAGE = { ...realValidateStyleCoverage };
|
|
27
|
+
|
|
28
|
+
afterAll(() => {
|
|
29
|
+
mock.module('../jsonLoader', () => REAL_JSON_LOADER);
|
|
30
|
+
mock.module('./imageMetadata', () => REAL_IMAGE_METADATA);
|
|
31
|
+
mock.module('../services/configService', () => REAL_CONFIG_SERVICE_MODULE);
|
|
32
|
+
mock.module('../validateStyleCoverage', () => REAL_VALIDATE_STYLE_COVERAGE);
|
|
33
|
+
});
|
|
34
|
+
|
|
13
35
|
// We'll store reference to mocks so tests can change behavior
|
|
14
36
|
let mockImageMetadataMap = new Map();
|
|
15
37
|
let mockImageFormat: 'webp' | 'avif' = 'webp';
|
|
@@ -44,6 +66,7 @@ mock.module('../services/configService', () => ({
|
|
|
44
66
|
configService: {
|
|
45
67
|
getImageFormat: () => mockImageFormat,
|
|
46
68
|
getResponsiveScales: () => ({ enabled: false, baseReference: 16 }),
|
|
69
|
+
getSocial: () => ({}),
|
|
47
70
|
load: async () => {},
|
|
48
71
|
isLoaded: () => true,
|
|
49
72
|
reset: () => {},
|
|
@@ -57,6 +80,7 @@ mock.module('../validateStyleCoverage', () => ({
|
|
|
57
80
|
}));
|
|
58
81
|
|
|
59
82
|
import { buildComponentHTML, renderPageSSR } from './ssrRenderer';
|
|
83
|
+
import { generateRuleForClass } from '../../shared/cssGeneration';
|
|
60
84
|
import type { ComponentDefinition, JSONPage } from '../../shared/types';
|
|
61
85
|
|
|
62
86
|
// ---------------------------------------------------------------------------
|
|
@@ -394,11 +418,7 @@ describe('ssrRenderer', () => {
|
|
|
394
418
|
const node = {
|
|
395
419
|
type: 'node',
|
|
396
420
|
tag: 'p',
|
|
397
|
-
children: [
|
|
398
|
-
'Hello ',
|
|
399
|
-
{ type: 'node', tag: 'strong', children: 'World' },
|
|
400
|
-
'!',
|
|
401
|
-
],
|
|
421
|
+
children: ['Hello ', { type: 'node', tag: 'strong', children: 'World' }, '!'],
|
|
402
422
|
};
|
|
403
423
|
const html = await render(node);
|
|
404
424
|
expect(html).toContain('Hello ');
|
|
@@ -472,11 +492,7 @@ describe('ssrRenderer', () => {
|
|
|
472
492
|
const node = {
|
|
473
493
|
type: 'node',
|
|
474
494
|
tag: 'p',
|
|
475
|
-
children: [
|
|
476
|
-
en,
|
|
477
|
-
' literal ',
|
|
478
|
-
{ type: 'node', tag: 'span', children: 'static' },
|
|
479
|
-
],
|
|
495
|
+
children: [en, ' literal ', { type: 'node', tag: 'span', children: 'static' }],
|
|
480
496
|
};
|
|
481
497
|
const html = await render(node, { i18nConfig });
|
|
482
498
|
expect(html).toContain('Hello');
|
|
@@ -819,7 +835,7 @@ describe('ssrRenderer', () => {
|
|
|
819
835
|
{
|
|
820
836
|
type: 'node',
|
|
821
837
|
tag: 'span',
|
|
822
|
-
if: { _mapping: true, prop: 'showBadge', values: {
|
|
838
|
+
if: { _mapping: true, prop: 'showBadge', values: { true: true, false: false } },
|
|
823
839
|
children: 'Badge',
|
|
824
840
|
},
|
|
825
841
|
],
|
|
@@ -849,7 +865,7 @@ describe('ssrRenderer', () => {
|
|
|
849
865
|
{
|
|
850
866
|
type: 'node',
|
|
851
867
|
tag: 'span',
|
|
852
|
-
if: { _mapping: true, prop: 'showBadge', values: {
|
|
868
|
+
if: { _mapping: true, prop: 'showBadge', values: { true: true, false: false } },
|
|
853
869
|
children: 'Badge',
|
|
854
870
|
},
|
|
855
871
|
],
|
|
@@ -879,7 +895,7 @@ describe('ssrRenderer', () => {
|
|
|
879
895
|
{
|
|
880
896
|
type: 'node',
|
|
881
897
|
tag: 'span',
|
|
882
|
-
if: { _mapping: true, prop: 'variant', values: {
|
|
898
|
+
if: { _mapping: true, prop: 'variant', values: { hidden: false } },
|
|
883
899
|
children: 'Visible',
|
|
884
900
|
},
|
|
885
901
|
],
|
|
@@ -976,9 +992,7 @@ describe('ssrRenderer', () => {
|
|
|
976
992
|
structure: {
|
|
977
993
|
type: 'node',
|
|
978
994
|
tag: 'div',
|
|
979
|
-
children: [
|
|
980
|
-
{ type: 'node', tag: 'span', children: '{{title}}' },
|
|
981
|
-
],
|
|
995
|
+
children: [{ type: 'node', tag: 'span', children: '{{title}}' }],
|
|
982
996
|
},
|
|
983
997
|
interface: {
|
|
984
998
|
title: { type: 'string', default: 'Default Title' },
|
|
@@ -1156,7 +1170,7 @@ describe('ssrRenderer', () => {
|
|
|
1156
1170
|
// The data-props should be keyed by component name, with label but not count
|
|
1157
1171
|
const propsMatch = html.match(/data-props="([^"]*)"/);
|
|
1158
1172
|
if (propsMatch) {
|
|
1159
|
-
const propsJson = propsMatch[1]
|
|
1173
|
+
const propsJson = propsMatch[1]!.replace(/"/g, '"');
|
|
1160
1174
|
const allProps = JSON.parse(propsJson);
|
|
1161
1175
|
expect(allProps.VarsComp2).toBeDefined();
|
|
1162
1176
|
expect(allProps.VarsComp2.label).toBe('Hello');
|
|
@@ -1251,27 +1265,34 @@ describe('ssrRenderer', () => {
|
|
|
1251
1265
|
// data-component should contain both names (space-separated)
|
|
1252
1266
|
const dcMatch = html.match(/data-component="([^"]*)"/);
|
|
1253
1267
|
expect(dcMatch).toBeTruthy();
|
|
1254
|
-
const names = dcMatch![1]
|
|
1268
|
+
const names = dcMatch![1]!.split(' ');
|
|
1255
1269
|
expect(names).toContain('OuterComp');
|
|
1256
1270
|
expect(names).toContain('InnerComp');
|
|
1257
1271
|
|
|
1258
1272
|
// data-props should be keyed by component name
|
|
1259
1273
|
const dpMatch = html.match(/data-props="([^"]*)"/);
|
|
1260
1274
|
expect(dpMatch).toBeTruthy();
|
|
1261
|
-
const allProps = JSON.parse(dpMatch![1]
|
|
1275
|
+
const allProps = JSON.parse(dpMatch![1]!.replace(/"/g, '"'));
|
|
1262
1276
|
expect(allProps.OuterComp).toEqual({ title: 'Hello' });
|
|
1263
1277
|
expect(allProps.InnerComp).toEqual({ value: 'Hello' });
|
|
1264
1278
|
});
|
|
1265
1279
|
|
|
1266
1280
|
test('component where processStructure returns a string renders it', async () => {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1281
|
+
// Legacy data shape: old projects may store a bare template string as the
|
|
1282
|
+
// component structure. The current ComponentDefinition type only allows a
|
|
1283
|
+
// ComponentNode, but the renderer still supports the string fallback
|
|
1284
|
+
// (renderComponent's `typeof processedStructure === 'string'` branch), so
|
|
1285
|
+
// model the legacy value the way it actually arrives — parsed from JSON.
|
|
1286
|
+
const componentDef: ComponentDefinition = JSON.parse(
|
|
1287
|
+
JSON.stringify({
|
|
1288
|
+
component: {
|
|
1289
|
+
structure: '{{text}}',
|
|
1290
|
+
interface: {
|
|
1291
|
+
text: { type: 'string', default: 'Just text' },
|
|
1292
|
+
},
|
|
1272
1293
|
},
|
|
1273
|
-
},
|
|
1274
|
-
|
|
1294
|
+
}),
|
|
1295
|
+
);
|
|
1275
1296
|
|
|
1276
1297
|
const node = {
|
|
1277
1298
|
type: 'component',
|
|
@@ -1380,10 +1401,7 @@ describe('ssrRenderer', () => {
|
|
|
1380
1401
|
type: 'node',
|
|
1381
1402
|
tag: 'Link',
|
|
1382
1403
|
attributes: { to: '/multi' },
|
|
1383
|
-
children: [
|
|
1384
|
-
{ type: 'node', tag: 'span', children: 'Icon' },
|
|
1385
|
-
'Text',
|
|
1386
|
-
],
|
|
1404
|
+
children: [{ type: 'node', tag: 'span', children: 'Icon' }, 'Text'],
|
|
1387
1405
|
};
|
|
1388
1406
|
const html = await render(node);
|
|
1389
1407
|
expect(html).toContain('<a');
|
|
@@ -1609,10 +1627,14 @@ describe('ssrRenderer', () => {
|
|
|
1609
1627
|
const result = await renderPageSSR(pageData, { Button: buttonDef });
|
|
1610
1628
|
// The <a> should exist with the component's default class plus the instance override classes.
|
|
1611
1629
|
expect(result.html).toMatch(/<a\s+href="\/partner"[^>]*class="[^"]*olink[^"]*"/);
|
|
1612
|
-
//
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
expect(result.html).toContain('
|
|
1630
|
+
// Hex color values become Tailwind arbitrary-value classes. Verify the
|
|
1631
|
+
// INSTANCE overrides (#252525 / #ffffff), not the structure defaults, won.
|
|
1632
|
+
expect(result.html).toContain('bg-[#252525]');
|
|
1633
|
+
expect(result.html).not.toContain('bg-[#ffffff]');
|
|
1634
|
+
expect(generateRuleForClass('bg-[#252525]')).toBe('background-color: #252525;');
|
|
1635
|
+
expect(result.html).toContain('text-[#ffffff]');
|
|
1636
|
+
expect(result.html).not.toContain('text-[#000000]');
|
|
1637
|
+
expect(generateRuleForClass('text-[#ffffff]')).toBe('color: #ffffff;');
|
|
1616
1638
|
});
|
|
1617
1639
|
|
|
1618
1640
|
test('instance style overrides structure style on the same property', async () => {
|
|
@@ -1651,10 +1673,10 @@ describe('ssrRenderer', () => {
|
|
|
1651
1673
|
};
|
|
1652
1674
|
|
|
1653
1675
|
const result = await renderPageSSR(pageData, { Button: buttonDef });
|
|
1654
|
-
expect(result.html).toContain('mb-
|
|
1655
|
-
expect(result.html).not.toContain('mb-0');
|
|
1676
|
+
expect(result.html).toContain('mb-8');
|
|
1677
|
+
expect(result.html).not.toContain('mb-[0]');
|
|
1656
1678
|
// Untouched structure properties remain.
|
|
1657
|
-
expect(result.html).toContain('p-
|
|
1679
|
+
expect(result.html).toContain('p-2');
|
|
1658
1680
|
});
|
|
1659
1681
|
|
|
1660
1682
|
test('extracts locale from path when not explicitly provided', async () => {
|
|
@@ -1684,9 +1706,7 @@ describe('ssrRenderer', () => {
|
|
|
1684
1706
|
type: 'list',
|
|
1685
1707
|
sourceType: 'prop',
|
|
1686
1708
|
source: ['Apple', 'Banana', 'Cherry'],
|
|
1687
|
-
children: [
|
|
1688
|
-
{ type: 'node', tag: 'li', children: '{{item}}' },
|
|
1689
|
-
],
|
|
1709
|
+
children: [{ type: 'node', tag: 'li', children: '{{item}}' }],
|
|
1690
1710
|
};
|
|
1691
1711
|
const html = await render(node);
|
|
1692
1712
|
expect(html).toContain('<li');
|
|
@@ -1699,9 +1719,7 @@ describe('ssrRenderer', () => {
|
|
|
1699
1719
|
type: 'list',
|
|
1700
1720
|
sourceType: 'prop',
|
|
1701
1721
|
source: [],
|
|
1702
|
-
children: [
|
|
1703
|
-
{ type: 'node', tag: 'li', children: '{{item}}' },
|
|
1704
|
-
],
|
|
1722
|
+
children: [{ type: 'node', tag: 'li', children: '{{item}}' }],
|
|
1705
1723
|
};
|
|
1706
1724
|
const html = await render(node);
|
|
1707
1725
|
expect(html).toBe('');
|
|
@@ -1712,9 +1730,7 @@ describe('ssrRenderer', () => {
|
|
|
1712
1730
|
type: 'list',
|
|
1713
1731
|
sourceType: 'collection',
|
|
1714
1732
|
source: 'posts',
|
|
1715
|
-
children: [
|
|
1716
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1717
|
-
],
|
|
1733
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1718
1734
|
};
|
|
1719
1735
|
// Without cmsService, returns empty string
|
|
1720
1736
|
const html = await render(node);
|
|
@@ -1801,9 +1817,7 @@ describe('ssrRenderer', () => {
|
|
|
1801
1817
|
type: 'list',
|
|
1802
1818
|
sourceType: 'collection',
|
|
1803
1819
|
source: 'posts',
|
|
1804
|
-
children: [
|
|
1805
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1806
|
-
],
|
|
1820
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1807
1821
|
};
|
|
1808
1822
|
|
|
1809
1823
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1828,9 +1842,7 @@ describe('ssrRenderer', () => {
|
|
|
1828
1842
|
sourceType: 'collection',
|
|
1829
1843
|
source: 'authors',
|
|
1830
1844
|
itemAs: 'author',
|
|
1831
|
-
children: [
|
|
1832
|
-
{ type: 'node', tag: 'span', children: '{{author.name}}' },
|
|
1833
|
-
],
|
|
1845
|
+
children: [{ type: 'node', tag: 'span', children: '{{author.name}}' }],
|
|
1834
1846
|
};
|
|
1835
1847
|
|
|
1836
1848
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1840,9 +1852,7 @@ describe('ssrRenderer', () => {
|
|
|
1840
1852
|
|
|
1841
1853
|
test('list node with collection source and direct item IDs', async () => {
|
|
1842
1854
|
const mockCmsService = {
|
|
1843
|
-
getItemsByIds: async (
|
|
1844
|
-
{ _id: 'a1', title: 'Featured Post' },
|
|
1845
|
-
],
|
|
1855
|
+
getItemsByIds: async (_collection: string, _ids: string[]) => [{ _id: 'a1', title: 'Featured Post' }],
|
|
1846
1856
|
getSchema: () => undefined,
|
|
1847
1857
|
};
|
|
1848
1858
|
|
|
@@ -1851,9 +1861,7 @@ describe('ssrRenderer', () => {
|
|
|
1851
1861
|
sourceType: 'collection',
|
|
1852
1862
|
source: 'posts',
|
|
1853
1863
|
items: ['a1'],
|
|
1854
|
-
children: [
|
|
1855
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1856
|
-
],
|
|
1864
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1857
1865
|
};
|
|
1858
1866
|
|
|
1859
1867
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1862,7 +1870,7 @@ describe('ssrRenderer', () => {
|
|
|
1862
1870
|
|
|
1863
1871
|
test('list node with collection source and items as single string ID', async () => {
|
|
1864
1872
|
const mockCmsService = {
|
|
1865
|
-
getItemsByIds: async (
|
|
1873
|
+
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
1866
1874
|
expect(ids).toEqual(['single-id']);
|
|
1867
1875
|
return [{ _id: 'single-id', title: 'Single Post' }];
|
|
1868
1876
|
},
|
|
@@ -1874,9 +1882,7 @@ describe('ssrRenderer', () => {
|
|
|
1874
1882
|
sourceType: 'collection',
|
|
1875
1883
|
source: 'posts',
|
|
1876
1884
|
items: 'single-id',
|
|
1877
|
-
children: [
|
|
1878
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1879
|
-
],
|
|
1885
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1880
1886
|
};
|
|
1881
1887
|
|
|
1882
1888
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1897,9 +1903,7 @@ describe('ssrRenderer', () => {
|
|
|
1897
1903
|
sourceType: 'collection',
|
|
1898
1904
|
source: 'posts',
|
|
1899
1905
|
excludeCurrentItem: true,
|
|
1900
|
-
children: [
|
|
1901
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1902
|
-
],
|
|
1906
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1903
1907
|
};
|
|
1904
1908
|
|
|
1905
1909
|
const cmsContext = { cms: { _id: 'current-id', title: 'Current' } };
|
|
@@ -1912,14 +1916,10 @@ describe('ssrRenderer', () => {
|
|
|
1912
1916
|
const node = {
|
|
1913
1917
|
type: 'list',
|
|
1914
1918
|
sourceType: 'prop',
|
|
1915
|
-
source: [
|
|
1916
|
-
{ name: 'A' }, { name: 'B' }, { name: 'C' }, { name: 'D' }, { name: 'E' },
|
|
1917
|
-
],
|
|
1919
|
+
source: [{ name: 'A' }, { name: 'B' }, { name: 'C' }, { name: 'D' }, { name: 'E' }],
|
|
1918
1920
|
offset: 1,
|
|
1919
1921
|
limit: 2,
|
|
1920
|
-
children: [
|
|
1921
|
-
{ type: 'node', tag: 'span', children: '{{item.name}}' },
|
|
1922
|
-
],
|
|
1922
|
+
children: [{ type: 'node', tag: 'span', children: '{{item.name}}' }],
|
|
1923
1923
|
};
|
|
1924
1924
|
const html = await render(node);
|
|
1925
1925
|
// After offset 1, items are [B, C, D, E]; after limit 2, items are [B, C]
|
|
@@ -1931,9 +1931,7 @@ describe('ssrRenderer', () => {
|
|
|
1931
1931
|
|
|
1932
1932
|
test('list node with emitTemplate emits a template element', async () => {
|
|
1933
1933
|
const mockCmsService = {
|
|
1934
|
-
queryItems: async () => [
|
|
1935
|
-
{ _id: '1', title: 'Post 1' },
|
|
1936
|
-
],
|
|
1934
|
+
queryItems: async () => [{ _id: '1', title: 'Post 1' }],
|
|
1937
1935
|
getSchema: () => undefined,
|
|
1938
1936
|
};
|
|
1939
1937
|
|
|
@@ -1942,9 +1940,7 @@ describe('ssrRenderer', () => {
|
|
|
1942
1940
|
sourceType: 'collection',
|
|
1943
1941
|
source: 'posts',
|
|
1944
1942
|
emitTemplate: true,
|
|
1945
|
-
children: [
|
|
1946
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
1947
|
-
],
|
|
1943
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
1948
1944
|
};
|
|
1949
1945
|
|
|
1950
1946
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1955,9 +1951,7 @@ describe('ssrRenderer', () => {
|
|
|
1955
1951
|
|
|
1956
1952
|
test('list node with collection source and schema adds _url to items', async () => {
|
|
1957
1953
|
const mockCmsService = {
|
|
1958
|
-
queryItems: async () => [
|
|
1959
|
-
{ _id: '1', title: 'Post 1', _slug: 'post-1' },
|
|
1960
|
-
],
|
|
1954
|
+
queryItems: async () => [{ _id: '1', title: 'Post 1', _slug: 'post-1' }],
|
|
1961
1955
|
getSchema: () => ({
|
|
1962
1956
|
id: 'posts',
|
|
1963
1957
|
name: 'Posts',
|
|
@@ -1971,9 +1965,7 @@ describe('ssrRenderer', () => {
|
|
|
1971
1965
|
type: 'list',
|
|
1972
1966
|
sourceType: 'collection',
|
|
1973
1967
|
source: 'posts',
|
|
1974
|
-
children: [
|
|
1975
|
-
{ type: 'node', tag: 'a', children: '{{post.title}}' },
|
|
1976
|
-
],
|
|
1968
|
+
children: [{ type: 'node', tag: 'a', children: '{{post.title}}' }],
|
|
1977
1969
|
};
|
|
1978
1970
|
|
|
1979
1971
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -1985,9 +1977,7 @@ describe('ssrRenderer', () => {
|
|
|
1985
1977
|
type: 'list',
|
|
1986
1978
|
sourceType: 'prop',
|
|
1987
1979
|
source: ['a', 'b'],
|
|
1988
|
-
children: [
|
|
1989
|
-
{ type: 'node', tag: 'span', children: '{{item}}' },
|
|
1990
|
-
],
|
|
1980
|
+
children: [{ type: 'node', tag: 'span', children: '{{item}}' }],
|
|
1991
1981
|
};
|
|
1992
1982
|
const html = await render(node);
|
|
1993
1983
|
// Should just be the repeated children, no wrapper
|
|
@@ -1998,18 +1988,14 @@ describe('ssrRenderer', () => {
|
|
|
1998
1988
|
|
|
1999
1989
|
test('legacy cms-list type is treated as collection sourceType', async () => {
|
|
2000
1990
|
const mockCmsService = {
|
|
2001
|
-
queryItems: async () => [
|
|
2002
|
-
{ _id: '1', title: 'Legacy Post' },
|
|
2003
|
-
],
|
|
1991
|
+
queryItems: async () => [{ _id: '1', title: 'Legacy Post' }],
|
|
2004
1992
|
getSchema: () => undefined,
|
|
2005
1993
|
};
|
|
2006
1994
|
|
|
2007
1995
|
const node = {
|
|
2008
1996
|
type: 'cms-list',
|
|
2009
1997
|
collection: 'posts',
|
|
2010
|
-
children: [
|
|
2011
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
2012
|
-
],
|
|
1998
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
2013
1999
|
};
|
|
2014
2000
|
|
|
2015
2001
|
const html = await render(node, { cmsService: mockCmsService });
|
|
@@ -2020,7 +2006,7 @@ describe('ssrRenderer', () => {
|
|
|
2020
2006
|
|
|
2021
2007
|
test('list node with items as CMS template resolves from cmsContext', async () => {
|
|
2022
2008
|
const mockCmsService = {
|
|
2023
|
-
getItemsByIds: async (
|
|
2009
|
+
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
2024
2010
|
expect(ids).toEqual(['ref-1', 'ref-2']);
|
|
2025
2011
|
return [
|
|
2026
2012
|
{ _id: 'ref-1', name: 'Referenced 1' },
|
|
@@ -2035,9 +2021,7 @@ describe('ssrRenderer', () => {
|
|
|
2035
2021
|
sourceType: 'collection',
|
|
2036
2022
|
source: 'authors',
|
|
2037
2023
|
items: '{{cms.authorIds}}',
|
|
2038
|
-
children: [
|
|
2039
|
-
{ type: 'node', tag: 'span', children: '{{author.name}}' },
|
|
2040
|
-
],
|
|
2024
|
+
children: [{ type: 'node', tag: 'span', children: '{{author.name}}' }],
|
|
2041
2025
|
};
|
|
2042
2026
|
|
|
2043
2027
|
const cmsContext = { cms: { authorIds: ['ref-1', 'ref-2'] } };
|
|
@@ -2057,9 +2041,7 @@ describe('ssrRenderer', () => {
|
|
|
2057
2041
|
sourceType: 'collection',
|
|
2058
2042
|
source: 'authors',
|
|
2059
2043
|
items: '{{cms.nonexistent}}',
|
|
2060
|
-
children: [
|
|
2061
|
-
{ type: 'node', tag: 'span', children: '{{author.name}}' },
|
|
2062
|
-
],
|
|
2044
|
+
children: [{ type: 'node', tag: 'span', children: '{{author.name}}' }],
|
|
2063
2045
|
};
|
|
2064
2046
|
|
|
2065
2047
|
const cmsContext = { cms: { title: 'exists' } };
|
|
@@ -2069,7 +2051,7 @@ describe('ssrRenderer', () => {
|
|
|
2069
2051
|
|
|
2070
2052
|
test('list node with items as CMS template resolves single value', async () => {
|
|
2071
2053
|
const mockCmsService = {
|
|
2072
|
-
getItemsByIds: async (
|
|
2054
|
+
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
2073
2055
|
expect(ids).toEqual(['single-ref']);
|
|
2074
2056
|
return [{ _id: 'single-ref', name: 'Single' }];
|
|
2075
2057
|
},
|
|
@@ -2081,9 +2063,7 @@ describe('ssrRenderer', () => {
|
|
|
2081
2063
|
sourceType: 'collection',
|
|
2082
2064
|
source: 'authors',
|
|
2083
2065
|
items: '{{cms.authorId}}',
|
|
2084
|
-
children: [
|
|
2085
|
-
{ type: 'node', tag: 'span', children: '{{author.name}}' },
|
|
2086
|
-
],
|
|
2066
|
+
children: [{ type: 'node', tag: 'span', children: '{{author.name}}' }],
|
|
2087
2067
|
};
|
|
2088
2068
|
|
|
2089
2069
|
const cmsContext = { cms: { authorId: 'single-ref' } };
|
|
@@ -2098,9 +2078,7 @@ describe('ssrRenderer', () => {
|
|
|
2098
2078
|
type: 'list',
|
|
2099
2079
|
sourceType: 'prop',
|
|
2100
2080
|
source: 'tags',
|
|
2101
|
-
children: [
|
|
2102
|
-
{ type: 'node', tag: 'span', children: '{{item.label}}' },
|
|
2103
|
-
],
|
|
2081
|
+
children: [{ type: 'node', tag: 'span', children: '{{item.label}}' }],
|
|
2104
2082
|
};
|
|
2105
2083
|
|
|
2106
2084
|
const cmsContext = { cms: { tags: [{ label: 'tag1' }, { label: 'tag2' }] } };
|
|
@@ -2114,9 +2092,7 @@ describe('ssrRenderer', () => {
|
|
|
2114
2092
|
type: 'list',
|
|
2115
2093
|
sourceType: 'prop',
|
|
2116
2094
|
source: '',
|
|
2117
|
-
children: [
|
|
2118
|
-
{ type: 'node', tag: 'span', children: '{{item}}' },
|
|
2119
|
-
],
|
|
2095
|
+
children: [{ type: 'node', tag: 'span', children: '{{item}}' }],
|
|
2120
2096
|
};
|
|
2121
2097
|
const html = await render(node);
|
|
2122
2098
|
expect(html).toBe('');
|
|
@@ -2159,13 +2135,10 @@ describe('ssrRenderer', () => {
|
|
|
2159
2135
|
tag: 'div',
|
|
2160
2136
|
children: `${RAW_HTML_PREFIX}${rawSlice}`,
|
|
2161
2137
|
};
|
|
2162
|
-
const result = await buildComponentHTML(
|
|
2163
|
-
|
|
2164
|
-
{},
|
|
2165
|
-
|
|
2166
|
-
'fr',
|
|
2167
|
-
{ defaultLocale: 'en', locales: [{ code: 'en' }, { code: 'fr' }] } as any,
|
|
2168
|
-
);
|
|
2138
|
+
const result = await buildComponentHTML(node as any, {}, {}, 'fr', {
|
|
2139
|
+
defaultLocale: 'en',
|
|
2140
|
+
locales: [{ code: 'en' }, { code: 'fr' }],
|
|
2141
|
+
} as any);
|
|
2169
2142
|
const processed = result.processedRawHtmlCollector.get(rawSlice);
|
|
2170
2143
|
expect(processed).toContain('href="/fr/about"');
|
|
2171
2144
|
});
|
|
@@ -2188,10 +2161,7 @@ describe('ssrRenderer', () => {
|
|
|
2188
2161
|
});
|
|
2189
2162
|
|
|
2190
2163
|
test('renders mixed array of strings and nodes', async () => {
|
|
2191
|
-
const nodes = [
|
|
2192
|
-
'Plain text ',
|
|
2193
|
-
{ type: 'node', tag: 'b', children: 'bold' },
|
|
2194
|
-
];
|
|
2164
|
+
const nodes = ['Plain text ', { type: 'node', tag: 'b', children: 'bold' }];
|
|
2195
2165
|
const result = await buildComponentHTML(nodes as any);
|
|
2196
2166
|
expect(result.html).toContain('Plain text');
|
|
2197
2167
|
expect(result.html).toContain('<b');
|
|
@@ -2248,11 +2218,7 @@ describe('ssrRenderer', () => {
|
|
|
2248
2218
|
const node = {
|
|
2249
2219
|
type: 'node',
|
|
2250
2220
|
tag: 'div',
|
|
2251
|
-
children: [
|
|
2252
|
-
null,
|
|
2253
|
-
{ type: 'node', tag: 'span', children: 'valid' },
|
|
2254
|
-
undefined,
|
|
2255
|
-
],
|
|
2221
|
+
children: [null, { type: 'node', tag: 'span', children: 'valid' }, undefined],
|
|
2256
2222
|
};
|
|
2257
2223
|
const html = await render(node);
|
|
2258
2224
|
expect(html).toContain('valid');
|
|
@@ -2551,8 +2517,8 @@ describe('ssrRenderer', () => {
|
|
|
2551
2517
|
};
|
|
2552
2518
|
const result = await renderFull(node);
|
|
2553
2519
|
expect(result.preloadImages.length).toBe(1);
|
|
2554
|
-
expect(result.preloadImages[0]
|
|
2555
|
-
expect(result.preloadImages[0]
|
|
2520
|
+
expect(result.preloadImages[0]!.type).toBe('image/avif');
|
|
2521
|
+
expect(result.preloadImages[0]!.srcset).toBe('/hero.avif 800w');
|
|
2556
2522
|
});
|
|
2557
2523
|
|
|
2558
2524
|
test('collects high-priority images with WebP when no AVIF available', async () => {
|
|
@@ -2569,7 +2535,7 @@ describe('ssrRenderer', () => {
|
|
|
2569
2535
|
};
|
|
2570
2536
|
const result = await renderFull(node);
|
|
2571
2537
|
expect(result.preloadImages.length).toBe(1);
|
|
2572
|
-
expect(result.preloadImages[0]
|
|
2538
|
+
expect(result.preloadImages[0]!.type).toBe('image/webp');
|
|
2573
2539
|
});
|
|
2574
2540
|
|
|
2575
2541
|
test('does not collect preload images when fetchpriority is not high', async () => {
|
|
@@ -2662,9 +2628,7 @@ describe('ssrRenderer', () => {
|
|
|
2662
2628
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr', icon: '/flags/fr.svg' },
|
|
2663
2629
|
],
|
|
2664
2630
|
};
|
|
2665
|
-
const slugMappings = [
|
|
2666
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2667
|
-
];
|
|
2631
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2668
2632
|
|
|
2669
2633
|
const node = {
|
|
2670
2634
|
type: 'locale-list',
|
|
@@ -2690,9 +2654,7 @@ describe('ssrRenderer', () => {
|
|
|
2690
2654
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2691
2655
|
],
|
|
2692
2656
|
};
|
|
2693
|
-
const slugMappings = [
|
|
2694
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2695
|
-
];
|
|
2657
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2696
2658
|
|
|
2697
2659
|
const node = {
|
|
2698
2660
|
type: 'locale-list',
|
|
@@ -2717,9 +2679,7 @@ describe('ssrRenderer', () => {
|
|
|
2717
2679
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2718
2680
|
],
|
|
2719
2681
|
};
|
|
2720
|
-
const slugMappings = [
|
|
2721
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2722
|
-
];
|
|
2682
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2723
2683
|
|
|
2724
2684
|
const node = {
|
|
2725
2685
|
type: 'locale-list',
|
|
@@ -2744,9 +2704,7 @@ describe('ssrRenderer', () => {
|
|
|
2744
2704
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2745
2705
|
],
|
|
2746
2706
|
};
|
|
2747
|
-
const slugMappings = [
|
|
2748
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2749
|
-
];
|
|
2707
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2750
2708
|
|
|
2751
2709
|
const node = {
|
|
2752
2710
|
type: 'locale-list',
|
|
@@ -2770,9 +2728,7 @@ describe('ssrRenderer', () => {
|
|
|
2770
2728
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2771
2729
|
],
|
|
2772
2730
|
};
|
|
2773
|
-
const slugMappings = [
|
|
2774
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2775
|
-
];
|
|
2731
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2776
2732
|
|
|
2777
2733
|
const node = {
|
|
2778
2734
|
type: 'locale-list',
|
|
@@ -2798,9 +2754,7 @@ describe('ssrRenderer', () => {
|
|
|
2798
2754
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr', icon: '/flags/fr.svg' },
|
|
2799
2755
|
],
|
|
2800
2756
|
};
|
|
2801
|
-
const slugMappings = [
|
|
2802
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2803
|
-
];
|
|
2757
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2804
2758
|
|
|
2805
2759
|
const node = {
|
|
2806
2760
|
type: 'locale-list',
|
|
@@ -2824,9 +2778,7 @@ describe('ssrRenderer', () => {
|
|
|
2824
2778
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr', icon: '/flags/fr.svg' },
|
|
2825
2779
|
],
|
|
2826
2780
|
};
|
|
2827
|
-
const slugMappings = [
|
|
2828
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
2829
|
-
];
|
|
2781
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
2830
2782
|
|
|
2831
2783
|
const node = {
|
|
2832
2784
|
type: 'locale-list',
|
|
@@ -2851,9 +2803,7 @@ describe('ssrRenderer', () => {
|
|
|
2851
2803
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2852
2804
|
],
|
|
2853
2805
|
};
|
|
2854
|
-
const slugMappings = [
|
|
2855
|
-
{ pageId: 'index', slugs: { en: '', fr: '' } },
|
|
2856
|
-
];
|
|
2806
|
+
const slugMappings = [{ pageId: 'index', slugs: { en: '', fr: '' } }];
|
|
2857
2807
|
|
|
2858
2808
|
const node = {
|
|
2859
2809
|
type: 'locale-list',
|
|
@@ -2882,9 +2832,7 @@ describe('ssrRenderer', () => {
|
|
|
2882
2832
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
2883
2833
|
],
|
|
2884
2834
|
};
|
|
2885
|
-
const slugMappings = [
|
|
2886
|
-
{ pageId: 'index', slugs: { en: '', fr: '' } },
|
|
2887
|
-
];
|
|
2835
|
+
const slugMappings = [{ pageId: 'index', slugs: { en: '', fr: '' } }];
|
|
2888
2836
|
|
|
2889
2837
|
const node = {
|
|
2890
2838
|
type: 'locale-list',
|
|
@@ -2999,9 +2947,7 @@ describe('ssrRenderer', () => {
|
|
|
2999
2947
|
structure: {
|
|
3000
2948
|
type: 'node',
|
|
3001
2949
|
tag: 'div',
|
|
3002
|
-
children: [
|
|
3003
|
-
{ type: 'slot' },
|
|
3004
|
-
],
|
|
2950
|
+
children: [{ type: 'slot' }],
|
|
3005
2951
|
},
|
|
3006
2952
|
interface: {},
|
|
3007
2953
|
},
|
|
@@ -3010,9 +2956,7 @@ describe('ssrRenderer', () => {
|
|
|
3010
2956
|
const node = {
|
|
3011
2957
|
type: 'component',
|
|
3012
2958
|
component: 'Wrapper',
|
|
3013
|
-
children: [
|
|
3014
|
-
{ type: 'node', tag: 'p', children: 'Slotted Content' },
|
|
3015
|
-
],
|
|
2959
|
+
children: [{ type: 'node', tag: 'p', children: 'Slotted Content' }],
|
|
3016
2960
|
};
|
|
3017
2961
|
|
|
3018
2962
|
const html = await render(node, { globalComponents: { Wrapper: wrapperDef } });
|
|
@@ -3085,7 +3029,16 @@ describe('ssrRenderer', () => {
|
|
|
3085
3029
|
};
|
|
3086
3030
|
|
|
3087
3031
|
const cmsContext = { cms: { title: 'My Article', body: 'Article body content' } };
|
|
3088
|
-
const result = await renderPageSSR(
|
|
3032
|
+
const result = await renderPageSSR(
|
|
3033
|
+
pageData,
|
|
3034
|
+
{ Article: articleDef },
|
|
3035
|
+
'/',
|
|
3036
|
+
'',
|
|
3037
|
+
undefined,
|
|
3038
|
+
undefined,
|
|
3039
|
+
undefined,
|
|
3040
|
+
cmsContext,
|
|
3041
|
+
);
|
|
3089
3042
|
|
|
3090
3043
|
expect(result.html).toContain('<article');
|
|
3091
3044
|
expect(result.html).toContain('My Article');
|
|
@@ -3102,9 +3055,7 @@ describe('ssrRenderer', () => {
|
|
|
3102
3055
|
const node = {
|
|
3103
3056
|
type: 'node',
|
|
3104
3057
|
tag: 'div',
|
|
3105
|
-
interactiveStyles: [
|
|
3106
|
-
{ trigger: 'hover', style: { opacity: '0.8' } },
|
|
3107
|
-
],
|
|
3058
|
+
interactiveStyles: [{ trigger: 'hover', style: { opacity: '0.8' } }],
|
|
3108
3059
|
children: 'Interactive',
|
|
3109
3060
|
};
|
|
3110
3061
|
const result = await renderFull(node, { pagePath: '/test-page' });
|
|
@@ -3129,9 +3080,7 @@ describe('ssrRenderer', () => {
|
|
|
3129
3080
|
structure: {
|
|
3130
3081
|
type: 'node',
|
|
3131
3082
|
tag: 'div',
|
|
3132
|
-
interactiveStyles: [
|
|
3133
|
-
{ trigger: 'hover', style: { opacity: '0.5' } },
|
|
3134
|
-
],
|
|
3083
|
+
interactiveStyles: [{ postfix: ':hover', style: { opacity: '0.5' } }],
|
|
3135
3084
|
children: 'hover me',
|
|
3136
3085
|
},
|
|
3137
3086
|
interface: {},
|
|
@@ -3166,9 +3115,9 @@ describe('ssrRenderer', () => {
|
|
|
3166
3115
|
tag: 'div',
|
|
3167
3116
|
interactiveStyles: [
|
|
3168
3117
|
{
|
|
3169
|
-
|
|
3118
|
+
postfix: ':hover',
|
|
3170
3119
|
style: {
|
|
3171
|
-
backgroundColor: { _mapping: true, prop: 'hoverColor', values: {
|
|
3120
|
+
backgroundColor: { _mapping: true, prop: 'hoverColor', values: { red: 'red', blue: 'blue' } },
|
|
3172
3121
|
},
|
|
3173
3122
|
},
|
|
3174
3123
|
],
|
|
@@ -3232,8 +3181,8 @@ describe('ssrRenderer', () => {
|
|
|
3232
3181
|
// Since we can't directly set templateContext through buildComponentHTML,
|
|
3233
3182
|
// we use a CMS context + items with {{cms.field}} template
|
|
3234
3183
|
const mockCmsService = {
|
|
3235
|
-
getItemsByIds: async (
|
|
3236
|
-
return ids.map(id => ({ _id: id, name: `Author ${id}` }));
|
|
3184
|
+
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
3185
|
+
return ids.map((id) => ({ _id: id, name: `Author ${id}` }));
|
|
3237
3186
|
},
|
|
3238
3187
|
getSchema: () => undefined,
|
|
3239
3188
|
};
|
|
@@ -3243,9 +3192,7 @@ describe('ssrRenderer', () => {
|
|
|
3243
3192
|
sourceType: 'collection',
|
|
3244
3193
|
source: 'authors',
|
|
3245
3194
|
items: '{{cms.relatedAuthors}}',
|
|
3246
|
-
children: [
|
|
3247
|
-
{ type: 'node', tag: 'span', children: '{{author.name}}' },
|
|
3248
|
-
],
|
|
3195
|
+
children: [{ type: 'node', tag: 'span', children: '{{author.name}}' }],
|
|
3249
3196
|
};
|
|
3250
3197
|
|
|
3251
3198
|
const cmsContext = { cms: { relatedAuthors: ['a1', 'a2'] } };
|
|
@@ -3262,7 +3209,7 @@ describe('ssrRenderer', () => {
|
|
|
3262
3209
|
const mockCmsService = {
|
|
3263
3210
|
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
3264
3211
|
calledWith.push(ids);
|
|
3265
|
-
return ids.map(id => ({ _id: id, name: `Tag ${id}` }));
|
|
3212
|
+
return ids.map((id) => ({ _id: id, name: `Tag ${id}` }));
|
|
3266
3213
|
},
|
|
3267
3214
|
getSchema: () => undefined,
|
|
3268
3215
|
};
|
|
@@ -3270,7 +3217,7 @@ describe('ssrRenderer', () => {
|
|
|
3270
3217
|
const Card: ComponentDefinition = {
|
|
3271
3218
|
component: {
|
|
3272
3219
|
interface: {
|
|
3273
|
-
config: { type: 'list' as
|
|
3220
|
+
config: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3274
3221
|
},
|
|
3275
3222
|
structure: {
|
|
3276
3223
|
type: 'list',
|
|
@@ -3278,9 +3225,7 @@ describe('ssrRenderer', () => {
|
|
|
3278
3225
|
source: 'tags',
|
|
3279
3226
|
itemAs: 'tag',
|
|
3280
3227
|
items: '{{config.0.tagIds}}',
|
|
3281
|
-
children: [
|
|
3282
|
-
{ type: 'node', tag: 'span', children: '{{tag.name}}' },
|
|
3283
|
-
],
|
|
3228
|
+
children: [{ type: 'node', tag: 'span', children: '{{tag.name}}' }],
|
|
3284
3229
|
},
|
|
3285
3230
|
},
|
|
3286
3231
|
};
|
|
@@ -3318,7 +3263,7 @@ describe('ssrRenderer', () => {
|
|
|
3318
3263
|
const Card: ComponentDefinition = {
|
|
3319
3264
|
component: {
|
|
3320
3265
|
interface: {
|
|
3321
|
-
config: { type: 'list' as
|
|
3266
|
+
config: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3322
3267
|
},
|
|
3323
3268
|
structure: {
|
|
3324
3269
|
type: 'list',
|
|
@@ -3326,9 +3271,7 @@ describe('ssrRenderer', () => {
|
|
|
3326
3271
|
source: 'posts',
|
|
3327
3272
|
itemAs: 'post',
|
|
3328
3273
|
filter: { category: '{{config.0.collection}}' },
|
|
3329
|
-
children: [
|
|
3330
|
-
{ type: 'node', tag: 'div', children: '{{post.title}}' },
|
|
3331
|
-
],
|
|
3274
|
+
children: [{ type: 'node', tag: 'div', children: '{{post.title}}' }],
|
|
3332
3275
|
},
|
|
3333
3276
|
},
|
|
3334
3277
|
};
|
|
@@ -3366,7 +3309,7 @@ describe('ssrRenderer', () => {
|
|
|
3366
3309
|
],
|
|
3367
3310
|
getItemsByIds: async (_collection: string, ids: string[]) => {
|
|
3368
3311
|
calledWith.push(ids);
|
|
3369
|
-
return ids.map(id => ({ _id: id, name: `Item ${id}` }));
|
|
3312
|
+
return ids.map((id) => ({ _id: id, name: `Item ${id}` }));
|
|
3370
3313
|
},
|
|
3371
3314
|
getSchema: () => undefined,
|
|
3372
3315
|
};
|
|
@@ -3377,7 +3320,7 @@ describe('ssrRenderer', () => {
|
|
|
3377
3320
|
// type: 'list' so the array prop value passes validation. The prop
|
|
3378
3321
|
// `tag` will land in componentResolvedProps; the parent loop var
|
|
3379
3322
|
// `tag` (set to the current outer item) will land in templateContext.
|
|
3380
|
-
tag: { type: 'list' as
|
|
3323
|
+
tag: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3381
3324
|
},
|
|
3382
3325
|
structure: {
|
|
3383
3326
|
type: 'list',
|
|
@@ -3385,9 +3328,7 @@ describe('ssrRenderer', () => {
|
|
|
3385
3328
|
source: 'tags',
|
|
3386
3329
|
itemAs: 'inner',
|
|
3387
3330
|
items: '{{tag.id}}',
|
|
3388
|
-
children: [
|
|
3389
|
-
{ type: 'node', tag: 'span', children: '{{inner.name}}' },
|
|
3390
|
-
],
|
|
3331
|
+
children: [{ type: 'node', tag: 'span', children: '{{inner.name}}' }],
|
|
3391
3332
|
},
|
|
3392
3333
|
},
|
|
3393
3334
|
};
|
|
@@ -3421,9 +3362,10 @@ describe('ssrRenderer', () => {
|
|
|
3421
3362
|
test('page-root filter does not silently resolve {{cms.X}} (regression)', async () => {
|
|
3422
3363
|
// Without a host component, ctx.componentResolvedProps is undefined and
|
|
3423
3364
|
// ctx.templateContext is undefined → buildListResolutionScope returns
|
|
3424
|
-
// undefined →
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3365
|
+
// undefined → the template cannot resolve. This pins down the documented
|
|
3366
|
+
// behavior: the cmsContext namespace does NOT silently merge into filter
|
|
3367
|
+
// scope — the unresolvable condition is dropped from the query (and
|
|
3368
|
+
// surfaced via logRuntimeError) instead of resolving against cmsContext.
|
|
3427
3369
|
const queries: any[] = [];
|
|
3428
3370
|
const mockCmsService = {
|
|
3429
3371
|
queryItems: async (q: any) => {
|
|
@@ -3448,7 +3390,8 @@ describe('ssrRenderer', () => {
|
|
|
3448
3390
|
});
|
|
3449
3391
|
|
|
3450
3392
|
expect(queries.length).toBe(1);
|
|
3451
|
-
|
|
3393
|
+
// The condition is dropped, NOT resolved to { category: 'shouldNotBeUsed' }.
|
|
3394
|
+
expect(queries[0].filter).toEqual({});
|
|
3452
3395
|
});
|
|
3453
3396
|
});
|
|
3454
3397
|
|
|
@@ -3495,9 +3438,7 @@ describe('ssrRenderer', () => {
|
|
|
3495
3438
|
{ code: 'fr', name: 'French', nativeName: 'Francais', langTag: 'fr' },
|
|
3496
3439
|
],
|
|
3497
3440
|
};
|
|
3498
|
-
const slugMappings = [
|
|
3499
|
-
{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } },
|
|
3500
|
-
];
|
|
3441
|
+
const slugMappings = [{ pageId: 'about', slugs: { en: 'about', fr: 'a-propos' } }];
|
|
3501
3442
|
|
|
3502
3443
|
const node = {
|
|
3503
3444
|
type: 'link',
|
|
@@ -3524,9 +3465,7 @@ describe('ssrRenderer', () => {
|
|
|
3524
3465
|
type: 'list',
|
|
3525
3466
|
sourceType: 'prop',
|
|
3526
3467
|
source: [{ name: 'a' }, { name: 'b' }],
|
|
3527
|
-
children: [
|
|
3528
|
-
{ type: 'node', tag: 'span', children: '{{item.name}}' },
|
|
3529
|
-
],
|
|
3468
|
+
children: [{ type: 'node', tag: 'span', children: '{{item.name}}' }],
|
|
3530
3469
|
};
|
|
3531
3470
|
|
|
3532
3471
|
const result = await renderFull(node, { pagePath: '/test' });
|
|
@@ -3546,9 +3485,7 @@ describe('ssrRenderer', () => {
|
|
|
3546
3485
|
const node = {
|
|
3547
3486
|
type: 'embed',
|
|
3548
3487
|
html: '<b>test</b>',
|
|
3549
|
-
interactiveStyles: [
|
|
3550
|
-
{ trigger: 'hover', style: { opacity: '0.5' } },
|
|
3551
|
-
],
|
|
3488
|
+
interactiveStyles: [{ trigger: 'hover', style: { opacity: '0.5' } }],
|
|
3552
3489
|
};
|
|
3553
3490
|
|
|
3554
3491
|
const result = await renderFull(node, { pagePath: '/test' });
|
|
@@ -3576,9 +3513,7 @@ describe('ssrRenderer', () => {
|
|
|
3576
3513
|
const node = {
|
|
3577
3514
|
type: 'link',
|
|
3578
3515
|
href: '/test',
|
|
3579
|
-
interactiveStyles: [
|
|
3580
|
-
{ trigger: 'hover', style: { color: 'blue' } },
|
|
3581
|
-
],
|
|
3516
|
+
interactiveStyles: [{ trigger: 'hover', style: { color: 'blue' } }],
|
|
3582
3517
|
children: 'Hover Link',
|
|
3583
3518
|
};
|
|
3584
3519
|
|
|
@@ -3657,9 +3592,7 @@ describe('ssrRenderer', () => {
|
|
|
3657
3592
|
structure: {
|
|
3658
3593
|
type: 'node',
|
|
3659
3594
|
tag: 'div',
|
|
3660
|
-
children: [
|
|
3661
|
-
{ type: 'slot' },
|
|
3662
|
-
],
|
|
3595
|
+
children: [{ type: 'slot' }],
|
|
3663
3596
|
},
|
|
3664
3597
|
interface: {},
|
|
3665
3598
|
},
|
|
@@ -3672,9 +3605,7 @@ describe('ssrRenderer', () => {
|
|
|
3672
3605
|
{
|
|
3673
3606
|
type: 'node',
|
|
3674
3607
|
tag: 'span',
|
|
3675
|
-
interactiveStyles: [
|
|
3676
|
-
{ trigger: 'hover', style: { opacity: '0.5' } },
|
|
3677
|
-
],
|
|
3608
|
+
interactiveStyles: [{ trigger: 'hover', style: { opacity: '0.5' } }],
|
|
3678
3609
|
children: 'Slotted',
|
|
3679
3610
|
},
|
|
3680
3611
|
],
|
|
@@ -3688,8 +3619,8 @@ describe('ssrRenderer', () => {
|
|
|
3688
3619
|
expect(result.interactiveStylesMap.size).toBeGreaterThan(0);
|
|
3689
3620
|
// The class should use page context (p_ prefix) not component context (c_ prefix)
|
|
3690
3621
|
const classNames = Array.from(result.interactiveStylesMap.keys());
|
|
3691
|
-
const
|
|
3692
|
-
const
|
|
3622
|
+
const _hasPageContext = classNames.some((cls) => cls.startsWith('p_'));
|
|
3623
|
+
const _hasComponentContext = classNames.some((cls) => cls.startsWith('c_'));
|
|
3693
3624
|
// At least check that the styles were registered
|
|
3694
3625
|
expect(classNames.length).toBeGreaterThan(0);
|
|
3695
3626
|
});
|
|
@@ -3789,7 +3720,7 @@ describe('ssrRenderer', () => {
|
|
|
3789
3720
|
const FeatureGrid: ComponentDefinition = {
|
|
3790
3721
|
component: {
|
|
3791
3722
|
interface: {
|
|
3792
|
-
features: { type: 'list' as
|
|
3723
|
+
features: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3793
3724
|
},
|
|
3794
3725
|
structure: {
|
|
3795
3726
|
type: 'node',
|
|
@@ -3871,7 +3802,7 @@ describe('ssrRenderer', () => {
|
|
|
3871
3802
|
const CardGrid: ComponentDefinition = {
|
|
3872
3803
|
component: {
|
|
3873
3804
|
interface: {
|
|
3874
|
-
cards: { type: 'list' as
|
|
3805
|
+
cards: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3875
3806
|
},
|
|
3876
3807
|
structure: {
|
|
3877
3808
|
type: 'list',
|
|
@@ -3947,7 +3878,7 @@ describe('ssrRenderer', () => {
|
|
|
3947
3878
|
const FeatureGrid: ComponentDefinition = {
|
|
3948
3879
|
component: {
|
|
3949
3880
|
interface: {
|
|
3950
|
-
features: { type: 'list' as
|
|
3881
|
+
features: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
3951
3882
|
},
|
|
3952
3883
|
structure: {
|
|
3953
3884
|
type: 'node',
|
|
@@ -4026,7 +3957,7 @@ describe('ssrRenderer', () => {
|
|
|
4026
3957
|
const SectionComp: ComponentDefinition = {
|
|
4027
3958
|
component: {
|
|
4028
3959
|
interface: {
|
|
4029
|
-
features: { type: 'list' as
|
|
3960
|
+
features: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4030
3961
|
},
|
|
4031
3962
|
structure: {
|
|
4032
3963
|
type: 'list',
|
|
@@ -4109,15 +4040,13 @@ describe('ssrRenderer', () => {
|
|
|
4109
4040
|
const ListGrid: ComponentDefinition = {
|
|
4110
4041
|
component: {
|
|
4111
4042
|
interface: {
|
|
4112
|
-
items: { type: 'list' as
|
|
4043
|
+
items: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4113
4044
|
},
|
|
4114
4045
|
structure: {
|
|
4115
4046
|
type: 'list',
|
|
4116
4047
|
sourceType: 'prop',
|
|
4117
4048
|
source: 'items',
|
|
4118
|
-
children: [
|
|
4119
|
-
{ type: 'slot' },
|
|
4120
|
-
],
|
|
4049
|
+
children: [{ type: 'slot' }],
|
|
4121
4050
|
},
|
|
4122
4051
|
},
|
|
4123
4052
|
};
|
|
@@ -4159,7 +4088,7 @@ describe('ssrRenderer', () => {
|
|
|
4159
4088
|
const Gallery: ComponentDefinition = {
|
|
4160
4089
|
component: {
|
|
4161
4090
|
interface: {
|
|
4162
|
-
items: { type: 'list' as
|
|
4091
|
+
items: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4163
4092
|
},
|
|
4164
4093
|
structure: {
|
|
4165
4094
|
type: 'list',
|
|
@@ -4217,9 +4146,7 @@ describe('ssrRenderer', () => {
|
|
|
4217
4146
|
structure: {
|
|
4218
4147
|
type: 'node',
|
|
4219
4148
|
tag: 'section',
|
|
4220
|
-
children: [
|
|
4221
|
-
{ type: 'slot' },
|
|
4222
|
-
],
|
|
4149
|
+
children: [{ type: 'slot' }],
|
|
4223
4150
|
},
|
|
4224
4151
|
},
|
|
4225
4152
|
};
|
|
@@ -4229,7 +4156,7 @@ describe('ssrRenderer', () => {
|
|
|
4229
4156
|
const Host: ComponentDefinition = {
|
|
4230
4157
|
component: {
|
|
4231
4158
|
interface: {
|
|
4232
|
-
testimonials: { type: 'list' as
|
|
4159
|
+
testimonials: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4233
4160
|
},
|
|
4234
4161
|
structure: {
|
|
4235
4162
|
type: 'node',
|
|
@@ -4244,9 +4171,7 @@ describe('ssrRenderer', () => {
|
|
|
4244
4171
|
type: 'list',
|
|
4245
4172
|
sourceType: 'prop',
|
|
4246
4173
|
source: 'testimonials',
|
|
4247
|
-
children: [
|
|
4248
|
-
{ type: 'node', tag: 'p', children: '{{item.name}}' },
|
|
4249
|
-
],
|
|
4174
|
+
children: [{ type: 'node', tag: 'p', children: '{{item.name}}' }],
|
|
4250
4175
|
},
|
|
4251
4176
|
],
|
|
4252
4177
|
},
|
|
@@ -4259,11 +4184,7 @@ describe('ssrRenderer', () => {
|
|
|
4259
4184
|
type: 'component',
|
|
4260
4185
|
component: 'Host',
|
|
4261
4186
|
props: {
|
|
4262
|
-
testimonials: [
|
|
4263
|
-
{ name: 'Alice' },
|
|
4264
|
-
{ name: 'Bob' },
|
|
4265
|
-
{ name: 'Carol' },
|
|
4266
|
-
],
|
|
4187
|
+
testimonials: [{ name: 'Alice' }, { name: 'Bob' }, { name: 'Carol' }],
|
|
4267
4188
|
},
|
|
4268
4189
|
};
|
|
4269
4190
|
|
|
@@ -4289,7 +4210,7 @@ describe('ssrRenderer', () => {
|
|
|
4289
4210
|
const Host: ComponentDefinition = {
|
|
4290
4211
|
component: {
|
|
4291
4212
|
interface: {
|
|
4292
|
-
testimonials: { type: 'list' as
|
|
4213
|
+
testimonials: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4293
4214
|
},
|
|
4294
4215
|
structure: {
|
|
4295
4216
|
type: 'component',
|
|
@@ -4299,9 +4220,7 @@ describe('ssrRenderer', () => {
|
|
|
4299
4220
|
type: 'list',
|
|
4300
4221
|
sourceType: 'prop',
|
|
4301
4222
|
source: '{{testimonials}}',
|
|
4302
|
-
children: [
|
|
4303
|
-
{ type: 'node', tag: 'p', children: '{{item.name}}' },
|
|
4304
|
-
],
|
|
4223
|
+
children: [{ type: 'node', tag: 'p', children: '{{item.name}}' }],
|
|
4305
4224
|
},
|
|
4306
4225
|
],
|
|
4307
4226
|
},
|
|
@@ -4322,15 +4241,13 @@ describe('ssrRenderer', () => {
|
|
|
4322
4241
|
const Direct: ComponentDefinition = {
|
|
4323
4242
|
component: {
|
|
4324
4243
|
interface: {
|
|
4325
|
-
items: { type: 'list' as
|
|
4244
|
+
items: { type: 'list' as const, itemSchema: {}, default: [] },
|
|
4326
4245
|
},
|
|
4327
4246
|
structure: {
|
|
4328
4247
|
type: 'list',
|
|
4329
4248
|
sourceType: 'prop',
|
|
4330
4249
|
source: 'items',
|
|
4331
|
-
children: [
|
|
4332
|
-
{ type: 'node', tag: 'li', children: '{{item.label}}' },
|
|
4333
|
-
],
|
|
4250
|
+
children: [{ type: 'node', tag: 'li', children: '{{item.label}}' }],
|
|
4334
4251
|
},
|
|
4335
4252
|
},
|
|
4336
4253
|
};
|
|
@@ -4369,9 +4286,7 @@ describe('ssrRenderer', () => {
|
|
|
4369
4286
|
type: 'list',
|
|
4370
4287
|
sourceType: 'prop',
|
|
4371
4288
|
source: 'missing',
|
|
4372
|
-
children: [
|
|
4373
|
-
{ type: 'node', tag: 'p', children: '{{item}}' },
|
|
4374
|
-
],
|
|
4289
|
+
children: [{ type: 'node', tag: 'p', children: '{{item}}' }],
|
|
4375
4290
|
},
|
|
4376
4291
|
],
|
|
4377
4292
|
},
|
|
@@ -4565,7 +4480,16 @@ describe('ssrRenderer', () => {
|
|
|
4565
4480
|
};
|
|
4566
4481
|
const result = await buildComponentHTML(
|
|
4567
4482
|
node as any,
|
|
4568
|
-
{},
|
|
4483
|
+
{},
|
|
4484
|
+
{},
|
|
4485
|
+
undefined,
|
|
4486
|
+
undefined,
|
|
4487
|
+
undefined,
|
|
4488
|
+
undefined,
|
|
4489
|
+
undefined,
|
|
4490
|
+
undefined,
|
|
4491
|
+
undefined,
|
|
4492
|
+
true,
|
|
4569
4493
|
);
|
|
4570
4494
|
expect(result.html).toContain('data-element-path="0"');
|
|
4571
4495
|
expect(result.html).toContain('data-element-path="0,0"');
|
|
@@ -4582,19 +4506,29 @@ describe('ssrRenderer', () => {
|
|
|
4582
4506
|
structure: {
|
|
4583
4507
|
type: 'node',
|
|
4584
4508
|
tag: 'div',
|
|
4585
|
-
children: [
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4509
|
+
children: [
|
|
4510
|
+
{
|
|
4511
|
+
type: 'list',
|
|
4512
|
+
sourceType: 'prop',
|
|
4513
|
+
source: 'items',
|
|
4514
|
+
children: [{ type: 'node', tag: 'p', children: '{{item.label}}' }],
|
|
4515
|
+
},
|
|
4516
|
+
],
|
|
4591
4517
|
},
|
|
4592
4518
|
},
|
|
4593
4519
|
};
|
|
4594
4520
|
const result = await buildComponentHTML(
|
|
4595
4521
|
{ type: 'component', component: 'Card' } as any,
|
|
4596
4522
|
{ Card },
|
|
4597
|
-
{},
|
|
4523
|
+
{},
|
|
4524
|
+
undefined,
|
|
4525
|
+
undefined,
|
|
4526
|
+
undefined,
|
|
4527
|
+
undefined,
|
|
4528
|
+
undefined,
|
|
4529
|
+
undefined,
|
|
4530
|
+
undefined,
|
|
4531
|
+
true,
|
|
4598
4532
|
);
|
|
4599
4533
|
expect(result.html).toContain('data-cms-item-index="0"');
|
|
4600
4534
|
expect(result.html).toContain('data-cms-item-index="1"');
|
|
@@ -4611,7 +4545,15 @@ describe('ssrRenderer', () => {
|
|
|
4611
4545
|
const result = await buildComponentHTML(
|
|
4612
4546
|
{ type: 'component', component: 'Card' } as any,
|
|
4613
4547
|
{ Card },
|
|
4614
|
-
{},
|
|
4548
|
+
{},
|
|
4549
|
+
undefined,
|
|
4550
|
+
undefined,
|
|
4551
|
+
undefined,
|
|
4552
|
+
undefined,
|
|
4553
|
+
undefined,
|
|
4554
|
+
undefined,
|
|
4555
|
+
undefined,
|
|
4556
|
+
true,
|
|
4615
4557
|
);
|
|
4616
4558
|
expect(result.html).toContain('data-component-root="true"');
|
|
4617
4559
|
expect(result.html).toContain('data-component-context="Card"');
|
|
@@ -4643,11 +4585,129 @@ describe('ssrRenderer', () => {
|
|
|
4643
4585
|
const result = await buildComponentHTML(
|
|
4644
4586
|
page as any,
|
|
4645
4587
|
{ Card },
|
|
4646
|
-
{},
|
|
4588
|
+
{},
|
|
4589
|
+
undefined,
|
|
4590
|
+
undefined,
|
|
4591
|
+
undefined,
|
|
4592
|
+
undefined,
|
|
4593
|
+
undefined,
|
|
4594
|
+
undefined,
|
|
4595
|
+
undefined,
|
|
4596
|
+
true,
|
|
4647
4597
|
);
|
|
4648
4598
|
expect(result.html).toContain('data-element-path="0"');
|
|
4649
4599
|
expect(result.html).toContain('data-element-path="0,0"');
|
|
4650
4600
|
expect(result.html).toContain('data-element-path="0,0,0"');
|
|
4651
4601
|
});
|
|
4602
|
+
|
|
4603
|
+
// Island / Custom nodes are black boxes meno-core can't render — they show a
|
|
4604
|
+
// placeholder on the canvas. The wrapper must still carry data-element-path so
|
|
4605
|
+
// a click selects the node like any regular element (it didn't, before).
|
|
4606
|
+
test('emits data-element-path on an empty island placeholder wrapper', async () => {
|
|
4607
|
+
const page = {
|
|
4608
|
+
type: 'node',
|
|
4609
|
+
tag: 'div',
|
|
4610
|
+
children: [{ type: 'island', src: 'Counter.tsx', client: { directive: 'load' } }],
|
|
4611
|
+
};
|
|
4612
|
+
const result = await buildComponentHTML(
|
|
4613
|
+
page as any,
|
|
4614
|
+
{},
|
|
4615
|
+
{},
|
|
4616
|
+
undefined,
|
|
4617
|
+
undefined,
|
|
4618
|
+
undefined,
|
|
4619
|
+
undefined,
|
|
4620
|
+
undefined,
|
|
4621
|
+
undefined,
|
|
4622
|
+
undefined,
|
|
4623
|
+
true,
|
|
4624
|
+
);
|
|
4625
|
+
expect(result.html).toContain('data-island="Counter.tsx"');
|
|
4626
|
+
expect(result.html).toContain('data-element-path="0,0"');
|
|
4627
|
+
// Placeholder shows the island's basename when it has no slotted children.
|
|
4628
|
+
expect(result.html).toContain('Counter');
|
|
4629
|
+
});
|
|
4630
|
+
|
|
4631
|
+
test('emits data-element-path on a custom node wrapper and stamps its slotted children', async () => {
|
|
4632
|
+
const page = {
|
|
4633
|
+
type: 'node',
|
|
4634
|
+
tag: 'div',
|
|
4635
|
+
children: [
|
|
4636
|
+
{
|
|
4637
|
+
type: 'custom',
|
|
4638
|
+
src: 'Fancy.astro',
|
|
4639
|
+
children: [{ type: 'node', tag: 'span', children: ['slotted'] }],
|
|
4640
|
+
},
|
|
4641
|
+
],
|
|
4642
|
+
};
|
|
4643
|
+
const result = await buildComponentHTML(
|
|
4644
|
+
page as any,
|
|
4645
|
+
{},
|
|
4646
|
+
{},
|
|
4647
|
+
undefined,
|
|
4648
|
+
undefined,
|
|
4649
|
+
undefined,
|
|
4650
|
+
undefined,
|
|
4651
|
+
undefined,
|
|
4652
|
+
undefined,
|
|
4653
|
+
undefined,
|
|
4654
|
+
true,
|
|
4655
|
+
);
|
|
4656
|
+
expect(result.html).toContain('data-custom="Fancy.astro"');
|
|
4657
|
+
// Wrapper is page-absolute [0,0]; the slotted child appends its index → [0,0,0].
|
|
4658
|
+
expect(result.html).toContain('data-element-path="0,0"');
|
|
4659
|
+
expect(result.html).toContain('data-element-path="0,0,0"');
|
|
4660
|
+
expect(result.html).toContain('slotted');
|
|
4661
|
+
});
|
|
4662
|
+
});
|
|
4663
|
+
|
|
4664
|
+
// Remote-data lists (sourceType: 'remote'): the SSR fetches the endpoint at render time and
|
|
4665
|
+
// applies the query, mirroring the meno-astro runtime's getRemoteData so the editor SSR matches
|
|
4666
|
+
// the real Astro build (previously processList had no 'remote' branch → always rendered empty).
|
|
4667
|
+
describe('processList — remote source', () => {
|
|
4668
|
+
const realFetch = globalThis.fetch;
|
|
4669
|
+
const mockFetch = (payload: unknown, ok = true) => {
|
|
4670
|
+
globalThis.fetch = mock(
|
|
4671
|
+
async () => ({ ok, json: async () => payload }) as unknown as Response,
|
|
4672
|
+
) as unknown as typeof fetch;
|
|
4673
|
+
};
|
|
4674
|
+
afterAll(() => {
|
|
4675
|
+
globalThis.fetch = realFetch;
|
|
4676
|
+
});
|
|
4677
|
+
|
|
4678
|
+
const remoteList = (extra: Record<string, unknown> = {}) => ({
|
|
4679
|
+
type: 'node',
|
|
4680
|
+
tag: 'div',
|
|
4681
|
+
children: [
|
|
4682
|
+
{
|
|
4683
|
+
type: 'list',
|
|
4684
|
+
sourceType: 'remote',
|
|
4685
|
+
url: 'https://api.example.com/coins',
|
|
4686
|
+
children: [{ type: 'node', tag: 'span', children: ['{{item.name}}'] }],
|
|
4687
|
+
...extra,
|
|
4688
|
+
},
|
|
4689
|
+
],
|
|
4690
|
+
});
|
|
4691
|
+
|
|
4692
|
+
test('fetches the endpoint and renders an element per item', async () => {
|
|
4693
|
+
mockFetch([{ name: 'Bitcoin' }, { name: 'Ethereum' }]);
|
|
4694
|
+
const html = await render(remoteList());
|
|
4695
|
+
expect(html).toContain('Bitcoin');
|
|
4696
|
+
expect(html).toContain('Ethereum');
|
|
4697
|
+
});
|
|
4698
|
+
|
|
4699
|
+
test('navigates the dot-path and applies limit', async () => {
|
|
4700
|
+
mockFetch({ data: { items: [{ name: 'A' }, { name: 'B' }, { name: 'C' }] } });
|
|
4701
|
+
const html = await render(remoteList({ path: 'data.items', limit: 2 }));
|
|
4702
|
+
expect(html).toContain('A');
|
|
4703
|
+
expect(html).toContain('B');
|
|
4704
|
+
expect(html).not.toContain('C');
|
|
4705
|
+
});
|
|
4706
|
+
|
|
4707
|
+
test('renders empty (no crash) when the endpoint fails', async () => {
|
|
4708
|
+
mockFetch(null, false);
|
|
4709
|
+
const html = await render(remoteList());
|
|
4710
|
+
expect(html).not.toContain('span');
|
|
4711
|
+
});
|
|
4652
4712
|
});
|
|
4653
4713
|
});
|