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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Component Service
|
|
3
3
|
* Handles component loading and management
|
|
4
|
-
*
|
|
4
|
+
*
|
|
5
5
|
* Provides methods for loading components from the file system, caching them in memory,
|
|
6
6
|
* and managing component definitions including saving JavaScript and CSS files.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { join } from 'path';
|
|
10
|
-
import { existsSync, mkdirSync,
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { existsSync, mkdirSync, readdirSync, rmdirSync } from 'node:fs';
|
|
11
11
|
import { loadComponentDirectory, loadJSONFile, parseJSON } from '../jsonLoader';
|
|
12
12
|
import type { ComponentWithCategory, ComponentDirectoryResult, ComponentLoadDiagnostic } from '../jsonLoader';
|
|
13
13
|
import { projectPaths } from '../projectContext';
|
|
@@ -15,6 +15,7 @@ import type { ComponentDefinition, JSONPage } from '../../shared/types';
|
|
|
15
15
|
import { readTextFile, fileExists } from '../runtime';
|
|
16
16
|
import type { PageService } from './pageService';
|
|
17
17
|
import { rewriteComponentRefs } from '../../shared/componentRefs';
|
|
18
|
+
import { isSafePathSegment, isPathWithinRoot } from '../../shared/pathSecurity';
|
|
18
19
|
|
|
19
20
|
// Re-export for callers that already imported the helper from this module.
|
|
20
21
|
export { rewriteComponentRefs };
|
|
@@ -157,7 +158,10 @@ export class ComponentService {
|
|
|
157
158
|
|
|
158
159
|
if (this.loader) {
|
|
159
160
|
// Legacy loader interface returns plain Map
|
|
160
|
-
loadedComponents = await this.loader.loadDirectory(projectPaths.components()) as Map<
|
|
161
|
+
loadedComponents = (await this.loader.loadDirectory(projectPaths.components())) as Map<
|
|
162
|
+
string,
|
|
163
|
+
ComponentWithCategory
|
|
164
|
+
>;
|
|
161
165
|
} else {
|
|
162
166
|
const result: ComponentDirectoryResult = await loadComponentDirectory(projectPaths.components());
|
|
163
167
|
loadedComponents = result.components;
|
|
@@ -189,10 +193,10 @@ export class ComponentService {
|
|
|
189
193
|
|
|
190
194
|
/**
|
|
191
195
|
* Get component by name
|
|
192
|
-
*
|
|
196
|
+
*
|
|
193
197
|
* @param name - Component name (without .json extension)
|
|
194
198
|
* @returns ComponentDefinition if found, undefined otherwise
|
|
195
|
-
*
|
|
199
|
+
*
|
|
196
200
|
* @example
|
|
197
201
|
* ```typescript
|
|
198
202
|
* const button = componentService.getComponent('Button');
|
|
@@ -207,10 +211,10 @@ export class ComponentService {
|
|
|
207
211
|
|
|
208
212
|
/**
|
|
209
213
|
* Check if component exists
|
|
210
|
-
*
|
|
214
|
+
*
|
|
211
215
|
* @param name - Component name to check
|
|
212
216
|
* @returns True if component exists in cache, false otherwise
|
|
213
|
-
*
|
|
217
|
+
*
|
|
214
218
|
* @example
|
|
215
219
|
* ```typescript
|
|
216
220
|
* if (componentService.hasComponent('Button')) {
|
|
@@ -266,7 +270,7 @@ export class ComponentService {
|
|
|
266
270
|
this.components.forEach((value, key) => {
|
|
267
271
|
record[key] = {
|
|
268
272
|
definition: value,
|
|
269
|
-
category: this.componentCategories.get(key)
|
|
273
|
+
category: this.componentCategories.get(key),
|
|
270
274
|
};
|
|
271
275
|
});
|
|
272
276
|
return record;
|
|
@@ -308,10 +312,10 @@ export class ComponentService {
|
|
|
308
312
|
|
|
309
313
|
/**
|
|
310
314
|
* Validate that a component has a valid structure
|
|
311
|
-
*
|
|
315
|
+
*
|
|
312
316
|
* @param componentDef - ComponentDefinition to validate
|
|
313
317
|
* @returns true if component has valid structure, false otherwise
|
|
314
|
-
*
|
|
318
|
+
*
|
|
315
319
|
* @example
|
|
316
320
|
* ```typescript
|
|
317
321
|
* const component = componentService.getComponent('Button');
|
|
@@ -324,35 +328,35 @@ export class ComponentService {
|
|
|
324
328
|
if (!componentDef) {
|
|
325
329
|
return false;
|
|
326
330
|
}
|
|
327
|
-
|
|
331
|
+
|
|
328
332
|
// Handle ComponentDefinition in any format:
|
|
329
333
|
// 1. Legacy: { type: string, component: { structure: {...} } }
|
|
330
334
|
// 2. New: { component: { structure: {...} } }
|
|
331
335
|
// 3. Direct: { component: { structure: {...} } } (already normalized)
|
|
332
|
-
|
|
336
|
+
|
|
333
337
|
// Access component field safely (handle both TypeScript types and runtime data)
|
|
334
338
|
const component = (componentDef as any).component;
|
|
335
|
-
|
|
339
|
+
|
|
336
340
|
if (!component || typeof component !== 'object') {
|
|
337
341
|
return false;
|
|
338
342
|
}
|
|
339
|
-
|
|
343
|
+
|
|
340
344
|
// Check for structure in component
|
|
341
345
|
const structure = component.structure;
|
|
342
346
|
if (!structure) {
|
|
343
347
|
return false;
|
|
344
348
|
}
|
|
345
|
-
|
|
349
|
+
|
|
346
350
|
// Validate structure is a proper object with type property
|
|
347
351
|
if (typeof structure !== 'object' || structure === null || Array.isArray(structure)) {
|
|
348
352
|
return false;
|
|
349
353
|
}
|
|
350
|
-
|
|
354
|
+
|
|
351
355
|
// Structure must have a type property that's a string
|
|
352
356
|
if (!('type' in structure) || typeof structure.type !== 'string') {
|
|
353
357
|
return false;
|
|
354
358
|
}
|
|
355
|
-
|
|
359
|
+
|
|
356
360
|
return true;
|
|
357
361
|
}
|
|
358
362
|
|
|
@@ -362,8 +366,15 @@ export class ComponentService {
|
|
|
362
366
|
*/
|
|
363
367
|
private getComponentDir(name: string, category?: string): string {
|
|
364
368
|
const componentsDir = this.componentsBaseDir();
|
|
369
|
+
// A component name becomes a filename — it must be a single safe segment (no
|
|
370
|
+
// `..`/separators). The resolved category dir must stay within the components
|
|
371
|
+
// root (nested categories are allowed, traversal is not). Mirrors the guards
|
|
372
|
+
// renameComponent/createFolder already apply; the save path previously lacked them.
|
|
373
|
+
if (!isSafePathSegment(name)) throw new Error(`unsafe component name: ${name}`);
|
|
365
374
|
const cat = this.resolveComponentCategory(name, category);
|
|
366
|
-
|
|
375
|
+
const dir = cat ? join(componentsDir, cat) : componentsDir;
|
|
376
|
+
if (!isPathWithinRoot(dir, componentsDir)) throw new Error(`component category escapes components dir: ${cat}`);
|
|
377
|
+
return dir;
|
|
367
378
|
}
|
|
368
379
|
|
|
369
380
|
/**
|
|
@@ -447,7 +458,7 @@ export class ComponentService {
|
|
|
447
458
|
return await readTextFile(jsFilePath);
|
|
448
459
|
}
|
|
449
460
|
return null;
|
|
450
|
-
} catch (
|
|
461
|
+
} catch (_error) {
|
|
451
462
|
return null;
|
|
452
463
|
}
|
|
453
464
|
}
|
|
@@ -480,11 +491,27 @@ export class ComponentService {
|
|
|
480
491
|
* ```
|
|
481
492
|
*/
|
|
482
493
|
async saveComponent(name: string, data: ComponentDefinition, category?: string): Promise<void> {
|
|
483
|
-
//
|
|
494
|
+
// The `.json` PAYLOAD never carries javascript (it lives in the sibling `.js`
|
|
495
|
+
// file), so the JSON write below strips it. But the component's JS must NOT be
|
|
496
|
+
// lost in the process:
|
|
497
|
+
// - single-file writers (the astro `.astro` format) re-emit the WHOLE file
|
|
498
|
+
// from the def they're given — writing a js-less def silently deletes the
|
|
499
|
+
// component's `<script>` from the source of truth;
|
|
500
|
+
// - the in-memory cache previously took the stripped copy too, so even JSON
|
|
501
|
+
// projects lost the component's JS from the live cache (SSR preview renders
|
|
502
|
+
// without it) until a file-watcher reload.
|
|
503
|
+
// Preserve: the incoming def's javascript wins; absent that, the cached one.
|
|
484
504
|
const dataWithoutJS = JSON.parse(JSON.stringify(data));
|
|
485
505
|
if (dataWithoutJS?.component?.javascript !== undefined) {
|
|
486
506
|
delete dataWithoutJS.component.javascript;
|
|
487
507
|
}
|
|
508
|
+
const incomingJs = (data as { component?: { javascript?: string } })?.component?.javascript;
|
|
509
|
+
const cachedJs = this.components.get(name)?.component?.javascript;
|
|
510
|
+
const javascript = incomingJs !== undefined ? incomingJs : cachedJs;
|
|
511
|
+
const dataWithJS: ComponentDefinition =
|
|
512
|
+
javascript !== undefined
|
|
513
|
+
? { ...dataWithoutJS, component: { ...dataWithoutJS.component, javascript } }
|
|
514
|
+
: dataWithoutJS;
|
|
488
515
|
|
|
489
516
|
// Determine target directory. Resolver consults the explicit arg, then
|
|
490
517
|
// the in-memory cache, then disk — so a save during the file-watcher
|
|
@@ -496,13 +523,13 @@ export class ComponentService {
|
|
|
496
523
|
|
|
497
524
|
if (this.writer) {
|
|
498
525
|
// Format-aware write (e.g. emit a `.astro` file). The writer owns directory
|
|
499
|
-
// creation + serialization
|
|
500
|
-
|
|
526
|
+
// creation + serialization, and for single-file formats the def must carry
|
|
527
|
+
// EVERYTHING — including javascript (see the ComponentWriter contract).
|
|
528
|
+
await this.writer.writeComponent(componentDir, name, dataWithJS);
|
|
501
529
|
} else {
|
|
502
|
-
// JSON path — unchanged, byte-identical to legacy behavior
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
: (await import('fs/promises')).writeFile;
|
|
530
|
+
// JSON path — unchanged, byte-identical to legacy behavior (js-less payload;
|
|
531
|
+
// the sibling `.js` file owns the script).
|
|
532
|
+
const writeFile = this.fs ? this.fs.writeFile.bind(this.fs) : (await import('node:fs/promises')).writeFile;
|
|
506
533
|
|
|
507
534
|
// Create category directory if needed
|
|
508
535
|
if (targetCategory && !existsSync(componentDir)) {
|
|
@@ -513,8 +540,9 @@ export class ComponentService {
|
|
|
513
540
|
await writeFile(filePath, JSON.stringify(dataWithoutJS, null, 2), 'utf-8');
|
|
514
541
|
}
|
|
515
542
|
|
|
516
|
-
// Update in-memory cache
|
|
517
|
-
|
|
543
|
+
// Update in-memory cache with the FULL def (js preserved) so the live preview
|
|
544
|
+
// keeps rendering the component's script between save and watcher reload.
|
|
545
|
+
this.components.set(name, dataWithJS);
|
|
518
546
|
this.componentCategories.set(name, targetCategory);
|
|
519
547
|
}
|
|
520
548
|
|
|
@@ -550,9 +578,7 @@ export class ComponentService {
|
|
|
550
578
|
return;
|
|
551
579
|
}
|
|
552
580
|
|
|
553
|
-
const writeFile = this.fs
|
|
554
|
-
? this.fs.writeFile.bind(this.fs)
|
|
555
|
-
: (await import('fs/promises')).writeFile;
|
|
581
|
+
const writeFile = this.fs ? this.fs.writeFile.bind(this.fs) : (await import('node:fs/promises')).writeFile;
|
|
556
582
|
const jsFilePath = join(componentDir, `${name}.js`);
|
|
557
583
|
await writeFile(jsFilePath, javascript || '', 'utf-8');
|
|
558
584
|
|
|
@@ -569,7 +595,7 @@ export class ComponentService {
|
|
|
569
595
|
}
|
|
570
596
|
parsed.component.javascript = jsContent;
|
|
571
597
|
}
|
|
572
|
-
} catch (
|
|
598
|
+
} catch (_error) {
|
|
573
599
|
// Silently handle JS file read errors
|
|
574
600
|
}
|
|
575
601
|
this.components.set(name, parsed);
|
|
@@ -608,9 +634,7 @@ export class ComponentService {
|
|
|
608
634
|
return;
|
|
609
635
|
}
|
|
610
636
|
|
|
611
|
-
const writeFile = this.fs
|
|
612
|
-
? this.fs.writeFile.bind(this.fs)
|
|
613
|
-
: (await import('fs/promises')).writeFile;
|
|
637
|
+
const writeFile = this.fs ? this.fs.writeFile.bind(this.fs) : (await import('node:fs/promises')).writeFile;
|
|
614
638
|
const cssFilePath = join(componentDir, `${name}.css`);
|
|
615
639
|
await writeFile(cssFilePath, css || '', 'utf-8');
|
|
616
640
|
|
|
@@ -627,7 +651,7 @@ export class ComponentService {
|
|
|
627
651
|
}
|
|
628
652
|
parsed.component.css = cssContent;
|
|
629
653
|
}
|
|
630
|
-
} catch (
|
|
654
|
+
} catch (_error) {
|
|
631
655
|
// Silently handle CSS file read errors
|
|
632
656
|
}
|
|
633
657
|
this.components.set(name, parsed);
|
|
@@ -657,8 +681,8 @@ export class ComponentService {
|
|
|
657
681
|
try {
|
|
658
682
|
const entries = readdirSync(componentsDir, { withFileTypes: true });
|
|
659
683
|
return entries
|
|
660
|
-
.filter(entry => entry.isDirectory())
|
|
661
|
-
.map(entry => entry.name)
|
|
684
|
+
.filter((entry) => entry.isDirectory())
|
|
685
|
+
.map((entry) => entry.name)
|
|
662
686
|
.sort();
|
|
663
687
|
} catch {
|
|
664
688
|
return [];
|
|
@@ -687,7 +711,10 @@ export class ComponentService {
|
|
|
687
711
|
}
|
|
688
712
|
|
|
689
713
|
// Validate folder name (alphanumeric, dashes, underscores only)
|
|
690
|
-
const sanitized = folderName
|
|
714
|
+
const sanitized = folderName
|
|
715
|
+
.trim()
|
|
716
|
+
.toLowerCase()
|
|
717
|
+
.replace(/[^a-z0-9-_]/g, '-');
|
|
691
718
|
if (sanitized !== folderName.trim()) {
|
|
692
719
|
throw new Error('Folder name can only contain lowercase letters, numbers, dashes, and underscores');
|
|
693
720
|
}
|
|
@@ -748,7 +775,7 @@ export class ComponentService {
|
|
|
748
775
|
|
|
749
776
|
// Move all component files (.json, .js, .css)
|
|
750
777
|
const extensions = ['.json', '.js', '.css'];
|
|
751
|
-
const { rename } = await import('fs/promises');
|
|
778
|
+
const { rename } = await import('node:fs/promises');
|
|
752
779
|
|
|
753
780
|
for (const ext of extensions) {
|
|
754
781
|
const sourcePath = join(sourceDir, `${name}${ext}`);
|
|
@@ -843,7 +870,7 @@ export class ComponentService {
|
|
|
843
870
|
}
|
|
844
871
|
}
|
|
845
872
|
|
|
846
|
-
const { rename } = await import('fs/promises');
|
|
873
|
+
const { rename } = await import('node:fs/promises');
|
|
847
874
|
for (const ext of ['.json', '.js', '.css'] as const) {
|
|
848
875
|
const src = join(dir, `${oldName}${ext}`);
|
|
849
876
|
const dst = join(dir, `${trimmedNew}${ext}`);
|
|
@@ -864,9 +891,7 @@ export class ComponentService {
|
|
|
864
891
|
// each one that actually changed. The owning component's own structure
|
|
865
892
|
// shouldn't reference itself, so we don't bother walking it.
|
|
866
893
|
let componentRefs = 0;
|
|
867
|
-
const writeFile = this.fs
|
|
868
|
-
? this.fs.writeFile.bind(this.fs)
|
|
869
|
-
: (await import('fs/promises')).writeFile;
|
|
894
|
+
const writeFile = this.fs ? this.fs.writeFile.bind(this.fs) : (await import('node:fs/promises')).writeFile;
|
|
870
895
|
for (const [name, otherDef] of this.components.entries()) {
|
|
871
896
|
if (name === trimmedNew) continue;
|
|
872
897
|
const structure = otherDef.component?.structure;
|
|
@@ -912,4 +937,3 @@ export class ComponentService {
|
|
|
912
937
|
};
|
|
913
938
|
}
|
|
914
939
|
}
|
|
915
|
-
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { describe, test, expect, beforeEach, afterEach
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'fs';
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'node:fs';
|
|
4
4
|
|
|
5
5
|
// We need to mock projectPaths.config() before importing ConfigService.
|
|
6
6
|
// Since projectPaths is a module-level export, we mock the projectContext module.
|
|
7
7
|
import { setProjectRoot, getProjectRoot } from '../../server/projectContext';
|
|
8
8
|
import { ConfigService } from './configService';
|
|
9
|
-
import type { IconsConfig } from './configService';
|
|
10
9
|
import { DEFAULT_BREAKPOINTS } from '../../shared/breakpoints';
|
|
11
10
|
import { DEFAULT_RESPONSIVE_SCALES } from '../../shared/responsiveScaling';
|
|
12
11
|
import { DEFAULT_I18N_CONFIG } from '../../shared/i18n';
|
|
@@ -209,8 +208,8 @@ describe('ConfigService', () => {
|
|
|
209
208
|
await service.load();
|
|
210
209
|
|
|
211
210
|
const result = service.getBreakpoints();
|
|
212
|
-
expect(result.tablet
|
|
213
|
-
expect(result.tablet
|
|
211
|
+
expect(result.tablet!.breakpoint).toBe(1024);
|
|
212
|
+
expect(result.tablet!.previewPoint).toBe(1024);
|
|
214
213
|
});
|
|
215
214
|
|
|
216
215
|
test('should fall back to defaults for non-positive number values', async () => {
|
|
@@ -278,7 +277,7 @@ describe('ConfigService', () => {
|
|
|
278
277
|
await service.load();
|
|
279
278
|
|
|
280
279
|
const result = service.getBreakpoints();
|
|
281
|
-
expect(result.tablet
|
|
280
|
+
expect(result.tablet!.previewPoint).toBe(1024);
|
|
282
281
|
});
|
|
283
282
|
|
|
284
283
|
test('should support custom breakpoint names', async () => {
|
|
@@ -329,12 +328,12 @@ describe('ConfigService', () => {
|
|
|
329
328
|
const result = service.getI18n();
|
|
330
329
|
expect(result.defaultLocale).toBe('en');
|
|
331
330
|
expect(result.locales).toHaveLength(2);
|
|
332
|
-
expect(result.locales[0]
|
|
333
|
-
expect(result.locales[1]
|
|
331
|
+
expect(result.locales[0]!.code).toBe('en');
|
|
332
|
+
expect(result.locales[1]!.code).toBe('pl');
|
|
334
333
|
// Migrated locales should have name, nativeName, langTag
|
|
335
|
-
expect(result.locales[1]
|
|
336
|
-
expect(result.locales[1]
|
|
337
|
-
expect(result.locales[1]
|
|
334
|
+
expect(result.locales[1]!.name).toBe('PL');
|
|
335
|
+
expect(result.locales[1]!.nativeName).toBe('PL');
|
|
336
|
+
expect(result.locales[1]!.langTag).toBe('pl-PL');
|
|
338
337
|
});
|
|
339
338
|
|
|
340
339
|
test('should pass through new LocaleConfig[] format', async () => {
|
|
@@ -547,10 +546,7 @@ describe('ConfigService', () => {
|
|
|
547
546
|
test('should normalize string URLs to object format for JS libraries', async () => {
|
|
548
547
|
writeConfig({
|
|
549
548
|
libraries: {
|
|
550
|
-
js: [
|
|
551
|
-
'https://cdn.example.com/lib.js',
|
|
552
|
-
'https://cdn.example.com/other.js',
|
|
553
|
-
],
|
|
549
|
+
js: ['https://cdn.example.com/lib.js', 'https://cdn.example.com/other.js'],
|
|
554
550
|
},
|
|
555
551
|
});
|
|
556
552
|
await service.load();
|
|
@@ -565,62 +561,44 @@ describe('ConfigService', () => {
|
|
|
565
561
|
test('should normalize string URLs to object format for CSS libraries', async () => {
|
|
566
562
|
writeConfig({
|
|
567
563
|
libraries: {
|
|
568
|
-
css: [
|
|
569
|
-
'https://fonts.googleapis.com/css2?family=Inter',
|
|
570
|
-
],
|
|
564
|
+
css: ['https://fonts.googleapis.com/css2?family=Inter'],
|
|
571
565
|
},
|
|
572
566
|
});
|
|
573
567
|
await service.load();
|
|
574
568
|
|
|
575
569
|
const result = service.getLibraries();
|
|
576
|
-
expect(result.css).toEqual([
|
|
577
|
-
{ url: 'https://fonts.googleapis.com/css2?family=Inter' },
|
|
578
|
-
]);
|
|
570
|
+
expect(result.css).toEqual([{ url: 'https://fonts.googleapis.com/css2?family=Inter' }]);
|
|
579
571
|
});
|
|
580
572
|
|
|
581
573
|
test('should pass through object format for JS libraries', async () => {
|
|
582
574
|
writeConfig({
|
|
583
575
|
libraries: {
|
|
584
|
-
js: [
|
|
585
|
-
{ url: 'https://cdn.example.com/lib.js', mode: 'async', position: 'head' },
|
|
586
|
-
],
|
|
576
|
+
js: [{ url: 'https://cdn.example.com/lib.js', mode: 'async', position: 'head' }],
|
|
587
577
|
},
|
|
588
578
|
});
|
|
589
579
|
await service.load();
|
|
590
580
|
|
|
591
581
|
const result = service.getLibraries();
|
|
592
|
-
expect(result.js).toEqual([
|
|
593
|
-
{ url: 'https://cdn.example.com/lib.js', mode: 'async', position: 'head' },
|
|
594
|
-
]);
|
|
582
|
+
expect(result.js).toEqual([{ url: 'https://cdn.example.com/lib.js', mode: 'async', position: 'head' }]);
|
|
595
583
|
});
|
|
596
584
|
|
|
597
585
|
test('should pass through object format for CSS libraries', async () => {
|
|
598
586
|
writeConfig({
|
|
599
587
|
libraries: {
|
|
600
|
-
css: [
|
|
601
|
-
{ url: 'https://fonts.googleapis.com/style.css', media: 'screen' },
|
|
602
|
-
],
|
|
588
|
+
css: [{ url: 'https://fonts.googleapis.com/style.css', media: 'screen' }],
|
|
603
589
|
},
|
|
604
590
|
});
|
|
605
591
|
await service.load();
|
|
606
592
|
|
|
607
593
|
const result = service.getLibraries();
|
|
608
|
-
expect(result.css).toEqual([
|
|
609
|
-
{ url: 'https://fonts.googleapis.com/style.css', media: 'screen' },
|
|
610
|
-
]);
|
|
594
|
+
expect(result.css).toEqual([{ url: 'https://fonts.googleapis.com/style.css', media: 'screen' }]);
|
|
611
595
|
});
|
|
612
596
|
|
|
613
597
|
test('should handle mixed string and object formats', async () => {
|
|
614
598
|
writeConfig({
|
|
615
599
|
libraries: {
|
|
616
|
-
js: [
|
|
617
|
-
|
|
618
|
-
{ url: 'https://cdn.example.com/complex.js', mode: 'defer' },
|
|
619
|
-
],
|
|
620
|
-
css: [
|
|
621
|
-
'https://cdn.example.com/simple.css',
|
|
622
|
-
{ url: 'https://cdn.example.com/complex.css', media: 'print' },
|
|
623
|
-
],
|
|
600
|
+
js: ['https://cdn.example.com/simple.js', { url: 'https://cdn.example.com/complex.js', mode: 'defer' }],
|
|
601
|
+
css: ['https://cdn.example.com/simple.css', { url: 'https://cdn.example.com/complex.css', media: 'print' }],
|
|
624
602
|
},
|
|
625
603
|
});
|
|
626
604
|
await service.load();
|
|
@@ -807,8 +785,8 @@ describe('ConfigService', () => {
|
|
|
807
785
|
|
|
808
786
|
// Verify all getters work correctly together
|
|
809
787
|
const breakpoints = service.getBreakpoints();
|
|
810
|
-
expect(breakpoints.tablet
|
|
811
|
-
expect(breakpoints.mobile
|
|
788
|
+
expect(breakpoints.tablet!.breakpoint).toBe(1024);
|
|
789
|
+
expect(breakpoints.mobile!.previewPoint).toBe(375);
|
|
812
790
|
|
|
813
791
|
const scales = service.getResponsiveScales();
|
|
814
792
|
expect(scales.enabled).toBe(true);
|
|
@@ -827,7 +805,6 @@ describe('ConfigService', () => {
|
|
|
827
805
|
|
|
828
806
|
const csp = service.getCSP();
|
|
829
807
|
expect(csp.scriptSrc).toEqual(['https://cdn.example.com']);
|
|
830
|
-
|
|
831
808
|
});
|
|
832
809
|
|
|
833
810
|
test('should handle empty config object', async () => {
|
|
@@ -19,6 +19,7 @@ import { DEFAULT_REM_CONFIG } from '../../shared/pxToRem';
|
|
|
19
19
|
import { DEFAULT_I18N_CONFIG, migrateI18nConfig } from '../../shared/i18n';
|
|
20
20
|
import { projectPaths } from '../projectContext';
|
|
21
21
|
import { readTextFile, fileExists } from '../runtime';
|
|
22
|
+
import { logRuntimeError } from '../../shared/errorLogger';
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Icons configuration
|
|
@@ -36,6 +37,17 @@ export interface SocialConfig {
|
|
|
36
37
|
twitterHandle?: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Sanity CMS integration config (read-only, public datasets).
|
|
42
|
+
* Connection info only — the runtime builds GROQ query URLs from this. No token (public).
|
|
43
|
+
*/
|
|
44
|
+
export interface SanityConfig {
|
|
45
|
+
projectId: string;
|
|
46
|
+
dataset: string;
|
|
47
|
+
apiVersion?: string;
|
|
48
|
+
useCdn?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
/**
|
|
40
52
|
* Raw project config structure from project.config.json
|
|
41
53
|
*/
|
|
@@ -47,6 +59,7 @@ interface RawProjectConfig {
|
|
|
47
59
|
i18n?: unknown;
|
|
48
60
|
icons?: IconsConfig;
|
|
49
61
|
social?: SocialConfig;
|
|
62
|
+
integrations?: { sanity?: SanityConfig };
|
|
50
63
|
libraries?: LibrariesConfig;
|
|
51
64
|
csp?: CSPConfig;
|
|
52
65
|
baseComponent?: string;
|
|
@@ -78,8 +91,9 @@ export class ConfigService {
|
|
|
78
91
|
const content = await readTextFile(projectPaths.config());
|
|
79
92
|
this.config = JSON.parse(content);
|
|
80
93
|
}
|
|
81
|
-
} catch {
|
|
94
|
+
} catch (error) {
|
|
82
95
|
// Fall through to defaults
|
|
96
|
+
logRuntimeError('configService.load', error, { filePath: projectPaths.config() });
|
|
83
97
|
this.config = null;
|
|
84
98
|
}
|
|
85
99
|
|
|
@@ -123,9 +137,8 @@ export class ConfigService {
|
|
|
123
137
|
if (typeof entry.breakpoint === 'number' && entry.breakpoint > 0) {
|
|
124
138
|
validInput[key] = {
|
|
125
139
|
breakpoint: entry.breakpoint,
|
|
126
|
-
previewPoint:
|
|
127
|
-
? entry.previewPoint
|
|
128
|
-
: entry.breakpoint,
|
|
140
|
+
previewPoint:
|
|
141
|
+
typeof entry.previewPoint === 'number' && entry.previewPoint > 0 ? entry.previewPoint : entry.breakpoint,
|
|
129
142
|
};
|
|
130
143
|
}
|
|
131
144
|
}
|
|
@@ -157,7 +170,7 @@ export class ConfigService {
|
|
|
157
170
|
*/
|
|
158
171
|
private mergeScaleCategory(
|
|
159
172
|
userScales: BreakpointScales | undefined,
|
|
160
|
-
defaultScales: BreakpointScales | undefined
|
|
173
|
+
defaultScales: BreakpointScales | undefined,
|
|
161
174
|
): BreakpointScales | undefined {
|
|
162
175
|
if (!userScales && !defaultScales) return undefined;
|
|
163
176
|
if (!userScales) return defaultScales ? { ...defaultScales } : undefined;
|
|
@@ -186,34 +199,30 @@ export class ConfigService {
|
|
|
186
199
|
enabled: userScales.enabled ?? DEFAULT_RESPONSIVE_SCALES.enabled,
|
|
187
200
|
mode: (userScales as { mode?: 'breakpoints' | 'fluid' }).mode ?? DEFAULT_RESPONSIVE_SCALES.mode,
|
|
188
201
|
baseReference: userScales.baseReference ?? DEFAULT_RESPONSIVE_SCALES.baseReference,
|
|
189
|
-
fluidRange:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
202
|
+
fluidRange:
|
|
203
|
+
(userScales as { fluidRange?: { min: number; max: number } }).fluidRange ??
|
|
204
|
+
(DEFAULT_RESPONSIVE_SCALES.fluidRange ? { ...DEFAULT_RESPONSIVE_SCALES.fluidRange } : undefined),
|
|
205
|
+
siteMargin:
|
|
206
|
+
(userScales as { siteMargin?: { min: number; max: number } }).siteMargin ??
|
|
207
|
+
(DEFAULT_RESPONSIVE_SCALES.siteMargin ? { ...DEFAULT_RESPONSIVE_SCALES.siteMargin } : undefined),
|
|
193
208
|
fontSize: this.mergeScaleCategory(
|
|
194
209
|
userScales.fontSize as BreakpointScales | undefined,
|
|
195
|
-
DEFAULT_RESPONSIVE_SCALES.fontSize
|
|
210
|
+
DEFAULT_RESPONSIVE_SCALES.fontSize,
|
|
196
211
|
),
|
|
197
212
|
padding: this.mergeScaleCategory(
|
|
198
213
|
userScales.padding as BreakpointScales | undefined,
|
|
199
|
-
DEFAULT_RESPONSIVE_SCALES.padding
|
|
214
|
+
DEFAULT_RESPONSIVE_SCALES.padding,
|
|
200
215
|
),
|
|
201
216
|
margin: this.mergeScaleCategory(
|
|
202
217
|
userScales.margin as BreakpointScales | undefined,
|
|
203
|
-
DEFAULT_RESPONSIVE_SCALES.margin
|
|
204
|
-
),
|
|
205
|
-
gap: this.mergeScaleCategory(
|
|
206
|
-
userScales.gap as BreakpointScales | undefined,
|
|
207
|
-
DEFAULT_RESPONSIVE_SCALES.gap
|
|
218
|
+
DEFAULT_RESPONSIVE_SCALES.margin,
|
|
208
219
|
),
|
|
220
|
+
gap: this.mergeScaleCategory(userScales.gap as BreakpointScales | undefined, DEFAULT_RESPONSIVE_SCALES.gap),
|
|
209
221
|
borderRadius: this.mergeScaleCategory(
|
|
210
222
|
userScales.borderRadius as BreakpointScales | undefined,
|
|
211
|
-
DEFAULT_RESPONSIVE_SCALES.borderRadius
|
|
212
|
-
),
|
|
213
|
-
size: this.mergeScaleCategory(
|
|
214
|
-
userScales.size as BreakpointScales | undefined,
|
|
215
|
-
DEFAULT_RESPONSIVE_SCALES.size
|
|
223
|
+
DEFAULT_RESPONSIVE_SCALES.borderRadius,
|
|
216
224
|
),
|
|
225
|
+
size: this.mergeScaleCategory(userScales.size as BreakpointScales | undefined, DEFAULT_RESPONSIVE_SCALES.size),
|
|
217
226
|
};
|
|
218
227
|
}
|
|
219
228
|
|
|
@@ -254,6 +263,24 @@ export class ConfigService {
|
|
|
254
263
|
return this.config.social;
|
|
255
264
|
}
|
|
256
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Get Sanity CMS integration config (read-only, public datasets).
|
|
268
|
+
* Returns undefined when not configured or missing required projectId/dataset.
|
|
269
|
+
*/
|
|
270
|
+
getSanity(): SanityConfig | undefined {
|
|
271
|
+
const sanity = this.config?.integrations?.sanity;
|
|
272
|
+
if (!sanity || typeof sanity !== 'object') {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
if (typeof sanity.projectId !== 'string' || typeof sanity.dataset !== 'string') {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
if (sanity.projectId.trim() === '' || sanity.dataset.trim() === '') {
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
return sanity;
|
|
282
|
+
}
|
|
283
|
+
|
|
257
284
|
/**
|
|
258
285
|
* Get libraries configuration
|
|
259
286
|
* Returns empty arrays if not configured
|
|
@@ -268,16 +295,12 @@ export class ConfigService {
|
|
|
268
295
|
|
|
269
296
|
// Normalize JS libraries: support both string URLs and object format
|
|
270
297
|
const normalizedJs = Array.isArray(libs.js)
|
|
271
|
-
? libs.js.map((lib) =>
|
|
272
|
-
typeof lib === 'string' ? { url: lib } : lib
|
|
273
|
-
) as JSLibraryConfig[]
|
|
298
|
+
? (libs.js.map((lib) => (typeof lib === 'string' ? { url: lib } : lib)) as JSLibraryConfig[])
|
|
274
299
|
: [];
|
|
275
300
|
|
|
276
301
|
// Normalize CSS libraries: support both string URLs and object format
|
|
277
302
|
const normalizedCss = Array.isArray(libs.css)
|
|
278
|
-
? libs.css.map((lib) =>
|
|
279
|
-
typeof lib === 'string' ? { url: lib } : lib
|
|
280
|
-
) as CSSLibraryConfig[]
|
|
303
|
+
? (libs.css.map((lib) => (typeof lib === 'string' ? { url: lib } : lib)) as CSSLibraryConfig[])
|
|
281
304
|
: [];
|
|
282
305
|
|
|
283
306
|
return {
|