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
|
@@ -4,15 +4,30 @@
|
|
|
4
4
|
* Tests the conversion of HTML strings into Tiptap document format.
|
|
5
5
|
* Uses happy-dom DOMParser for browser-like HTML parsing.
|
|
6
6
|
*/
|
|
7
|
-
import { describe, test, expect, beforeAll } from 'bun:test';
|
|
8
|
-
import {
|
|
7
|
+
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
8
|
+
import { window as domWindow } from '../../test-utils/dom-setup';
|
|
9
9
|
import { htmlToTiptap } from './htmlToTiptap';
|
|
10
10
|
import type { TiptapDocument, TiptapNode } from './types';
|
|
11
11
|
|
|
12
|
-
// Register DOMParser globally so htmlToTiptap uses the DOM-based path
|
|
12
|
+
// Register DOMParser globally so htmlToTiptap uses the DOM-based path.
|
|
13
|
+
// IMPORTANT: reuse the preloaded dom-setup window — it carries the
|
|
14
|
+
// `window.SyntaxError` patch that happy-dom's selector parser needs (it does
|
|
15
|
+
// `new this.window.SyntaxError(...)` on every querySelector). A fresh
|
|
16
|
+
// `new Window()` lacks the patch, so every element.querySelector() in
|
|
17
|
+
// parseCodeBlock/parseTable throws "undefined is not a constructor".
|
|
18
|
+
const originalDOMParser = (globalThis as { DOMParser?: unknown }).DOMParser;
|
|
13
19
|
beforeAll(() => {
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
(globalThis as { DOMParser?: unknown }).DOMParser = domWindow.DOMParser;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Restore the global afterwards so it doesn't leak into later test files —
|
|
24
|
+
// htmlToTiptap (and other modules) branch on `typeof DOMParser`.
|
|
25
|
+
afterAll(() => {
|
|
26
|
+
if (originalDOMParser === undefined) {
|
|
27
|
+
delete (globalThis as { DOMParser?: unknown }).DOMParser;
|
|
28
|
+
} else {
|
|
29
|
+
(globalThis as { DOMParser?: unknown }).DOMParser = originalDOMParser;
|
|
30
|
+
}
|
|
16
31
|
});
|
|
17
32
|
|
|
18
33
|
// ---------------------------------------------------------------------------
|
|
@@ -35,7 +50,7 @@ function findNode(doc: TiptapDocument, type: string): TiptapNode | undefined {
|
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
/** Collect all nodes of a given type */
|
|
38
|
-
function
|
|
53
|
+
function _findAllNodes(doc: TiptapDocument, type: string): TiptapNode[] {
|
|
39
54
|
const result: TiptapNode[] = [];
|
|
40
55
|
function search(nodes: TiptapNode[]) {
|
|
41
56
|
for (const n of nodes) {
|
|
@@ -75,10 +90,10 @@ describe('htmlToTiptap', () => {
|
|
|
75
90
|
const result = htmlToTiptap('Hello world');
|
|
76
91
|
expect(result.type).toBe('doc');
|
|
77
92
|
expect(result.content.length).toBe(1);
|
|
78
|
-
expect(result.content[0]
|
|
79
|
-
expect(result.content[0]
|
|
80
|
-
expect(result.content[0]
|
|
81
|
-
expect(result.content[0]
|
|
93
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
94
|
+
expect(result.content[0]!.content).toBeDefined();
|
|
95
|
+
expect(result.content[0]!.content![0]!.type).toBe('text');
|
|
96
|
+
expect(result.content[0]!.content![0]!.text).toBe('Hello world');
|
|
82
97
|
});
|
|
83
98
|
});
|
|
84
99
|
|
|
@@ -89,13 +104,13 @@ describe('htmlToTiptap', () => {
|
|
|
89
104
|
test('should convert <p> to paragraph node', () => {
|
|
90
105
|
const result = htmlToTiptap('<p>Hello</p>');
|
|
91
106
|
expect(result.content.length).toBe(1);
|
|
92
|
-
expect(result.content[0]
|
|
93
|
-
expect(result.content[0]
|
|
107
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
108
|
+
expect(result.content[0]!.content![0]!.text).toBe('Hello');
|
|
94
109
|
});
|
|
95
110
|
|
|
96
111
|
test('should convert <p> with text-align style to paragraph with textAlign attr', () => {
|
|
97
112
|
const result = htmlToTiptap('<p style="text-align: center">Centered</p>');
|
|
98
|
-
const p = result.content[0]
|
|
113
|
+
const p = result.content[0]!;
|
|
99
114
|
expect(p.type).toBe('paragraph');
|
|
100
115
|
expect(p.attrs).toBeDefined();
|
|
101
116
|
expect(p.attrs!.textAlign).toBe('center');
|
|
@@ -103,12 +118,12 @@ describe('htmlToTiptap', () => {
|
|
|
103
118
|
|
|
104
119
|
test('should convert <p> with text-align justify', () => {
|
|
105
120
|
const result = htmlToTiptap('<p style="text-align: justify">Justified</p>');
|
|
106
|
-
expect(result.content[0]
|
|
121
|
+
expect(result.content[0]!.attrs!.textAlign).toBe('justify');
|
|
107
122
|
});
|
|
108
123
|
|
|
109
124
|
test('should not set textAlign for unsupported alignment value', () => {
|
|
110
125
|
const result = htmlToTiptap('<p style="text-align: start">Text</p>');
|
|
111
|
-
const p = result.content[0]
|
|
126
|
+
const p = result.content[0]!;
|
|
112
127
|
// 'start' is not in the allowed list
|
|
113
128
|
expect(p.attrs?.textAlign).toBeUndefined();
|
|
114
129
|
});
|
|
@@ -117,16 +132,16 @@ describe('htmlToTiptap', () => {
|
|
|
117
132
|
for (let level = 1; level <= 6; level++) {
|
|
118
133
|
test(`should convert <h${level}> to heading with level ${level}`, () => {
|
|
119
134
|
const result = htmlToTiptap(`<h${level}>Heading ${level}</h${level}>`);
|
|
120
|
-
const heading = result.content[0]
|
|
135
|
+
const heading = result.content[0]!;
|
|
121
136
|
expect(heading.type).toBe('heading');
|
|
122
137
|
expect(heading.attrs!.level).toBe(level);
|
|
123
|
-
expect(heading.content![0]
|
|
138
|
+
expect(heading.content![0]!.text).toBe(`Heading ${level}`);
|
|
124
139
|
});
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
test('should convert <h2> with text-align to heading with textAlign', () => {
|
|
128
143
|
const result = htmlToTiptap('<h2 style="text-align: right">Right Heading</h2>');
|
|
129
|
-
const heading = result.content[0]
|
|
144
|
+
const heading = result.content[0]!;
|
|
130
145
|
expect(heading.type).toBe('heading');
|
|
131
146
|
expect(heading.attrs!.level).toBe(2);
|
|
132
147
|
expect(heading.attrs!.textAlign).toBe('right');
|
|
@@ -137,97 +152,97 @@ describe('htmlToTiptap', () => {
|
|
|
137
152
|
test('should convert <ul><li> to bulletList with listItem children', () => {
|
|
138
153
|
const result = htmlToTiptap('<ul><li>Item 1</li><li>Item 2</li></ul>');
|
|
139
154
|
expect(result.content.length).toBe(1);
|
|
140
|
-
const list = result.content[0]
|
|
155
|
+
const list = result.content[0]!;
|
|
141
156
|
expect(list.type).toBe('bulletList');
|
|
142
157
|
expect(list.content!.length).toBe(2);
|
|
143
|
-
expect(list.content![0]
|
|
144
|
-
expect(list.content![1]
|
|
158
|
+
expect(list.content![0]!.type).toBe('listItem');
|
|
159
|
+
expect(list.content![1]!.type).toBe('listItem');
|
|
145
160
|
});
|
|
146
161
|
|
|
147
162
|
test('should convert <ol><li> to orderedList', () => {
|
|
148
163
|
const result = htmlToTiptap('<ol><li>First</li><li>Second</li></ol>');
|
|
149
|
-
const list = result.content[0]
|
|
164
|
+
const list = result.content[0]!;
|
|
150
165
|
expect(list.type).toBe('orderedList');
|
|
151
166
|
expect(list.content!.length).toBe(2);
|
|
152
167
|
});
|
|
153
168
|
|
|
154
169
|
test('should convert <ol start="3"> to orderedList with start attr', () => {
|
|
155
170
|
const result = htmlToTiptap('<ol start="3"><li>Third</li></ol>');
|
|
156
|
-
const list = result.content[0]
|
|
171
|
+
const list = result.content[0]!;
|
|
157
172
|
expect(list.type).toBe('orderedList');
|
|
158
173
|
expect(list.attrs!.start).toBe(3);
|
|
159
174
|
});
|
|
160
175
|
|
|
161
176
|
test('should wrap list item inline content in paragraph', () => {
|
|
162
177
|
const result = htmlToTiptap('<ul><li>Simple text</li></ul>');
|
|
163
|
-
const listItem = result.content[0]
|
|
178
|
+
const listItem = result.content[0]!.content![0]!;
|
|
164
179
|
expect(listItem.type).toBe('listItem');
|
|
165
|
-
expect(listItem.content![0]
|
|
166
|
-
expect(listItem.content![0]
|
|
180
|
+
expect(listItem.content![0]!.type).toBe('paragraph');
|
|
181
|
+
expect(listItem.content![0]!.content![0]!.text).toBe('Simple text');
|
|
167
182
|
});
|
|
168
183
|
|
|
169
184
|
test('should preserve block content inside list items', () => {
|
|
170
185
|
const result = htmlToTiptap('<ul><li><p>Block content</p></li></ul>');
|
|
171
|
-
const listItem = result.content[0]
|
|
186
|
+
const listItem = result.content[0]!.content![0]!;
|
|
172
187
|
expect(listItem.type).toBe('listItem');
|
|
173
|
-
expect(listItem.content![0]
|
|
188
|
+
expect(listItem.content![0]!.type).toBe('paragraph');
|
|
174
189
|
});
|
|
175
190
|
|
|
176
191
|
test('should give empty list item an empty paragraph', () => {
|
|
177
192
|
const result = htmlToTiptap('<ul><li></li></ul>');
|
|
178
|
-
const listItem = result.content[0]
|
|
193
|
+
const listItem = result.content[0]!.content![0]!;
|
|
179
194
|
expect(listItem.type).toBe('listItem');
|
|
180
195
|
expect(listItem.content!.length).toBe(1);
|
|
181
|
-
expect(listItem.content![0]
|
|
196
|
+
expect(listItem.content![0]!.type).toBe('paragraph');
|
|
182
197
|
});
|
|
183
198
|
});
|
|
184
199
|
|
|
185
200
|
test('should convert <blockquote> to blockquote node', () => {
|
|
186
201
|
const result = htmlToTiptap('<blockquote>Quoted text</blockquote>');
|
|
187
|
-
const bq = result.content[0]
|
|
202
|
+
const bq = result.content[0]!;
|
|
188
203
|
expect(bq.type).toBe('blockquote');
|
|
189
204
|
// Content should be wrapped in paragraph
|
|
190
|
-
expect(bq.content![0]
|
|
205
|
+
expect(bq.content![0]!.type).toBe('paragraph');
|
|
191
206
|
});
|
|
192
207
|
|
|
193
208
|
test('should convert empty <blockquote> to blockquote with empty paragraph', () => {
|
|
194
209
|
const result = htmlToTiptap('<blockquote></blockquote>');
|
|
195
|
-
const bq = result.content[0]
|
|
210
|
+
const bq = result.content[0]!;
|
|
196
211
|
expect(bq.type).toBe('blockquote');
|
|
197
212
|
expect(bq.content!.length).toBe(1);
|
|
198
|
-
expect(bq.content![0]
|
|
213
|
+
expect(bq.content![0]!.type).toBe('paragraph');
|
|
199
214
|
});
|
|
200
215
|
|
|
201
216
|
test('should convert <pre><code> to codeBlock', () => {
|
|
202
217
|
const result = htmlToTiptap('<pre><code>const x = 1;</code></pre>');
|
|
203
|
-
const codeBlock = result.content[0]
|
|
218
|
+
const codeBlock = result.content[0]!;
|
|
204
219
|
expect(codeBlock.type).toBe('codeBlock');
|
|
205
|
-
expect(codeBlock.content![0]
|
|
220
|
+
expect(codeBlock.content![0]!.text).toBe('const x = 1;');
|
|
206
221
|
});
|
|
207
222
|
|
|
208
223
|
test('should parse language from <code class="language-javascript">', () => {
|
|
209
224
|
const result = htmlToTiptap('<pre><code class="language-javascript">let y = 2;</code></pre>');
|
|
210
|
-
const codeBlock = result.content[0]
|
|
225
|
+
const codeBlock = result.content[0]!;
|
|
211
226
|
expect(codeBlock.type).toBe('codeBlock');
|
|
212
227
|
expect(codeBlock.attrs!.language).toBe('javascript');
|
|
213
228
|
});
|
|
214
229
|
|
|
215
230
|
test('should handle <pre> without <code> child', () => {
|
|
216
231
|
const result = htmlToTiptap('<pre>Plain preformatted</pre>');
|
|
217
|
-
const codeBlock = result.content[0]
|
|
232
|
+
const codeBlock = result.content[0]!;
|
|
218
233
|
expect(codeBlock.type).toBe('codeBlock');
|
|
219
|
-
expect(codeBlock.content![0]
|
|
234
|
+
expect(codeBlock.content![0]!.text).toBe('Plain preformatted');
|
|
220
235
|
});
|
|
221
236
|
|
|
222
237
|
test('should convert <hr> to horizontalRule', () => {
|
|
223
238
|
const result = htmlToTiptap('<p>Above</p><hr><p>Below</p>');
|
|
224
|
-
const hr = result.content[1]
|
|
239
|
+
const hr = result.content[1]!;
|
|
225
240
|
expect(hr.type).toBe('horizontalRule');
|
|
226
241
|
});
|
|
227
242
|
|
|
228
243
|
test('should convert <br> to hardBreak', () => {
|
|
229
244
|
const result = htmlToTiptap('<p>Line 1<br>Line 2</p>');
|
|
230
|
-
const p = result.content[0]
|
|
245
|
+
const p = result.content[0]!;
|
|
231
246
|
expect(p.type).toBe('paragraph');
|
|
232
247
|
// Should contain text, hardBreak, text
|
|
233
248
|
const types = p.content!.map((n) => n.type);
|
|
@@ -265,7 +280,7 @@ describe('htmlToTiptap', () => {
|
|
|
265
280
|
describe('inline marks', () => {
|
|
266
281
|
test('should convert <strong> to bold mark', () => {
|
|
267
282
|
const result = htmlToTiptap('<p><strong>Bold text</strong></p>');
|
|
268
|
-
const textNode = result.content[0]
|
|
283
|
+
const textNode = result.content[0]!.content![0]!;
|
|
269
284
|
expect(textNode.text).toBe('Bold text');
|
|
270
285
|
expect(textNode.marks).toBeDefined();
|
|
271
286
|
expect(textNode.marks!.some((m) => m.type === 'bold')).toBe(true);
|
|
@@ -273,51 +288,51 @@ describe('htmlToTiptap', () => {
|
|
|
273
288
|
|
|
274
289
|
test('should convert <b> to bold mark', () => {
|
|
275
290
|
const result = htmlToTiptap('<p><b>Bold text</b></p>');
|
|
276
|
-
const textNode = result.content[0]
|
|
291
|
+
const textNode = result.content[0]!.content![0]!;
|
|
277
292
|
expect(textNode.marks!.some((m) => m.type === 'bold')).toBe(true);
|
|
278
293
|
});
|
|
279
294
|
|
|
280
295
|
test('should convert <em> to italic mark', () => {
|
|
281
296
|
const result = htmlToTiptap('<p><em>Italic text</em></p>');
|
|
282
|
-
const textNode = result.content[0]
|
|
297
|
+
const textNode = result.content[0]!.content![0]!;
|
|
283
298
|
expect(textNode.text).toBe('Italic text');
|
|
284
299
|
expect(textNode.marks!.some((m) => m.type === 'italic')).toBe(true);
|
|
285
300
|
});
|
|
286
301
|
|
|
287
302
|
test('should convert <i> to italic mark', () => {
|
|
288
303
|
const result = htmlToTiptap('<p><i>Italic text</i></p>');
|
|
289
|
-
const textNode = result.content[0]
|
|
304
|
+
const textNode = result.content[0]!.content![0]!;
|
|
290
305
|
expect(textNode.marks!.some((m) => m.type === 'italic')).toBe(true);
|
|
291
306
|
});
|
|
292
307
|
|
|
293
308
|
test('should convert <u> to underline mark', () => {
|
|
294
309
|
const result = htmlToTiptap('<p><u>Underlined</u></p>');
|
|
295
|
-
const textNode = result.content[0]
|
|
310
|
+
const textNode = result.content[0]!.content![0]!;
|
|
296
311
|
expect(textNode.marks!.some((m) => m.type === 'underline')).toBe(true);
|
|
297
312
|
});
|
|
298
313
|
|
|
299
314
|
test('should convert <s> to strike mark', () => {
|
|
300
315
|
const result = htmlToTiptap('<p><s>Struck</s></p>');
|
|
301
|
-
const textNode = result.content[0]
|
|
316
|
+
const textNode = result.content[0]!.content![0]!;
|
|
302
317
|
expect(textNode.marks!.some((m) => m.type === 'strike')).toBe(true);
|
|
303
318
|
});
|
|
304
319
|
|
|
305
320
|
test('should convert <del> to strike mark', () => {
|
|
306
321
|
const result = htmlToTiptap('<p><del>Deleted</del></p>');
|
|
307
|
-
const textNode = result.content[0]
|
|
322
|
+
const textNode = result.content[0]!.content![0]!;
|
|
308
323
|
expect(textNode.marks!.some((m) => m.type === 'strike')).toBe(true);
|
|
309
324
|
});
|
|
310
325
|
|
|
311
326
|
test('should convert <code> to code mark', () => {
|
|
312
327
|
const result = htmlToTiptap('<p><code>inline code</code></p>');
|
|
313
|
-
const textNode = result.content[0]
|
|
328
|
+
const textNode = result.content[0]!.content![0]!;
|
|
314
329
|
expect(textNode.text).toBe('inline code');
|
|
315
330
|
expect(textNode.marks!.some((m) => m.type === 'code')).toBe(true);
|
|
316
331
|
});
|
|
317
332
|
|
|
318
333
|
test('should convert <a> to text with link mark', () => {
|
|
319
334
|
const result = htmlToTiptap('<p><a href="https://example.com">Click here</a></p>');
|
|
320
|
-
const textNode = result.content[0]
|
|
335
|
+
const textNode = result.content[0]!.content![0]!;
|
|
321
336
|
expect(textNode.text).toBe('Click here');
|
|
322
337
|
const linkMark = textNode.marks!.find((m) => m.type === 'link');
|
|
323
338
|
expect(linkMark).toBeDefined();
|
|
@@ -325,10 +340,8 @@ describe('htmlToTiptap', () => {
|
|
|
325
340
|
});
|
|
326
341
|
|
|
327
342
|
test('should convert <a> with target and rel attributes', () => {
|
|
328
|
-
const result = htmlToTiptap(
|
|
329
|
-
|
|
330
|
-
);
|
|
331
|
-
const textNode = result.content[0].content![0];
|
|
343
|
+
const result = htmlToTiptap('<p><a href="/page" target="_blank" rel="noopener">Link</a></p>');
|
|
344
|
+
const textNode = result.content[0]!.content![0]!;
|
|
332
345
|
const linkMark = textNode.marks!.find((m) => m.type === 'link');
|
|
333
346
|
expect(linkMark!.attrs!.href).toBe('/page');
|
|
334
347
|
expect(linkMark!.attrs!.target).toBe('_blank');
|
|
@@ -342,7 +355,7 @@ describe('htmlToTiptap', () => {
|
|
|
342
355
|
describe('nested marks', () => {
|
|
343
356
|
test('should apply both bold and italic marks for <strong><em>', () => {
|
|
344
357
|
const result = htmlToTiptap('<p><strong><em>Bold and italic</em></strong></p>');
|
|
345
|
-
const textNode = result.content[0]
|
|
358
|
+
const textNode = result.content[0]!.content![0]!;
|
|
346
359
|
expect(textNode.text).toBe('Bold and italic');
|
|
347
360
|
expect(textNode.marks!.length).toBeGreaterThanOrEqual(2);
|
|
348
361
|
const markTypes = textNode.marks!.map((m) => m.type);
|
|
@@ -352,17 +365,15 @@ describe('htmlToTiptap', () => {
|
|
|
352
365
|
|
|
353
366
|
test('should apply bold and italic for <em><strong> (reverse nesting)', () => {
|
|
354
367
|
const result = htmlToTiptap('<p><em><strong>Italic and bold</strong></em></p>');
|
|
355
|
-
const textNode = result.content[0]
|
|
368
|
+
const textNode = result.content[0]!.content![0]!;
|
|
356
369
|
const markTypes = textNode.marks!.map((m) => m.type);
|
|
357
370
|
expect(markTypes).toContain('bold');
|
|
358
371
|
expect(markTypes).toContain('italic');
|
|
359
372
|
});
|
|
360
373
|
|
|
361
374
|
test('should handle bold inside link', () => {
|
|
362
|
-
const result = htmlToTiptap(
|
|
363
|
-
|
|
364
|
-
);
|
|
365
|
-
const textNode = result.content[0].content![0];
|
|
375
|
+
const result = htmlToTiptap('<p><a href="/url"><strong>Bold link</strong></a></p>');
|
|
376
|
+
const textNode = result.content[0]!.content![0]!;
|
|
366
377
|
expect(textNode.text).toBe('Bold link');
|
|
367
378
|
const markTypes = textNode.marks!.map((m) => m.type);
|
|
368
379
|
expect(markTypes).toContain('bold');
|
|
@@ -376,7 +387,7 @@ describe('htmlToTiptap', () => {
|
|
|
376
387
|
describe('span handling', () => {
|
|
377
388
|
test('should convert <span data-meno-span="custom"> to textStyle mark', () => {
|
|
378
389
|
const result = htmlToTiptap('<p><span data-meno-span="custom">Styled</span></p>');
|
|
379
|
-
const textNode = result.content[0]
|
|
390
|
+
const textNode = result.content[0]!.content![0]!;
|
|
380
391
|
expect(textNode.text).toBe('Styled');
|
|
381
392
|
const styleMark = textNode.marks!.find((m) => m.type === 'textStyle');
|
|
382
393
|
expect(styleMark).toBeDefined();
|
|
@@ -385,7 +396,7 @@ describe('htmlToTiptap', () => {
|
|
|
385
396
|
|
|
386
397
|
test('should pass through <span> without special attrs as just children', () => {
|
|
387
398
|
const result = htmlToTiptap('<p><span>Plain span</span></p>');
|
|
388
|
-
const textNode = result.content[0]
|
|
399
|
+
const textNode = result.content[0]!.content![0]!;
|
|
389
400
|
expect(textNode.type).toBe('text');
|
|
390
401
|
expect(textNode.text).toBe('Plain span');
|
|
391
402
|
// Should not have any marks (or at least no textStyle mark)
|
|
@@ -395,7 +406,7 @@ describe('htmlToTiptap', () => {
|
|
|
395
406
|
|
|
396
407
|
test('should handle <span> with style but no data-meno-span (returns children)', () => {
|
|
397
408
|
const result = htmlToTiptap('<p><span style="color: red">Red</span></p>');
|
|
398
|
-
const textNode = result.content[0]
|
|
409
|
+
const textNode = result.content[0]!.content![0]!;
|
|
399
410
|
expect(textNode.text).toBe('Red');
|
|
400
411
|
// Currently the implementation returns children without marks for styled spans
|
|
401
412
|
const hasTextStyle = textNode.marks?.some((m) => m.type === 'textStyle');
|
|
@@ -424,15 +435,15 @@ describe('htmlToTiptap', () => {
|
|
|
424
435
|
expect(table!.content!.length).toBe(2); // 2 rows
|
|
425
436
|
|
|
426
437
|
// First row should have tableHeader cells
|
|
427
|
-
const headerRow = table!.content![0]
|
|
438
|
+
const headerRow = table!.content![0]!;
|
|
428
439
|
expect(headerRow.type).toBe('tableRow');
|
|
429
|
-
expect(headerRow.content![0]
|
|
430
|
-
expect(headerRow.content![1]
|
|
440
|
+
expect(headerRow.content![0]!.type).toBe('tableHeader');
|
|
441
|
+
expect(headerRow.content![1]!.type).toBe('tableHeader');
|
|
431
442
|
|
|
432
443
|
// Second row should have tableCell cells
|
|
433
|
-
const bodyRow = table!.content![1]
|
|
444
|
+
const bodyRow = table!.content![1]!;
|
|
434
445
|
expect(bodyRow.type).toBe('tableRow');
|
|
435
|
-
expect(bodyRow.content![0]
|
|
446
|
+
expect(bodyRow.content![0]!.type).toBe('tableCell');
|
|
436
447
|
});
|
|
437
448
|
|
|
438
449
|
test('should handle table cells with colspan and rowspan', () => {
|
|
@@ -450,9 +461,9 @@ describe('htmlToTiptap', () => {
|
|
|
450
461
|
const table = findNode(result, 'table');
|
|
451
462
|
expect(table).toBeDefined();
|
|
452
463
|
expect(table!.content!.length).toBe(1);
|
|
453
|
-
const row = table!.content![0]
|
|
464
|
+
const row = table!.content![0]!;
|
|
454
465
|
expect(row.type).toBe('tableRow');
|
|
455
|
-
expect(row.content![0]
|
|
466
|
+
expect(row.content![0]!.type).toBe('tableCell');
|
|
456
467
|
});
|
|
457
468
|
|
|
458
469
|
test('should give empty table cells an empty paragraph', () => {
|
|
@@ -461,7 +472,7 @@ describe('htmlToTiptap', () => {
|
|
|
461
472
|
const cell = findNode(result, 'tableCell');
|
|
462
473
|
expect(cell).toBeDefined();
|
|
463
474
|
expect(cell!.content!.length).toBe(1);
|
|
464
|
-
expect(cell!.content![0]
|
|
475
|
+
expect(cell!.content![0]!.type).toBe('paragraph');
|
|
465
476
|
});
|
|
466
477
|
|
|
467
478
|
test('should handle <th> cells in thead as tableHeader', () => {
|
|
@@ -487,33 +498,33 @@ describe('htmlToTiptap', () => {
|
|
|
487
498
|
test('should parse <div> children without wrapping node', () => {
|
|
488
499
|
const result = htmlToTiptap('<div><p>Inside div</p></div>');
|
|
489
500
|
// The div should not produce a wrapping node; its children (the <p>) should be at top level
|
|
490
|
-
expect(result.content[0]
|
|
491
|
-
expect(result.content[0]
|
|
501
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
502
|
+
expect(result.content[0]!.content![0]!.text).toBe('Inside div');
|
|
492
503
|
});
|
|
493
504
|
|
|
494
505
|
test('should parse <article> children without wrapping node', () => {
|
|
495
506
|
const result = htmlToTiptap('<article><p>Article content</p></article>');
|
|
496
|
-
expect(result.content[0]
|
|
507
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
497
508
|
});
|
|
498
509
|
|
|
499
510
|
test('should parse <section> children without wrapping node', () => {
|
|
500
511
|
const result = htmlToTiptap('<section><p>Section content</p></section>');
|
|
501
|
-
expect(result.content[0]
|
|
512
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
502
513
|
});
|
|
503
514
|
|
|
504
515
|
test('should parse <main> children without wrapping node', () => {
|
|
505
516
|
const result = htmlToTiptap('<main><p>Main content</p></main>');
|
|
506
|
-
expect(result.content[0]
|
|
517
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
507
518
|
});
|
|
508
519
|
|
|
509
520
|
test('should parse <header> children without wrapping node', () => {
|
|
510
521
|
const result = htmlToTiptap('<header><h1>Title</h1></header>');
|
|
511
|
-
expect(result.content[0]
|
|
522
|
+
expect(result.content[0]!.type).toBe('heading');
|
|
512
523
|
});
|
|
513
524
|
|
|
514
525
|
test('should parse <footer> children without wrapping node', () => {
|
|
515
526
|
const result = htmlToTiptap('<footer><p>Footer</p></footer>');
|
|
516
|
-
expect(result.content[0]
|
|
527
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
517
528
|
});
|
|
518
529
|
});
|
|
519
530
|
|
|
@@ -524,7 +535,7 @@ describe('htmlToTiptap', () => {
|
|
|
524
535
|
test('should wrap top-level text in a paragraph', () => {
|
|
525
536
|
const result = htmlToTiptap('Just text');
|
|
526
537
|
expect(result.content.length).toBe(1);
|
|
527
|
-
expect(result.content[0]
|
|
538
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
528
539
|
});
|
|
529
540
|
|
|
530
541
|
test('should wrap mixed inline and block siblings correctly', () => {
|
|
@@ -532,21 +543,21 @@ describe('htmlToTiptap', () => {
|
|
|
532
543
|
const result = htmlToTiptap('Before<p>Block</p>After');
|
|
533
544
|
// 'Before' should be wrapped in a paragraph, then the <p>, then 'After' in a paragraph
|
|
534
545
|
expect(result.content.length).toBe(3);
|
|
535
|
-
expect(result.content[0]
|
|
536
|
-
expect(result.content[0]
|
|
537
|
-
expect(result.content[1]
|
|
538
|
-
expect(result.content[1]
|
|
539
|
-
expect(result.content[2]
|
|
540
|
-
expect(result.content[2]
|
|
546
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
547
|
+
expect(result.content[0]!.content![0]!.text).toBe('Before');
|
|
548
|
+
expect(result.content[1]!.type).toBe('paragraph');
|
|
549
|
+
expect(result.content[1]!.content![0]!.text).toBe('Block');
|
|
550
|
+
expect(result.content[2]!.type).toBe('paragraph');
|
|
551
|
+
expect(result.content[2]!.content![0]!.text).toBe('After');
|
|
541
552
|
});
|
|
542
553
|
|
|
543
554
|
test('should group consecutive inline elements into one paragraph', () => {
|
|
544
555
|
const result = htmlToTiptap('<strong>Bold</strong> and <em>italic</em>');
|
|
545
556
|
// All inline content should end up in a single paragraph
|
|
546
557
|
expect(result.content.length).toBe(1);
|
|
547
|
-
expect(result.content[0]
|
|
558
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
548
559
|
// Should have multiple text nodes inside
|
|
549
|
-
expect(result.content[0]
|
|
560
|
+
expect(result.content[0]!.content!.length).toBeGreaterThanOrEqual(2);
|
|
550
561
|
});
|
|
551
562
|
});
|
|
552
563
|
|
|
@@ -557,27 +568,27 @@ describe('htmlToTiptap', () => {
|
|
|
557
568
|
test('should convert multiple paragraphs', () => {
|
|
558
569
|
const result = htmlToTiptap('<p>First</p><p>Second</p><p>Third</p>');
|
|
559
570
|
expect(result.content.length).toBe(3);
|
|
560
|
-
expect(result.content[0]
|
|
561
|
-
expect(result.content[1]
|
|
562
|
-
expect(result.content[2]
|
|
571
|
+
expect(result.content[0]!.content![0]!.text).toBe('First');
|
|
572
|
+
expect(result.content[1]!.content![0]!.text).toBe('Second');
|
|
573
|
+
expect(result.content[2]!.content![0]!.text).toBe('Third');
|
|
563
574
|
});
|
|
564
575
|
|
|
565
576
|
test('should convert paragraph with mixed text and marks', () => {
|
|
566
577
|
const result = htmlToTiptap('<p>Normal <strong>bold</strong> normal</p>');
|
|
567
|
-
const p = result.content[0]
|
|
578
|
+
const p = result.content[0]!;
|
|
568
579
|
expect(p.type).toBe('paragraph');
|
|
569
580
|
expect(p.content!.length).toBe(3);
|
|
570
|
-
expect(p.content![0]
|
|
571
|
-
expect(p.content![0]
|
|
572
|
-
expect(p.content![1]
|
|
573
|
-
expect(p.content![1]
|
|
574
|
-
expect(p.content![2]
|
|
581
|
+
expect(p.content![0]!.text).toBe('Normal ');
|
|
582
|
+
expect(p.content![0]!.marks).toBeUndefined();
|
|
583
|
+
expect(p.content![1]!.text).toBe('bold');
|
|
584
|
+
expect(p.content![1]!.marks![0]!.type).toBe('bold');
|
|
585
|
+
expect(p.content![2]!.text).toBe(' normal');
|
|
575
586
|
});
|
|
576
587
|
|
|
577
588
|
test('should handle empty paragraph', () => {
|
|
578
589
|
const result = htmlToTiptap('<p></p>');
|
|
579
590
|
expect(result.content.length).toBe(1);
|
|
580
|
-
const p = result.content[0]
|
|
591
|
+
const p = result.content[0]!;
|
|
581
592
|
expect(p.type).toBe('paragraph');
|
|
582
593
|
// Empty paragraph should not have content property (or have undefined content)
|
|
583
594
|
expect(p.content).toBeUndefined();
|
|
@@ -586,16 +597,16 @@ describe('htmlToTiptap', () => {
|
|
|
586
597
|
test('should handle heading followed by paragraph', () => {
|
|
587
598
|
const result = htmlToTiptap('<h1>Title</h1><p>Body text</p>');
|
|
588
599
|
expect(result.content.length).toBe(2);
|
|
589
|
-
expect(result.content[0]
|
|
590
|
-
expect(result.content[1]
|
|
600
|
+
expect(result.content[0]!.type).toBe('heading');
|
|
601
|
+
expect(result.content[1]!.type).toBe('paragraph');
|
|
591
602
|
});
|
|
592
603
|
|
|
593
604
|
test('should handle blockquote containing paragraph', () => {
|
|
594
605
|
const result = htmlToTiptap('<blockquote><p>Quoted paragraph</p></blockquote>');
|
|
595
|
-
const bq = result.content[0]
|
|
606
|
+
const bq = result.content[0]!;
|
|
596
607
|
expect(bq.type).toBe('blockquote');
|
|
597
|
-
expect(bq.content![0]
|
|
598
|
-
expect(bq.content![0]
|
|
608
|
+
expect(bq.content![0]!.type).toBe('paragraph');
|
|
609
|
+
expect(bq.content![0]!.content![0]!.text).toBe('Quoted paragraph');
|
|
599
610
|
});
|
|
600
611
|
});
|
|
601
612
|
|
|
@@ -606,8 +617,8 @@ describe('htmlToTiptap', () => {
|
|
|
606
617
|
test('should parse children of unknown elements', () => {
|
|
607
618
|
const result = htmlToTiptap('<custom-element><p>Content</p></custom-element>');
|
|
608
619
|
// Unknown element should have its children parsed
|
|
609
|
-
expect(result.content[0]
|
|
610
|
-
expect(result.content[0]
|
|
620
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
621
|
+
expect(result.content[0]!.content![0]!.text).toBe('Content');
|
|
611
622
|
});
|
|
612
623
|
});
|
|
613
624
|
|
|
@@ -633,20 +644,20 @@ describe('htmlToTiptap', () => {
|
|
|
633
644
|
describe('code block edge cases', () => {
|
|
634
645
|
test('should handle <pre><code> with no language class', () => {
|
|
635
646
|
const result = htmlToTiptap('<pre><code>plain code</code></pre>');
|
|
636
|
-
const codeBlock = result.content[0]
|
|
647
|
+
const codeBlock = result.content[0]!;
|
|
637
648
|
expect(codeBlock.type).toBe('codeBlock');
|
|
638
649
|
expect(codeBlock.attrs?.language).toBeUndefined();
|
|
639
650
|
});
|
|
640
651
|
|
|
641
652
|
test('should handle <pre><code class="language-python">', () => {
|
|
642
653
|
const result = htmlToTiptap('<pre><code class="language-python">print("hi")</code></pre>');
|
|
643
|
-
const codeBlock = result.content[0]
|
|
654
|
+
const codeBlock = result.content[0]!;
|
|
644
655
|
expect(codeBlock.attrs!.language).toBe('python');
|
|
645
656
|
});
|
|
646
657
|
|
|
647
658
|
test('should handle empty code block', () => {
|
|
648
659
|
const result = htmlToTiptap('<pre><code></code></pre>');
|
|
649
|
-
const codeBlock = result.content[0]
|
|
660
|
+
const codeBlock = result.content[0]!;
|
|
650
661
|
expect(codeBlock.type).toBe('codeBlock');
|
|
651
662
|
expect(codeBlock.content).toEqual([]);
|
|
652
663
|
});
|
|
@@ -678,7 +689,7 @@ describe('htmlToTiptap', () => {
|
|
|
678
689
|
describe('link edge cases', () => {
|
|
679
690
|
test('should handle <a> without href', () => {
|
|
680
691
|
const result = htmlToTiptap('<p><a>No href</a></p>');
|
|
681
|
-
const textNode = result.content[0]
|
|
692
|
+
const textNode = result.content[0]!.content![0]!;
|
|
682
693
|
const linkMark = textNode.marks!.find((m) => m.type === 'link');
|
|
683
694
|
expect(linkMark).toBeDefined();
|
|
684
695
|
expect(linkMark!.attrs!.href).toBe('');
|
|
@@ -686,7 +697,7 @@ describe('htmlToTiptap', () => {
|
|
|
686
697
|
|
|
687
698
|
test('should handle <a> with only href (no target or rel)', () => {
|
|
688
699
|
const result = htmlToTiptap('<p><a href="/page">Link</a></p>');
|
|
689
|
-
const textNode = result.content[0]
|
|
700
|
+
const textNode = result.content[0]!.content![0]!;
|
|
690
701
|
const linkMark = textNode.marks!.find((m) => m.type === 'link');
|
|
691
702
|
expect(linkMark!.attrs!.href).toBe('/page');
|
|
692
703
|
expect(linkMark!.attrs!.target).toBeUndefined();
|
|
@@ -708,9 +719,9 @@ describe('htmlToTiptap', () => {
|
|
|
708
719
|
test('should handle <hr> between paragraphs', () => {
|
|
709
720
|
const result = htmlToTiptap('<p>Above</p><hr><p>Below</p>');
|
|
710
721
|
expect(result.content.length).toBe(3);
|
|
711
|
-
expect(result.content[0]
|
|
712
|
-
expect(result.content[1]
|
|
713
|
-
expect(result.content[2]
|
|
722
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
723
|
+
expect(result.content[1]!.type).toBe('horizontalRule');
|
|
724
|
+
expect(result.content[2]!.type).toBe('paragraph');
|
|
714
725
|
});
|
|
715
726
|
});
|
|
716
727
|
|
|
@@ -721,9 +732,9 @@ describe('htmlToTiptap', () => {
|
|
|
721
732
|
test('should handle nested <ul> inside <li>', () => {
|
|
722
733
|
const html = '<ul><li>Parent<ul><li>Child</li></ul></li></ul>';
|
|
723
734
|
const result = htmlToTiptap(html);
|
|
724
|
-
const outerList = result.content[0]
|
|
735
|
+
const outerList = result.content[0]!;
|
|
725
736
|
expect(outerList.type).toBe('bulletList');
|
|
726
|
-
const listItem = outerList.content![0]
|
|
737
|
+
const listItem = outerList.content![0]!;
|
|
727
738
|
expect(listItem.type).toBe('listItem');
|
|
728
739
|
// The list item should contain both a paragraph (for "Parent") and a nested bulletList
|
|
729
740
|
const nestedList = listItem.content!.find((n) => n.type === 'bulletList');
|
|
@@ -737,7 +748,7 @@ describe('htmlToTiptap', () => {
|
|
|
737
748
|
describe('whitespace handling', () => {
|
|
738
749
|
test('should preserve meaningful whitespace in text', () => {
|
|
739
750
|
const result = htmlToTiptap('<p>Hello World</p>');
|
|
740
|
-
expect(result.content[0]
|
|
751
|
+
expect(result.content[0]!.content![0]!.text).toBe('Hello World');
|
|
741
752
|
});
|
|
742
753
|
});
|
|
743
754
|
|
|
@@ -752,10 +763,10 @@ describe('htmlToTiptap', () => {
|
|
|
752
763
|
const result = htmlToTiptap('<p>Hello <strong>world</strong></p>');
|
|
753
764
|
expect(result.type).toBe('doc');
|
|
754
765
|
expect(result.content.length).toBe(1);
|
|
755
|
-
expect(result.content[0]
|
|
766
|
+
expect(result.content[0]!.type).toBe('paragraph');
|
|
756
767
|
// Should strip HTML tags and return plain text
|
|
757
|
-
expect(result.content[0]
|
|
758
|
-
expect(result.content[0]
|
|
768
|
+
expect(result.content[0]!.content![0]!.type).toBe('text');
|
|
769
|
+
expect(result.content[0]!.content![0]!.text).toBe('Hello world');
|
|
759
770
|
} finally {
|
|
760
771
|
(globalThis as any).DOMParser = originalDOMParser;
|
|
761
772
|
}
|
|
@@ -766,7 +777,7 @@ describe('htmlToTiptap', () => {
|
|
|
766
777
|
delete (globalThis as any).DOMParser;
|
|
767
778
|
try {
|
|
768
779
|
const result = htmlToTiptap('Just plain text');
|
|
769
|
-
expect(result.content[0]
|
|
780
|
+
expect(result.content[0]!.content![0]!.text).toBe('Just plain text');
|
|
770
781
|
} finally {
|
|
771
782
|
(globalThis as any).DOMParser = originalDOMParser;
|
|
772
783
|
}
|
|
@@ -779,7 +790,7 @@ describe('htmlToTiptap', () => {
|
|
|
779
790
|
describe('non-element node types', () => {
|
|
780
791
|
test('should ignore HTML comments', () => {
|
|
781
792
|
const result = htmlToTiptap('<p>Before<!-- comment -->After</p>');
|
|
782
|
-
const p = result.content[0]
|
|
793
|
+
const p = result.content[0]!;
|
|
783
794
|
expect(p.type).toBe('paragraph');
|
|
784
795
|
// Comment node should be ignored, only text nodes remain
|
|
785
796
|
const textNodes = p.content!.filter((n) => n.type === 'text');
|
|
@@ -796,7 +807,7 @@ describe('htmlToTiptap', () => {
|
|
|
796
807
|
describe('table elements parsed standalone via parseNode', () => {
|
|
797
808
|
test('should handle <strike> tag as strike mark', () => {
|
|
798
809
|
const result = htmlToTiptap('<p><strike>Strikethrough</strike></p>');
|
|
799
|
-
const textNode = result.content[0]
|
|
810
|
+
const textNode = result.content[0]!.content![0]!;
|
|
800
811
|
expect(textNode.marks!.some((m) => m.type === 'strike')).toBe(true);
|
|
801
812
|
});
|
|
802
813
|
|
|
@@ -839,12 +850,10 @@ describe('htmlToTiptap', () => {
|
|
|
839
850
|
test('should apply mark recursively to nested inline content', () => {
|
|
840
851
|
// <strong> wrapping a <br> and text - hardBreak is a non-text node
|
|
841
852
|
const result = htmlToTiptap('<p><strong>Before<br>After</strong></p>');
|
|
842
|
-
const p = result.content[0]
|
|
853
|
+
const p = result.content[0]!;
|
|
843
854
|
expect(p.type).toBe('paragraph');
|
|
844
855
|
// Should have text nodes with bold mark and a hardBreak
|
|
845
|
-
const boldTexts = p.content!.filter(
|
|
846
|
-
(n) => n.type === 'text' && n.marks?.some((m) => m.type === 'bold')
|
|
847
|
-
);
|
|
856
|
+
const boldTexts = p.content!.filter((n) => n.type === 'text' && n.marks?.some((m) => m.type === 'bold'));
|
|
848
857
|
expect(boldTexts.length).toBeGreaterThanOrEqual(1);
|
|
849
858
|
});
|
|
850
859
|
|
|
@@ -854,8 +863,8 @@ describe('htmlToTiptap', () => {
|
|
|
854
863
|
// but is not a text node itself. However, parseLink returns text nodes with link mark,
|
|
855
864
|
// so the bold mark is applied to text nodes that already have marks.
|
|
856
865
|
const result = htmlToTiptap('<p><strong><a href="/url">Link text</a></strong></p>');
|
|
857
|
-
const p = result.content[0]
|
|
858
|
-
const textNode = p.content![0]
|
|
866
|
+
const p = result.content[0]!;
|
|
867
|
+
const textNode = p.content![0]!;
|
|
859
868
|
expect(textNode.type).toBe('text');
|
|
860
869
|
expect(textNode.text).toBe('Link text');
|
|
861
870
|
const markTypes = textNode.marks!.map((m) => m.type);
|
|
@@ -872,7 +881,7 @@ describe('htmlToTiptap', () => {
|
|
|
872
881
|
// This should trigger the 'tr' case in parseNode switch (line 207-208)
|
|
873
882
|
const result = htmlToTiptap('<div><tr><td>Cell</td></tr></div>');
|
|
874
883
|
// The tr is parsed via parseTableRow -> produces tableRow
|
|
875
|
-
const
|
|
884
|
+
const _tableRow = findNode(result, 'tableRow');
|
|
876
885
|
// Depending on happy-dom's handling, the <tr> may or may not survive
|
|
877
886
|
// But parseNode should handle it
|
|
878
887
|
expect(result.type).toBe('doc');
|
|
@@ -897,7 +906,7 @@ describe('htmlToTiptap', () => {
|
|
|
897
906
|
describe('empty heading', () => {
|
|
898
907
|
test('should handle empty heading', () => {
|
|
899
908
|
const result = htmlToTiptap('<h1></h1>');
|
|
900
|
-
const heading = result.content[0]
|
|
909
|
+
const heading = result.content[0]!;
|
|
901
910
|
expect(heading.type).toBe('heading');
|
|
902
911
|
expect(heading.attrs!.level).toBe(1);
|
|
903
912
|
expect(heading.content).toBeUndefined();
|
|
@@ -942,7 +951,7 @@ describe('htmlToTiptap', () => {
|
|
|
942
951
|
const result = htmlToTiptap('<div><li>Item outside list</li></div>');
|
|
943
952
|
const listItem = findNode(result, 'listItem');
|
|
944
953
|
expect(listItem).toBeDefined();
|
|
945
|
-
expect(listItem!.content![0]
|
|
954
|
+
expect(listItem!.content![0]!.type).toBe('paragraph');
|
|
946
955
|
});
|
|
947
956
|
});
|
|
948
957
|
});
|