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
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
* Manages file watching for pages and components
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { FileWatcher
|
|
7
|
-
import { WebSocketManager } from '../websocketManager';
|
|
8
|
-
import { ComponentService } from './componentService';
|
|
9
|
-
import { PageService } from './pageService';
|
|
6
|
+
import { FileWatcher } from '../fileWatcher';
|
|
7
|
+
import type { WebSocketManager } from '../websocketManager';
|
|
8
|
+
import type { ComponentService } from './componentService';
|
|
9
|
+
import type { PageService } from './pageService';
|
|
10
10
|
import type { CMSService } from './cmsService';
|
|
11
|
-
import { PageCache } from '../pageCache';
|
|
11
|
+
import type { PageCache } from '../pageCache';
|
|
12
12
|
import { colorService } from './ColorService';
|
|
13
13
|
import { variableService } from './VariableService';
|
|
14
14
|
import { enumService } from './EnumService';
|
|
@@ -22,10 +22,10 @@ export class FileWatcherService {
|
|
|
22
22
|
constructor(
|
|
23
23
|
private componentService: ComponentService,
|
|
24
24
|
private pageService: PageService,
|
|
25
|
-
|
|
25
|
+
_pageCache: PageCache,
|
|
26
26
|
private wsManager: WebSocketManager,
|
|
27
27
|
private cmsService?: CMSService,
|
|
28
|
-
private onImageAdded?: (filename: string) => Promise<void
|
|
28
|
+
private onImageAdded?: (filename: string) => Promise<void>,
|
|
29
29
|
) {}
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -63,6 +63,41 @@ export class FileWatcherService {
|
|
|
63
63
|
}, 50);
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
|
+
onCMSTemplateChange: async () => {
|
|
67
|
+
// An astro CMS template page (src/pages/<col>/[slug].astro) was added,
|
|
68
|
+
// edited, or removed. Unlike a regular page edit this changes the set of
|
|
69
|
+
// collections, so refresh the CMS schema cache AND reconcile the page
|
|
70
|
+
// cache's /templates/<id> entries — otherwise the new collection and its
|
|
71
|
+
// template page only appear after a dev-server restart. Debounced (shares
|
|
72
|
+
// refreshSchemasTimer) to coalesce burst fs.watch events from one write.
|
|
73
|
+
if (!this.cmsService) return;
|
|
74
|
+
if (this.refreshSchemasTimer !== null) {
|
|
75
|
+
clearTimeout(this.refreshSchemasTimer);
|
|
76
|
+
}
|
|
77
|
+
const cmsService = this.cmsService;
|
|
78
|
+
const pageService = this.pageService;
|
|
79
|
+
const wsManager = this.wsManager;
|
|
80
|
+
this.refreshSchemasTimer = setTimeout(async () => {
|
|
81
|
+
this.refreshSchemasTimer = null;
|
|
82
|
+
await cmsService.refreshSchemas();
|
|
83
|
+
// In astro projects every /templates/<id> page-cache entry is a CMS
|
|
84
|
+
// template (regular pages keep their real path), so reconcile them 1:1
|
|
85
|
+
// with the fresh schema set: drop entries for removed collections,
|
|
86
|
+
// (re)load entries for current ones.
|
|
87
|
+
const ids = new Set(cmsService.getAllSchemas().keys());
|
|
88
|
+
for (const path of pageService.getAllPagePaths()) {
|
|
89
|
+
if (path.startsWith('/templates/') && !ids.has(path.slice('/templates/'.length))) {
|
|
90
|
+
await pageService.deletePage(path);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (const id of ids) {
|
|
94
|
+
await pageService.reloadPageFromDisk(`/templates/${id}`);
|
|
95
|
+
}
|
|
96
|
+
// Refresh the studio's collections list (CMS panel) and page list (dropdown).
|
|
97
|
+
wsManager.broadcastCollectionsUpdate();
|
|
98
|
+
wsManager.broadcastUpdate();
|
|
99
|
+
}, 50);
|
|
100
|
+
},
|
|
66
101
|
onColorsChange: async () => {
|
|
67
102
|
// Clear the color service cache so it reloads on next request
|
|
68
103
|
colorService.clearCache();
|
|
@@ -119,4 +154,3 @@ export class FileWatcherService {
|
|
|
119
154
|
}
|
|
120
155
|
}
|
|
121
156
|
}
|
|
122
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach } from 'bun:test';
|
|
2
2
|
import { PageService } from './pageService';
|
|
3
3
|
import { PageCache } from '../pageCache';
|
|
4
4
|
import { createMockPageData } from '../../test-utils';
|
|
@@ -122,9 +122,7 @@ describe('PageService', () => {
|
|
|
122
122
|
const serviceWithoutProvider = new PageService(pageCache);
|
|
123
123
|
const testData = createMockPageData();
|
|
124
124
|
|
|
125
|
-
await expect(serviceWithoutProvider.savePage('/', testData)).rejects.toThrow(
|
|
126
|
-
'PageProvider not set'
|
|
127
|
-
);
|
|
125
|
+
await expect(serviceWithoutProvider.savePage('/', testData)).rejects.toThrow('PageProvider not set');
|
|
128
126
|
});
|
|
129
127
|
|
|
130
128
|
test('should strip _lineMap from saved data', async () => {
|
|
@@ -229,8 +227,8 @@ describe('PageService', () => {
|
|
|
229
227
|
const mappings = pageService.getSlugMappings();
|
|
230
228
|
|
|
231
229
|
expect(mappings).toHaveLength(1);
|
|
232
|
-
expect(mappings[0]
|
|
233
|
-
expect(mappings[0]
|
|
230
|
+
expect(mappings[0]!.pageId).toBe('about');
|
|
231
|
+
expect(mappings[0]!.slugs).toEqual({ en: 'about', pl: 'o-nas' });
|
|
234
232
|
});
|
|
235
233
|
|
|
236
234
|
test('should create default slug for pages without explicit slugs', () => {
|
|
@@ -240,8 +238,8 @@ describe('PageService', () => {
|
|
|
240
238
|
const mappings = pageService.getSlugMappings();
|
|
241
239
|
|
|
242
240
|
expect(mappings).toHaveLength(1);
|
|
243
|
-
expect(mappings[0]
|
|
244
|
-
expect(mappings[0]
|
|
241
|
+
expect(mappings[0]!.pageId).toBe('about');
|
|
242
|
+
expect(mappings[0]!.slugs).toEqual({ _default: 'about' });
|
|
245
243
|
});
|
|
246
244
|
|
|
247
245
|
test('should handle index page correctly', () => {
|
|
@@ -251,8 +249,8 @@ describe('PageService', () => {
|
|
|
251
249
|
const mappings = pageService.getSlugMappings();
|
|
252
250
|
|
|
253
251
|
expect(mappings).toHaveLength(1);
|
|
254
|
-
expect(mappings[0]
|
|
255
|
-
expect(mappings[0]
|
|
252
|
+
expect(mappings[0]!.pageId).toBe('index');
|
|
253
|
+
expect(mappings[0]!.slugs).toEqual({ _default: '' });
|
|
256
254
|
});
|
|
257
255
|
});
|
|
258
256
|
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
* and managing page data including saving and deletion.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { existsSync, readdirSync, mkdirSync, rmdirSync } from 'fs';
|
|
10
|
-
import { join } from 'path';
|
|
11
|
-
import { PageCache } from '../pageCache';
|
|
9
|
+
import { existsSync, readdirSync, mkdirSync, rmdirSync } from 'node:fs';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import type { PageCache } from '../pageCache';
|
|
12
12
|
import { parseJSON } from '../jsonLoader';
|
|
13
13
|
import { buildLineMap } from '../utils/jsonLineMapper';
|
|
14
14
|
import { projectPaths } from '../projectContext';
|
|
15
|
+
import { logRuntimeError } from '../../shared/errorLogger';
|
|
16
|
+
import { isPathWithinRoot } from '../../shared/pathSecurity';
|
|
15
17
|
import type { JSONPage } from '../../shared/types';
|
|
16
18
|
import type { SlugMap } from '../../shared/slugTranslator';
|
|
17
19
|
import type { PageProvider } from '../../shared/interfaces/contentProvider';
|
|
@@ -25,7 +27,7 @@ export class PageService {
|
|
|
25
27
|
*/
|
|
26
28
|
constructor(
|
|
27
29
|
private pageCache: PageCache,
|
|
28
|
-
private provider?: PageProvider
|
|
30
|
+
private provider?: PageProvider,
|
|
29
31
|
) {}
|
|
30
32
|
|
|
31
33
|
/**
|
|
@@ -165,8 +167,10 @@ export class PageService {
|
|
|
165
167
|
let content: string | null = null;
|
|
166
168
|
try {
|
|
167
169
|
content = await this.provider.get(path);
|
|
168
|
-
} catch {
|
|
170
|
+
} catch (error) {
|
|
169
171
|
// Transient (mid-write / partial .astro parse error) — keep the cache as-is.
|
|
172
|
+
// Logged (dev-gated) so a persistent read/parse failure isn't fully invisible.
|
|
173
|
+
logRuntimeError('pageService.reloadPageFromDisk', error, { path });
|
|
170
174
|
return false;
|
|
171
175
|
}
|
|
172
176
|
|
|
@@ -241,19 +245,24 @@ export class PageService {
|
|
|
241
245
|
* // [{ path: "/", isDraft: false }, { path: "/about", isDraft: true }]
|
|
242
246
|
* ```
|
|
243
247
|
*/
|
|
244
|
-
getAllPagesWithInfo(): Array<{ path: string; isDraft: boolean; folder?: string }> {
|
|
245
|
-
const paths = this.pageCache.keys().filter(p => !p.startsWith('/_sketch/'));
|
|
246
|
-
return paths.map(path => {
|
|
248
|
+
getAllPagesWithInfo(): Array<{ path: string; isDraft: boolean; folder?: string; source?: 'cms' | 'ssr' }> {
|
|
249
|
+
const paths = this.pageCache.keys().filter((p) => !p.startsWith('/_sketch/'));
|
|
250
|
+
return paths.map((path) => {
|
|
247
251
|
const pageData = this.getPageData(path);
|
|
248
252
|
// Extract folder from path: /blog/post -> blog, /templates/blog-post -> templates
|
|
249
253
|
// Root-level pages (e.g., /, /about) have no folder
|
|
250
254
|
const pathWithoutSlash = path === '/' ? 'index' : path.substring(1);
|
|
251
255
|
const slashIndex = pathWithoutSlash.lastIndexOf('/');
|
|
252
256
|
const folder = slashIndex > 0 ? pathWithoutSlash.substring(0, slashIndex) : undefined;
|
|
257
|
+
// Surface a non-static page source (`cms` template / `ssr` live page) so the editor's
|
|
258
|
+
// page picker can group them apart from plain static pages. Static/undefined omitted.
|
|
259
|
+
const metaSource = pageData?.meta?.source;
|
|
260
|
+
const source = metaSource === 'cms' || metaSource === 'ssr' ? metaSource : undefined;
|
|
253
261
|
return {
|
|
254
262
|
path,
|
|
255
263
|
isDraft: pageData?.meta?.draft === true,
|
|
256
|
-
...(folder ? { folder } : {})
|
|
264
|
+
...(folder ? { folder } : {}),
|
|
265
|
+
...(source ? { source } : {}),
|
|
257
266
|
};
|
|
258
267
|
});
|
|
259
268
|
}
|
|
@@ -309,7 +318,9 @@ export class PageService {
|
|
|
309
318
|
result.push(...collectFolders(join(dir, entry.name), relativePath));
|
|
310
319
|
}
|
|
311
320
|
return result;
|
|
312
|
-
} catch {
|
|
321
|
+
} catch {
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
313
324
|
};
|
|
314
325
|
|
|
315
326
|
return collectFolders(pagesDir, '').sort();
|
|
@@ -359,7 +370,7 @@ export class PageService {
|
|
|
359
370
|
* @param newFolder - Target folder name (empty string for root)
|
|
360
371
|
*/
|
|
361
372
|
async movePage(pagePath: string, newFolder: string): Promise<void> {
|
|
362
|
-
const { rename } = await import('fs/promises');
|
|
373
|
+
const { rename } = await import('node:fs/promises');
|
|
363
374
|
const pagesDir = this.pagesBaseDir();
|
|
364
375
|
const ext = this.pageExt();
|
|
365
376
|
|
|
@@ -370,9 +381,14 @@ export class PageService {
|
|
|
370
381
|
const baseName = slashIndex >= 0 ? pageName.substring(slashIndex + 1) : pageName;
|
|
371
382
|
|
|
372
383
|
const sourceFile = join(pagesDir, `${pageName}${ext}`);
|
|
373
|
-
const targetFile = newFolder
|
|
374
|
-
|
|
375
|
-
|
|
384
|
+
const targetFile = newFolder ? join(pagesDir, newFolder, `${baseName}${ext}`) : join(pagesDir, `${baseName}${ext}`);
|
|
385
|
+
|
|
386
|
+
// Defense-in-depth: a crafted pagePath / newFolder must not move a file out of
|
|
387
|
+
// (or in from) outside the pages dir. Siblings createFolder/renamePage validate;
|
|
388
|
+
// movePage previously did not.
|
|
389
|
+
if (!isPathWithinRoot(sourceFile, pagesDir) || !isPathWithinRoot(targetFile, pagesDir)) {
|
|
390
|
+
throw new Error(`movePage path escapes pages dir: ${pagePath} -> ${newFolder}`);
|
|
391
|
+
}
|
|
376
392
|
|
|
377
393
|
// Create target directory if needed
|
|
378
394
|
if (newFolder) {
|
|
@@ -399,9 +415,7 @@ export class PageService {
|
|
|
399
415
|
}
|
|
400
416
|
|
|
401
417
|
// Clean up empty source directory
|
|
402
|
-
const sourceDir = slashIndex >= 0
|
|
403
|
-
? join(pagesDir, pageName.substring(0, slashIndex))
|
|
404
|
-
: null;
|
|
418
|
+
const sourceDir = slashIndex >= 0 ? join(pagesDir, pageName.substring(0, slashIndex)) : null;
|
|
405
419
|
|
|
406
420
|
if (sourceDir && sourceDir !== pagesDir) {
|
|
407
421
|
try {
|
|
@@ -454,7 +468,7 @@ export class PageService {
|
|
|
454
468
|
throw new Error('Page slug can only contain lowercase letters, numbers, dashes, and underscores');
|
|
455
469
|
}
|
|
456
470
|
|
|
457
|
-
const { rename } = await import('fs/promises');
|
|
471
|
+
const { rename } = await import('node:fs/promises');
|
|
458
472
|
const pagesDir = this.pagesBaseDir();
|
|
459
473
|
const ext = this.pageExt();
|
|
460
474
|
const sourceFile = join(pagesDir, `${oldName}${ext}`);
|
|
@@ -27,9 +27,21 @@ export function escapeHtml(unsafe: string): string {
|
|
|
27
27
|
|
|
28
28
|
/** HTML attributes that must NOT be kebab-cased (single-word or special casing) */
|
|
29
29
|
const NO_KEBAB_ATTRS = new Set([
|
|
30
|
-
'autoplay',
|
|
31
|
-
'
|
|
32
|
-
'
|
|
30
|
+
'autoplay',
|
|
31
|
+
'playsinline',
|
|
32
|
+
'crossorigin',
|
|
33
|
+
'novalidate',
|
|
34
|
+
'readonly',
|
|
35
|
+
'nomodule',
|
|
36
|
+
'formnovalidate',
|
|
37
|
+
'ismap',
|
|
38
|
+
'usemap',
|
|
39
|
+
'srcset',
|
|
40
|
+
'inputmode',
|
|
41
|
+
'enterkeyhint',
|
|
42
|
+
'autocomplete',
|
|
43
|
+
'autofocus',
|
|
44
|
+
'contenteditable',
|
|
33
45
|
]);
|
|
34
46
|
|
|
35
47
|
/**
|
|
@@ -61,9 +73,7 @@ export function buildAttributes(props: Record<string, unknown>, exclude: string[
|
|
|
61
73
|
const attrName = NO_KEBAB_ATTRS.has(key.toLowerCase())
|
|
62
74
|
? key.toLowerCase()
|
|
63
75
|
: key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
64
|
-
const attrValue = typeof value === 'boolean'
|
|
65
|
-
? (value ? '' : undefined)
|
|
66
|
-
: escapeHtml(String(value));
|
|
76
|
+
const attrValue = typeof value === 'boolean' ? (value ? '' : undefined) : escapeHtml(String(value));
|
|
67
77
|
|
|
68
78
|
if (attrValue !== undefined) {
|
|
69
79
|
if (typeof value === 'boolean' && value) {
|
|
@@ -74,7 +84,7 @@ export function buildAttributes(props: Record<string, unknown>, exclude: string[
|
|
|
74
84
|
}
|
|
75
85
|
}
|
|
76
86
|
|
|
77
|
-
return attrs.length > 0 ?
|
|
87
|
+
return attrs.length > 0 ? ` ${attrs.join(' ')}` : '';
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
/**
|
|
@@ -115,7 +125,7 @@ export function buildEditorAttrs(opts: {
|
|
|
115
125
|
parts.push(`data-component-context="${escapeHtml(opts.componentContext)}"`);
|
|
116
126
|
}
|
|
117
127
|
|
|
118
|
-
return
|
|
128
|
+
return ` ${parts.join(' ')}`;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
/**
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export interface BuildError {
|
|
7
|
-
file: string;
|
|
8
|
-
message: string;
|
|
9
|
-
type: string;
|
|
7
|
+
file: string; // e.g., "pages/posts.json"
|
|
8
|
+
message: string; // Error message
|
|
9
|
+
type: string; // 'minify' | 'render' | 'parse' | 'cms'
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface BuildErrorsData {
|
|
@@ -30,10 +30,7 @@ function escapeHtml(str: string): string {
|
|
|
30
30
|
* Safely encode data for embedding in a script tag
|
|
31
31
|
*/
|
|
32
32
|
function safeJsonForScript(data: unknown): string {
|
|
33
|
-
return JSON.stringify(data)
|
|
34
|
-
.replace(/</g, '\\u003c')
|
|
35
|
-
.replace(/>/g, '\\u003e')
|
|
36
|
-
.replace(/&/g, '\\u0026');
|
|
33
|
+
return JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
/**
|
|
@@ -52,12 +49,12 @@ export function generateBuildErrorPage(data: BuildErrorsData, cspNonce?: string)
|
|
|
52
49
|
const nonceAttr = cspNonce ? ` nonce="${cspNonce}"` : '';
|
|
53
50
|
|
|
54
51
|
// Generate plain text for copying to AI
|
|
55
|
-
const plainTextErrors = errors.map(err =>
|
|
56
|
-
`[${err.type.toUpperCase()}] ${err.file}\n${err.message}`
|
|
57
|
-
).join('\n\n');
|
|
52
|
+
const plainTextErrors = errors.map((err) => `[${err.type.toUpperCase()}] ${err.file}\n${err.message}`).join('\n\n');
|
|
58
53
|
const copyText = `Build failed with ${errors.length} error(s):\n\n${plainTextErrors}`;
|
|
59
54
|
|
|
60
|
-
const errorListHtml = errors
|
|
55
|
+
const errorListHtml = errors
|
|
56
|
+
.map(
|
|
57
|
+
(err) => `
|
|
61
58
|
<div class="error-item">
|
|
62
59
|
<div class="error-item-header">
|
|
63
60
|
<div class="error-type">${escapeHtml(err.type)}</div>
|
|
@@ -65,7 +62,9 @@ export function generateBuildErrorPage(data: BuildErrorsData, cspNonce?: string)
|
|
|
65
62
|
</div>
|
|
66
63
|
<div class="error-message">${escapeHtml(err.message)}</div>
|
|
67
64
|
</div>
|
|
68
|
-
|
|
65
|
+
`,
|
|
66
|
+
)
|
|
67
|
+
.join('');
|
|
69
68
|
|
|
70
69
|
return `<!DOCTYPE html>
|
|
71
70
|
<html lang="en">
|
|
@@ -18,10 +18,7 @@ const SYSTEM_FIELDS = ['_id', '_url', '_filename', '_slug'];
|
|
|
18
18
|
* @param fields Fields to include (undefined or empty = all fields)
|
|
19
19
|
* @returns Filtered items with only specified fields
|
|
20
20
|
*/
|
|
21
|
-
export function filterItemFields(
|
|
22
|
-
items: CMSItem[],
|
|
23
|
-
fields: string[] | undefined
|
|
24
|
-
): CMSItem[] {
|
|
21
|
+
export function filterItemFields(items: CMSItem[], fields: string[] | undefined): CMSItem[] {
|
|
25
22
|
// If no field restriction, return all items as-is
|
|
26
23
|
if (!fields || fields.length === 0) {
|
|
27
24
|
return items;
|
|
@@ -31,7 +28,7 @@ export function filterItemFields(
|
|
|
31
28
|
const allowedFields = new Set([...SYSTEM_FIELDS, ...fields]);
|
|
32
29
|
|
|
33
30
|
// Filter each item to only include allowed fields
|
|
34
|
-
return items.map(item => {
|
|
31
|
+
return items.map((item) => {
|
|
35
32
|
const filtered: CMSItem = { _id: item._id };
|
|
36
33
|
|
|
37
34
|
for (const [key, value] of Object.entries(item)) {
|
|
@@ -52,15 +49,9 @@ export function filterItemFields(
|
|
|
52
49
|
* @param items Items to embed
|
|
53
50
|
* @returns HTML script tag string
|
|
54
51
|
*/
|
|
55
|
-
export function generateInlineDataScript(
|
|
56
|
-
collection: string,
|
|
57
|
-
items: CMSItem[]
|
|
58
|
-
): string {
|
|
52
|
+
export function generateInlineDataScript(collection: string, items: CMSItem[]): string {
|
|
59
53
|
// Escape HTML entities in JSON to prevent XSS
|
|
60
|
-
const jsonString = JSON.stringify(items)
|
|
61
|
-
.replace(/</g, '\\u003c')
|
|
62
|
-
.replace(/>/g, '\\u003e')
|
|
63
|
-
.replace(/&/g, '\\u0026');
|
|
54
|
+
const jsonString = JSON.stringify(items).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
|
|
64
55
|
|
|
65
56
|
return `<script type="application/json" id="meno-cms-${escapeAttr(collection)}">${jsonString}</script>`;
|
|
66
57
|
}
|
|
@@ -84,10 +75,7 @@ function escapeAttr(str: string): string {
|
|
|
84
75
|
* @param itemCount Number of items in collection
|
|
85
76
|
* @returns Effective strategy ('inline' or 'static')
|
|
86
77
|
*/
|
|
87
|
-
export function determineStrategy(
|
|
88
|
-
config: CMSClientDataConfig,
|
|
89
|
-
itemCount: number
|
|
90
|
-
): 'inline' | 'static' {
|
|
78
|
+
export function determineStrategy(config: CMSClientDataConfig, itemCount: number): 'inline' | 'static' {
|
|
91
79
|
if (config.strategy === 'inline') return 'inline';
|
|
92
80
|
if (config.strategy === 'static') return 'static';
|
|
93
81
|
|
|
@@ -122,7 +110,7 @@ export interface ClientDataCollection {
|
|
|
122
110
|
export function prepareClientData(
|
|
123
111
|
collection: string,
|
|
124
112
|
items: CMSItem[],
|
|
125
|
-
config: CMSClientDataConfig | undefined
|
|
113
|
+
config: CMSClientDataConfig | undefined,
|
|
126
114
|
): ClientDataCollection | null {
|
|
127
115
|
// Skip if not enabled
|
|
128
116
|
if (!config?.enabled) {
|
|
@@ -146,9 +134,7 @@ export function prepareClientData(
|
|
|
146
134
|
* @param collections Map of collection ID to prepared client data
|
|
147
135
|
* @returns Combined HTML string with all data scripts
|
|
148
136
|
*/
|
|
149
|
-
export function generateAllInlineDataScripts(
|
|
150
|
-
collections: Map<string, ClientDataCollection>
|
|
151
|
-
): string {
|
|
137
|
+
export function generateAllInlineDataScripts(collections: Map<string, ClientDataCollection>): string {
|
|
152
138
|
const scripts: string[] = [];
|
|
153
139
|
|
|
154
140
|
for (const [collectionId, data] of collections) {
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { I18nConfig } from '../../shared/types/components';
|
|
7
|
-
import type { TemplateContext } from '../../shared/types';
|
|
8
7
|
import { DEFAULT_I18N_CONFIG, resolveI18nValue, isI18nValue } from '../../shared/i18n';
|
|
9
8
|
import type { ValueResolver } from '../../shared/itemTemplateUtils';
|
|
10
9
|
import { tiptapToHtml } from '../../shared/richtext/tiptapToHtml';
|
|
@@ -24,10 +23,7 @@ export interface CMSContext {
|
|
|
24
23
|
/**
|
|
25
24
|
* Get i18n resolver function for value resolution
|
|
26
25
|
*/
|
|
27
|
-
export function createI18nResolver(
|
|
28
|
-
locale?: string,
|
|
29
|
-
i18nConfig?: I18nConfig
|
|
30
|
-
): ValueResolver | undefined {
|
|
26
|
+
export function createI18nResolver(locale?: string, i18nConfig?: I18nConfig): ValueResolver | undefined {
|
|
31
27
|
if (!locale || !i18nConfig) return undefined;
|
|
32
28
|
return (value: unknown) => resolveI18nValue(value, locale, i18nConfig);
|
|
33
29
|
}
|
|
@@ -41,12 +37,12 @@ export function processCMSTemplate(
|
|
|
41
37
|
template: string,
|
|
42
38
|
cmsItem: Record<string, unknown>,
|
|
43
39
|
locale?: string,
|
|
44
|
-
i18nConfig?: I18nConfig
|
|
40
|
+
i18nConfig?: I18nConfig,
|
|
45
41
|
): string {
|
|
46
42
|
const config = i18nConfig || DEFAULT_I18N_CONFIG;
|
|
47
43
|
const effectiveLocale = locale || config.defaultLocale;
|
|
48
44
|
|
|
49
|
-
return template.replace(/\{\{cms\.([^}]+)\}\}/g, (
|
|
45
|
+
return template.replace(/\{\{cms\.([^}]+)\}\}/g, (_match, fieldPath) => {
|
|
50
46
|
// Support nested paths like cms.author.name
|
|
51
47
|
const parts = fieldPath.trim().split('.');
|
|
52
48
|
let value: unknown = cmsItem;
|
|
@@ -94,7 +90,7 @@ export function processCMSPropsTemplate(
|
|
|
94
90
|
props: Record<string, unknown>,
|
|
95
91
|
cmsItem: Record<string, unknown>,
|
|
96
92
|
locale?: string,
|
|
97
|
-
i18nConfig?: I18nConfig
|
|
93
|
+
i18nConfig?: I18nConfig,
|
|
98
94
|
): Record<string, unknown> {
|
|
99
95
|
const result: Record<string, unknown> = {};
|
|
100
96
|
|
|
@@ -10,7 +10,7 @@ import type { ComponentDefinition } from '../../shared/types';
|
|
|
10
10
|
*/
|
|
11
11
|
export function collectComponentCSS(
|
|
12
12
|
globalComponents: Record<string, ComponentDefinition> = {},
|
|
13
|
-
pageComponents: Record<string, ComponentDefinition> = {}
|
|
13
|
+
pageComponents: Record<string, ComponentDefinition> = {},
|
|
14
14
|
): string {
|
|
15
15
|
const cssBlocks: string[] = [];
|
|
16
16
|
|
|
@@ -64,7 +64,7 @@ describe('generateErrorPage', () => {
|
|
|
64
64
|
expect(html).toContain("A section couldn't load its content");
|
|
65
65
|
expect(html).toContain('error-friendly');
|
|
66
66
|
// ...while the raw message is still present (under technical details / copy).
|
|
67
|
-
expect(html).toContain(
|
|
67
|
+
expect(html).toContain('Cannot read properties of null (reading 'length')');
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
it('sends friendly + raw fields in the PREVIEW_ERROR payload', () => {
|
|
@@ -44,10 +44,7 @@ function escapeHtml(str: string): string {
|
|
|
44
44
|
* Escapes </script> sequences and other problematic characters
|
|
45
45
|
*/
|
|
46
46
|
function safeJsonForScript(data: unknown): string {
|
|
47
|
-
return JSON.stringify(data)
|
|
48
|
-
.replace(/</g, '\\u003c')
|
|
49
|
-
.replace(/>/g, '\\u003e')
|
|
50
|
-
.replace(/&/g, '\\u0026');
|
|
47
|
+
return JSON.stringify(data).replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/&/g, '\\u0026');
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
/**
|
|
@@ -66,8 +63,7 @@ export function generateErrorPage(error: unknown, context?: string, cspNonce?: s
|
|
|
66
63
|
const errorMessage = escapeHtml(errorInfo.message);
|
|
67
64
|
const errorStack = errorInfo.stack ? escapeHtml(errorInfo.stack) : '';
|
|
68
65
|
// Escape first (XSS-safe), then turn `backtick` spans into <code> for display.
|
|
69
|
-
const renderInline = (s: string) =>
|
|
70
|
-
escapeHtml(s).replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
66
|
+
const renderInline = (s: string) => escapeHtml(s).replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
71
67
|
const friendlyTitle = escapeHtml(friendly.title);
|
|
72
68
|
const friendlyMessage = renderInline(friendly.friendlyMessage);
|
|
73
69
|
const friendlyHint = friendly.hint ? renderInline(friendly.hint) : '';
|
|
@@ -89,9 +85,7 @@ export function generateErrorPage(error: unknown, context?: string, cspNonce?: s
|
|
|
89
85
|
});
|
|
90
86
|
|
|
91
87
|
// Full error text for copying
|
|
92
|
-
const fullErrorText = safeJsonForScript(
|
|
93
|
-
errorInfo.message + (errorInfo.stack ? '\n\n' + errorInfo.stack : '')
|
|
94
|
-
);
|
|
88
|
+
const fullErrorText = safeJsonForScript(errorInfo.message + (errorInfo.stack ? `\n\n${errorInfo.stack}` : ''));
|
|
95
89
|
|
|
96
90
|
return `<!DOCTYPE html>
|
|
97
91
|
<html lang="en">
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { test, expect, describe } from 'bun:test';
|
|
21
|
-
import * as fs from 'fs';
|
|
22
|
-
import * as path from 'path';
|
|
21
|
+
import * as fs from 'node:fs';
|
|
22
|
+
import * as path from 'node:path';
|
|
23
23
|
|
|
24
24
|
const CORE = path.resolve(import.meta.dir, '../..', '..');
|
|
25
25
|
const SSR_DIR = path.join(CORE, 'lib/server/ssr');
|
|
@@ -55,21 +55,18 @@ const SCRIPT_OPEN_TAG = /<script\b([^>]*)>/g;
|
|
|
55
55
|
* newlines for the comment content.
|
|
56
56
|
*/
|
|
57
57
|
function stripComments(src: string): string {
|
|
58
|
-
const blockStripped = src.replace(/\/\*[\s\S]*?\*\//g, (m) =>
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
return blockStripped.replace(/(^|[^:])\/\/[^\n]*/g, (m, prefix) =>
|
|
62
|
-
prefix + ' '.repeat(m.length - prefix.length)
|
|
63
|
-
);
|
|
58
|
+
const blockStripped = src.replace(/\/\*[\s\S]*?\*\//g, (m) => m.replace(/[^\n]/g, ' '));
|
|
59
|
+
return blockStripped.replace(/(^|[^:])\/\/[^\n]*/g, (m, prefix) => prefix + ' '.repeat(m.length - prefix.length));
|
|
64
60
|
}
|
|
65
61
|
|
|
66
62
|
function findInlineScripts(src: string): { attrs: string; index: number }[] {
|
|
67
63
|
const clean = stripComments(src);
|
|
68
64
|
const out: { attrs: string; index: number }[] = [];
|
|
69
|
-
let match: RegExpExecArray | null;
|
|
70
65
|
SCRIPT_OPEN_TAG.lastIndex = 0;
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
let match: RegExpExecArray | null = SCRIPT_OPEN_TAG.exec(clean);
|
|
67
|
+
while (match) {
|
|
68
|
+
out.push({ attrs: match[1]!, index: match.index });
|
|
69
|
+
match = SCRIPT_OPEN_TAG.exec(clean);
|
|
73
70
|
}
|
|
74
71
|
return out;
|
|
75
72
|
}
|
|
@@ -108,9 +105,7 @@ describe('SSR inline script nonce contract', () => {
|
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
if (offenders.length > 0) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Inline <script> tags missing \${nonceAttr}:\n ${offenders.join('\n ')}`,
|
|
113
|
-
);
|
|
108
|
+
throw new Error(`Inline <script> tags missing \${nonceAttr}:\n ${offenders.join('\n ')}`);
|
|
114
109
|
}
|
|
115
110
|
});
|
|
116
111
|
});
|