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,29 +1,52 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
stylesToClasses,
|
|
4
|
+
responsiveStylesToClasses,
|
|
5
|
+
classToStyle,
|
|
6
|
+
classesToStyles,
|
|
7
|
+
splitStyleByClassability,
|
|
8
|
+
isClassableStyleValue,
|
|
9
|
+
styleHasMapping,
|
|
10
|
+
} from './utilityClassMapper';
|
|
3
11
|
import { getStyleValue, clearRegistry } from './styleValueRegistry';
|
|
4
12
|
import type { ResponsiveScales } from './responsiveScaling';
|
|
5
13
|
|
|
6
14
|
describe('utilityClassMapper', () => {
|
|
7
15
|
describe('stylesToClasses', () => {
|
|
8
|
-
test('converts padding to
|
|
9
|
-
|
|
10
|
-
expect(
|
|
16
|
+
test('converts padding on an exact scale step to a named token', () => {
|
|
17
|
+
expect(stylesToClasses({ padding: '16px' })).toContain('p-4');
|
|
18
|
+
expect(stylesToClasses({ padding: '10px' })).toContain('p-2.5');
|
|
11
19
|
});
|
|
12
20
|
|
|
13
|
-
test('
|
|
21
|
+
test('off-scale padding falls back to the arbitrary form', () => {
|
|
22
|
+
expect(stylesToClasses({ padding: '13px' })).toContain('p-[13px]');
|
|
23
|
+
expect(stylesToClasses({ padding: '5px' })).toContain('p-[5px]');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('converts display flex to Tailwind static utility', () => {
|
|
14
27
|
const classes = stylesToClasses({ display: 'flex' });
|
|
15
|
-
expect(classes).toContain('
|
|
28
|
+
expect(classes).toContain('flex');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('converts display none to hidden', () => {
|
|
32
|
+
const classes = stylesToClasses({ display: 'none' });
|
|
33
|
+
expect(classes).toContain('hidden');
|
|
16
34
|
});
|
|
17
35
|
|
|
18
36
|
test('converts multiple properties', () => {
|
|
19
37
|
const classes = stylesToClasses({ padding: '10px', margin: '20px' });
|
|
20
|
-
expect(classes).toContain('p-
|
|
21
|
-
expect(classes).toContain('m-
|
|
38
|
+
expect(classes).toContain('p-2.5');
|
|
39
|
+
expect(classes).toContain('m-5');
|
|
22
40
|
});
|
|
23
41
|
|
|
24
42
|
test('handles percentage values', () => {
|
|
25
43
|
const classes = stylesToClasses({ width: '50%' });
|
|
26
|
-
expect(classes).toContain('w-
|
|
44
|
+
expect(classes).toContain('w-[50%]');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('maps width 100% to w-full', () => {
|
|
48
|
+
const classes = stylesToClasses({ width: '100%' });
|
|
49
|
+
expect(classes).toContain('w-full');
|
|
27
50
|
});
|
|
28
51
|
|
|
29
52
|
test('handles auto values', () => {
|
|
@@ -31,9 +54,9 @@ describe('utilityClassMapper', () => {
|
|
|
31
54
|
expect(classes).toContain('m-auto');
|
|
32
55
|
});
|
|
33
56
|
|
|
34
|
-
test('handles values with spaces', () => {
|
|
57
|
+
test('handles values with spaces (underscore encoding)', () => {
|
|
35
58
|
const classes = stylesToClasses({ padding: '92px 0' });
|
|
36
|
-
expect(classes).toContain('p-
|
|
59
|
+
expect(classes).toContain('p-[92px_0]');
|
|
37
60
|
});
|
|
38
61
|
|
|
39
62
|
test('returns empty array for null', () => {
|
|
@@ -41,60 +64,129 @@ describe('utilityClassMapper', () => {
|
|
|
41
64
|
expect(stylesToClasses(undefined)).toEqual([]);
|
|
42
65
|
});
|
|
43
66
|
|
|
44
|
-
test('handles color
|
|
45
|
-
|
|
46
|
-
expect(
|
|
67
|
+
test('handles bare color tokens (Meno token form)', () => {
|
|
68
|
+
expect(stylesToClasses({ color: 'text' })).toContain('text-text');
|
|
69
|
+
expect(stylesToClasses({ backgroundColor: 'primary' })).toContain('bg-primary');
|
|
70
|
+
expect(stylesToClasses({ borderColor: 'border-light' })).toContain('border-border-light');
|
|
47
71
|
});
|
|
48
72
|
|
|
49
|
-
test('
|
|
73
|
+
test('named CSS colors / hex / Tailwind palette shapes stay arbitrary (not bare tokens)', () => {
|
|
74
|
+
expect(stylesToClasses({ backgroundColor: 'red' })).toContain('bg-[red]');
|
|
75
|
+
expect(stylesToClasses({ color: '#fff' })).toContain('text-[#fff]');
|
|
76
|
+
expect(stylesToClasses({ backgroundColor: 'gray-500' })).toContain('bg-[gray-500]');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('handles font-size with explicit value', () => {
|
|
80
|
+
const classes = stylesToClasses({ fontSize: '48px' });
|
|
81
|
+
expect(classes).toContain('text-[48px]');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('handles font-size variables with length hint', () => {
|
|
85
|
+
const classes = stylesToClasses({ fontSize: 'var(--font-size-lg)' });
|
|
86
|
+
expect(classes).toContain('text-(length:--font-size-lg)');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('font-weight var carries a number hint (not misread as font-family)', () => {
|
|
90
|
+
// `font-*` is shared by weight/family; without the hint a var binding resolves to
|
|
91
|
+
// font-family (`font-family: 450`), so the weight never applies and the family changes.
|
|
92
|
+
expect(stylesToClasses({ fontWeight: 'var(--h-fw)' })).toContain('font-(number:--h-fw)');
|
|
93
|
+
expect(stylesToClasses({ fontFamily: 'var(--l-ff)' })).toContain('font-(--l-ff)');
|
|
94
|
+
// round-trips back to the right property
|
|
95
|
+
expect(classToStyle('font-(number:--h-fw)')).toEqual({ prop: 'fontWeight', value: 'var(--h-fw)' });
|
|
96
|
+
expect(classToStyle('font-(--l-ff)')).toEqual({ prop: 'fontFamily', value: 'var(--l-ff)' });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('border/outline shorthand var binding stays the shorthand (not the color longhand)', () => {
|
|
100
|
+
// `border`/`outline` roots are shared by a shorthand and a color longhand. A `var(--x)` value
|
|
101
|
+
// is opaque and reads as a color, so the bare `border-(--x)` form would round-trip to
|
|
102
|
+
// `borderColor`. The shorthand must emit the explicit arbitrary-property form instead.
|
|
103
|
+
expect(stylesToClasses({ border: 'var(--bs)' })).toEqual(['[border:var(--bs)]']);
|
|
104
|
+
expect(stylesToClasses({ outline: 'var(--ol)' })).toEqual(['[outline:var(--ol)]']);
|
|
105
|
+
expect(stylesToClasses({ outlineWidth: 'var(--ow)' })).toEqual(['[outline-width:var(--ow)]']);
|
|
106
|
+
// round-trips back to the SAME property + var value
|
|
107
|
+
expect(classToStyle('[border:var(--bs)]')).toEqual({ prop: 'border', value: 'var(--bs)' });
|
|
108
|
+
expect(classToStyle('[outline:var(--ol)]')).toEqual({ prop: 'outline', value: 'var(--ol)' });
|
|
109
|
+
// the COLOR longhand emits the BARE token form (`var(--bs)` is a token reference); the
|
|
110
|
+
// token-aware inverse recovers it, and the `var(--bs)` shorthand still parses (back-compat).
|
|
111
|
+
expect(stylesToClasses({ borderColor: 'var(--bs)' })).toEqual(['border-bs']);
|
|
112
|
+
expect(classToStyle('border-bs', new Set(['bs']))).toEqual({ prop: 'borderColor', value: 'bs' });
|
|
113
|
+
expect(classToStyle('border-(--bs)')).toEqual({ prop: 'borderColor', value: 'bs' });
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('converts shadow preset to shadow- class', () => {
|
|
50
117
|
const classes = stylesToClasses({ boxShadow: '2' });
|
|
51
|
-
expect(classes).toContain('
|
|
118
|
+
expect(classes).toContain('shadow-2');
|
|
52
119
|
});
|
|
53
120
|
|
|
54
|
-
test('converts gradient preset to
|
|
121
|
+
test('converts gradient preset to bg-gradient- class', () => {
|
|
55
122
|
const classes = stylesToClasses({ background: '1' });
|
|
56
|
-
expect(classes).toContain('
|
|
123
|
+
expect(classes).toContain('bg-gradient-1');
|
|
57
124
|
});
|
|
58
125
|
|
|
59
|
-
test('converts border preset to
|
|
126
|
+
test('converts border preset to border-preset- class', () => {
|
|
60
127
|
const classes = stylesToClasses({ border: '0' });
|
|
61
|
-
expect(classes).toContain('
|
|
128
|
+
expect(classes).toContain('border-preset-0');
|
|
62
129
|
});
|
|
63
130
|
|
|
64
131
|
test('handles aspectRatio with slash value', () => {
|
|
65
132
|
const classes = stylesToClasses({ aspectRatio: '1/1' });
|
|
66
|
-
expect(classes).toContain('
|
|
133
|
+
expect(classes).toContain('aspect-[1/1]');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('handles CSS function values', () => {
|
|
137
|
+
const classes = stylesToClasses({ transform: 'translateY(-50%)' });
|
|
138
|
+
expect(classes).toContain('transform-[translateY(-50%)]');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('handles grid template repeat()', () => {
|
|
142
|
+
const classes = stylesToClasses({ gridTemplateColumns: 'repeat(3, 1fr)' });
|
|
143
|
+
expect(classes).toContain('grid-cols-[repeat(3,_1fr)]');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('handles z-index Tailwind-style bare number', () => {
|
|
147
|
+
const classes = stylesToClasses({ zIndex: 10 });
|
|
148
|
+
expect(classes).toContain('z-10');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('handles opacity on Tailwind 0-100 scale', () => {
|
|
152
|
+
const classes = stylesToClasses({ opacity: 0.5 });
|
|
153
|
+
expect(classes).toContain('opacity-50');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('handles properties without a Tailwind root via arbitrary properties', () => {
|
|
157
|
+
const classes = stylesToClasses({ textOverflow: 'ellipsis' });
|
|
158
|
+
expect(classes).toContain('[text-overflow:ellipsis]');
|
|
67
159
|
});
|
|
68
160
|
|
|
69
161
|
test('align-items stretch maps to a distinct class from flex-start', () => {
|
|
70
|
-
// Regression: stretch
|
|
71
|
-
//
|
|
72
|
-
// — exactly what made Finsweet's mobile CTA buttons line
|
|
73
|
-
// left instead of stretching to full width.
|
|
162
|
+
// Regression: stretch must never share a class with flex-start,
|
|
163
|
+
// otherwise imported `align-items: stretch` silently renders as
|
|
164
|
+
// flex-start — exactly what made Finsweet's mobile CTA buttons line
|
|
165
|
+
// up to the left instead of stretching to full width.
|
|
74
166
|
const stretchClasses = stylesToClasses({ alignItems: 'stretch' });
|
|
75
167
|
const flexStartClasses = stylesToClasses({ alignItems: 'flex-start' });
|
|
76
|
-
expect(stretchClasses).toContain('
|
|
77
|
-
expect(flexStartClasses).toContain('
|
|
78
|
-
expect(stretchClasses).not.toContain('
|
|
79
|
-
expect(classToStyle('
|
|
168
|
+
expect(stretchClasses).toContain('items-stretch');
|
|
169
|
+
expect(flexStartClasses).toContain('items-start');
|
|
170
|
+
expect(stretchClasses).not.toContain('items-start');
|
|
171
|
+
expect(classToStyle('items-stretch')).toEqual({ prop: 'alignItems', value: 'stretch' });
|
|
80
172
|
});
|
|
81
173
|
|
|
82
174
|
test('justify-content space-evenly maps to a distinct class from flex-end', () => {
|
|
83
|
-
// Same bug shape as align-items: `space-evenly`
|
|
84
|
-
//
|
|
175
|
+
// Same bug shape as align-items: `space-evenly` must not alias to
|
|
176
|
+
// flex-end, silently changing layout.
|
|
85
177
|
const evenly = stylesToClasses({ justifyContent: 'space-evenly' });
|
|
86
178
|
const end = stylesToClasses({ justifyContent: 'flex-end' });
|
|
87
|
-
expect(evenly).toContain('
|
|
88
|
-
expect(end).toContain('
|
|
89
|
-
expect(evenly).not.toContain('
|
|
90
|
-
expect(classToStyle('
|
|
179
|
+
expect(evenly).toContain('justify-evenly');
|
|
180
|
+
expect(end).toContain('justify-end');
|
|
181
|
+
expect(evenly).not.toContain('justify-end');
|
|
182
|
+
expect(classToStyle('justify-evenly')).toEqual({ prop: 'justifyContent', value: 'space-evenly' });
|
|
91
183
|
});
|
|
92
184
|
});
|
|
93
185
|
|
|
94
186
|
describe('responsiveStylesToClasses', () => {
|
|
95
187
|
test('handles flat style object', () => {
|
|
96
188
|
const classes = responsiveStylesToClasses({ padding: '10px' });
|
|
97
|
-
expect(classes).toContain('p-
|
|
189
|
+
expect(classes).toContain('p-2.5');
|
|
98
190
|
});
|
|
99
191
|
|
|
100
192
|
test('handles responsive styles with base', () => {
|
|
@@ -102,17 +194,19 @@ describe('utilityClassMapper', () => {
|
|
|
102
194
|
base: { padding: '10px' },
|
|
103
195
|
tablet: { padding: '20px' },
|
|
104
196
|
});
|
|
105
|
-
expect(classes).toContain('p-
|
|
106
|
-
|
|
197
|
+
expect(classes).toContain('p-2.5');
|
|
198
|
+
// Desktop-first: breakpoint overrides carry Tailwind's max-width `max-` marker.
|
|
199
|
+
expect(classes).toContain('max-lg:p-5');
|
|
107
200
|
});
|
|
108
201
|
|
|
109
|
-
test('handles mobile styles with
|
|
202
|
+
test('handles mobile styles with mobile: variant prefix', () => {
|
|
110
203
|
const classes = responsiveStylesToClasses({
|
|
111
204
|
base: { padding: '10px' },
|
|
112
205
|
mobile: { padding: '5px' },
|
|
113
206
|
});
|
|
114
|
-
expect(classes).toContain('p-
|
|
115
|
-
|
|
207
|
+
expect(classes).toContain('p-2.5');
|
|
208
|
+
// 5px is off the scale → stays arbitrary
|
|
209
|
+
expect(classes).toContain('max-sm:p-[5px]');
|
|
116
210
|
});
|
|
117
211
|
|
|
118
212
|
test('returns empty array for null', () => {
|
|
@@ -120,6 +214,35 @@ describe('utilityClassMapper', () => {
|
|
|
120
214
|
expect(responsiveStylesToClasses(undefined)).toEqual([]);
|
|
121
215
|
});
|
|
122
216
|
|
|
217
|
+
test('desktop-first breakpoint prefix: emits Tailwind max-lg:/max-sm:, parses every legacy form', () => {
|
|
218
|
+
// Meno is desktop-first (max-width); the two default tiers carry Tailwind's canonical scale
|
|
219
|
+
// names in the class string — tablet → max-lg: (1024px = Tailwind lg), mobile → max-sm: (540px,
|
|
220
|
+
// Meno keeps its threshold). The breakpoint IDENTITY in the style object stays tablet/mobile.
|
|
221
|
+
const classes = responsiveStylesToClasses({
|
|
222
|
+
base: { padding: '16px' },
|
|
223
|
+
tablet: { padding: '8px' },
|
|
224
|
+
mobile: { padding: '4px' },
|
|
225
|
+
});
|
|
226
|
+
expect(classes).toEqual(['p-4', 'max-lg:p-2', 'max-sm:p-1']);
|
|
227
|
+
|
|
228
|
+
const expected = { base: { padding: '16px' }, tablet: { padding: '8px' }, mobile: { padding: '4px' } };
|
|
229
|
+
// The canonical max-lg:/max-sm: form round-trips back to the tablet/mobile buckets.
|
|
230
|
+
expect(classesToStyles(['p-4', 'max-lg:p-2', 'max-sm:p-1'])).toEqual(expected);
|
|
231
|
+
// Transitional un-aliased max-tablet:/max-mobile: form (emitted briefly before the alias) parses.
|
|
232
|
+
expect(classesToStyles(['p-4', 'max-tablet:p-2', 'max-mobile:p-1'])).toEqual(expected);
|
|
233
|
+
// Legacy bare prefixes (pre-`max-` projects) still parse to the same buckets.
|
|
234
|
+
expect(classesToStyles(['p-4', 'tablet:p-2', 'mobile:p-1'])).toEqual(expected);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('foreign real-Tailwind min-width prefixes (bare lg:/sm:) are NOT absorbed as breakpoints', () => {
|
|
238
|
+
// A bare `lg:`/`sm:` is real-Tailwind MIN-width (opposite cascade) — Meno never emits it, so it
|
|
239
|
+
// must stay unrecognized rather than fold into the tablet/mobile buckets. (classesToStyles only
|
|
240
|
+
// buckets tablet/mobile; an un-mapped variant lands in base, but the studio adapter keeps such
|
|
241
|
+
// tokens foreign — see classAttrStyleAdapter.) Here: bare `lg:`/`sm:` do not map to tablet/mobile.
|
|
242
|
+
expect(classesToStyles(['lg:p-2'])?.tablet).toBeUndefined();
|
|
243
|
+
expect(classesToStyles(['sm:p-2'])?.mobile).toBeUndefined();
|
|
244
|
+
});
|
|
245
|
+
|
|
123
246
|
describe('fluid mode — mobile-as-MIN consumption', () => {
|
|
124
247
|
const fluidScales: ResponsiveScales = {
|
|
125
248
|
enabled: true,
|
|
@@ -130,39 +253,39 @@ describe('utilityClassMapper', () => {
|
|
|
130
253
|
padding: { tablet: 0.75, mobile: 0.5 },
|
|
131
254
|
};
|
|
132
255
|
|
|
133
|
-
test('consumes scalable mobile override into base clamp; no
|
|
256
|
+
test('consumes scalable mobile override into base clamp; no mobile: class emitted', () => {
|
|
134
257
|
clearRegistry();
|
|
135
258
|
const classes = responsiveStylesToClasses(
|
|
136
259
|
{ base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
|
|
137
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
260
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
138
261
|
);
|
|
139
|
-
// base became
|
|
140
|
-
const
|
|
141
|
-
expect(
|
|
142
|
-
expect(getStyleValue(
|
|
143
|
-
expect(getStyleValue(
|
|
144
|
-
// no
|
|
145
|
-
expect(classes.some((c) => c.startsWith('
|
|
262
|
+
// base became an arbitrary class wrapping clamp(94px, ..., 150px)
|
|
263
|
+
const clamped = classes.find((c) => c.startsWith('text-[clamp('));
|
|
264
|
+
expect(clamped).toBeDefined();
|
|
265
|
+
expect(String(getStyleValue(clamped!))).toMatch(/^clamp\(94px,/);
|
|
266
|
+
expect(String(getStyleValue(clamped!))).toContain(', 150px)');
|
|
267
|
+
// no mobile:text-* emitted
|
|
268
|
+
expect(classes.some((c) => c.startsWith('mobile:text-'))).toBe(false);
|
|
146
269
|
});
|
|
147
270
|
|
|
148
|
-
test('mobile-only non-scalable property (display) stays as
|
|
271
|
+
test('mobile-only non-scalable property (display) stays as mobile: class', () => {
|
|
149
272
|
clearRegistry();
|
|
150
273
|
const classes = responsiveStylesToClasses(
|
|
151
274
|
{
|
|
152
275
|
base: { fontSize: '150px' },
|
|
153
276
|
mobile: { fontSize: '94px', display: 'none' },
|
|
154
277
|
},
|
|
155
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
278
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
156
279
|
);
|
|
157
280
|
// fontSize consumed into base clamp
|
|
158
|
-
const
|
|
159
|
-
expect(
|
|
160
|
-
expect(getStyleValue(
|
|
281
|
+
const clamped = classes.find((c) => c.startsWith('text-[clamp('));
|
|
282
|
+
expect(clamped).toBeDefined();
|
|
283
|
+
expect(String(getStyleValue(clamped!))).toMatch(/^clamp\(94px,/);
|
|
161
284
|
// display:none survives on mobile
|
|
162
|
-
expect(classes).toContain('
|
|
285
|
+
expect(classes).toContain('max-sm:hidden');
|
|
163
286
|
});
|
|
164
287
|
|
|
165
|
-
test('tablet override stays as
|
|
288
|
+
test('tablet override stays as max-lg: class (unchanged behavior)', () => {
|
|
166
289
|
clearRegistry();
|
|
167
290
|
const classes = responsiveStylesToClasses(
|
|
168
291
|
{
|
|
@@ -170,96 +293,132 @@ describe('utilityClassMapper', () => {
|
|
|
170
293
|
tablet: { fontSize: '120px' },
|
|
171
294
|
mobile: { fontSize: '94px' },
|
|
172
295
|
},
|
|
173
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
296
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
174
297
|
);
|
|
175
|
-
expect(classes).toContain('
|
|
298
|
+
expect(classes).toContain('max-lg:text-[120px]');
|
|
176
299
|
// mobile consumed
|
|
177
|
-
expect(classes.some((c) => c.startsWith('
|
|
300
|
+
expect(classes.some((c) => c.startsWith('max-sm:text-'))).toBe(false);
|
|
178
301
|
});
|
|
179
302
|
|
|
180
303
|
test('no mobile override → base passes through unchanged (no consumption)', () => {
|
|
181
304
|
clearRegistry();
|
|
182
305
|
const classes = responsiveStylesToClasses(
|
|
183
306
|
{ base: { fontSize: '150px' } },
|
|
184
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
307
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
185
308
|
);
|
|
186
|
-
expect(classes).toContain('
|
|
187
|
-
expect(classes.some((c) =>
|
|
309
|
+
expect(classes).toContain('text-[150px]');
|
|
310
|
+
expect(classes.some((c) => c.startsWith('text-[clamp('))).toBe(false);
|
|
188
311
|
});
|
|
189
312
|
|
|
190
|
-
test('mismatched units (rem vs px) → falls back,
|
|
313
|
+
test('mismatched units (rem vs px) → falls back, mobile: class still emitted', () => {
|
|
191
314
|
clearRegistry();
|
|
192
315
|
const classes = responsiveStylesToClasses(
|
|
193
316
|
{ base: { fontSize: '10rem' }, mobile: { fontSize: '94px' } },
|
|
194
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
317
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
195
318
|
);
|
|
196
|
-
expect(classes).toContain('
|
|
197
|
-
expect(classes).toContain('
|
|
319
|
+
expect(classes).toContain('text-[10rem]');
|
|
320
|
+
expect(classes).toContain('max-sm:text-[94px]');
|
|
198
321
|
});
|
|
199
322
|
|
|
200
323
|
test('non-scalable mobile override (display) is not consumed', () => {
|
|
201
324
|
clearRegistry();
|
|
202
325
|
const classes = responsiveStylesToClasses(
|
|
203
326
|
{ base: { display: 'flex' }, mobile: { display: 'block' } },
|
|
204
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
327
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
205
328
|
);
|
|
206
|
-
expect(classes).toContain('
|
|
207
|
-
expect(classes).toContain('
|
|
329
|
+
expect(classes).toContain('flex');
|
|
330
|
+
expect(classes).toContain('max-sm:block');
|
|
208
331
|
});
|
|
209
332
|
|
|
210
|
-
test('fluidActive off → no consumption,
|
|
333
|
+
test('fluidActive off → no consumption, mobile: class emitted', () => {
|
|
211
334
|
clearRegistry();
|
|
212
335
|
const classes = responsiveStylesToClasses(
|
|
213
336
|
{ base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
|
|
214
|
-
{ fluidActive: false, responsiveScales: fluidScales }
|
|
337
|
+
{ fluidActive: false, responsiveScales: fluidScales },
|
|
215
338
|
);
|
|
216
|
-
expect(classes).toContain('
|
|
217
|
-
expect(classes).toContain('
|
|
339
|
+
expect(classes).toContain('text-[150px]');
|
|
340
|
+
expect(classes).toContain('max-sm:text-[94px]');
|
|
218
341
|
});
|
|
219
342
|
|
|
220
343
|
test('fluidActive on but responsiveScales disabled → no consumption', () => {
|
|
221
344
|
clearRegistry();
|
|
222
345
|
const classes = responsiveStylesToClasses(
|
|
223
346
|
{ base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
|
|
224
|
-
{ fluidActive: true, responsiveScales: { ...fluidScales, enabled: false } }
|
|
347
|
+
{ fluidActive: true, responsiveScales: { ...fluidScales, enabled: false } },
|
|
225
348
|
);
|
|
226
|
-
expect(classes).toContain('
|
|
227
|
-
expect(classes).toContain('
|
|
349
|
+
expect(classes).toContain('text-[150px]');
|
|
350
|
+
expect(classes).toContain('max-sm:text-[94px]');
|
|
228
351
|
});
|
|
229
352
|
|
|
230
353
|
test('clamp math: MIN=94px, MAX=150px, fluidRange 320–1700', () => {
|
|
231
354
|
clearRegistry();
|
|
232
355
|
const classes = responsiveStylesToClasses(
|
|
233
356
|
{ base: { fontSize: '150px' }, mobile: { fontSize: '94px' } },
|
|
234
|
-
{ fluidActive: true, responsiveScales: fluidScales }
|
|
357
|
+
{ fluidActive: true, responsiveScales: fluidScales },
|
|
235
358
|
);
|
|
236
|
-
const
|
|
237
|
-
const value = String(getStyleValue(
|
|
359
|
+
const clamped = classes.find((c) => c.startsWith('text-[clamp('))!;
|
|
360
|
+
const value = String(getStyleValue(clamped));
|
|
238
361
|
// slope = (150-94)/(1700-320) = 56/1380 ≈ 0.04058 px/px → slopeVw ≈ 4.058vw
|
|
239
362
|
// intercept = 94 - 0.04058*320 = 94 - 12.9855 ≈ 81.0145px
|
|
240
363
|
expect(value).toMatch(/clamp\(94px,\s*81\.0145px\s*\+\s*4\.058vw,\s*150px\)/);
|
|
241
364
|
});
|
|
242
365
|
});
|
|
366
|
+
|
|
367
|
+
describe('fluid mode — container intent (width === maxWidth)', () => {
|
|
368
|
+
test('rewrites equal width/maxWidth into the fluid container pattern', () => {
|
|
369
|
+
const classes = responsiveStylesToClasses(
|
|
370
|
+
{ base: { width: '1200px', maxWidth: '1200px' } },
|
|
371
|
+
{ fluidActive: true },
|
|
372
|
+
);
|
|
373
|
+
// width becomes a margin-aware calc, gains mx-auto; maxWidth (the cap) is kept.
|
|
374
|
+
expect(classes).toContain('w-[calc(100%_-_var(--site-margin)_*_2)]');
|
|
375
|
+
expect(classes).toContain('ml-auto');
|
|
376
|
+
expect(classes).toContain('mr-auto');
|
|
377
|
+
expect(classes).toContain('max-w-[1200px]');
|
|
378
|
+
expect(classes).not.toContain('w-[1200px]');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('fluidActive off → plain width/maxWidth classes, no container rewrite', () => {
|
|
382
|
+
expect(responsiveStylesToClasses({ base: { width: '1200px', maxWidth: '1200px' } })).toEqual([
|
|
383
|
+
'w-[1200px]',
|
|
384
|
+
'max-w-[1200px]',
|
|
385
|
+
]);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('width !== maxWidth is not container intent (left untouched even in fluid mode)', () => {
|
|
389
|
+
const classes = responsiveStylesToClasses(
|
|
390
|
+
{ base: { width: '1200px', maxWidth: '100%' } },
|
|
391
|
+
{ fluidActive: true },
|
|
392
|
+
);
|
|
393
|
+
expect(classes).toContain('w-[1200px]');
|
|
394
|
+
expect(classes).not.toContain('w-[calc(100%_-_var(--site-margin)_*_2)]');
|
|
395
|
+
});
|
|
396
|
+
});
|
|
243
397
|
});
|
|
244
398
|
|
|
245
399
|
describe('classToStyle', () => {
|
|
246
400
|
test('parses padding class', () => {
|
|
247
|
-
const style = classToStyle('p-10px');
|
|
401
|
+
const style = classToStyle('p-[10px]');
|
|
248
402
|
expect(style).toEqual({ prop: 'padding', value: '10px' });
|
|
249
403
|
});
|
|
250
404
|
|
|
251
|
-
test('parses
|
|
252
|
-
const style = classToStyle('
|
|
405
|
+
test('parses static flex utility', () => {
|
|
406
|
+
const style = classToStyle('flex');
|
|
253
407
|
expect(style).toEqual({ prop: 'display', value: 'flex' });
|
|
254
408
|
});
|
|
255
409
|
|
|
256
|
-
test('
|
|
257
|
-
const style = classToStyle('
|
|
410
|
+
test('parses hidden utility', () => {
|
|
411
|
+
const style = classToStyle('hidden');
|
|
412
|
+
expect(style).toEqual({ prop: 'display', value: 'none' });
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('handles tablet variant prefix', () => {
|
|
416
|
+
const style = classToStyle('tablet:p-[20px]');
|
|
258
417
|
expect(style).toEqual({ prop: 'padding', value: '20px' });
|
|
259
418
|
});
|
|
260
419
|
|
|
261
|
-
test('handles mobile prefix', () => {
|
|
262
|
-
const style = classToStyle('
|
|
420
|
+
test('handles mobile variant prefix', () => {
|
|
421
|
+
const style = classToStyle('mobile:p-[5px]');
|
|
263
422
|
expect(style).toEqual({ prop: 'padding', value: '5px' });
|
|
264
423
|
});
|
|
265
424
|
|
|
@@ -268,32 +427,69 @@ describe('utilityClassMapper', () => {
|
|
|
268
427
|
expect(classToStyle('unknown-class')).toBeNull();
|
|
269
428
|
});
|
|
270
429
|
|
|
430
|
+
test('decodes underscores back to spaces', () => {
|
|
431
|
+
const style = classToStyle('p-[92px_0]');
|
|
432
|
+
expect(style).toEqual({ prop: 'padding', value: '92px 0' });
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test('decodes color variables to bare tokens', () => {
|
|
436
|
+
const style = classToStyle('bg-(--primary)');
|
|
437
|
+
expect(style).toEqual({ prop: 'backgroundColor', value: 'primary' });
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test('decodes non-color variables to var() values', () => {
|
|
441
|
+
const style = classToStyle('w-(--site-width)');
|
|
442
|
+
expect(style).toEqual({ prop: 'width', value: 'var(--site-width)' });
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test('disambiguates text- by value: font-size vs color', () => {
|
|
446
|
+
expect(classToStyle('text-[14px]')).toEqual({ prop: 'fontSize', value: '14px' });
|
|
447
|
+
expect(classToStyle('text-[#fff]')).toEqual({ prop: 'color', value: '#fff' });
|
|
448
|
+
expect(classToStyle('text-(length:--font-size-lg)')).toEqual({
|
|
449
|
+
prop: 'fontSize',
|
|
450
|
+
value: 'var(--font-size-lg)',
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
test('decodes keyword utilities', () => {
|
|
455
|
+
expect(classToStyle('w-full')).toEqual({ prop: 'width', value: '100%' });
|
|
456
|
+
expect(classToStyle('h-screen')).toEqual({ prop: 'height', value: '100vh' });
|
|
457
|
+
expect(classToStyle('rounded-full')).toEqual({ prop: 'borderRadius', value: '9999px' });
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('decodes arbitrary-property classes', () => {
|
|
461
|
+
expect(classToStyle('[text-overflow:ellipsis]')).toEqual({ prop: 'textOverflow', value: 'ellipsis' });
|
|
462
|
+
});
|
|
463
|
+
|
|
271
464
|
test('parses shadow preset class', () => {
|
|
272
|
-
const style = classToStyle('
|
|
465
|
+
const style = classToStyle('shadow-2');
|
|
273
466
|
expect(style).toEqual({ prop: 'boxShadow', value: '0 4px 12px rgba(0, 0, 0, 0.1)' });
|
|
274
467
|
});
|
|
275
468
|
|
|
276
469
|
test('parses gradient preset class', () => {
|
|
277
|
-
const style = classToStyle('
|
|
278
|
-
expect(style).toEqual({
|
|
470
|
+
const style = classToStyle('bg-gradient-1');
|
|
471
|
+
expect(style).toEqual({
|
|
472
|
+
prop: 'background',
|
|
473
|
+
value: 'linear-gradient(90deg, rgba(10,22,40,0.85) 0%, rgba(10,22,40,0.2) 100%)',
|
|
474
|
+
});
|
|
279
475
|
});
|
|
280
476
|
|
|
281
477
|
test('parses border preset class', () => {
|
|
282
|
-
const style = classToStyle('
|
|
478
|
+
const style = classToStyle('border-preset-0');
|
|
283
479
|
expect(style).toEqual({ prop: 'border', value: '1px solid var(--border)' });
|
|
284
480
|
});
|
|
285
481
|
});
|
|
286
482
|
|
|
287
483
|
describe('classesToStyles', () => {
|
|
288
484
|
test('converts base classes to style object', () => {
|
|
289
|
-
const styles = classesToStyles(['p-10px', 'm-20px']);
|
|
485
|
+
const styles = classesToStyles(['p-[10px]', 'm-[20px]']);
|
|
290
486
|
expect(styles).toEqual({
|
|
291
487
|
base: { padding: '10px', margin: '20px' },
|
|
292
488
|
});
|
|
293
489
|
});
|
|
294
490
|
|
|
295
491
|
test('handles responsive classes', () => {
|
|
296
|
-
const styles = classesToStyles(['p-10px', '
|
|
492
|
+
const styles = classesToStyles(['p-[10px]', 'tablet:p-[20px]']);
|
|
297
493
|
expect(styles).toEqual({
|
|
298
494
|
base: { padding: '10px' },
|
|
299
495
|
tablet: { padding: '20px' },
|
|
@@ -301,7 +497,7 @@ describe('utilityClassMapper', () => {
|
|
|
301
497
|
});
|
|
302
498
|
|
|
303
499
|
test('handles mobile classes', () => {
|
|
304
|
-
const styles = classesToStyles(['p-10px', '
|
|
500
|
+
const styles = classesToStyles(['p-[10px]', 'mobile:p-[5px]']);
|
|
305
501
|
expect(styles).toEqual({
|
|
306
502
|
base: { padding: '10px' },
|
|
307
503
|
mobile: { padding: '5px' },
|
|
@@ -312,11 +508,153 @@ describe('utilityClassMapper', () => {
|
|
|
312
508
|
expect(classesToStyles([])).toBeNull();
|
|
313
509
|
});
|
|
314
510
|
|
|
511
|
+
test('keeps a narrower-breakpoint-only class string (no base) instead of returning null', () => {
|
|
512
|
+
// A class can style only a narrower breakpoint; this must not collapse to null (§11 adapter).
|
|
513
|
+
expect(classesToStyles(['tablet:p-[16px]'])).toEqual({ base: {}, tablet: { padding: '16px' } });
|
|
514
|
+
expect(classesToStyles(['mobile:p-[8px]'])).toEqual({ base: {}, mobile: { padding: '8px' } });
|
|
515
|
+
});
|
|
516
|
+
|
|
315
517
|
test('removes empty breakpoints', () => {
|
|
316
|
-
const styles = classesToStyles(['p-10px']);
|
|
518
|
+
const styles = classesToStyles(['p-[10px]']);
|
|
317
519
|
expect(styles).toEqual({ base: { padding: '10px' } });
|
|
318
520
|
expect(styles?.tablet).toBeUndefined();
|
|
319
521
|
expect(styles?.mobile).toBeUndefined();
|
|
320
522
|
});
|
|
523
|
+
|
|
524
|
+
test('round-trips styles → classes → styles', () => {
|
|
525
|
+
const original = {
|
|
526
|
+
base: { padding: '92px 0', display: 'flex', color: 'primary', width: '100%' },
|
|
527
|
+
mobile: { padding: '12px 0' },
|
|
528
|
+
};
|
|
529
|
+
const classes = responsiveStylesToClasses(original);
|
|
530
|
+
// `color: primary` emits the bare token `text-primary`, which round-trips
|
|
531
|
+
// only against the project's known tokens (the new token-aware contract).
|
|
532
|
+
expect(classesToStyles(classes, new Set(['primary']))).toEqual(original);
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
describe('named spacing scale', () => {
|
|
537
|
+
test('forward: exact steps emit named tokens; off-scale stays arbitrary', () => {
|
|
538
|
+
expect(stylesToClasses({ padding: '16px' })).toEqual(['p-4']);
|
|
539
|
+
expect(stylesToClasses({ gap: '24px' })).toEqual(['gap-6']);
|
|
540
|
+
expect(stylesToClasses({ marginTop: '8px' })).toEqual(['mt-2']);
|
|
541
|
+
expect(stylesToClasses({ paddingInline: '16px' })).toEqual(['px-4']);
|
|
542
|
+
expect(stylesToClasses({ width: '256px' })).toEqual(['w-64']);
|
|
543
|
+
expect(stylesToClasses({ padding: '0px' })).toEqual(['p-0']);
|
|
544
|
+
expect(stylesToClasses({ padding: '2px' })).toEqual(['p-0.5']);
|
|
545
|
+
// off-scale + negatives keep the arbitrary form
|
|
546
|
+
expect(stylesToClasses({ padding: '13px' })).toEqual(['p-[13px]']);
|
|
547
|
+
expect(stylesToClasses({ margin: '-16px' })).toEqual(['m-[-16px]']);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test('keyword values still win over the scale', () => {
|
|
551
|
+
expect(stylesToClasses({ width: '100%' })).toEqual(['w-full']);
|
|
552
|
+
expect(stylesToClasses({ minHeight: '100vh' })).toEqual(['min-h-screen']);
|
|
553
|
+
expect(stylesToClasses({ margin: 'auto' })).toEqual(['m-auto']);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
test('inverse: named steps decode back to px (durable from the name alone)', () => {
|
|
557
|
+
expect(classToStyle('p-4')).toEqual({ prop: 'padding', value: '16px' });
|
|
558
|
+
expect(classToStyle('p-2.5')).toEqual({ prop: 'padding', value: '10px' });
|
|
559
|
+
expect(classToStyle('gap-6')).toEqual({ prop: 'gap', value: '24px' });
|
|
560
|
+
expect(classToStyle('px-4')).toEqual({ prop: 'paddingInline', value: '16px' });
|
|
561
|
+
expect(classToStyle('w-64')).toEqual({ prop: 'width', value: '256px' });
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
test('round-trips through classesToStyles', () => {
|
|
565
|
+
const classes = responsiveStylesToClasses({ base: { padding: '16px', gap: '24px' }, tablet: { padding: '8px' } });
|
|
566
|
+
expect(classes).toEqual(['p-4', 'gap-6', 'max-lg:p-2']);
|
|
567
|
+
expect(classesToStyles(classes)).toEqual({ base: { padding: '16px', gap: '24px' }, tablet: { padding: '8px' } });
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
test('back-compat: the arbitrary form still parses', () => {
|
|
571
|
+
expect(classToStyle('p-[16px]')).toEqual({ prop: 'padding', value: '16px' });
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
describe('bare color tokens (token-aware)', () => {
|
|
576
|
+
const TOKENS = new Set(['primary', 'text', 'border', 'border-light']);
|
|
577
|
+
|
|
578
|
+
test('forward emits the bare token form', () => {
|
|
579
|
+
expect(stylesToClasses({ backgroundColor: 'primary' })).toEqual(['bg-primary']);
|
|
580
|
+
expect(stylesToClasses({ color: 'text' })).toEqual(['text-text']);
|
|
581
|
+
expect(stylesToClasses({ borderColor: 'border-light' })).toEqual(['border-border-light']);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
test('a var() value on a color prop also emits the bare form (var(--muted) → bg-muted)', () => {
|
|
585
|
+
// The editor stores color tokens as `var(--x)`; that IS a token reference, so it emits the same
|
|
586
|
+
// bare form as the bare value would — `var(--muted)` and `muted` agree on `bg-muted`.
|
|
587
|
+
expect(stylesToClasses({ backgroundColor: 'var(--muted)' })).toEqual(['bg-muted']);
|
|
588
|
+
expect(stylesToClasses({ color: 'var(--text)' })).toEqual(['text-text']);
|
|
589
|
+
expect(stylesToClasses({ borderColor: 'var(--border)' })).toEqual(['border-border']);
|
|
590
|
+
expect(stylesToClasses({ outlineColor: 'var(--ring)' })).toEqual(['outline-ring']);
|
|
591
|
+
expect(classToStyle('bg-muted', new Set(['muted']))).toEqual({ prop: 'backgroundColor', value: 'muted' });
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test('inverse needs the project token set; foreign classes are never tokens', () => {
|
|
595
|
+
expect(classToStyle('bg-primary', TOKENS)).toEqual({ prop: 'backgroundColor', value: 'primary' });
|
|
596
|
+
expect(classToStyle('border-border-light', TOKENS)).toEqual({ prop: 'borderColor', value: 'border-light' });
|
|
597
|
+
// unknown name → not a token (left for foreign-class passthrough)
|
|
598
|
+
expect(classToStyle('text-wrapper', TOKENS)).toBeNull();
|
|
599
|
+
// no token set → bare form is unrecognized (foreign-safe default)
|
|
600
|
+
expect(classToStyle('bg-primary')).toBeNull();
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test('round-trips with the token set', () => {
|
|
604
|
+
const classes = responsiveStylesToClasses({ base: { backgroundColor: 'primary', color: 'text' } });
|
|
605
|
+
expect(classes).toEqual(['bg-primary', 'text-text']);
|
|
606
|
+
expect(classesToStyles(classes, TOKENS)).toEqual({ base: { backgroundColor: 'primary', color: 'text' } });
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
test('back-compat: the var() shorthand still parses without a token set', () => {
|
|
610
|
+
expect(classToStyle('bg-(--primary)')).toEqual({ prop: 'backgroundColor', value: 'primary' });
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
describe('splitStyleByClassability', () => {
|
|
615
|
+
test('isClassableStyleValue: static strings/numbers yes; templates/objects no', () => {
|
|
616
|
+
expect(isClassableStyleValue('24px')).toBe(true);
|
|
617
|
+
expect(isClassableStyleValue(100)).toBe(true);
|
|
618
|
+
expect(isClassableStyleValue('{{x}}px')).toBe(false);
|
|
619
|
+
expect(isClassableStyleValue({ _mapping: true })).toBe(false);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test('separates static (classable) from template/mapping (residual), preserving breakpoints', () => {
|
|
623
|
+
const mapping = { _mapping: true as const, prop: 'theme', values: { a: 'red' } };
|
|
624
|
+
expect(
|
|
625
|
+
splitStyleByClassability({
|
|
626
|
+
base: { padding: '24px', width: '{{w}}px', color: mapping },
|
|
627
|
+
tablet: { padding: '16px' },
|
|
628
|
+
}),
|
|
629
|
+
).toEqual({
|
|
630
|
+
classable: { base: { padding: '24px' }, tablet: { padding: '16px' } },
|
|
631
|
+
residual: { base: { width: '{{w}}px', color: mapping } },
|
|
632
|
+
});
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
test('flat object splits under base; null/empty → null both sides', () => {
|
|
636
|
+
expect(splitStyleByClassability({ width: 100, label: '{{t}}' })).toEqual({
|
|
637
|
+
classable: { base: { width: 100 } },
|
|
638
|
+
residual: { base: { label: '{{t}}' } },
|
|
639
|
+
});
|
|
640
|
+
expect(splitStyleByClassability(null)).toEqual({ classable: null, residual: null });
|
|
641
|
+
expect(splitStyleByClassability({})).toEqual({ classable: null, residual: null });
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
describe('styleHasMapping', () => {
|
|
646
|
+
const mapping = { _mapping: true as const, prop: 'theme', values: { a: 'red' } };
|
|
647
|
+
test('detects a prop _mapping in any breakpoint (flat or responsive)', () => {
|
|
648
|
+
expect(styleHasMapping({ base: { color: mapping } })).toBe(true);
|
|
649
|
+
expect(styleHasMapping({ base: { padding: '8px' }, tablet: { color: mapping } })).toBe(true);
|
|
650
|
+
expect(styleHasMapping({ color: mapping })).toBe(true); // flat
|
|
651
|
+
});
|
|
652
|
+
test('false for plain / empty / nullish styles', () => {
|
|
653
|
+
expect(styleHasMapping({ base: { padding: '24px' } })).toBe(false);
|
|
654
|
+
expect(styleHasMapping({ padding: '24px' })).toBe(false);
|
|
655
|
+
expect(styleHasMapping({})).toBe(false);
|
|
656
|
+
expect(styleHasMapping(null)).toBe(false);
|
|
657
|
+
expect(styleHasMapping(undefined)).toBe(false);
|
|
658
|
+
});
|
|
321
659
|
});
|
|
322
660
|
});
|