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
package/lib/shared/errors.ts
CHANGED
|
@@ -31,7 +31,7 @@ export function createError(
|
|
|
31
31
|
code: string,
|
|
32
32
|
message: string,
|
|
33
33
|
context?: Record<string, unknown>,
|
|
34
|
-
cause?: Error
|
|
34
|
+
cause?: Error,
|
|
35
35
|
): AppError {
|
|
36
36
|
return { category, code, message, context, cause };
|
|
37
37
|
}
|
|
@@ -40,11 +40,5 @@ export function createError(
|
|
|
40
40
|
* Type guard for AppError
|
|
41
41
|
*/
|
|
42
42
|
export function isAppError(error: unknown): error is AppError {
|
|
43
|
-
return
|
|
44
|
-
typeof error === 'object' &&
|
|
45
|
-
error !== null &&
|
|
46
|
-
'category' in error &&
|
|
47
|
-
'code' in error &&
|
|
48
|
-
'message' in error
|
|
49
|
-
);
|
|
43
|
+
return typeof error === 'object' && error !== null && 'category' in error && 'code' in error && 'message' in error;
|
|
50
44
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression Evaluator Tests
|
|
3
|
+
*
|
|
4
|
+
* The headline concern here is the SSOT (single-source-of-truth) invariant between
|
|
5
|
+
* `evaluateNode`'s switch and `SUPPORTED_NODE_TYPES`. `isSupportedTemplateExpression`
|
|
6
|
+
* (used by the .astro codec to decide modelable {{binding}} vs verbatim _code) trusts
|
|
7
|
+
* `SUPPORTED_NODE_TYPES` to mirror exactly the node types `evaluateNode` can actually
|
|
8
|
+
* evaluate. If they drift, the failure is silent — either evaluatable code gets emitted
|
|
9
|
+
* as verbatim _code, or un-evaluatable code gets coerced into a fake {{binding}} that
|
|
10
|
+
* shows up literally in SSR output. These tests force a contributor who touches one side
|
|
11
|
+
* to touch the other.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, test, expect } from 'bun:test';
|
|
15
|
+
import { safeEvaluate, isSupportedTemplateExpression, SUPPORTED_NODE_TYPES } from './expressionEvaluator';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The complete set of jsep node types `evaluateNode` has a `case` for AND returns a
|
|
19
|
+
* value from (i.e. does NOT throw). This list is intentionally hardcoded: adding a new
|
|
20
|
+
* `case` to evaluateNode without adding it here (or vice-versa) must fail the exact-set
|
|
21
|
+
* assertion below, forcing the SSOT to be kept in lockstep.
|
|
22
|
+
*
|
|
23
|
+
* Note: `LogicalExpression` is handled defensively in the same switch case as
|
|
24
|
+
* `BinaryExpression`, but jsep classifies `&&`/`||` as `BinaryExpression` by default, so
|
|
25
|
+
* it is never produced from a parsed string — hence it has no representative expression.
|
|
26
|
+
*/
|
|
27
|
+
const EXPECTED_SUPPORTED_NODE_TYPES = [
|
|
28
|
+
'Identifier',
|
|
29
|
+
'Literal',
|
|
30
|
+
'MemberExpression',
|
|
31
|
+
'BinaryExpression',
|
|
32
|
+
'LogicalExpression',
|
|
33
|
+
'ConditionalExpression',
|
|
34
|
+
'UnaryExpression',
|
|
35
|
+
'ArrayExpression',
|
|
36
|
+
].sort();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A representative, parseable expression for every supported node type jsep can actually
|
|
40
|
+
* emit, paired with the value `evaluateNode` should return for it given `CONTEXT`. Each
|
|
41
|
+
* entry double-proves the SSOT: the expression both (a) is reported supported by
|
|
42
|
+
* `isSupportedTemplateExpression`, and (b) is actually evaluated (not thrown) by
|
|
43
|
+
* `safeEvaluate` — so a type listed in SUPPORTED_NODE_TYPES but missing from the switch
|
|
44
|
+
* would surface as a wrong/undefined evaluation result here.
|
|
45
|
+
*/
|
|
46
|
+
const CONTEXT = { a: 2, b: 3, flag: true, name: 'Meno', obj: { x: 7 }, key: 'x' };
|
|
47
|
+
const REPRESENTATIVE: Array<{ nodeType: string; expr: string; expected: unknown }> = [
|
|
48
|
+
{ nodeType: 'Identifier', expr: 'name', expected: 'Meno' },
|
|
49
|
+
{ nodeType: 'Literal', expr: '42', expected: 42 },
|
|
50
|
+
{ nodeType: 'MemberExpression', expr: 'obj.x', expected: 7 },
|
|
51
|
+
{ nodeType: 'MemberExpression(computed)', expr: 'obj[key]', expected: 7 },
|
|
52
|
+
{ nodeType: 'BinaryExpression', expr: 'a + b', expected: 5 },
|
|
53
|
+
{ nodeType: 'BinaryExpression(&&)', expr: 'flag && name', expected: 'Meno' },
|
|
54
|
+
{ nodeType: 'BinaryExpression(||)', expr: 'a || b', expected: 2 },
|
|
55
|
+
{ nodeType: 'ConditionalExpression', expr: 'flag ? a : b', expected: 2 },
|
|
56
|
+
{ nodeType: 'UnaryExpression', expr: '!flag', expected: false },
|
|
57
|
+
{ nodeType: 'ArrayExpression', expr: '[a, b]', expected: [2, 3] },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
/** Node types the evaluator deliberately refuses (its switch throws for these). */
|
|
61
|
+
const UNSUPPORTED_EXPRESSIONS: Array<{ nodeType: string; expr: string }> = [
|
|
62
|
+
{ nodeType: 'CallExpression', expr: 'obj.x.toFixed(2)' },
|
|
63
|
+
{ nodeType: 'Compound', expr: 'a, b' },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
describe('expressionEvaluator SSOT invariant', () => {
|
|
67
|
+
test('SUPPORTED_NODE_TYPES exactly equals the expected hardcoded set', () => {
|
|
68
|
+
// If this fails, evaluateNode's switch and SUPPORTED_NODE_TYPES have drifted —
|
|
69
|
+
// update the switch, SUPPORTED_NODE_TYPES, AND this test's expected list together.
|
|
70
|
+
expect([...SUPPORTED_NODE_TYPES].sort()).toEqual(EXPECTED_SUPPORTED_NODE_TYPES);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('every representative supported expression is reported supported AND evaluated', () => {
|
|
74
|
+
for (const { nodeType: _nodeType, expr, expected } of REPRESENTATIVE) {
|
|
75
|
+
// (a) the codec predicate must accept it
|
|
76
|
+
expect(isSupportedTemplateExpression(expr)).toBe(true);
|
|
77
|
+
// (b) the evaluator must actually handle it (not throw -> undefined fallback)
|
|
78
|
+
expect(safeEvaluate(expr, CONTEXT)).toEqual(expected as never);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('every representative node type (jsep-producible) is present in SUPPORTED_NODE_TYPES', () => {
|
|
83
|
+
// Maps the labelled representatives back to their base jsep node type and asserts
|
|
84
|
+
// membership — closes the "added a case to the switch but forgot the set" gap for
|
|
85
|
+
// any type that has been given a representative expression.
|
|
86
|
+
const producibleBaseTypes = new Set(REPRESENTATIVE.map((r) => r.nodeType.replace(/\(.*\)$/, '')));
|
|
87
|
+
for (const baseType of producibleBaseTypes) {
|
|
88
|
+
expect(SUPPORTED_NODE_TYPES.has(baseType)).toBe(true);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('isSupportedTemplateExpression', () => {
|
|
94
|
+
test('returns false for function/method calls (verbatim _code territory)', () => {
|
|
95
|
+
for (const { expr } of UNSUPPORTED_EXPRESSIONS) {
|
|
96
|
+
expect(isSupportedTemplateExpression(expr)).toBe(false);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('returns false for empty / whitespace / syntactically invalid expressions', () => {
|
|
101
|
+
expect(isSupportedTemplateExpression('')).toBe(false);
|
|
102
|
+
expect(isSupportedTemplateExpression(' ')).toBe(false);
|
|
103
|
+
expect(isSupportedTemplateExpression('a +')).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('rejects an expression nesting an unsupported call deep inside a supported tree', () => {
|
|
107
|
+
// The recursive walk must catch unsupported nodes that are not at the root.
|
|
108
|
+
expect(isSupportedTemplateExpression('a + obj.x.toFixed(2)')).toBe(false);
|
|
109
|
+
expect(isSupportedTemplateExpression('flag ? name : obj.x.toString()')).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('safeEvaluate', () => {
|
|
114
|
+
test('returns undefined (no throw) for unsupported expressions', () => {
|
|
115
|
+
for (const { expr } of UNSUPPORTED_EXPRESSIONS) {
|
|
116
|
+
expect(safeEvaluate(expr, CONTEXT)).toBeUndefined();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('optional chaining (?.) and nullish coalescing (??)', () => {
|
|
122
|
+
const CTX = { user: { email: 'a@b.com' }, missing: undefined as unknown, zero: 0, empty: '' };
|
|
123
|
+
|
|
124
|
+
test('?. short-circuits to undefined when the object is null/undefined (not a throw)', () => {
|
|
125
|
+
expect(safeEvaluate('user?.email', CTX)).toBe('a@b.com');
|
|
126
|
+
expect(safeEvaluate('missing?.email', CTX)).toBeUndefined();
|
|
127
|
+
expect(safeEvaluate('missing?.email?.length', CTX)).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('?? falls through only on null/undefined, not other falsy values', () => {
|
|
131
|
+
expect(safeEvaluate("missing ?? 'fallback'", CTX)).toBe('fallback');
|
|
132
|
+
expect(safeEvaluate("zero ?? 'fallback'", CTX)).toBe(0); // 0 is NOT nullish
|
|
133
|
+
expect(safeEvaluate("empty ?? 'fallback'", CTX)).toBe(''); // '' is NOT nullish
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("the real-world promoted shape `user?.name ?? ''` evaluates correctly (no longer a pretend binding)", () => {
|
|
137
|
+
expect(safeEvaluate("user?.email ?? 'none'", CTX)).toBe('a@b.com');
|
|
138
|
+
expect(safeEvaluate("missing?.email ?? 'none'", CTX)).toBe('none');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('both are reported supported (and now actually evaluable, closing the soundness gap)', () => {
|
|
142
|
+
expect(isSupportedTemplateExpression('user?.email')).toBe(true);
|
|
143
|
+
expect(isSupportedTemplateExpression("missing?.email ?? ''")).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Supports: ternary, binary, logical, unary operators, member access, arrays
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import jsep from 'jsep';
|
|
10
9
|
import type { Expression } from 'jsep';
|
|
10
|
+
import jsep from 'jsep';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Safely evaluates a jsep AST node against a context object
|
|
@@ -61,6 +61,15 @@ function evaluateNode(node: Expression, context: Record<string, unknown>): unkno
|
|
|
61
61
|
const left = evaluateNode(binaryNode.left, context);
|
|
62
62
|
return left ? left : evaluateNode(binaryNode.right, context);
|
|
63
63
|
}
|
|
64
|
+
// Nullish coalescing: only fall through on null/undefined (NOT other falsy values).
|
|
65
|
+
// jsep parses `??` as a BinaryExpression (already a supported node type); without this
|
|
66
|
+
// case the operator switch below would throw and `a ?? b` would silently evaluate to
|
|
67
|
+
// undefined. Combined with the MemberExpression null-guard above, `a?.b ?? c` evaluates
|
|
68
|
+
// correctly — so these are real editable bindings, not pretend ones.
|
|
69
|
+
if (binaryNode.operator === '??') {
|
|
70
|
+
const left = evaluateNode(binaryNode.left, context);
|
|
71
|
+
return left === null || left === undefined ? evaluateNode(binaryNode.right, context) : left;
|
|
72
|
+
}
|
|
64
73
|
|
|
65
74
|
// Evaluate both sides for non-logical operators
|
|
66
75
|
const left = evaluateNode(binaryNode.left, context);
|
|
@@ -73,18 +82,32 @@ function evaluateNode(node: Expression, context: Record<string, unknown>): unkno
|
|
|
73
82
|
return String(left ?? '') + String(right ?? '');
|
|
74
83
|
}
|
|
75
84
|
return (left as number) + (right as number);
|
|
76
|
-
case '-':
|
|
77
|
-
|
|
78
|
-
case '
|
|
79
|
-
|
|
80
|
-
case '
|
|
81
|
-
|
|
82
|
-
case '
|
|
83
|
-
|
|
84
|
-
case '
|
|
85
|
-
|
|
86
|
-
case '
|
|
87
|
-
|
|
85
|
+
case '-':
|
|
86
|
+
return (left as number) - (right as number);
|
|
87
|
+
case '*':
|
|
88
|
+
return (left as number) * (right as number);
|
|
89
|
+
case '/':
|
|
90
|
+
return (left as number) / (right as number);
|
|
91
|
+
case '%':
|
|
92
|
+
return (left as number) % (right as number);
|
|
93
|
+
case '===':
|
|
94
|
+
return left === right;
|
|
95
|
+
case '!==':
|
|
96
|
+
return left !== right;
|
|
97
|
+
case '==':
|
|
98
|
+
// biome-ignore lint/suspicious/noDoubleEquals: loose equality IS the semantics of the `==` operator in template expressions
|
|
99
|
+
return left == right;
|
|
100
|
+
case '!=':
|
|
101
|
+
// biome-ignore lint/suspicious/noDoubleEquals: loose inequality IS the semantics of the `!=` operator in template expressions
|
|
102
|
+
return left != right;
|
|
103
|
+
case '<':
|
|
104
|
+
return (left as number) < (right as number);
|
|
105
|
+
case '>':
|
|
106
|
+
return (left as number) > (right as number);
|
|
107
|
+
case '<=':
|
|
108
|
+
return (left as number) <= (right as number);
|
|
109
|
+
case '>=':
|
|
110
|
+
return (left as number) >= (right as number);
|
|
88
111
|
default:
|
|
89
112
|
throw new Error(`Unsupported operator: ${binaryNode.operator}`);
|
|
90
113
|
}
|
|
@@ -94,9 +117,7 @@ function evaluateNode(node: Expression, context: Record<string, unknown>): unkno
|
|
|
94
117
|
// Ternary: condition ? consequent : alternate
|
|
95
118
|
const condNode = node as jsep.ConditionalExpression;
|
|
96
119
|
const test = evaluateNode(condNode.test, context);
|
|
97
|
-
return test
|
|
98
|
-
? evaluateNode(condNode.consequent, context)
|
|
99
|
-
: evaluateNode(condNode.alternate, context);
|
|
120
|
+
return test ? evaluateNode(condNode.consequent, context) : evaluateNode(condNode.alternate, context);
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
case 'UnaryExpression': {
|
|
@@ -105,9 +126,12 @@ function evaluateNode(node: Expression, context: Record<string, unknown>): unkno
|
|
|
105
126
|
const arg = evaluateNode(unaryNode.argument, context);
|
|
106
127
|
|
|
107
128
|
switch (unaryNode.operator) {
|
|
108
|
-
case '!':
|
|
109
|
-
|
|
110
|
-
case '
|
|
129
|
+
case '!':
|
|
130
|
+
return !arg;
|
|
131
|
+
case '-':
|
|
132
|
+
return -(arg as number);
|
|
133
|
+
case '+':
|
|
134
|
+
return +(arg as number);
|
|
111
135
|
default:
|
|
112
136
|
throw new Error(`Unsupported unary operator: ${unaryNode.operator}`);
|
|
113
137
|
}
|
|
@@ -116,9 +140,7 @@ function evaluateNode(node: Expression, context: Record<string, unknown>): unkno
|
|
|
116
140
|
case 'ArrayExpression': {
|
|
117
141
|
// Array literals: [1, 2, 3]
|
|
118
142
|
const arrayNode = node as jsep.ArrayExpression;
|
|
119
|
-
return arrayNode.elements.map(el =>
|
|
120
|
-
el ? evaluateNode(el, context) : undefined
|
|
121
|
-
);
|
|
143
|
+
return arrayNode.elements.map((el) => (el ? evaluateNode(el, context) : undefined));
|
|
122
144
|
}
|
|
123
145
|
|
|
124
146
|
case 'CallExpression': {
|
|
@@ -160,8 +182,14 @@ export function isComplexExpression(expression: string): boolean {
|
|
|
160
182
|
return /[?:+\-*/%<>=!&|]/.test(expression);
|
|
161
183
|
}
|
|
162
184
|
|
|
163
|
-
/**
|
|
164
|
-
|
|
185
|
+
/**
|
|
186
|
+
* jsep node types `evaluateNode` (above) can evaluate — the rest throw at runtime.
|
|
187
|
+
*
|
|
188
|
+
* SSOT: this set MUST stay in lockstep with the `case` labels in `evaluateNode`'s
|
|
189
|
+
* switch. Drift is silent (lost code or literal `{{…}}` in SSR output), so the
|
|
190
|
+
* invariant is pinned by expressionEvaluator.test.ts — update both together.
|
|
191
|
+
*/
|
|
192
|
+
export const SUPPORTED_NODE_TYPES = new Set([
|
|
165
193
|
'Identifier',
|
|
166
194
|
'Literal',
|
|
167
195
|
'MemberExpression',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { describe, test, expect, afterAll } from 'bun:test';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
4
|
-
import { tmpdir } from 'os';
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
5
|
import { setProjectRoot, getProjectRoot } from '../server/projectContext';
|
|
6
6
|
|
|
7
7
|
// Save original project root to restore after tests
|
|
@@ -13,10 +13,23 @@ const testDir = join(tmpdir(), `fontLoader-test-${Date.now()}`);
|
|
|
13
13
|
mkdirSync(testDir, { recursive: true });
|
|
14
14
|
setProjectRoot(testDir);
|
|
15
15
|
|
|
16
|
+
// fontLoader keeps a module-level config cache that survives across test
|
|
17
|
+
// files in a single `bun test` process. Earlier files (e.g. anything pulling
|
|
18
|
+
// in the SSR htmlGenerator) may already have populated it with `{ fonts: [] }`,
|
|
19
|
+
// which would make loadProjectConfig() return that stale cache instead of
|
|
20
|
+
// reading our test config. Reset on both ends so this file neither inherits
|
|
21
|
+
// nor leaks cached config.
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
resetFontConfig();
|
|
24
|
+
});
|
|
25
|
+
|
|
16
26
|
// Restore original project root after all tests in this file
|
|
17
27
|
afterAll(() => {
|
|
18
28
|
setProjectRoot(originalProjectRoot);
|
|
19
|
-
|
|
29
|
+
resetFontConfig();
|
|
30
|
+
try {
|
|
31
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
32
|
+
} catch {}
|
|
20
33
|
});
|
|
21
34
|
|
|
22
35
|
// Helper to write a project.config.json in the test directory
|
|
@@ -24,7 +37,7 @@ function writeConfig(config: Record<string, unknown>): void {
|
|
|
24
37
|
writeFileSync(join(testDir, 'project.config.json'), JSON.stringify(config));
|
|
25
38
|
}
|
|
26
39
|
|
|
27
|
-
function
|
|
40
|
+
function _removeConfig(): void {
|
|
28
41
|
try {
|
|
29
42
|
rmSync(join(testDir, 'project.config.json'));
|
|
30
43
|
} catch {
|
|
@@ -99,6 +112,7 @@ import {
|
|
|
99
112
|
generateFontCSS,
|
|
100
113
|
generateFontPreloadTags,
|
|
101
114
|
getFontFamilies,
|
|
115
|
+
resetFontConfig,
|
|
102
116
|
} from './fontLoader';
|
|
103
117
|
|
|
104
118
|
describe('fontLoader', () => {
|
|
@@ -215,7 +229,7 @@ describe('fontLoader', () => {
|
|
|
215
229
|
test('generateFontCSS omits font-display when not specified', () => {
|
|
216
230
|
const css = generateFontCSS();
|
|
217
231
|
// The Roboto bold entry has no fontDisplay
|
|
218
|
-
const robotoBlock = css.split('\n\n').find(b => b.includes('roboto-bold.ttf'))!;
|
|
232
|
+
const robotoBlock = css.split('\n\n').find((b) => b.includes('roboto-bold.ttf'))!;
|
|
219
233
|
expect(robotoBlock).toBeDefined();
|
|
220
234
|
expect(robotoBlock).not.toContain('font-display');
|
|
221
235
|
});
|
|
@@ -277,7 +291,7 @@ describe('fontLoader', () => {
|
|
|
277
291
|
|
|
278
292
|
test('generateFontPreloadTags defaults to font/ttf for unknown extension', () => {
|
|
279
293
|
const tags = generateFontPreloadTags();
|
|
280
|
-
const mysteryTag = tags.split('\n ').find(t => t.includes('mystery.xyz'))!;
|
|
294
|
+
const mysteryTag = tags.split('\n ').find((t) => t.includes('mystery.xyz'))!;
|
|
281
295
|
expect(mysteryTag).toContain('type="font/ttf"');
|
|
282
296
|
});
|
|
283
297
|
|
|
@@ -297,7 +311,7 @@ describe('fontLoader', () => {
|
|
|
297
311
|
test('generateFontPreloadTags produces correct link tag format', () => {
|
|
298
312
|
const tags = generateFontPreloadTags();
|
|
299
313
|
expect(tags).toContain(
|
|
300
|
-
'<link rel="preload" href="/fonts/inter-variable.woff2" as="font" type="font/woff2" crossorigin>'
|
|
314
|
+
'<link rel="preload" href="/fonts/inter-variable.woff2" as="font" type="font/woff2" crossorigin>',
|
|
301
315
|
);
|
|
302
316
|
});
|
|
303
317
|
|
|
@@ -305,7 +319,7 @@ describe('fontLoader', () => {
|
|
|
305
319
|
|
|
306
320
|
test('getFontFamilies expands variable weight range in 100 increments', () => {
|
|
307
321
|
const families = getFontFamilies();
|
|
308
|
-
expect(families
|
|
322
|
+
expect(families.Inter).toEqual([100, 200, 300, 400, 500, 600, 700, 800, 900]);
|
|
309
323
|
});
|
|
310
324
|
|
|
311
325
|
test('getFontFamilies extracts family name from path', () => {
|
|
@@ -316,24 +330,24 @@ describe('fontLoader', () => {
|
|
|
316
330
|
test('getFontFamilies groups weights and deduplicates across static and variable', () => {
|
|
317
331
|
const families = getFontFamilies();
|
|
318
332
|
// Roboto: 700 (bold static), 400 (regular static), 300-500 variable (400 deduped)
|
|
319
|
-
expect(families
|
|
320
|
-
expect(families
|
|
321
|
-
expect(families
|
|
322
|
-
expect(families
|
|
333
|
+
expect(families.Roboto).toContain(700);
|
|
334
|
+
expect(families.Roboto).toContain(400);
|
|
335
|
+
expect(families.Roboto).toContain(300);
|
|
336
|
+
expect(families.Roboto).toContain(500);
|
|
323
337
|
// No duplicates
|
|
324
|
-
const unique = [...new Set(families
|
|
325
|
-
expect(families
|
|
338
|
+
const unique = [...new Set(families.Roboto)];
|
|
339
|
+
expect(families.Roboto!.length).toBe(unique.length);
|
|
326
340
|
});
|
|
327
341
|
|
|
328
342
|
test('getFontFamilies uses default weight 400 when not specified', () => {
|
|
329
343
|
const families = getFontFamilies();
|
|
330
|
-
expect(families
|
|
344
|
+
expect(families.Playfair).toEqual([400]);
|
|
331
345
|
});
|
|
332
346
|
|
|
333
347
|
test('getFontFamilies handles single-weight variable font', () => {
|
|
334
348
|
const families = getFontFamilies();
|
|
335
349
|
// Mono: weight=400, weightMax=400 -> [400]
|
|
336
|
-
expect(families
|
|
350
|
+
expect(families.Mono).toEqual([400]);
|
|
337
351
|
});
|
|
338
352
|
|
|
339
353
|
test('getFontFamilies handles extracted name for unknown extension', () => {
|
package/lib/shared/fontLoader.ts
CHANGED
|
@@ -54,8 +54,8 @@ export function getFontFamilies(): Record<string, number[]> {
|
|
|
54
54
|
const fonts = config.fonts || [];
|
|
55
55
|
const familiesMap: Record<string, number[]> = {};
|
|
56
56
|
|
|
57
|
-
fonts.forEach((font:
|
|
58
|
-
const fontPath: string = font.path || font.src;
|
|
57
|
+
fonts.forEach((font: { path?: string; src?: string; family?: string; weight?: number; weightMax?: number }) => {
|
|
58
|
+
const fontPath: string = font.path || font.src || '';
|
|
59
59
|
if (!fontPath) return;
|
|
60
60
|
const family = font.family || extractFamilyName(fontPath);
|
|
61
61
|
const weight = font.weight ?? 400;
|
|
@@ -83,7 +83,7 @@ const RULES: Rule[] = [
|
|
|
83
83
|
test: /Cannot find module|Failed to resolve module|Failed to load module|Error loading dynamically imported module/i,
|
|
84
84
|
build: () => ({
|
|
85
85
|
title: "A required file couldn't be loaded",
|
|
86
|
-
friendlyMessage:
|
|
86
|
+
friendlyMessage: "An import or module reference couldn't be found.",
|
|
87
87
|
hint: 'Check the import path.',
|
|
88
88
|
}),
|
|
89
89
|
},
|
|
@@ -114,8 +114,7 @@ export function toFriendlyError(input: unknown): FriendlyError {
|
|
|
114
114
|
|
|
115
115
|
return {
|
|
116
116
|
title: 'This section ran into a problem',
|
|
117
|
-
friendlyMessage:
|
|
118
|
-
'Something went wrong while rendering this part of the page. The rest of the page is fine.',
|
|
117
|
+
friendlyMessage: 'Something went wrong while rendering this part of the page. The rest of the page is fine.',
|
|
119
118
|
raw,
|
|
120
119
|
};
|
|
121
120
|
}
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
import {
|
|
3
|
-
isGradientValue,
|
|
4
|
-
parseGradient,
|
|
5
|
-
buildGradientCss,
|
|
6
|
-
} from './gradientUtils';
|
|
2
|
+
import { isGradientValue, parseGradient, buildGradientCss } from './gradientUtils';
|
|
7
3
|
|
|
8
4
|
describe('isGradientValue', () => {
|
|
9
5
|
test('identifies gradient values', () => {
|
|
@@ -51,8 +47,8 @@ describe('parseGradient', () => {
|
|
|
51
47
|
const result = parseGradient('linear-gradient(180deg, red, blue)');
|
|
52
48
|
expect(result).not.toBeNull();
|
|
53
49
|
expect(result!.stops.length).toBe(2);
|
|
54
|
-
expect(result!.stops[0]
|
|
55
|
-
expect(result!.stops[1]
|
|
50
|
+
expect(result!.stops[0]!.position).toBe(0);
|
|
51
|
+
expect(result!.stops[1]!.position).toBe(100);
|
|
56
52
|
});
|
|
57
53
|
|
|
58
54
|
test('parses radial gradient with circle', () => {
|
|
@@ -22,9 +22,7 @@ const CONIC_RE = /^conic-gradient\((.+)\)$/i;
|
|
|
22
22
|
export function isGradientValue(value: string): boolean {
|
|
23
23
|
if (!value) return false;
|
|
24
24
|
const v = value.trim().toLowerCase();
|
|
25
|
-
return v.startsWith('linear-gradient(') ||
|
|
26
|
-
v.startsWith('radial-gradient(') ||
|
|
27
|
-
v.startsWith('conic-gradient(');
|
|
25
|
+
return v.startsWith('linear-gradient(') || v.startsWith('radial-gradient(') || v.startsWith('conic-gradient(');
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
/** Parse a CSS gradient string into a GradientConfig */
|
|
@@ -35,17 +33,24 @@ export function parseGradient(cssValue: string): GradientConfig | null {
|
|
|
35
33
|
let match: RegExpMatchArray | null;
|
|
36
34
|
let type: GradientConfig['type'];
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
const linearMatch = v.match(LINEAR_RE);
|
|
37
|
+
const radialMatch = linearMatch ? null : v.match(RADIAL_RE);
|
|
38
|
+
const conicMatch = linearMatch || radialMatch ? null : v.match(CONIC_RE);
|
|
39
|
+
|
|
40
|
+
if (linearMatch) {
|
|
41
|
+
match = linearMatch;
|
|
39
42
|
type = 'linear';
|
|
40
|
-
} else if (
|
|
43
|
+
} else if (radialMatch) {
|
|
44
|
+
match = radialMatch;
|
|
41
45
|
type = 'radial';
|
|
42
|
-
} else if (
|
|
46
|
+
} else if (conicMatch) {
|
|
47
|
+
match = conicMatch;
|
|
43
48
|
type = 'conic';
|
|
44
49
|
} else {
|
|
45
50
|
return null;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
const inner = match[1];
|
|
53
|
+
const inner = match[1] ?? '';
|
|
49
54
|
const parts = splitGradientArgs(inner);
|
|
50
55
|
|
|
51
56
|
let angle = type === 'linear' ? 180 : 0;
|
|
@@ -80,7 +85,7 @@ export function parseGradient(cssValue: string): GradientConfig | null {
|
|
|
80
85
|
if (first) {
|
|
81
86
|
const fromAngle = first.match(/^from\s+(\d+(?:\.\d+)?)deg/i);
|
|
82
87
|
if (fromAngle) {
|
|
83
|
-
angle = parseFloat(fromAngle[1]);
|
|
88
|
+
angle = parseFloat(fromAngle[1] ?? '');
|
|
84
89
|
stopsStartIndex = 1;
|
|
85
90
|
}
|
|
86
91
|
}
|
|
@@ -94,9 +99,7 @@ export function parseGradient(cssValue: string): GradientConfig | null {
|
|
|
94
99
|
|
|
95
100
|
/** Build a CSS gradient string from a GradientConfig */
|
|
96
101
|
export function buildGradientCss(config: GradientConfig): string {
|
|
97
|
-
const stopStrings = config.stops.map(
|
|
98
|
-
s => `${s.color} ${Math.round(s.position)}%`
|
|
99
|
-
);
|
|
102
|
+
const stopStrings = config.stops.map((s) => `${s.color} ${Math.round(s.position)}%`);
|
|
100
103
|
|
|
101
104
|
switch (config.type) {
|
|
102
105
|
case 'linear': {
|
|
@@ -137,7 +140,7 @@ function splitGradientArgs(inner: string): string[] {
|
|
|
137
140
|
|
|
138
141
|
function parseAngle(part: string): number | null {
|
|
139
142
|
const m = part.match(/^(-?\d+(?:\.\d+)?)deg$/i);
|
|
140
|
-
return m ? parseFloat(m[1]) : null;
|
|
143
|
+
return m ? parseFloat(m[1] ?? '') : null;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
function directionToAngle(direction: string): number {
|
|
@@ -160,7 +163,7 @@ function parseStops(parts: string[]): GradientStop[] {
|
|
|
160
163
|
const total = parts.length;
|
|
161
164
|
|
|
162
165
|
for (let i = 0; i < total; i++) {
|
|
163
|
-
const part = parts[i]
|
|
166
|
+
const part = parts[i]?.trim();
|
|
164
167
|
if (!part) continue;
|
|
165
168
|
|
|
166
169
|
// Match color and optional position percentage
|
|
@@ -170,7 +173,7 @@ function parseStops(parts: string[]): GradientStop[] {
|
|
|
170
173
|
|
|
171
174
|
if (posMatch) {
|
|
172
175
|
color = part.slice(0, posMatch.index!).trim();
|
|
173
|
-
position = parseFloat(posMatch[1]);
|
|
176
|
+
position = parseFloat(posMatch[1] ?? '');
|
|
174
177
|
} else {
|
|
175
178
|
color = part;
|
|
176
179
|
// Auto-distribute position
|
|
@@ -56,8 +56,8 @@ describe('rewriteHrefRefs', () => {
|
|
|
56
56
|
],
|
|
57
57
|
};
|
|
58
58
|
expect(rewriteHrefRefs(tree, '/about', '/about-us')).toBe(true);
|
|
59
|
-
expect(tree.children[0]
|
|
60
|
-
expect((tree.children[1]
|
|
59
|
+
expect(tree.children[0]!.href).toBe('/about-us');
|
|
60
|
+
expect((tree.children[1]!.children![0] as any).href).toBe('/about-us/team');
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
test('walks prop trees (component nodes with object props)', () => {
|
package/lib/shared/hrefRefs.ts
CHANGED
|
@@ -16,11 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
function rewriteHref(value: string, oldHref: string, newHref: string): string | null {
|
|
18
18
|
if (value === oldHref) return newHref;
|
|
19
|
-
if (
|
|
20
|
-
value.startsWith(oldHref + '/') ||
|
|
21
|
-
value.startsWith(oldHref + '?') ||
|
|
22
|
-
value.startsWith(oldHref + '#')
|
|
23
|
-
) {
|
|
19
|
+
if (value.startsWith(`${oldHref}/`) || value.startsWith(`${oldHref}?`) || value.startsWith(`${oldHref}#`)) {
|
|
24
20
|
return newHref + value.substring(oldHref.length);
|
|
25
21
|
}
|
|
26
22
|
return null;
|
|
@@ -34,11 +30,7 @@ function rewriteHref(value: string, oldHref: string, newHref: string): string |
|
|
|
34
30
|
* Walks `children` recursively (array or single object) and any object-valued
|
|
35
31
|
* field on the node (covers component-prop-trees, nested structure, etc.).
|
|
36
32
|
*/
|
|
37
|
-
export function rewriteHrefRefs(
|
|
38
|
-
node: unknown,
|
|
39
|
-
oldHref: string,
|
|
40
|
-
newHref: string,
|
|
41
|
-
): boolean {
|
|
33
|
+
export function rewriteHrefRefs(node: unknown, oldHref: string, newHref: string): boolean {
|
|
42
34
|
if (!node || typeof node !== 'object') return false;
|
|
43
35
|
if (!oldHref || oldHref === newHref) return false;
|
|
44
36
|
|
package/lib/shared/i18n.test.ts
CHANGED
|
@@ -32,7 +32,7 @@ describe('i18n', () => {
|
|
|
32
32
|
|
|
33
33
|
test('should have English locale configured', () => {
|
|
34
34
|
expect(DEFAULT_I18N_CONFIG.locales).toHaveLength(1);
|
|
35
|
-
expect(DEFAULT_I18N_CONFIG.locales[0]
|
|
35
|
+
expect(DEFAULT_I18N_CONFIG.locales[0]!.code).toBe('en');
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
|
|
@@ -83,8 +83,8 @@ describe('i18n', () => {
|
|
|
83
83
|
const migrated = migrateI18nConfig(oldConfig);
|
|
84
84
|
expect(migrated.defaultLocale).toBe('en');
|
|
85
85
|
expect(migrated.locales).toHaveLength(3);
|
|
86
|
-
expect(migrated.locales[0]
|
|
87
|
-
expect(migrated.locales[1]
|
|
86
|
+
expect(migrated.locales[0]!.code).toBe('en');
|
|
87
|
+
expect(migrated.locales[1]!.code).toBe('pl');
|
|
88
88
|
});
|
|
89
89
|
|
|
90
90
|
test('should preserve new format config', () => {
|
|
@@ -199,7 +199,7 @@ describe('i18n', () => {
|
|
|
199
199
|
test('should return empty array if no translation available for list i18n', () => {
|
|
200
200
|
const listValue = {
|
|
201
201
|
_i18n: true,
|
|
202
|
-
en: [],
|
|
202
|
+
en: [], // Add empty array to make it a list i18n
|
|
203
203
|
} as I18nValue;
|
|
204
204
|
// Remove the empty array to test empty fallback detection
|
|
205
205
|
delete (listValue as any).en;
|