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,256 +1,382 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
generateRuleForClass,
|
|
4
|
+
generateUtilityCSS,
|
|
5
|
+
generateSingleClassCSS,
|
|
6
|
+
extractUtilityClassesFromHTML,
|
|
7
|
+
generateInteractiveCSS,
|
|
8
|
+
applyContainerPattern,
|
|
9
|
+
styleObjectToCSS,
|
|
10
|
+
} from './cssGeneration';
|
|
3
11
|
import { DEFAULT_BREAKPOINTS } from './breakpoints';
|
|
4
12
|
import type { ResponsiveScales } from './responsiveScaling';
|
|
5
13
|
import type { InteractiveStyles } from './types/styles';
|
|
6
14
|
import { registerStyleValue, clearRegistry } from './styleValueRegistry';
|
|
7
15
|
|
|
8
16
|
describe('extractUtilityClassesFromHTML', () => {
|
|
9
|
-
test('extracts
|
|
10
|
-
const html = '<div class="
|
|
17
|
+
test('extracts inset classes', () => {
|
|
18
|
+
const html = '<div class="inset-[0] inset-auto inset-[auto_0_0]"></div>';
|
|
11
19
|
const classes = extractUtilityClassesFromHTML(html);
|
|
12
|
-
expect(classes.has('
|
|
13
|
-
expect(classes.has('
|
|
14
|
-
expect(classes.has('
|
|
20
|
+
expect(classes.has('inset-[0]')).toBe(true);
|
|
21
|
+
expect(classes.has('inset-auto')).toBe(true);
|
|
22
|
+
expect(classes.has('inset-[auto_0_0]')).toBe(true);
|
|
15
23
|
});
|
|
16
24
|
|
|
17
|
-
test('extracts
|
|
18
|
-
const html = '<div class="
|
|
25
|
+
test('extracts background-image classes', () => {
|
|
26
|
+
const html = '<div class="bg-[url(/image.jpg)] bg-[linear-gradient(#000,_#fff)]"></div>';
|
|
19
27
|
const classes = extractUtilityClassesFromHTML(html);
|
|
20
|
-
expect(classes.has('
|
|
21
|
-
expect(classes.has('
|
|
28
|
+
expect(classes.has('bg-[url(/image.jpg)]')).toBe(true);
|
|
29
|
+
expect(classes.has('bg-[linear-gradient(#000,_#fff)]')).toBe(true);
|
|
22
30
|
});
|
|
23
31
|
|
|
24
|
-
test('extracts
|
|
25
|
-
const html =
|
|
32
|
+
test('extracts a mix of utility class forms alongside each other', () => {
|
|
33
|
+
const html =
|
|
34
|
+
'<div class="w-full h-[30%] bg-[linear-gradient(#0000,_#0f1442_94%)] absolute bottom-[0] inset-[auto_0_0] z-2"></div>';
|
|
26
35
|
const classes = extractUtilityClassesFromHTML(html);
|
|
27
|
-
expect(classes.has('w-
|
|
28
|
-
expect(classes.has('h-
|
|
29
|
-
expect(classes.has('
|
|
30
|
-
expect(classes.has('
|
|
31
|
-
expect(classes.has('
|
|
32
|
-
expect(classes.has('
|
|
36
|
+
expect(classes.has('w-full')).toBe(true);
|
|
37
|
+
expect(classes.has('h-[30%]')).toBe(true);
|
|
38
|
+
expect(classes.has('bg-[linear-gradient(#0000,_#0f1442_94%)]')).toBe(true);
|
|
39
|
+
expect(classes.has('absolute')).toBe(true);
|
|
40
|
+
expect(classes.has('bottom-[0]')).toBe(true);
|
|
41
|
+
expect(classes.has('inset-[auto_0_0]')).toBe(true);
|
|
33
42
|
expect(classes.has('z-2')).toBe(true);
|
|
34
43
|
});
|
|
35
44
|
|
|
36
|
-
test('extracts
|
|
37
|
-
const html = '<div class="
|
|
45
|
+
test('extracts responsive variant classes', () => {
|
|
46
|
+
const html = '<div class="p-[40px] tablet:p-[24px] mobile:hidden"></div>';
|
|
38
47
|
const classes = extractUtilityClassesFromHTML(html);
|
|
39
|
-
expect(classes.has('
|
|
48
|
+
expect(classes.has('p-[40px]')).toBe(true);
|
|
49
|
+
expect(classes.has('tablet:p-[24px]')).toBe(true);
|
|
50
|
+
expect(classes.has('mobile:hidden')).toBe(true);
|
|
40
51
|
});
|
|
41
52
|
|
|
42
|
-
test('extracts
|
|
43
|
-
const html = '<div class="
|
|
53
|
+
test('extracts arbitrary-property classes', () => {
|
|
54
|
+
const html = '<div class="[text-overflow:ellipsis]"></div>';
|
|
44
55
|
const classes = extractUtilityClassesFromHTML(html);
|
|
45
|
-
expect(classes.has('
|
|
56
|
+
expect(classes.has('[text-overflow:ellipsis]')).toBe(true);
|
|
46
57
|
});
|
|
47
58
|
|
|
48
|
-
test('
|
|
49
|
-
const html = '<div class="
|
|
59
|
+
test('does not extract non-utility authored classes', () => {
|
|
60
|
+
const html = '<div class="nav-open text-wrapper c_hero_title m_label_1abc"></div>';
|
|
50
61
|
const classes = extractUtilityClassesFromHTML(html);
|
|
51
|
-
expect(classes.
|
|
52
|
-
expect(classes.has('l-auto')).toBe(true);
|
|
53
|
-
expect(classes.has('l-0')).toBe(true);
|
|
62
|
+
expect(classes.size).toBe(0);
|
|
54
63
|
});
|
|
55
64
|
});
|
|
56
65
|
|
|
57
66
|
describe('generateUtilityCSS end-to-end', () => {
|
|
58
|
-
test('generates CSS for
|
|
59
|
-
const html = '<div class="
|
|
67
|
+
test('generates CSS for inset classes from HTML', () => {
|
|
68
|
+
const html = '<div class="inset-[auto_0_0]"></div>';
|
|
60
69
|
const classes = extractUtilityClassesFromHTML(html);
|
|
61
70
|
const css = generateUtilityCSS(classes);
|
|
62
71
|
expect(css).toContain('inset: auto 0 0');
|
|
63
72
|
});
|
|
64
73
|
|
|
65
|
-
test('generates CSS for
|
|
66
|
-
const html = '<div class="
|
|
74
|
+
test('generates CSS for background-image classes from HTML', () => {
|
|
75
|
+
const html = '<div class="bg-[linear-gradient(#000,_#fff)]"></div>';
|
|
67
76
|
const classes = extractUtilityClassesFromHTML(html);
|
|
68
77
|
const css = generateUtilityCSS(classes);
|
|
69
78
|
expect(css).toContain('background-image: linear-gradient(#000, #fff)');
|
|
70
79
|
});
|
|
71
80
|
|
|
72
|
-
test('generates CSS for
|
|
73
|
-
const html = '<div class="
|
|
81
|
+
test('generates CSS for left classes from HTML', () => {
|
|
82
|
+
const html = '<div class="left-[10px] left-auto left-[0]"></div>';
|
|
74
83
|
const classes = extractUtilityClassesFromHTML(html);
|
|
75
84
|
const css = generateUtilityCSS(classes);
|
|
76
85
|
expect(css).toContain('left: 10px');
|
|
77
86
|
expect(css).toContain('left: auto');
|
|
78
87
|
expect(css).toContain('left: 0');
|
|
79
88
|
});
|
|
89
|
+
|
|
90
|
+
test('generates @media rules for responsive variant classes', () => {
|
|
91
|
+
const css = generateUtilityCSS(new Set(['p-[40px]', 'tablet:p-[24px]', 'mobile:p-[12px]']));
|
|
92
|
+
expect(css).toContain('.p-\\[40px\\] { padding: 40px; }');
|
|
93
|
+
expect(css).toContain('@media (max-width: 1024px)');
|
|
94
|
+
expect(css).toContain('.tablet\\:p-\\[24px\\] { padding: 24px; }');
|
|
95
|
+
expect(css).toContain('@media (max-width: 540px)');
|
|
96
|
+
expect(css).toContain('.mobile\\:p-\\[12px\\] { padding: 12px; }');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('auto-responsive @media keeps shorthand BEFORE longhand so margin-bottom survives', () => {
|
|
100
|
+
// A centered heading: margin:auto (centering) + marginBottom (spacing). The classes can
|
|
101
|
+
// arrive in either order; the longhand must win in EVERY breakpoint block (equal
|
|
102
|
+
// specificity ⇒ source order), so margin:auto can't reset margin-bottom on tablet.
|
|
103
|
+
const scales: ResponsiveScales = {
|
|
104
|
+
enabled: true,
|
|
105
|
+
mode: 'breakpoints',
|
|
106
|
+
baseReference: 16,
|
|
107
|
+
margin: { tablet: 0.7, mobile: 0.45 },
|
|
108
|
+
};
|
|
109
|
+
// Insertion order with the longhand FIRST — the bug only manifests when the shorthand
|
|
110
|
+
// would otherwise be emitted after it inside @media.
|
|
111
|
+
const css = generateUtilityCSS(new Set(['mb-[24px]', 'm-auto']), DEFAULT_BREAKPOINTS, scales);
|
|
112
|
+
const tabletStart = css.indexOf('@media (max-width: 1024px)');
|
|
113
|
+
const tabletBlock = css.slice(tabletStart, css.indexOf('}\n}', tabletStart) + 3);
|
|
114
|
+
const posShorthand = tabletBlock.indexOf('.m-auto');
|
|
115
|
+
const posLonghand = tabletBlock.indexOf('.mb-\\[24px\\]');
|
|
116
|
+
expect(posShorthand).toBeGreaterThanOrEqual(0);
|
|
117
|
+
expect(posLonghand).toBeGreaterThan(posShorthand); // shorthand precedes longhand → mb wins
|
|
118
|
+
});
|
|
80
119
|
});
|
|
81
120
|
|
|
82
121
|
describe('cssGeneration', () => {
|
|
83
122
|
describe('generateRuleForClass', () => {
|
|
84
|
-
test('border-top generates
|
|
85
|
-
const rule = generateRuleForClass('
|
|
123
|
+
test('border-top generates width and style longhands', () => {
|
|
124
|
+
const rule = generateRuleForClass('border-t-[1px_solid]');
|
|
86
125
|
expect(rule).toBe('border-top-width: 1px; border-top-style: solid;');
|
|
87
126
|
});
|
|
88
127
|
|
|
89
|
-
test('border-bottom generates
|
|
90
|
-
const rule = generateRuleForClass('
|
|
128
|
+
test('border-bottom generates width and style longhands', () => {
|
|
129
|
+
const rule = generateRuleForClass('border-b-[2px_dashed]');
|
|
91
130
|
expect(rule).toBe('border-bottom-width: 2px; border-bottom-style: dashed;');
|
|
92
131
|
});
|
|
93
132
|
|
|
94
|
-
test('border-left generates
|
|
95
|
-
const rule = generateRuleForClass('
|
|
133
|
+
test('border-left generates width and style longhands', () => {
|
|
134
|
+
const rule = generateRuleForClass('border-l-[1px_dotted]');
|
|
96
135
|
expect(rule).toBe('border-left-width: 1px; border-left-style: dotted;');
|
|
97
136
|
});
|
|
98
137
|
|
|
99
|
-
test('border-right generates
|
|
100
|
-
const rule = generateRuleForClass('border-r-
|
|
138
|
+
test('border-right generates width and style longhands', () => {
|
|
139
|
+
const rule = generateRuleForClass('border-r-[3px_solid]');
|
|
101
140
|
expect(rule).toBe('border-right-width: 3px; border-right-style: solid;');
|
|
102
141
|
});
|
|
103
142
|
|
|
104
|
-
test('border
|
|
105
|
-
const rule = generateRuleForClass('
|
|
143
|
+
test('border side with color emits color as a longhand too', () => {
|
|
144
|
+
const rule = generateRuleForClass('border-t-[1px_solid_red]');
|
|
145
|
+
expect(rule).toBe('border-top-width: 1px; border-top-style: solid; border-top-color: red;');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('border (full shorthand) still includes color', () => {
|
|
149
|
+
const rule = generateRuleForClass('border-[1px_solid_var(--primary)]');
|
|
106
150
|
expect(rule).toBe('border: 1px solid var(--primary);');
|
|
107
151
|
});
|
|
108
152
|
|
|
109
153
|
test('border-color with named CSS color outputs directly', () => {
|
|
110
|
-
const rule = generateRuleForClass('
|
|
154
|
+
const rule = generateRuleForClass('border-[red]');
|
|
111
155
|
expect(rule).toBe('border-color: red;');
|
|
112
156
|
});
|
|
113
157
|
|
|
114
158
|
test('border-color with variable uses var()', () => {
|
|
115
|
-
const rule = generateRuleForClass('
|
|
159
|
+
const rule = generateRuleForClass('border-(--primary)');
|
|
116
160
|
expect(rule).toBe('border-color: var(--primary);');
|
|
117
161
|
});
|
|
162
|
+
|
|
163
|
+
test('border-color with a SPACED color function stays a longhand (not the border shorthand)', () => {
|
|
164
|
+
// Regression: `rgba(0, 0, 0, 0)` carries spaces INSIDE the parens. The old guard
|
|
165
|
+
// (`!value.includes(' ')`) misread it as a `border` shorthand, so a transparent
|
|
166
|
+
// badge border collapsed into `border: rgba(...)` and stopped overriding the
|
|
167
|
+
// `border: 1px solid` width/style — a dark currentColor border leaked through.
|
|
168
|
+
expect(generateRuleForClass('border-[rgba(0,_0,_0,_0)]')).toBe('border-color: rgba(0, 0, 0, 0);');
|
|
169
|
+
expect(generateRuleForClass('border-[rgb(0_0_0_/_50%)]')).toBe('border-color: rgb(0 0 0 / 50%);');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('transparent border-color sorts AFTER the border shorthand so it always wins', () => {
|
|
173
|
+
// The two classes target distinct properties (border vs border-color), so the longhand
|
|
174
|
+
// must follow the shorthand in source order regardless of which was collected first.
|
|
175
|
+
const css = generateUtilityCSS(
|
|
176
|
+
new Set(['border-[rgba(0,_0,_0,_0)]', 'border-[1px_solid]']),
|
|
177
|
+
DEFAULT_BREAKPOINTS,
|
|
178
|
+
{ enabled: false } as ResponsiveScales,
|
|
179
|
+
);
|
|
180
|
+
const posShorthand = css.indexOf('border: 1px solid;');
|
|
181
|
+
const posColor = css.indexOf('border-color: rgba(0, 0, 0, 0);');
|
|
182
|
+
expect(posShorthand).toBeGreaterThanOrEqual(0);
|
|
183
|
+
expect(posColor).toBeGreaterThan(posShorthand);
|
|
184
|
+
});
|
|
118
185
|
});
|
|
119
186
|
|
|
120
187
|
describe('CSS named colors vs variables', () => {
|
|
121
188
|
test('color with named color outputs directly', () => {
|
|
122
|
-
expect(generateRuleForClass('
|
|
123
|
-
expect(generateRuleForClass('
|
|
124
|
-
expect(generateRuleForClass('
|
|
189
|
+
expect(generateRuleForClass('text-[red]')).toBe('color: red;');
|
|
190
|
+
expect(generateRuleForClass('text-[blue]')).toBe('color: blue;');
|
|
191
|
+
expect(generateRuleForClass('text-transparent')).toBe('color: transparent;');
|
|
125
192
|
});
|
|
126
193
|
|
|
127
194
|
test('background-color with named color outputs directly', () => {
|
|
128
|
-
expect(generateRuleForClass('
|
|
129
|
-
expect(generateRuleForClass('
|
|
195
|
+
expect(generateRuleForClass('bg-[green]')).toBe('background-color: green;');
|
|
196
|
+
expect(generateRuleForClass('bg-[white]')).toBe('background-color: white;');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('color with variable shorthand uses var()', () => {
|
|
200
|
+
expect(generateRuleForClass('text-(--primary)')).toBe('color: var(--primary);');
|
|
201
|
+
expect(generateRuleForClass('text-(--text)')).toBe('color: var(--text);');
|
|
202
|
+
expect(generateRuleForClass('bg-(--background)')).toBe('background-color: var(--background);');
|
|
130
203
|
});
|
|
131
204
|
|
|
132
|
-
test('
|
|
133
|
-
expect(generateRuleForClass('
|
|
134
|
-
expect(generateRuleForClass('c-text')).toBe('color: var(--text);');
|
|
135
|
-
expect(generateRuleForClass('bgc-background')).toBe('background-color: var(--background);');
|
|
205
|
+
test('font-size variable with length hint', () => {
|
|
206
|
+
expect(generateRuleForClass('text-(length:--font-size-lg)')).toBe('font-size: var(--font-size-lg);');
|
|
136
207
|
});
|
|
137
208
|
});
|
|
138
209
|
|
|
139
|
-
describe('display
|
|
140
|
-
test('
|
|
141
|
-
expect(generateRuleForClass('
|
|
210
|
+
describe('display static utilities', () => {
|
|
211
|
+
test('flex generates display: flex', () => {
|
|
212
|
+
expect(generateRuleForClass('flex')).toBe('display: flex;');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('hidden generates display: none', () => {
|
|
216
|
+
expect(generateRuleForClass('hidden')).toBe('display: none;');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('contents generates display: contents', () => {
|
|
220
|
+
expect(generateRuleForClass('contents')).toBe('display: contents;');
|
|
142
221
|
});
|
|
143
222
|
|
|
144
|
-
test('
|
|
145
|
-
expect(generateRuleForClass('
|
|
223
|
+
test('flow-root generates display: flow-root', () => {
|
|
224
|
+
expect(generateRuleForClass('flow-root')).toBe('display: flow-root;');
|
|
146
225
|
});
|
|
147
226
|
|
|
148
|
-
test('
|
|
149
|
-
expect(generateRuleForClass('
|
|
227
|
+
test('table generates display: table', () => {
|
|
228
|
+
expect(generateRuleForClass('table')).toBe('display: table;');
|
|
150
229
|
});
|
|
151
230
|
});
|
|
152
231
|
|
|
153
|
-
describe('
|
|
154
|
-
test('
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
const
|
|
232
|
+
describe('border-color and border-side combination', () => {
|
|
233
|
+
test('border color class controls color independently from side class', () => {
|
|
234
|
+
// border-t- should NOT set color when absent, allowing border-(--x) to control it
|
|
235
|
+
const sideRule = generateRuleForClass('border-t-[1px_solid]');
|
|
236
|
+
const colorRule = generateRuleForClass('border-(--primary)');
|
|
158
237
|
|
|
159
|
-
|
|
160
|
-
expect(
|
|
161
|
-
expect(
|
|
162
|
-
expect(btRule).toContain('border-top-style');
|
|
238
|
+
expect(sideRule).not.toContain('color');
|
|
239
|
+
expect(sideRule).toContain('border-top-width');
|
|
240
|
+
expect(sideRule).toContain('border-top-style');
|
|
163
241
|
|
|
164
|
-
|
|
165
|
-
expect(bcRule).toContain('border-color');
|
|
242
|
+
expect(colorRule).toContain('border-color');
|
|
166
243
|
});
|
|
167
244
|
});
|
|
168
245
|
|
|
169
|
-
describe('inset
|
|
170
|
-
test('
|
|
171
|
-
expect(generateRuleForClass('
|
|
246
|
+
describe('inset root', () => {
|
|
247
|
+
test('inset-[0] generates correct CSS', () => {
|
|
248
|
+
expect(generateRuleForClass('inset-[0]')).toBe('inset: 0;');
|
|
172
249
|
});
|
|
173
250
|
|
|
174
|
-
test('
|
|
175
|
-
expect(generateRuleForClass('
|
|
251
|
+
test('inset-auto generates correct CSS', () => {
|
|
252
|
+
expect(generateRuleForClass('inset-auto')).toBe('inset: auto;');
|
|
176
253
|
});
|
|
177
254
|
|
|
178
|
-
test('
|
|
179
|
-
expect(generateRuleForClass('
|
|
255
|
+
test('inset-[auto_0_0] generates correct CSS with space-separated values', () => {
|
|
256
|
+
expect(generateRuleForClass('inset-[auto_0_0]')).toBe('inset: auto 0 0;');
|
|
180
257
|
});
|
|
181
258
|
|
|
182
|
-
test('
|
|
183
|
-
expect(generateRuleForClass('
|
|
259
|
+
test('inset-[0_0_auto_0] generates correct CSS', () => {
|
|
260
|
+
expect(generateRuleForClass('inset-[0_0_auto_0]')).toBe('inset: 0 0 auto 0;');
|
|
184
261
|
});
|
|
185
262
|
});
|
|
186
263
|
|
|
187
|
-
describe('
|
|
188
|
-
test('
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
264
|
+
describe('background-image via bg root', () => {
|
|
265
|
+
test('bg-[linear-gradient(…)] generates background-image', () => {
|
|
266
|
+
expect(generateRuleForClass('bg-[linear-gradient(#000,_#fff)]')).toBe(
|
|
267
|
+
'background-image: linear-gradient(#000, #fff);',
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('bg-[linear-gradient(…)] with color stop generates background-image', () => {
|
|
272
|
+
expect(generateRuleForClass('bg-[linear-gradient(#0000,_#0f1442_94%)]')).toBe(
|
|
273
|
+
'background-image: linear-gradient(#0000, #0f1442 94%);',
|
|
274
|
+
);
|
|
192
275
|
});
|
|
193
276
|
|
|
194
|
-
test('
|
|
195
|
-
|
|
196
|
-
// Encoded as: bgi-linear-gradient(#0000,-#0f1442-94p)
|
|
197
|
-
expect(generateRuleForClass('bgi-linear-gradient(#0000,-#0f1442-94p)')).toBe('background-image: linear-gradient(#0000, #0f1442 94%);');
|
|
277
|
+
test('bg-[url(…)] generates background-image and preserves the url', () => {
|
|
278
|
+
expect(generateRuleForClass('bg-[url(/image.jpg)]')).toBe('background-image: url(/image.jpg);');
|
|
198
279
|
});
|
|
199
280
|
|
|
200
|
-
test('
|
|
201
|
-
expect(generateRuleForClass('
|
|
281
|
+
test('underscores inside url() are preserved', () => {
|
|
282
|
+
expect(generateRuleForClass('bg-[url(/images/hero_bg.png)]')).toBe('background-image: url(/images/hero_bg.png);');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('multi-property axis roots', () => {
|
|
287
|
+
test('px-[16px] generates left and right padding', () => {
|
|
288
|
+
expect(generateRuleForClass('px-[16px]')).toBe('padding-left: 16px; padding-right: 16px;');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('my-[8px] generates top and bottom margin', () => {
|
|
292
|
+
expect(generateRuleForClass('my-[8px]')).toBe('margin-top: 8px; margin-bottom: 8px;');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('mx-auto centers horizontally', () => {
|
|
296
|
+
expect(generateRuleForClass('mx-auto')).toBe('margin-left: auto; margin-right: auto;');
|
|
202
297
|
});
|
|
203
298
|
});
|
|
204
299
|
|
|
205
300
|
describe('escapeCSSClassName', () => {
|
|
206
|
-
test('
|
|
207
|
-
|
|
208
|
-
const classes = new Set(['ar-1/1']);
|
|
301
|
+
test('bracket/slash/colon characters are escaped in CSS selectors', () => {
|
|
302
|
+
const classes = new Set(['aspect-[1/1]', 'tablet:w-[50%]']);
|
|
209
303
|
const css = generateUtilityCSS(classes);
|
|
210
|
-
expect(css).toContain('
|
|
211
|
-
expect(css).
|
|
304
|
+
expect(css).toContain('.aspect-\\[1\\/1\\]');
|
|
305
|
+
expect(css).toContain('.tablet\\:w-\\[50\\%\\]');
|
|
306
|
+
expect(css).not.toContain('.aspect-[1/1] ');
|
|
212
307
|
});
|
|
213
308
|
});
|
|
214
309
|
|
|
215
310
|
describe('style presets', () => {
|
|
216
311
|
test('shadow presets generate correct CSS', () => {
|
|
217
|
-
expect(generateRuleForClass('
|
|
218
|
-
expect(generateRuleForClass('
|
|
219
|
-
expect(generateRuleForClass('
|
|
220
|
-
expect(generateRuleForClass('
|
|
221
|
-
expect(generateRuleForClass('
|
|
312
|
+
expect(generateRuleForClass('shadow-0')).toBe('box-shadow: none;');
|
|
313
|
+
expect(generateRuleForClass('shadow-1')).toBe('box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);');
|
|
314
|
+
expect(generateRuleForClass('shadow-2')).toBe('box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);');
|
|
315
|
+
expect(generateRuleForClass('shadow-3')).toBe('box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);');
|
|
316
|
+
expect(generateRuleForClass('shadow-4')).toBe('box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2);');
|
|
222
317
|
});
|
|
223
318
|
|
|
224
319
|
test('gradient presets generate correct CSS', () => {
|
|
225
|
-
expect(generateRuleForClass('
|
|
226
|
-
|
|
320
|
+
expect(generateRuleForClass('bg-gradient-1')).toBe(
|
|
321
|
+
'background: linear-gradient(90deg, rgba(10,22,40,0.85) 0%, rgba(10,22,40,0.2) 100%);',
|
|
322
|
+
);
|
|
323
|
+
expect(generateRuleForClass('bg-gradient-2')).toBe(
|
|
324
|
+
'background: linear-gradient(0deg, #a9e8fd, #00aee8 25%, #0f1335 72%);',
|
|
325
|
+
);
|
|
227
326
|
});
|
|
228
327
|
|
|
229
328
|
test('border presets generate correct CSS', () => {
|
|
230
|
-
expect(generateRuleForClass('
|
|
231
|
-
expect(generateRuleForClass('
|
|
329
|
+
expect(generateRuleForClass('border-preset-0')).toBe('border: 1px solid var(--border);');
|
|
330
|
+
expect(generateRuleForClass('border-preset-1')).toBe('border: 1px solid var(--border-light);');
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('Tailwind-style keyword and numeric utilities', () => {
|
|
335
|
+
test('size keywords', () => {
|
|
336
|
+
expect(generateRuleForClass('w-full')).toBe('width: 100%;');
|
|
337
|
+
expect(generateRuleForClass('h-screen')).toBe('height: 100vh;');
|
|
338
|
+
expect(generateRuleForClass('max-w-full')).toBe('max-width: 100%;');
|
|
339
|
+
expect(generateRuleForClass('rounded-full')).toBe('border-radius: 9999px;');
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
test('bare numeric utilities', () => {
|
|
343
|
+
expect(generateRuleForClass('z-10')).toBe('z-index: 10;');
|
|
344
|
+
expect(generateRuleForClass('order-2')).toBe('order: 2;');
|
|
345
|
+
expect(generateRuleForClass('opacity-50')).toBe('opacity: 0.5;');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test('static flexbox utilities', () => {
|
|
349
|
+
expect(generateRuleForClass('justify-center')).toBe('justify-content: center;');
|
|
350
|
+
expect(generateRuleForClass('items-center')).toBe('align-items: center;');
|
|
351
|
+
expect(generateRuleForClass('flex-col')).toBe('flex-direction: column;');
|
|
352
|
+
expect(generateRuleForClass('grow')).toBe('flex-grow: 1;');
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('arbitrary-property classes', () => {
|
|
356
|
+
expect(generateRuleForClass('[text-overflow:ellipsis]')).toBe('text-overflow: ellipsis;');
|
|
357
|
+
expect(generateRuleForClass('[clip-path:circle(50%)]')).toBe('clip-path: circle(50%);');
|
|
232
358
|
});
|
|
233
359
|
});
|
|
234
360
|
});
|
|
235
361
|
|
|
236
362
|
describe('generateSingleClassCSS - responsive classes', () => {
|
|
237
363
|
test('generates correct media query for tablet-prefixed class', () => {
|
|
238
|
-
const css = generateSingleClassCSS('
|
|
364
|
+
const css = generateSingleClassCSS('tablet:text-[132px]', DEFAULT_BREAKPOINTS);
|
|
239
365
|
expect(css).toContain('@media (max-width: 1024px)');
|
|
240
366
|
expect(css).toContain('font-size: 132px');
|
|
241
|
-
expect(css).toContain('.
|
|
367
|
+
expect(css).toContain('.tablet\\:text-\\[132px\\]');
|
|
242
368
|
});
|
|
243
369
|
|
|
244
370
|
test('generates correct media query for mobile-prefixed class', () => {
|
|
245
|
-
const css = generateSingleClassCSS('
|
|
371
|
+
const css = generateSingleClassCSS('mobile:text-[24px]', DEFAULT_BREAKPOINTS);
|
|
246
372
|
expect(css).toContain('@media (max-width: 540px)');
|
|
247
373
|
expect(css).toContain('font-size: 24px');
|
|
248
|
-
expect(css).toContain('.
|
|
374
|
+
expect(css).toContain('.mobile\\:text-\\[24px\\]');
|
|
249
375
|
});
|
|
250
376
|
|
|
251
377
|
test('generates base rule for non-prefixed class', () => {
|
|
252
|
-
const css = generateSingleClassCSS('
|
|
253
|
-
expect(css).toContain('.
|
|
378
|
+
const css = generateSingleClassCSS('text-[48px]', DEFAULT_BREAKPOINTS);
|
|
379
|
+
expect(css).toContain('.text-\\[48px\\]');
|
|
254
380
|
expect(css).toContain('font-size: 48px');
|
|
255
381
|
expect(css).not.toContain('@media');
|
|
256
382
|
});
|
|
@@ -265,9 +391,7 @@ describe('generateInteractiveCSS — auto-responsive scaling', () => {
|
|
|
265
391
|
};
|
|
266
392
|
|
|
267
393
|
test('flat interactive style gets scaled @media overrides', () => {
|
|
268
|
-
const styles: InteractiveStyles = [
|
|
269
|
-
{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } },
|
|
270
|
-
];
|
|
394
|
+
const styles: InteractiveStyles = [{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } }];
|
|
271
395
|
const css = generateInteractiveCSS('c_heading_text', styles, DEFAULT_BREAKPOINTS, undefined, scalesEnabled);
|
|
272
396
|
expect(css).toContain('.c_heading_text:hover { font-size: 100px; }');
|
|
273
397
|
// tablet: 100 + (100-16)*(0.88-1) = 89.92 → 90
|
|
@@ -299,9 +423,7 @@ describe('generateInteractiveCSS — auto-responsive scaling', () => {
|
|
|
299
423
|
});
|
|
300
424
|
|
|
301
425
|
test('scaling disabled is a no-op', () => {
|
|
302
|
-
const styles: InteractiveStyles = [
|
|
303
|
-
{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } },
|
|
304
|
-
];
|
|
426
|
+
const styles: InteractiveStyles = [{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } }];
|
|
305
427
|
const cssNoScales = generateInteractiveCSS('c_heading', styles, DEFAULT_BREAKPOINTS);
|
|
306
428
|
const cssDisabled = generateInteractiveCSS('c_heading', styles, DEFAULT_BREAKPOINTS, undefined, {
|
|
307
429
|
...scalesEnabled,
|
|
@@ -323,11 +445,11 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
323
445
|
};
|
|
324
446
|
|
|
325
447
|
test('generateUtilityCSS emits a single clamp() rule with no @media for auto-responsive class', () => {
|
|
326
|
-
const classes = new Set(['
|
|
448
|
+
const classes = new Set(['text-[32px]']);
|
|
327
449
|
const css = generateUtilityCSS(classes, DEFAULT_BREAKPOINTS, fluidScales);
|
|
328
450
|
// mobile.breakpoint=540 is the smallest in DEFAULT_BREAKPOINTS, mobile scale=0.75
|
|
329
451
|
// so MIN = 32 + (32-16)*(0.75-1) = 32 - 4 = 28; MAX = 32
|
|
330
|
-
expect(css).toContain('.
|
|
452
|
+
expect(css).toContain('.text-\\[32px\\]');
|
|
331
453
|
expect(css).toContain('clamp(28px,');
|
|
332
454
|
expect(css).toContain(', 32px)');
|
|
333
455
|
expect(css).not.toContain('@media');
|
|
@@ -341,7 +463,7 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
341
463
|
});
|
|
342
464
|
|
|
343
465
|
test('generateSingleClassCSS emits clamp() in fluid mode', () => {
|
|
344
|
-
const css = generateSingleClassCSS('p-40px', DEFAULT_BREAKPOINTS, fluidScales);
|
|
466
|
+
const css = generateSingleClassCSS('p-[40px]', DEFAULT_BREAKPOINTS, fluidScales);
|
|
345
467
|
// mobile padding scale=0.5 → MIN = 40 + (40-16)*(0.5-1) = 40 - 12 = 28
|
|
346
468
|
expect(css).toContain('clamp(28px,');
|
|
347
469
|
expect(css).toContain(', 40px)');
|
|
@@ -350,7 +472,7 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
350
472
|
|
|
351
473
|
test('breakpoints mode (default when mode is undefined) keeps existing @media behavior', () => {
|
|
352
474
|
const breakpointScales: ResponsiveScales = { ...fluidScales, mode: undefined };
|
|
353
|
-
const classes = new Set(['
|
|
475
|
+
const classes = new Set(['text-[32px]']);
|
|
354
476
|
const css = generateUtilityCSS(classes, DEFAULT_BREAKPOINTS, breakpointScales);
|
|
355
477
|
expect(css).toContain('@media (max-width: 1024px)');
|
|
356
478
|
expect(css).toContain('@media (max-width: 540px)');
|
|
@@ -358,9 +480,7 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
358
480
|
});
|
|
359
481
|
|
|
360
482
|
test('generateInteractiveCSS encodes scaling as clamp() on base rule, no @media', () => {
|
|
361
|
-
const styles: InteractiveStyles = [
|
|
362
|
-
{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } },
|
|
363
|
-
];
|
|
483
|
+
const styles: InteractiveStyles = [{ prefix: '', postfix: ':hover', style: { fontSize: '100px' } }];
|
|
364
484
|
const css = generateInteractiveCSS('c_heading', styles, DEFAULT_BREAKPOINTS, undefined, fluidScales);
|
|
365
485
|
// mobile fontSize scale=0.75 → MIN = 100 + (100-16)*(0.75-1) = 79
|
|
366
486
|
expect(css).toContain('clamp(79px,');
|
|
@@ -373,7 +493,7 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
373
493
|
...fluidScales,
|
|
374
494
|
size: { tablet: 0.9, mobile: 0.75 },
|
|
375
495
|
};
|
|
376
|
-
const classes = new Set(['
|
|
496
|
+
const classes = new Set(['max-w-[1200px]']);
|
|
377
497
|
const css = generateUtilityCSS(classes, DEFAULT_BREAKPOINTS, fluidWithSize);
|
|
378
498
|
// mobile size scale=0.75 → MIN = 1200 + (1200-16)*(0.75-1) = 1200 - 296 = 904
|
|
379
499
|
expect(css).toContain('clamp(904px,');
|
|
@@ -399,27 +519,26 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
399
519
|
expect(css).toMatch(/@media \(max-width: 540px\) \{[^}]*font-size:\s*60px/);
|
|
400
520
|
});
|
|
401
521
|
|
|
402
|
-
// Hash-fallback utility classes (e.g. `
|
|
403
|
-
//
|
|
522
|
+
// Hash-fallback utility classes (e.g. `text-h1glej9a` for a value that can't
|
|
523
|
+
// be encoded in a class name — utilityClassMapper generates a hashed name
|
|
404
524
|
// and stores the real value in styleValueRegistry). The scaling pipeline
|
|
405
|
-
//
|
|
406
|
-
// for hashed classes. resolveScalablePropertyValue now consults the registry.
|
|
525
|
+
// resolves them through the registry via resolveScalablePropertyValue.
|
|
407
526
|
describe('hash-fallback classes', () => {
|
|
408
|
-
test('fluid mode: hashed
|
|
527
|
+
test('fluid mode: hashed text- class with px value gets clamp()', () => {
|
|
409
528
|
clearRegistry();
|
|
410
|
-
registerStyleValue('
|
|
411
|
-
const css = generateUtilityCSS(new Set(['
|
|
529
|
+
registerStyleValue('text-h1glej9a', '222.3px');
|
|
530
|
+
const css = generateUtilityCSS(new Set(['text-h1glej9a']), DEFAULT_BREAKPOINTS, fluidScales);
|
|
412
531
|
// mobile fontSize scale=0.75, baseRef=16
|
|
413
532
|
// MIN = 222.3 + (222.3 - 16)*(0.75 - 1) = 222.3 - 51.575 = 170.725 → round 171
|
|
414
|
-
expect(css).toMatch(/\.
|
|
533
|
+
expect(css).toMatch(/\.text-h1glej9a\s*\{\s*font-size:\s*clamp\(171px,/);
|
|
415
534
|
expect(css).toContain(', 222.3px)');
|
|
416
535
|
expect(css).not.toContain('@media');
|
|
417
536
|
});
|
|
418
537
|
|
|
419
538
|
test('fluid mode: hashed class at baseReference renders plain (MIN===MAX, no clamp)', () => {
|
|
420
539
|
clearRegistry();
|
|
421
|
-
registerStyleValue('
|
|
422
|
-
const css = generateUtilityCSS(new Set(['
|
|
540
|
+
registerStyleValue('text-hatref', '16px');
|
|
541
|
+
const css = generateUtilityCSS(new Set(['text-hatref']), DEFAULT_BREAKPOINTS, fluidScales);
|
|
423
542
|
// 16 === baseReference → calculateResponsiveValue returns 16 → MIN===MAX → no clamp
|
|
424
543
|
expect(css).toContain('font-size: 16px');
|
|
425
544
|
expect(css).not.toContain('clamp(');
|
|
@@ -427,8 +546,8 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
427
546
|
|
|
428
547
|
test('fluid mode: hashed class with fractional px value above baseReference clamps with rounded MIN', () => {
|
|
429
548
|
clearRegistry();
|
|
430
|
-
registerStyleValue('
|
|
431
|
-
const css = generateUtilityCSS(new Set(['
|
|
549
|
+
registerStyleValue('text-hfrac', '14.4px');
|
|
550
|
+
const css = generateUtilityCSS(new Set(['text-hfrac']), DEFAULT_BREAKPOINTS, fluidScales);
|
|
432
551
|
// 14.4 < baseRef (16) → calculateResponsiveValue floors to round(14.4) = 14
|
|
433
552
|
// MIN=14, MAX=14.4 → small clamp emitted
|
|
434
553
|
expect(css).toMatch(/font-size:\s*clamp\(14px,/);
|
|
@@ -460,8 +579,8 @@ describe('fluid mode — clamp() emission', () => {
|
|
|
460
579
|
|
|
461
580
|
test('generateSingleClassCSS handles hashed class in fluid mode', () => {
|
|
462
581
|
clearRegistry();
|
|
463
|
-
registerStyleValue('
|
|
464
|
-
const css = generateSingleClassCSS('
|
|
582
|
+
registerStyleValue('text-htest1', '100px');
|
|
583
|
+
const css = generateSingleClassCSS('text-htest1', DEFAULT_BREAKPOINTS, fluidScales);
|
|
465
584
|
// mobile fontSize 0.75 → MIN = 100 + (100-16)*(0.75-1) = 79
|
|
466
585
|
expect(css).toContain('clamp(79px,');
|
|
467
586
|
expect(css).toContain(', 100px)');
|
|
@@ -512,7 +631,7 @@ describe('applyContainerPattern', () => {
|
|
|
512
631
|
test('explicit marginLeft is overwritten with auto (per spec)', () => {
|
|
513
632
|
const result = applyContainerPattern(
|
|
514
633
|
{ width: '1200px', maxWidth: '1200px', marginLeft: '0', marginRight: '12px' },
|
|
515
|
-
true
|
|
634
|
+
true,
|
|
516
635
|
);
|
|
517
636
|
expect(result.marginLeft).toBe('auto');
|
|
518
637
|
expect(result.marginRight).toBe('auto');
|
|
@@ -521,7 +640,7 @@ describe('applyContainerPattern', () => {
|
|
|
521
640
|
test('preserves unrelated properties (padding, font-size)', () => {
|
|
522
641
|
const result = applyContainerPattern(
|
|
523
642
|
{ width: '1200px', maxWidth: '1200px', padding: '20px', fontSize: '16px' },
|
|
524
|
-
true
|
|
643
|
+
true,
|
|
525
644
|
);
|
|
526
645
|
expect(result.padding).toBe('20px');
|
|
527
646
|
expect(result.fontSize).toBe('16px');
|
|
@@ -562,3 +681,25 @@ describe('generateInteractiveCSS — container pattern integration', () => {
|
|
|
562
681
|
expect(css).toContain('max-width: 1200px');
|
|
563
682
|
});
|
|
564
683
|
});
|
|
684
|
+
|
|
685
|
+
describe('styleObjectToCSS', () => {
|
|
686
|
+
test('kebab-cases properties and joins declarations', () => {
|
|
687
|
+
expect(styleObjectToCSS({ paddingTop: '10px', fontSize: 16 })).toBe('padding-top: 10px; font-size: 16');
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
test('wraps bare color tokens in var(); leaves hex/rgb/var/functions intact', () => {
|
|
691
|
+
expect(styleObjectToCSS({ backgroundColor: 'primary' })).toBe('background-color: var(--primary)');
|
|
692
|
+
expect(styleObjectToCSS({ color: '#fff' })).toBe('color: #fff');
|
|
693
|
+
expect(styleObjectToCSS({ borderColor: 'var(--accent)' })).toBe('border-color: var(--accent)');
|
|
694
|
+
expect(styleObjectToCSS({ color: 'rgb(0,0,0)' })).toBe('color: rgb(0,0,0)');
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
test('skips _mapping (binding) values — bound props yield empty/partial output', () => {
|
|
698
|
+
expect(styleObjectToCSS({ color: { _mapping: { prop: 'x' } } as never })).toBe('');
|
|
699
|
+
expect(styleObjectToCSS({ color: { _mapping: { prop: 'x' } } as never, padding: '4px' })).toBe('padding: 4px');
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
test('empty object yields an empty string', () => {
|
|
703
|
+
expect(styleObjectToCSS({})).toBe('');
|
|
704
|
+
});
|
|
705
|
+
});
|