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,8 +1,9 @@
|
|
|
1
|
-
import { test, expect, describe, beforeEach, afterEach, mock } from
|
|
2
|
-
import { ErrorBoundary, withErrorBoundary } from
|
|
3
|
-
import React
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { test, expect, describe, beforeEach, afterEach, mock } from 'bun:test';
|
|
2
|
+
import { ErrorBoundary, withErrorBoundary } from './ErrorBoundary';
|
|
3
|
+
import type React from 'react';
|
|
4
|
+
import { createElement as h, Component } from 'react';
|
|
5
|
+
import { createRoot } from 'react-dom/client';
|
|
6
|
+
import { wait } from '../test-utils/helpers/asyncHelpers';
|
|
6
7
|
|
|
7
8
|
// Mock console.error to avoid noise in test output
|
|
8
9
|
const originalConsoleError = console.error;
|
|
@@ -19,7 +20,7 @@ afterEach(() => {
|
|
|
19
20
|
|
|
20
21
|
// Helper to create test container
|
|
21
22
|
function createTestContainer() {
|
|
22
|
-
const div = document.createElement(
|
|
23
|
+
const div = document.createElement('div');
|
|
23
24
|
document.body.appendChild(div);
|
|
24
25
|
return div;
|
|
25
26
|
}
|
|
@@ -27,7 +28,7 @@ function createTestContainer() {
|
|
|
27
28
|
function cleanupContainer(container: HTMLElement) {
|
|
28
29
|
try {
|
|
29
30
|
document.body.removeChild(container);
|
|
30
|
-
} catch (
|
|
31
|
+
} catch (_e) {
|
|
31
32
|
// Already removed
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -38,92 +39,101 @@ function ThrowingComponent({ error }: { error: Error }): React.ReactNode {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
// Component that throws conditionally
|
|
41
|
-
class
|
|
42
|
+
class _ConditionallyThrowingComponent extends Component<{ shouldThrow: boolean; error: Error }> {
|
|
42
43
|
render() {
|
|
43
44
|
if (this.props.shouldThrow) {
|
|
44
45
|
throw this.props.error;
|
|
45
46
|
}
|
|
46
|
-
return h(
|
|
47
|
+
return h('div', null, 'Safe content');
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
describe(
|
|
51
|
-
describe(
|
|
52
|
-
test(
|
|
51
|
+
describe('ErrorBoundary - Error catching', () => {
|
|
52
|
+
describe('Component render errors', () => {
|
|
53
|
+
test('should catch errors thrown during render and display fallback UI', async () => {
|
|
53
54
|
const container = createTestContainer();
|
|
54
55
|
const root = createRoot(container);
|
|
55
56
|
|
|
56
57
|
try {
|
|
57
|
-
const testError = new Error(
|
|
58
|
-
const errorBoundary = h(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
const testError = new Error('Test render error');
|
|
59
|
+
const errorBoundary = h(
|
|
60
|
+
ErrorBoundary,
|
|
61
|
+
{
|
|
62
|
+
level: 'component',
|
|
63
|
+
componentName: 'TestComponent',
|
|
64
|
+
},
|
|
65
|
+
h(ThrowingComponent, { error: testError }),
|
|
66
|
+
);
|
|
63
67
|
|
|
64
68
|
root.render(errorBoundary);
|
|
65
69
|
await wait(10); // Wait for React to process error
|
|
66
70
|
|
|
67
71
|
// Verify friendly fallback UI is displayed with the component name
|
|
68
|
-
expect(container.textContent).toContain(
|
|
69
|
-
expect(container.textContent).toContain(
|
|
72
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
73
|
+
expect(container.textContent).toContain('TestComponent');
|
|
70
74
|
} finally {
|
|
71
75
|
root.unmount();
|
|
72
76
|
cleanupContainer(container);
|
|
73
77
|
}
|
|
74
78
|
});
|
|
75
79
|
|
|
76
|
-
test(
|
|
80
|
+
test('should render children when no error occurs', async () => {
|
|
77
81
|
const container = createTestContainer();
|
|
78
82
|
const root = createRoot(container);
|
|
79
83
|
|
|
80
84
|
try {
|
|
81
|
-
const errorBoundary = h(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
const errorBoundary = h(
|
|
86
|
+
ErrorBoundary,
|
|
87
|
+
{
|
|
88
|
+
level: 'component',
|
|
89
|
+
componentName: 'TestComponent',
|
|
90
|
+
},
|
|
91
|
+
h('div', null, 'Safe content'),
|
|
92
|
+
);
|
|
86
93
|
|
|
87
94
|
root.render(errorBoundary);
|
|
88
95
|
await wait(10);
|
|
89
96
|
|
|
90
97
|
// Should render the safe content
|
|
91
|
-
expect(container.textContent).toContain(
|
|
98
|
+
expect(container.textContent).toContain('Safe content');
|
|
92
99
|
// Should NOT show error UI
|
|
93
|
-
expect(container.textContent).not.toContain(
|
|
100
|
+
expect(container.textContent).not.toContain('This section ran into a problem');
|
|
94
101
|
} finally {
|
|
95
102
|
root.unmount();
|
|
96
103
|
cleanupContainer(container);
|
|
97
104
|
}
|
|
98
105
|
});
|
|
99
106
|
|
|
100
|
-
test(
|
|
101
|
-
const error = new Error(
|
|
107
|
+
test('getDerivedStateFromError should return correct state shape', () => {
|
|
108
|
+
const error = new Error('Test error');
|
|
102
109
|
const state = ErrorBoundary.getDerivedStateFromError(error);
|
|
103
110
|
|
|
104
111
|
expect(state?.hasError).toBe(true);
|
|
105
112
|
expect(state?.error).toBe(error);
|
|
106
113
|
});
|
|
107
114
|
|
|
108
|
-
test(
|
|
115
|
+
test('should call onError callback when error is caught', async () => {
|
|
109
116
|
const container = createTestContainer();
|
|
110
117
|
const root = createRoot(container);
|
|
111
118
|
const onErrorMock = mock(() => {});
|
|
112
119
|
|
|
113
120
|
try {
|
|
114
|
-
const testError = new Error(
|
|
115
|
-
const errorBoundary = h(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
121
|
+
const testError = new Error('Callback test error');
|
|
122
|
+
const errorBoundary = h(
|
|
123
|
+
ErrorBoundary,
|
|
124
|
+
{
|
|
125
|
+
level: 'component',
|
|
126
|
+
onError: onErrorMock,
|
|
127
|
+
},
|
|
128
|
+
h(ThrowingComponent, { error: testError }),
|
|
129
|
+
);
|
|
120
130
|
|
|
121
131
|
root.render(errorBoundary);
|
|
122
132
|
await wait(10);
|
|
123
133
|
|
|
124
134
|
// Verify onError callback was called with the error
|
|
125
135
|
expect(onErrorMock).toHaveBeenCalled();
|
|
126
|
-
const callArgs = (onErrorMock.mock.calls as unknown as [Error, unknown][])[0]
|
|
136
|
+
const callArgs = (onErrorMock.mock.calls as unknown as [Error, unknown][])[0]!;
|
|
127
137
|
expect(callArgs[0]).toBe(testError);
|
|
128
138
|
} finally {
|
|
129
139
|
root.unmount();
|
|
@@ -132,46 +142,52 @@ describe("ErrorBoundary - Error catching", () => {
|
|
|
132
142
|
});
|
|
133
143
|
});
|
|
134
144
|
|
|
135
|
-
describe(
|
|
136
|
-
test(
|
|
145
|
+
describe('Page level errors', () => {
|
|
146
|
+
test('should display page-level error UI when level is page', async () => {
|
|
137
147
|
const container = createTestContainer();
|
|
138
148
|
const root = createRoot(container);
|
|
139
149
|
|
|
140
150
|
try {
|
|
141
|
-
const testError = new Error(
|
|
142
|
-
const errorBoundary = h(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
151
|
+
const testError = new Error('Page rendering failed');
|
|
152
|
+
const errorBoundary = h(
|
|
153
|
+
ErrorBoundary,
|
|
154
|
+
{
|
|
155
|
+
level: 'page',
|
|
156
|
+
},
|
|
157
|
+
h(ThrowingComponent, { error: testError }),
|
|
158
|
+
);
|
|
146
159
|
|
|
147
160
|
root.render(errorBoundary);
|
|
148
161
|
await wait(10);
|
|
149
162
|
|
|
150
163
|
// Should show friendly page-level error UI
|
|
151
|
-
expect(container.textContent).toContain(
|
|
164
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
152
165
|
// Raw message is preserved in the collapsible technical details.
|
|
153
|
-
expect(container.textContent).toContain(
|
|
166
|
+
expect(container.textContent).toContain('Page rendering failed');
|
|
154
167
|
} finally {
|
|
155
168
|
root.unmount();
|
|
156
169
|
cleanupContainer(container);
|
|
157
170
|
}
|
|
158
171
|
});
|
|
159
172
|
|
|
160
|
-
test(
|
|
173
|
+
test('should render page content when no error occurs', async () => {
|
|
161
174
|
const container = createTestContainer();
|
|
162
175
|
const root = createRoot(container);
|
|
163
176
|
|
|
164
177
|
try {
|
|
165
|
-
const errorBoundary = h(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
const errorBoundary = h(
|
|
179
|
+
ErrorBoundary,
|
|
180
|
+
{
|
|
181
|
+
level: 'page',
|
|
182
|
+
},
|
|
183
|
+
h('div', null, 'Page content'),
|
|
184
|
+
);
|
|
169
185
|
|
|
170
186
|
root.render(errorBoundary);
|
|
171
187
|
await wait(10);
|
|
172
188
|
|
|
173
|
-
expect(container.textContent).toContain(
|
|
174
|
-
expect(container.textContent).not.toContain(
|
|
189
|
+
expect(container.textContent).toContain('Page content');
|
|
190
|
+
expect(container.textContent).not.toContain('This section ran into a problem');
|
|
175
191
|
} finally {
|
|
176
192
|
root.unmount();
|
|
177
193
|
cleanupContainer(container);
|
|
@@ -179,28 +195,31 @@ describe("ErrorBoundary - Error catching", () => {
|
|
|
179
195
|
});
|
|
180
196
|
});
|
|
181
197
|
|
|
182
|
-
describe(
|
|
183
|
-
test(
|
|
198
|
+
describe('Custom fallback UI', () => {
|
|
199
|
+
test('should use custom fallback when provided', async () => {
|
|
184
200
|
const container = createTestContainer();
|
|
185
201
|
const root = createRoot(container);
|
|
186
202
|
|
|
187
203
|
try {
|
|
188
|
-
const testError = new Error(
|
|
189
|
-
const customFallback = (error: Error) => h(
|
|
190
|
-
|
|
191
|
-
const errorBoundary = h(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
const testError = new Error('Custom error');
|
|
205
|
+
const customFallback = (error: Error) => h('div', null, `Custom fallback: ${error.message}`);
|
|
206
|
+
|
|
207
|
+
const errorBoundary = h(
|
|
208
|
+
ErrorBoundary,
|
|
209
|
+
{
|
|
210
|
+
level: 'component',
|
|
211
|
+
fallback: customFallback,
|
|
212
|
+
},
|
|
213
|
+
h(ThrowingComponent, { error: testError }),
|
|
214
|
+
);
|
|
196
215
|
|
|
197
216
|
root.render(errorBoundary);
|
|
198
217
|
await wait(10);
|
|
199
218
|
|
|
200
219
|
// Should show custom fallback
|
|
201
|
-
expect(container.textContent).toContain(
|
|
220
|
+
expect(container.textContent).toContain('Custom fallback: Custom error');
|
|
202
221
|
// Should NOT show default error UI
|
|
203
|
-
expect(container.textContent).not.toContain(
|
|
222
|
+
expect(container.textContent).not.toContain('This section ran into a problem');
|
|
204
223
|
} finally {
|
|
205
224
|
root.unmount();
|
|
206
225
|
cleanupContainer(container);
|
|
@@ -208,26 +227,26 @@ describe("ErrorBoundary - Error catching", () => {
|
|
|
208
227
|
});
|
|
209
228
|
});
|
|
210
229
|
|
|
211
|
-
describe(
|
|
212
|
-
test(
|
|
213
|
-
const error = new Error(
|
|
230
|
+
describe('Error state management', () => {
|
|
231
|
+
test('getDerivedStateFromError should set hasError to true', () => {
|
|
232
|
+
const error = new Error('Test');
|
|
214
233
|
const state = ErrorBoundary.getDerivedStateFromError(error);
|
|
215
234
|
|
|
216
235
|
expect(state?.hasError).toBe(true);
|
|
217
236
|
expect(state?.error).toBe(error);
|
|
218
237
|
});
|
|
219
238
|
|
|
220
|
-
test(
|
|
221
|
-
const typeError = new TypeError(
|
|
222
|
-
const refError = new ReferenceError(
|
|
223
|
-
const syntaxError = new SyntaxError(
|
|
239
|
+
test('should handle different error types', () => {
|
|
240
|
+
const typeError = new TypeError('Type error');
|
|
241
|
+
const refError = new ReferenceError('Reference error');
|
|
242
|
+
const syntaxError = new SyntaxError('Syntax error');
|
|
224
243
|
|
|
225
244
|
expect(ErrorBoundary.getDerivedStateFromError(typeError)?.error).toBeInstanceOf(TypeError);
|
|
226
245
|
expect(ErrorBoundary.getDerivedStateFromError(refError)?.error).toBeInstanceOf(ReferenceError);
|
|
227
246
|
expect(ErrorBoundary.getDerivedStateFromError(syntaxError)?.error).toBeInstanceOf(SyntaxError);
|
|
228
247
|
});
|
|
229
248
|
|
|
230
|
-
test(
|
|
249
|
+
test('should handle null errors gracefully', () => {
|
|
231
250
|
const state = ErrorBoundary.getDerivedStateFromError(null as any);
|
|
232
251
|
|
|
233
252
|
expect(state?.hasError).toBe(true);
|
|
@@ -235,44 +254,50 @@ describe("ErrorBoundary - Error catching", () => {
|
|
|
235
254
|
});
|
|
236
255
|
});
|
|
237
256
|
|
|
238
|
-
describe(
|
|
239
|
-
describe(
|
|
240
|
-
test(
|
|
257
|
+
describe('ErrorBoundary - Error display', () => {
|
|
258
|
+
describe('Error message rendering', () => {
|
|
259
|
+
test('should display error message in fallback UI', async () => {
|
|
241
260
|
const container = createTestContainer();
|
|
242
261
|
const root = createRoot(container);
|
|
243
262
|
|
|
244
263
|
try {
|
|
245
|
-
const errorBoundary = h(
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
264
|
+
const errorBoundary = h(
|
|
265
|
+
ErrorBoundary,
|
|
266
|
+
{
|
|
267
|
+
level: 'component',
|
|
268
|
+
},
|
|
269
|
+
h(ThrowingComponent, { error: new Error('Specific error message') }),
|
|
270
|
+
);
|
|
249
271
|
|
|
250
272
|
root.render(errorBoundary);
|
|
251
273
|
await wait(10);
|
|
252
274
|
|
|
253
275
|
// Component-level shows friendly copy; the raw message is dev-only.
|
|
254
|
-
expect(container.textContent).toContain(
|
|
276
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
255
277
|
} finally {
|
|
256
278
|
root.unmount();
|
|
257
279
|
cleanupContainer(container);
|
|
258
280
|
}
|
|
259
281
|
});
|
|
260
282
|
|
|
261
|
-
test(
|
|
283
|
+
test('should handle errors without messages', async () => {
|
|
262
284
|
const container = createTestContainer();
|
|
263
285
|
const root = createRoot(container);
|
|
264
286
|
|
|
265
287
|
try {
|
|
266
|
-
const errorBoundary = h(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
const errorBoundary = h(
|
|
289
|
+
ErrorBoundary,
|
|
290
|
+
{
|
|
291
|
+
level: 'component',
|
|
292
|
+
},
|
|
293
|
+
h(ThrowingComponent, { error: new Error() }),
|
|
294
|
+
);
|
|
270
295
|
|
|
271
296
|
root.render(errorBoundary);
|
|
272
297
|
await wait(10);
|
|
273
298
|
|
|
274
299
|
// Should still show error UI even without message
|
|
275
|
-
expect(container.textContent).toContain(
|
|
300
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
276
301
|
} finally {
|
|
277
302
|
root.unmount();
|
|
278
303
|
cleanupContainer(container);
|
|
@@ -280,43 +305,49 @@ describe("ErrorBoundary - Error display", () => {
|
|
|
280
305
|
});
|
|
281
306
|
});
|
|
282
307
|
|
|
283
|
-
describe(
|
|
284
|
-
test(
|
|
308
|
+
describe('Component name display', () => {
|
|
309
|
+
test('should display componentName in error UI', async () => {
|
|
285
310
|
const container = createTestContainer();
|
|
286
311
|
const root = createRoot(container);
|
|
287
312
|
|
|
288
313
|
try {
|
|
289
|
-
const errorBoundary = h(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
314
|
+
const errorBoundary = h(
|
|
315
|
+
ErrorBoundary,
|
|
316
|
+
{
|
|
317
|
+
level: 'component',
|
|
318
|
+
componentName: 'MyCustomComponent',
|
|
319
|
+
},
|
|
320
|
+
h(ThrowingComponent, { error: new Error('Error') }),
|
|
321
|
+
);
|
|
294
322
|
|
|
295
323
|
root.render(errorBoundary);
|
|
296
324
|
await wait(10);
|
|
297
325
|
|
|
298
|
-
expect(container.textContent).toContain(
|
|
299
|
-
expect(container.textContent).toContain(
|
|
326
|
+
expect(container.textContent).toContain('MyCustomComponent');
|
|
327
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
300
328
|
} finally {
|
|
301
329
|
root.unmount();
|
|
302
330
|
cleanupContainer(container);
|
|
303
331
|
}
|
|
304
332
|
});
|
|
305
333
|
|
|
306
|
-
test(
|
|
334
|
+
test('should display generic error when componentName is not provided', async () => {
|
|
307
335
|
const container = createTestContainer();
|
|
308
336
|
const root = createRoot(container);
|
|
309
337
|
|
|
310
338
|
try {
|
|
311
|
-
const errorBoundary = h(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
339
|
+
const errorBoundary = h(
|
|
340
|
+
ErrorBoundary,
|
|
341
|
+
{
|
|
342
|
+
level: 'component',
|
|
343
|
+
},
|
|
344
|
+
h(ThrowingComponent, { error: new Error('Error') }),
|
|
345
|
+
);
|
|
315
346
|
|
|
316
347
|
root.render(errorBoundary);
|
|
317
348
|
await wait(10);
|
|
318
349
|
|
|
319
|
-
expect(container.textContent).toContain(
|
|
350
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
320
351
|
} finally {
|
|
321
352
|
root.unmount();
|
|
322
353
|
cleanupContainer(container);
|
|
@@ -324,40 +355,46 @@ describe("ErrorBoundary - Error display", () => {
|
|
|
324
355
|
});
|
|
325
356
|
});
|
|
326
357
|
|
|
327
|
-
describe(
|
|
328
|
-
test(
|
|
358
|
+
describe('Error level handling', () => {
|
|
359
|
+
test('should render component-level UI by default', async () => {
|
|
329
360
|
const container = createTestContainer();
|
|
330
361
|
const root = createRoot(container);
|
|
331
362
|
|
|
332
363
|
try {
|
|
333
|
-
const errorBoundary = h(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
364
|
+
const errorBoundary = h(
|
|
365
|
+
ErrorBoundary,
|
|
366
|
+
{
|
|
367
|
+
// No level prop - should default to 'component'
|
|
368
|
+
},
|
|
369
|
+
h(ThrowingComponent, { error: new Error('Test') }),
|
|
370
|
+
);
|
|
337
371
|
|
|
338
372
|
root.render(errorBoundary);
|
|
339
373
|
await wait(10);
|
|
340
374
|
|
|
341
375
|
// Component-level shows the friendly title...
|
|
342
|
-
expect(container.textContent).toContain(
|
|
376
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
343
377
|
// ...but NOT the page-level "Reload Page" affordance.
|
|
344
|
-
expect(container.textContent).not.toContain(
|
|
378
|
+
expect(container.textContent).not.toContain('Reload Page');
|
|
345
379
|
} finally {
|
|
346
380
|
root.unmount();
|
|
347
381
|
cleanupContainer(container);
|
|
348
382
|
}
|
|
349
383
|
});
|
|
350
384
|
|
|
351
|
-
test(
|
|
385
|
+
test('should log error to console with correct level', async () => {
|
|
352
386
|
const container = createTestContainer();
|
|
353
387
|
const root = createRoot(container);
|
|
354
388
|
|
|
355
389
|
try {
|
|
356
|
-
const errorBoundary = h(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
390
|
+
const errorBoundary = h(
|
|
391
|
+
ErrorBoundary,
|
|
392
|
+
{
|
|
393
|
+
level: 'page',
|
|
394
|
+
componentName: 'TestPage',
|
|
395
|
+
},
|
|
396
|
+
h(ThrowingComponent, { error: new Error('Log test') }),
|
|
397
|
+
);
|
|
361
398
|
|
|
362
399
|
root.render(errorBoundary);
|
|
363
400
|
await wait(10);
|
|
@@ -372,61 +409,61 @@ describe("ErrorBoundary - Error display", () => {
|
|
|
372
409
|
});
|
|
373
410
|
});
|
|
374
411
|
|
|
375
|
-
describe(
|
|
376
|
-
describe(
|
|
377
|
-
test(
|
|
412
|
+
describe('ErrorBoundary - withErrorBoundary utility', () => {
|
|
413
|
+
describe('Error catching', () => {
|
|
414
|
+
test('should catch synchronous errors in render function', () => {
|
|
378
415
|
const throwingFn = () => {
|
|
379
|
-
throw new Error(
|
|
416
|
+
throw new Error('Render error');
|
|
380
417
|
};
|
|
381
418
|
|
|
382
419
|
// Should not throw - returns an ErrorBoundary element instead
|
|
383
420
|
const result = withErrorBoundary(throwingFn, {
|
|
384
|
-
componentName:
|
|
421
|
+
componentName: 'TestComponent',
|
|
385
422
|
});
|
|
386
423
|
|
|
387
424
|
// Result should be a React element (ErrorBoundary)
|
|
388
425
|
expect(result).toBeDefined();
|
|
389
|
-
expect(typeof result).toBe(
|
|
426
|
+
expect(typeof result).toBe('object');
|
|
390
427
|
});
|
|
391
428
|
|
|
392
|
-
test(
|
|
393
|
-
const successFn = () =>
|
|
429
|
+
test('should return result when no error occurs', () => {
|
|
430
|
+
const successFn = () => 'Success';
|
|
394
431
|
|
|
395
432
|
const result = withErrorBoundary(successFn);
|
|
396
433
|
|
|
397
|
-
expect(result).toBe(
|
|
434
|
+
expect(result).toBe('Success');
|
|
398
435
|
});
|
|
399
436
|
|
|
400
|
-
test(
|
|
437
|
+
test('should render wrapped component content when no error', async () => {
|
|
401
438
|
const container = createTestContainer();
|
|
402
439
|
const root = createRoot(container);
|
|
403
440
|
|
|
404
441
|
try {
|
|
405
|
-
const successFn = () => h(
|
|
442
|
+
const successFn = () => h('div', null, 'Wrapped content');
|
|
406
443
|
const wrapped = withErrorBoundary(successFn, {
|
|
407
|
-
componentName:
|
|
444
|
+
componentName: 'WrappedComponent',
|
|
408
445
|
});
|
|
409
446
|
|
|
410
447
|
root.render(wrapped);
|
|
411
448
|
await wait(10);
|
|
412
449
|
|
|
413
|
-
expect(container.textContent).toContain(
|
|
450
|
+
expect(container.textContent).toContain('Wrapped content');
|
|
414
451
|
} finally {
|
|
415
452
|
root.unmount();
|
|
416
453
|
cleanupContainer(container);
|
|
417
454
|
}
|
|
418
455
|
});
|
|
419
456
|
|
|
420
|
-
test(
|
|
457
|
+
test('should render error boundary when function throws', async () => {
|
|
421
458
|
const container = createTestContainer();
|
|
422
459
|
const root = createRoot(container);
|
|
423
460
|
|
|
424
461
|
try {
|
|
425
462
|
const throwingFn = () => {
|
|
426
|
-
throw new Error(
|
|
463
|
+
throw new Error('Sync error');
|
|
427
464
|
};
|
|
428
465
|
const wrapped = withErrorBoundary(throwingFn, {
|
|
429
|
-
componentName:
|
|
466
|
+
componentName: 'FailingComponent',
|
|
430
467
|
});
|
|
431
468
|
|
|
432
469
|
root.render(wrapped);
|
|
@@ -442,26 +479,26 @@ describe("ErrorBoundary - withErrorBoundary utility", () => {
|
|
|
442
479
|
});
|
|
443
480
|
});
|
|
444
481
|
|
|
445
|
-
describe(
|
|
446
|
-
test(
|
|
482
|
+
describe('Error handling options', () => {
|
|
483
|
+
test('should pass options to the error boundary', () => {
|
|
447
484
|
const onErrorMock = mock(() => {});
|
|
448
485
|
|
|
449
486
|
const throwingFn = () => {
|
|
450
|
-
throw new Error(
|
|
487
|
+
throw new Error('Test error');
|
|
451
488
|
};
|
|
452
489
|
|
|
453
490
|
const result = withErrorBoundary(throwingFn, {
|
|
454
|
-
componentName:
|
|
455
|
-
level:
|
|
456
|
-
onError: onErrorMock
|
|
491
|
+
componentName: 'TestComponent',
|
|
492
|
+
level: 'page',
|
|
493
|
+
onError: onErrorMock,
|
|
457
494
|
});
|
|
458
495
|
|
|
459
496
|
// Result is an ErrorBoundary element with the provided props
|
|
460
497
|
expect(result).toBeDefined();
|
|
461
498
|
});
|
|
462
499
|
|
|
463
|
-
test(
|
|
464
|
-
const successFn = () => h(
|
|
500
|
+
test('should use default options when not provided', () => {
|
|
501
|
+
const successFn = () => h('div', null, 'Content');
|
|
465
502
|
|
|
466
503
|
const result = withErrorBoundary(successFn);
|
|
467
504
|
|
|
@@ -471,63 +508,72 @@ describe("ErrorBoundary - withErrorBoundary utility", () => {
|
|
|
471
508
|
});
|
|
472
509
|
});
|
|
473
510
|
|
|
474
|
-
describe(
|
|
475
|
-
test(
|
|
511
|
+
describe('ErrorBoundary - Error types', () => {
|
|
512
|
+
test('should catch and display TypeError', async () => {
|
|
476
513
|
const container = createTestContainer();
|
|
477
514
|
const root = createRoot(container);
|
|
478
515
|
|
|
479
516
|
try {
|
|
480
|
-
const errorBoundary = h(
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
517
|
+
const errorBoundary = h(
|
|
518
|
+
ErrorBoundary,
|
|
519
|
+
{
|
|
520
|
+
level: 'component',
|
|
521
|
+
},
|
|
522
|
+
h(ThrowingComponent, { error: new TypeError('Type mismatch') }),
|
|
523
|
+
);
|
|
484
524
|
|
|
485
525
|
root.render(errorBoundary);
|
|
486
526
|
await wait(10);
|
|
487
527
|
|
|
488
|
-
expect(container.textContent).toContain(
|
|
528
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
489
529
|
} finally {
|
|
490
530
|
root.unmount();
|
|
491
531
|
cleanupContainer(container);
|
|
492
532
|
}
|
|
493
533
|
});
|
|
494
534
|
|
|
495
|
-
test(
|
|
535
|
+
test('should catch and display ReferenceError', async () => {
|
|
496
536
|
const container = createTestContainer();
|
|
497
537
|
const root = createRoot(container);
|
|
498
538
|
|
|
499
539
|
try {
|
|
500
|
-
const errorBoundary = h(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
540
|
+
const errorBoundary = h(
|
|
541
|
+
ErrorBoundary,
|
|
542
|
+
{
|
|
543
|
+
level: 'component',
|
|
544
|
+
},
|
|
545
|
+
h(ThrowingComponent, { error: new ReferenceError('undefined variable') }),
|
|
546
|
+
);
|
|
504
547
|
|
|
505
548
|
root.render(errorBoundary);
|
|
506
549
|
await wait(10);
|
|
507
550
|
|
|
508
551
|
// Raw message is dev-only for component-level; friendly title always shows.
|
|
509
|
-
expect(container.textContent).toContain(
|
|
552
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
510
553
|
} finally {
|
|
511
554
|
root.unmount();
|
|
512
555
|
cleanupContainer(container);
|
|
513
556
|
}
|
|
514
557
|
});
|
|
515
558
|
|
|
516
|
-
test(
|
|
559
|
+
test('should display custom error messages clearly', async () => {
|
|
517
560
|
const container = createTestContainer();
|
|
518
561
|
const root = createRoot(container);
|
|
519
562
|
|
|
520
563
|
try {
|
|
521
|
-
const errorBoundary = h(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
564
|
+
const errorBoundary = h(
|
|
565
|
+
ErrorBoundary,
|
|
566
|
+
{
|
|
567
|
+
level: 'component',
|
|
568
|
+
},
|
|
569
|
+
h(ThrowingComponent, { error: new Error('Invalid component definition: Missing structure') }),
|
|
570
|
+
);
|
|
525
571
|
|
|
526
572
|
root.render(errorBoundary);
|
|
527
573
|
await wait(10);
|
|
528
574
|
|
|
529
575
|
// Friendly, plain-language copy replaces the raw JS message for end users.
|
|
530
|
-
expect(container.textContent).toContain(
|
|
576
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
531
577
|
} finally {
|
|
532
578
|
root.unmount();
|
|
533
579
|
cleanupContainer(container);
|
|
@@ -535,60 +581,74 @@ describe("ErrorBoundary - Error types", () => {
|
|
|
535
581
|
});
|
|
536
582
|
});
|
|
537
583
|
|
|
538
|
-
describe(
|
|
539
|
-
test(
|
|
584
|
+
describe('ErrorBoundary - Multiple instances', () => {
|
|
585
|
+
test('should isolate errors to their own boundary', async () => {
|
|
540
586
|
const container = createTestContainer();
|
|
541
587
|
const root = createRoot(container);
|
|
542
588
|
|
|
543
589
|
try {
|
|
544
590
|
// Create a parent with two error boundaries - one throws, one doesn't
|
|
545
|
-
const app = h(
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
591
|
+
const app = h(
|
|
592
|
+
'div',
|
|
593
|
+
null,
|
|
594
|
+
h(
|
|
595
|
+
ErrorBoundary,
|
|
596
|
+
{
|
|
597
|
+
level: 'component',
|
|
598
|
+
componentName: 'ThrowingChild',
|
|
599
|
+
},
|
|
600
|
+
h(ThrowingComponent, { error: new Error('Child error') }),
|
|
601
|
+
),
|
|
602
|
+
h(
|
|
603
|
+
ErrorBoundary,
|
|
604
|
+
{
|
|
605
|
+
level: 'component',
|
|
606
|
+
componentName: 'SafeChild',
|
|
607
|
+
},
|
|
608
|
+
h('div', null, 'Safe content'),
|
|
609
|
+
),
|
|
556
610
|
);
|
|
557
611
|
|
|
558
612
|
root.render(app);
|
|
559
613
|
await wait(10);
|
|
560
614
|
|
|
561
615
|
// Both should be in the DOM
|
|
562
|
-
expect(container.textContent).toContain(
|
|
563
|
-
expect(container.textContent).toContain(
|
|
616
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
617
|
+
expect(container.textContent).toContain('Safe content');
|
|
564
618
|
} finally {
|
|
565
619
|
root.unmount();
|
|
566
620
|
cleanupContainer(container);
|
|
567
621
|
}
|
|
568
622
|
});
|
|
569
623
|
|
|
570
|
-
test(
|
|
624
|
+
test('should handle nested error boundaries', async () => {
|
|
571
625
|
const container = createTestContainer();
|
|
572
626
|
const root = createRoot(container);
|
|
573
627
|
|
|
574
628
|
try {
|
|
575
629
|
// Outer boundary catches errors that inner boundary doesn't handle
|
|
576
|
-
const app = h(
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
level:
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
630
|
+
const app = h(
|
|
631
|
+
ErrorBoundary,
|
|
632
|
+
{
|
|
633
|
+
level: 'page',
|
|
634
|
+
},
|
|
635
|
+
h(
|
|
636
|
+
ErrorBoundary,
|
|
637
|
+
{
|
|
638
|
+
level: 'component',
|
|
639
|
+
componentName: 'InnerComponent',
|
|
640
|
+
},
|
|
641
|
+
h(ThrowingComponent, { error: new Error('Inner error') }),
|
|
642
|
+
),
|
|
643
|
+
);
|
|
584
644
|
|
|
585
645
|
root.render(app);
|
|
586
646
|
await wait(10);
|
|
587
647
|
|
|
588
648
|
// Inner (component-level) boundary catches it: friendly title shows,
|
|
589
649
|
// and the page-level "Reload Page" affordance does not.
|
|
590
|
-
expect(container.textContent).toContain(
|
|
591
|
-
expect(container.textContent).not.toContain(
|
|
650
|
+
expect(container.textContent).toContain('This section ran into a problem');
|
|
651
|
+
expect(container.textContent).not.toContain('Reload Page');
|
|
592
652
|
} finally {
|
|
593
653
|
root.unmount();
|
|
594
654
|
cleanupContainer(container);
|