meno-core 1.0.53 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +1 -3
- package/bin/cli.ts +66 -261
- package/build-astro.ts +150 -128
- package/dist/bin/cli.js +42 -185
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/chunk-2AR55GYH.js +42 -0
- package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
- package/dist/chunks/{chunk-JGWFTO6P.js → chunk-2IIQK7T3.js} +2624 -1389
- package/dist/chunks/chunk-2IIQK7T3.js.map +7 -0
- package/dist/chunks/chunk-4ZRU52J2.js +169 -0
- package/dist/chunks/chunk-4ZRU52J2.js.map +7 -0
- package/dist/chunks/{chunk-UB44F4Z2.js → chunk-5X4HCN7J.js} +5 -7
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-WQFG7PAH.js → chunk-7CCVOL5M.js} +15 -19
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-YBLHKYFF.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-O3NAGJP4.js → chunk-NUP7H7D3.js} +135 -41
- package/dist/chunks/chunk-NUP7H7D3.js.map +7 -0
- package/dist/chunks/{chunk-X754AHS5.js → chunk-QWTQZHG3.js} +194 -90
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-XTKNX4FW.js} +216 -87
- package/dist/chunks/chunk-XTKNX4FW.js.map +7 -0
- package/dist/chunks/{fs-JGINUXGL.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +1003 -475
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14393 -6371
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +342 -101
- package/dist/lib/shared/index.js.map +4 -4
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +64 -80
- package/dist/lib/test-utils/index.js.map +2 -2
- package/entries/client-router.tsx +10 -8
- package/lib/client/ClientInitializer.ts +8 -8
- package/lib/client/ErrorBoundary.test.tsx +246 -186
- package/lib/client/ErrorBoundary.tsx +183 -132
- package/lib/client/componentRegistry.test.ts +96 -108
- package/lib/client/componentRegistry.ts +1 -3
- package/lib/client/contexts/ThemeContext.tsx +3 -2
- package/lib/client/core/ComponentBuilder.test.ts +663 -560
- package/lib/client/core/ComponentBuilder.ts +427 -155
- package/lib/client/core/ComponentRenderer.test.tsx +2 -4
- package/lib/client/core/ComponentRenderer.tsx +46 -33
- package/lib/client/core/builders/embedBuilder.ts +242 -54
- package/lib/client/core/builders/linkBuilder.ts +72 -45
- package/lib/client/core/builders/linkNodeBuilder.ts +79 -54
- package/lib/client/core/builders/listBuilder.ts +155 -89
- package/lib/client/core/builders/localeListBuilder.ts +97 -62
- package/lib/client/core/builders/types.ts +5 -5
- package/lib/client/core/cmsTemplateProcessor.ts +8 -9
- package/lib/client/elementRegistry.ts +3 -3
- package/lib/client/fontFamiliesService.test.ts +2 -10
- package/lib/client/fontFamiliesService.ts +3 -3
- package/lib/client/hmr/HMRManager.tsx +47 -30
- package/lib/client/hmrCssReload.ts +49 -14
- package/lib/client/hmrWebSocket.ts +14 -19
- package/lib/client/hooks/useColorVariables.test.ts +21 -21
- package/lib/client/hooks/useColorVariables.ts +23 -18
- package/lib/client/hooks/usePropertyAutocomplete.ts +4 -6
- package/lib/client/hooks/useVariables.ts +6 -4
- package/lib/client/hydration/HydrationUtils.test.ts +24 -25
- package/lib/client/hydration/HydrationUtils.ts +3 -4
- package/lib/client/i18nConfigService.test.ts +2 -7
- package/lib/client/i18nConfigService.ts +2 -2
- package/lib/client/meno-filter/MenoFilter.test.ts +47 -49
- package/lib/client/meno-filter/MenoFilter.ts +14 -15
- package/lib/client/meno-filter/bindings.ts +18 -43
- package/lib/client/meno-filter/init.ts +4 -4
- package/lib/client/meno-filter/renderer.ts +30 -32
- package/lib/client/meno-filter/script.generated.ts +1 -3
- package/lib/client/meno-filter/ui.ts +4 -6
- package/lib/client/meno-filter/updates.ts +18 -23
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +174 -178
- package/lib/client/navigation.ts +0 -1
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +234 -232
- package/lib/client/responsiveStyleResolver.ts +74 -20
- package/lib/client/routing/RouteLoader.test.ts +24 -25
- package/lib/client/routing/RouteLoader.ts +27 -35
- package/lib/client/routing/Router.tsx +210 -153
- package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
- package/lib/client/scripts/ScriptExecutor.ts +18 -36
- package/lib/client/services/PrefetchService.test.ts +3 -3
- package/lib/client/services/PrefetchService.ts +23 -29
- package/lib/client/styleProcessor.test.ts +9 -9
- package/lib/client/styleProcessor.ts +24 -15
- package/lib/client/styles/StyleInjector.test.ts +122 -115
- package/lib/client/styles/StyleInjector.ts +10 -7
- package/lib/client/styles/UtilityClassCollector.ts +62 -28
- package/lib/client/styles/cspNonce.test.ts +2 -5
- package/lib/client/templateEngine.test.ts +554 -488
- package/lib/client/templateEngine.ts +203 -73
- package/lib/client/theme.test.ts +1 -1
- package/lib/client/theme.ts +0 -1
- package/lib/client/utils/toast.ts +0 -1
- package/lib/server/__integration__/api-routes.test.ts +8 -4
- package/lib/server/__integration__/cms-integration.test.ts +1 -4
- package/lib/server/__integration__/server-lifecycle.test.ts +4 -5
- package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
- package/lib/server/__integration__/static-assets.test.ts +3 -3
- package/lib/server/__integration__/test-helpers.ts +87 -74
- package/lib/server/ab/generateFunctions.ts +12 -10
- package/lib/server/astro/cmsPageEmitter.ts +26 -42
- package/lib/server/astro/componentEmitter.ts +35 -43
- package/lib/server/astro/cssCollector.ts +10 -26
- package/lib/server/astro/nodeToAstro.test.ts +1754 -34
- package/lib/server/astro/nodeToAstro.ts +232 -216
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +115 -119
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +11 -18
- package/lib/server/astro/pageEmitter.ts +9 -13
- package/lib/server/astro/tailwindMapper.test.ts +10 -37
- package/lib/server/astro/tailwindMapper.ts +33 -40
- package/lib/server/astro/templateTransformer.ts +17 -20
- package/lib/server/createServer.ts +7 -19
- package/lib/server/cssGenerator.test.ts +35 -44
- package/lib/server/cssGenerator.ts +8 -19
- package/lib/server/fileWatcher.test.ts +164 -13
- package/lib/server/fileWatcher.ts +209 -124
- package/lib/server/index.ts +14 -17
- package/lib/server/jsonLoader.test.ts +39 -2
- package/lib/server/jsonLoader.ts +39 -34
- package/lib/server/middleware/cors.test.ts +21 -21
- package/lib/server/middleware/cors.ts +28 -4
- package/lib/server/middleware/errorHandler.test.ts +7 -5
- package/lib/server/middleware/errorHandler.ts +3 -8
- package/lib/server/middleware/index.ts +0 -1
- package/lib/server/middleware/logger.test.ts +10 -8
- package/lib/server/middleware/logger.ts +17 -29
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/pageCache.test.ts +76 -77
- package/lib/server/pageCache.ts +0 -1
- package/lib/server/projectContext.ts +5 -4
- package/lib/server/providers/fileSystemCMSProvider.test.ts +142 -99
- package/lib/server/providers/fileSystemCMSProvider.ts +62 -38
- package/lib/server/routes/api/cms.test.ts +26 -14
- package/lib/server/routes/api/cms.ts +9 -14
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +31 -35
- package/lib/server/routes/api/config.ts +3 -4
- package/lib/server/routes/api/core-routes.ts +49 -76
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +9 -17
- package/lib/server/routes/api/index.ts +3 -6
- package/lib/server/routes/api/pages.ts +8 -11
- package/lib/server/routes/api/shared.test.ts +1 -1
- package/lib/server/routes/api/shared.ts +12 -9
- package/lib/server/routes/api/variables.test.ts +3 -5
- package/lib/server/routes/api/variables.ts +1 -1
- package/lib/server/routes/index.ts +25 -28
- package/lib/server/routes/pages.ts +35 -31
- package/lib/server/routes/static.ts +17 -6
- package/lib/server/runtime/bundler.ts +98 -61
- package/lib/server/runtime/fs.ts +16 -26
- package/lib/server/runtime/httpServer.ts +8 -12
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +37 -29
- package/lib/server/services/EnumService.test.ts +4 -8
- package/lib/server/services/EnumService.ts +11 -3
- package/lib/server/services/VariableService.test.ts +8 -12
- package/lib/server/services/VariableService.ts +11 -3
- package/lib/server/services/cmsService.test.ts +158 -120
- package/lib/server/services/cmsService.ts +36 -146
- package/lib/server/services/componentService.test.ts +125 -45
- package/lib/server/services/componentService.ts +70 -46
- package/lib/server/services/configService.test.ts +22 -45
- package/lib/server/services/configService.ts +50 -27
- package/lib/server/services/fileWatcherService.ts +42 -8
- package/lib/server/services/index.ts +0 -1
- package/lib/server/services/pageService.test.ts +8 -10
- package/lib/server/services/pageService.ts +32 -18
- package/lib/server/ssr/attributeBuilder.ts +18 -8
- package/lib/server/ssr/buildErrorOverlay.ts +11 -12
- package/lib/server/ssr/clientDataInjector.ts +7 -21
- package/lib/server/ssr/cmsSSRProcessor.ts +4 -8
- package/lib/server/ssr/cssCollector.ts +1 -1
- package/lib/server/ssr/errorOverlay.test.ts +1 -1
- package/lib/server/ssr/errorOverlay.ts +3 -9
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +9 -14
- package/lib/server/ssr/htmlGenerator.test.ts +120 -44
- package/lib/server/ssr/htmlGenerator.ts +126 -91
- package/lib/server/ssr/imageMetadata.test.ts +3 -1
- package/lib/server/ssr/imageMetadata.ts +27 -21
- package/lib/server/ssr/jsCollector.test.ts +8 -18
- package/lib/server/ssr/jsCollector.ts +3 -8
- package/lib/server/ssr/liveReloadIntegration.test.ts +76 -36
- package/lib/server/ssr/metaTagGenerator.ts +2 -2
- package/lib/server/ssr/ssrRenderer.branches.test.ts +1103 -0
- package/lib/server/ssr/ssrRenderer.test.ts +316 -256
- package/lib/server/ssr/ssrRenderer.ts +795 -243
- package/lib/server/ssrRenderer.test.ts +1048 -954
- package/lib/server/utils/jsonLineMapper.test.ts +28 -28
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +18 -21
- package/lib/server/websocketManager.ts +28 -19
- package/lib/shared/attributeNodeUtils.test.ts +15 -15
- package/lib/shared/attributeNodeUtils.ts +5 -12
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/breakpoints.ts +4 -11
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.test.ts +50 -42
- package/lib/shared/cmsQueryParser.ts +60 -38
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +27 -5
- package/lib/shared/colorVariableUtils.ts +21 -12
- package/lib/shared/componentRefs.ts +1 -5
- package/lib/shared/constants.test.ts +3 -3
- package/lib/shared/constants.ts +8 -8
- package/lib/shared/cssGeneration.test.ts +285 -144
- package/lib/shared/cssGeneration.ts +217 -531
- package/lib/shared/cssNamedColors.ts +152 -30
- package/lib/shared/cssProperties.test.ts +69 -70
- package/lib/shared/cssProperties.ts +298 -114
- package/lib/shared/elementClassName.test.ts +109 -109
- package/lib/shared/elementClassName.ts +3 -3
- package/lib/shared/elementUtils.ts +12 -16
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/errorLogger.ts +2 -10
- package/lib/shared/errors.test.ts +2 -13
- package/lib/shared/errors.ts +2 -8
- package/lib/shared/expressionEvaluator.test.ts +145 -0
- package/lib/shared/expressionEvaluator.ts +52 -24
- package/lib/shared/fontLoader.test.ts +32 -18
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/friendlyError.ts +2 -3
- package/lib/shared/gradientUtils.test.ts +3 -7
- package/lib/shared/gradientUtils.ts +17 -14
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +2 -10
- package/lib/shared/i18n.test.ts +4 -4
- package/lib/shared/i18n.ts +18 -38
- package/lib/shared/index.ts +27 -0
- package/lib/shared/inlineSvgStyleRules.ts +14 -13
- package/lib/shared/interactiveStyleMappings.test.ts +32 -54
- package/lib/shared/interactiveStyleMappings.ts +11 -24
- package/lib/shared/interactiveStyles.test.ts +165 -188
- package/lib/shared/interfaces/contentProvider.ts +1 -1
- package/lib/shared/itemTemplateUtils.test.ts +11 -17
- package/lib/shared/itemTemplateUtils.ts +27 -39
- package/lib/shared/jsonRepair.ts +16 -10
- package/lib/shared/libraryLoader.test.ts +21 -55
- package/lib/shared/libraryLoader.ts +7 -22
- package/lib/shared/linkUtils.ts +1 -1
- package/lib/shared/logger.test.ts +66 -0
- package/lib/shared/logger.ts +94 -0
- package/lib/shared/markdown.ts +25 -0
- package/lib/shared/netlifyLocale404.test.ts +179 -0
- package/lib/shared/netlifyLocale404.ts +110 -0
- package/lib/shared/nodeUtils.test.ts +28 -16
- package/lib/shared/nodeUtils.ts +76 -23
- package/lib/shared/pathArrayUtils.test.ts +1 -2
- package/lib/shared/pathArrayUtils.ts +1 -1
- package/lib/shared/pathSecurity.ts +2 -2
- package/lib/shared/pathUtils.test.ts +4 -6
- package/lib/shared/pathUtils.ts +42 -49
- package/lib/shared/paths/Path.test.ts +2 -2
- package/lib/shared/paths/Path.ts +0 -1
- package/lib/shared/paths/PathConverter.test.ts +1 -1
- package/lib/shared/paths/PathConverter.ts +17 -20
- package/lib/shared/paths/PathUtils.ts +13 -13
- package/lib/shared/paths/PathValidator.test.ts +2 -15
- package/lib/shared/paths/PathValidator.ts +12 -10
- package/lib/shared/paths/index.ts +1 -2
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +240 -244
- package/lib/shared/propResolver.ts +15 -26
- package/lib/shared/pxToRem.test.ts +7 -6
- package/lib/shared/pxToRem.ts +2 -5
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +12 -6
- package/lib/shared/registry/ClientRegistry.ts +0 -2
- package/lib/shared/registry/ComponentRegistry.test.ts +46 -32
- package/lib/shared/registry/ComponentRegistry.ts +12 -9
- package/lib/shared/registry/NodeTypeDefinition.ts +16 -9
- package/lib/shared/registry/RegistryManager.ts +1 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -2
- package/lib/shared/registry/createNodeType.ts +7 -9
- package/lib/shared/registry/defineNodeType.ts +2 -6
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/index.ts +0 -1
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +48 -19
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +117 -77
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +12 -8
- package/lib/shared/registry/nodeTypes/index.ts +26 -3
- package/lib/shared/responsiveScaling.test.ts +16 -34
- package/lib/shared/responsiveScaling.ts +61 -42
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/responsiveStyleUtils.ts +11 -13
- package/lib/shared/richtext/htmlToTiptap.test.ts +139 -130
- package/lib/shared/richtext/htmlToTiptap.ts +2 -4
- package/lib/shared/richtext/tiptapToHtml.test.ts +5 -6
- package/lib/shared/richtext/types.ts +1 -8
- package/lib/shared/slugTranslator.test.ts +13 -13
- package/lib/shared/slugTranslator.ts +12 -16
- package/lib/shared/slugify.ts +9 -15
- package/lib/shared/styleNodeUtils.test.ts +8 -8
- package/lib/shared/styleNodeUtils.ts +18 -20
- package/lib/shared/styleUtils.test.ts +87 -61
- package/lib/shared/styleUtils.ts +5 -6
- package/lib/shared/themeDefaults.test.ts +11 -14
- package/lib/shared/themeDefaults.ts +3 -4
- package/lib/shared/tree/PathBuilder.test.ts +63 -145
- package/lib/shared/tree/PathBuilder.ts +59 -47
- package/lib/shared/treePathUtils.test.ts +51 -12
- package/lib/shared/treePathUtils.ts +91 -60
- package/lib/shared/types/api.ts +105 -8
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +90 -34
- package/lib/shared/types/comment.ts +49 -16
- package/lib/shared/types/components.ts +51 -25
- package/lib/shared/types/errors.test.ts +1 -6
- package/lib/shared/types/errors.ts +3 -7
- package/lib/shared/types/experiments.ts +28 -28
- package/lib/shared/types/index.ts +25 -2
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/styles.ts +0 -1
- package/lib/shared/types/variables.test.ts +4 -13
- package/lib/shared/types/variables.ts +51 -30
- package/lib/shared/types.ts +1 -2
- package/lib/shared/utilityClassConfig.ts +751 -321
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +433 -95
- package/lib/shared/utilityClassMapper.ts +322 -249
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +458 -0
- package/lib/shared/utils.test.ts +2 -10
- package/lib/shared/utils.ts +19 -10
- package/lib/shared/validation/cmsValidators.ts +2 -1
- package/lib/shared/validation/commentValidators.test.ts +53 -0
- package/lib/shared/validation/commentValidators.ts +12 -1
- package/lib/shared/validation/index.ts +1 -0
- package/lib/shared/validation/permissionsValidators.test.ts +49 -0
- package/lib/shared/validation/permissionsValidators.ts +49 -0
- package/lib/shared/validation/propValidator.test.ts +19 -21
- package/lib/shared/validation/propValidator.ts +13 -18
- package/lib/shared/validation/schemas.test.ts +64 -32
- package/lib/shared/validation/schemas.ts +616 -345
- package/lib/shared/validation/validators.test.ts +3 -8
- package/lib/shared/validation/validators.ts +89 -68
- package/lib/shared/viewportUnits.integration.test.ts +14 -10
- package/lib/shared/viewportUnits.test.ts +24 -23
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/dom-setup.ts +1 -1
- package/lib/test-utils/factories/ConsoleMockFactory.ts +10 -14
- package/lib/test-utils/factories/DomMockFactory.ts +17 -23
- package/lib/test-utils/factories/EventMockFactory.ts +7 -13
- package/lib/test-utils/factories/FetchMockFactory.ts +39 -57
- package/lib/test-utils/factories/ServerMockFactory.ts +6 -10
- package/lib/test-utils/factories/StoreMockFactory.ts +18 -29
- package/lib/test-utils/fixtures.ts +45 -45
- package/lib/test-utils/helpers/asyncHelpers.test.ts +15 -18
- package/lib/test-utils/helpers/asyncHelpers.ts +11 -20
- package/lib/test-utils/helpers.ts +1 -5
- package/lib/test-utils/index.ts +0 -4
- package/lib/test-utils/mockFactories.ts +13 -19
- package/lib/test-utils/mocks.ts +6 -4
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +4 -7
- package/tsconfig.json +1 -0
- package/vite.config.ts +4 -4
- package/build-next.ts +0 -1361
- package/build-static.test.ts +0 -424
- package/build-static.ts +0 -1074
- package/dist/build-static.js +0 -36
- package/dist/chunks/chunk-2MHDV5BF.js +0 -251
- package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
- package/dist/chunks/chunk-AZQYF6KE.js +0 -559
- package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
- package/dist/chunks/chunk-GZHGVVW3.js +0 -322
- package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
- package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
- package/dist/chunks/chunk-I7YIGZXT.js +0 -467
- package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
- package/dist/chunks/chunk-IGYR22T6.js +0 -6192
- package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
- package/dist/chunks/chunk-JGP5A3Y5.js +0 -1136
- package/dist/chunks/chunk-JGP5A3Y5.js.map +0 -7
- package/dist/chunks/chunk-JGWFTO6P.js.map +0 -7
- package/dist/chunks/chunk-O3NAGJP4.js.map +0 -7
- package/dist/chunks/chunk-QB2LNO4W.js +0 -77
- package/dist/chunks/chunk-QB2LNO4W.js.map +0 -7
- package/dist/chunks/chunk-R6XHAFBF.js +0 -3821
- package/dist/chunks/chunk-R6XHAFBF.js.map +0 -7
- package/dist/chunks/chunk-UB44F4Z2.js.map +0 -7
- package/dist/chunks/chunk-WQFG7PAH.js.map +0 -7
- package/dist/chunks/chunk-X754AHS5.js.map +0 -7
- package/dist/chunks/chunk-YBLHKYFF.js.map +0 -7
- package/dist/chunks/configService-R3OGU2UD.js +0 -13
- package/dist/chunks/configService-R3OGU2UD.js.map +0 -7
- package/dist/chunks/constants-STK2YBIW.js +0 -46
- package/dist/chunks/constants-STK2YBIW.js.map +0 -7
- package/dist/chunks/fs-JGINUXGL.js.map +0 -7
- package/dist/entries/server-router.js +0 -67
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -81
- package/lib/server/providers/fileSystemPageProvider.ts +0 -160
- package/lib/server/webflow/buildWebflow.ts +0 -635
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3170
- package/lib/server/webflow/nodeToWebflow.ts +0 -2435
- package/lib/server/webflow/styleMapper.test.ts +0 -389
- package/lib/server/webflow/styleMapper.ts +0 -695
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -396
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
|
@@ -4,10 +4,16 @@
|
|
|
4
4
|
* Used by both client and server to ensure consistent CSS output
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { propertyOrder, staticUtilityReverse, presetClassReverse } from './utilityClassConfig';
|
|
8
|
+
import {
|
|
9
|
+
parseUtilityClass,
|
|
10
|
+
splitVariantPrefix,
|
|
11
|
+
breakpointClassPrefixes,
|
|
12
|
+
resolveRootProperty,
|
|
13
|
+
camelToKebab,
|
|
14
|
+
} from './utilityClassNames';
|
|
8
15
|
import { getStyleValue, getDynamicStyle, isDynamicClass } from './styleValueRegistry';
|
|
9
|
-
import {
|
|
10
|
-
import type { BreakpointConfig, LegacyBreakpointConfig } from './breakpoints';
|
|
16
|
+
import type { BreakpointConfig } from './breakpoints';
|
|
11
17
|
import { DEFAULT_BREAKPOINTS, getBreakpointValues } from './breakpoints';
|
|
12
18
|
import type { ResponsiveScales, CSSPropertyType, ResponsiveMode } from './responsiveScaling';
|
|
13
19
|
import {
|
|
@@ -21,49 +27,34 @@ import type { InteractiveStyles, StyleObject, ResponsiveStyleObject, StyleValue
|
|
|
21
27
|
import type { RemConversionConfig } from './pxToRem';
|
|
22
28
|
import { applyRemConversion, convertPxToRem, shouldConvertProperty } from './pxToRem';
|
|
23
29
|
|
|
24
|
-
/** Sorted prefix list (longest first) — cached at module level to avoid re-sorting per call */
|
|
25
|
-
const SORTED_PREFIXES: readonly string[] = Object.keys(prefixToCSSProperty)
|
|
26
|
-
.sort((a, b) => b.length - a.length);
|
|
27
|
-
|
|
28
30
|
/**
|
|
29
|
-
* Map from CSS property (kebab-case) to its index in
|
|
31
|
+
* Map from CSS property (kebab-case) to its index in propertyOrder.
|
|
30
32
|
* Shorthands like 'border' have lower indices than longhands like 'border-color',
|
|
31
33
|
* so sorting by this index ensures shorthand CSS rules appear before longhand rules.
|
|
32
34
|
*/
|
|
33
35
|
const cssPropertyOrderMap: Map<string, number> = (() => {
|
|
34
36
|
const map = new Map<string, number>();
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
for (let i = 0; i < propertyOrder.length; i++) {
|
|
38
|
+
const prop = propertyOrder[i];
|
|
39
|
+
if (prop === undefined) continue;
|
|
37
40
|
// Convert camelCase key to kebab-case CSS property
|
|
38
|
-
const kebab =
|
|
39
|
-
map.set(kebab, i);
|
|
41
|
+
const kebab = camelToKebab(prop);
|
|
42
|
+
if (!map.has(kebab)) map.set(kebab, i);
|
|
40
43
|
}
|
|
41
44
|
return map;
|
|
42
45
|
})();
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
48
|
* Get the property-order index for a utility class name.
|
|
46
|
-
* Returns the position of the class's CSS property in
|
|
49
|
+
* Returns the position of the class's CSS property in propertyOrder,
|
|
47
50
|
* or Infinity for unknown classes (placed last).
|
|
48
51
|
*/
|
|
49
52
|
function getClassPropertyOrder(className: string): number {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const idx = cssPropertyOrderMap.get(extracted.property);
|
|
53
|
+
const parsed = parseUtilityClass(className);
|
|
54
|
+
if (parsed) {
|
|
55
|
+
const idx = cssPropertyOrderMap.get(parsed.property);
|
|
54
56
|
if (idx !== undefined) return idx;
|
|
55
57
|
}
|
|
56
|
-
|
|
57
|
-
// Try predefined rules (e.g. 'f' → display: flex)
|
|
58
|
-
const rule = utilityClassRules[className];
|
|
59
|
-
if (rule) {
|
|
60
|
-
const propMatch = rule.match(/^([a-z-]+):/);
|
|
61
|
-
if (propMatch) {
|
|
62
|
-
const idx = cssPropertyOrderMap.get(propMatch[1]);
|
|
63
|
-
if (idx !== undefined) return idx;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
58
|
return Infinity;
|
|
68
59
|
}
|
|
69
60
|
|
|
@@ -85,18 +76,18 @@ function getResponsiveMode(scales: ResponsiveScales | undefined | null): Respons
|
|
|
85
76
|
|
|
86
77
|
/** CSS property → responsive scale category mapping (used by generateUtilityCSS and generateSingleClassCSS) */
|
|
87
78
|
const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
88
|
-
|
|
79
|
+
padding: 'padding',
|
|
89
80
|
'padding-left': 'padding',
|
|
90
81
|
'padding-right': 'padding',
|
|
91
82
|
'padding-top': 'padding',
|
|
92
83
|
'padding-bottom': 'padding',
|
|
93
|
-
|
|
84
|
+
margin: 'margin',
|
|
94
85
|
'margin-left': 'margin',
|
|
95
86
|
'margin-right': 'margin',
|
|
96
87
|
'margin-top': 'margin',
|
|
97
88
|
'margin-bottom': 'margin',
|
|
98
89
|
'font-size': 'fontSize',
|
|
99
|
-
|
|
90
|
+
gap: 'gap',
|
|
100
91
|
'row-gap': 'gap',
|
|
101
92
|
'column-gap': 'gap',
|
|
102
93
|
'border-radius': 'borderRadius',
|
|
@@ -104,8 +95,8 @@ const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
|
104
95
|
'border-top-right-radius': 'borderRadius',
|
|
105
96
|
'border-bottom-left-radius': 'borderRadius',
|
|
106
97
|
'border-bottom-right-radius': 'borderRadius',
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
width: 'size',
|
|
99
|
+
height: 'size',
|
|
109
100
|
'max-width': 'size',
|
|
110
101
|
'max-height': 'size',
|
|
111
102
|
'min-width': 'size',
|
|
@@ -113,126 +104,37 @@ const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
|
113
104
|
};
|
|
114
105
|
|
|
115
106
|
/**
|
|
116
|
-
* Escape special characters in CSS class names using backslash notation
|
|
117
|
-
*
|
|
107
|
+
* Escape special characters in CSS class names using backslash notation.
|
|
108
|
+
* Tailwind-style class names carry `[ ] ( ) : % # . , / *` and more — escape
|
|
109
|
+
* everything outside the safe identifier charset.
|
|
118
110
|
*/
|
|
119
111
|
function escapeCSSClassName(className: string): string {
|
|
120
|
-
|
|
121
|
-
// This includes: . # [ ] ( ) { } ; : , > + ~ % etc.
|
|
122
|
-
return className.replace(/[.#[\](){};<>+~:,%\/\s]/g, '\\$&');
|
|
112
|
+
return className.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
123
113
|
}
|
|
124
114
|
|
|
125
|
-
// Define all possible utility class rules
|
|
126
|
-
const utilityClassRules: Record<string, string> = {
|
|
127
|
-
// Display utilities (short forms from specialValueMappings)
|
|
128
|
-
f: 'display: flex;',
|
|
129
|
-
'fd-col': 'flex-direction: column;',
|
|
130
|
-
'fd-row': 'flex-direction: row;',
|
|
131
|
-
g: 'display: grid;',
|
|
132
|
-
b: 'display: block;',
|
|
133
|
-
i: 'display: inline;',
|
|
134
|
-
ib: 'display: inline-block;',
|
|
135
|
-
h: 'display: none;',
|
|
136
|
-
'if': 'display: inline-flex;',
|
|
137
|
-
ig: 'display: inline-grid;',
|
|
138
|
-
|
|
139
|
-
// Justify Content (short forms)
|
|
140
|
-
'jc-c': 'justify-content: center;',
|
|
141
|
-
'jc-s': 'justify-content: flex-start;',
|
|
142
|
-
'jc-e': 'justify-content: flex-end;',
|
|
143
|
-
'jc-b': 'justify-content: space-between;',
|
|
144
|
-
'jc-a': 'justify-content: space-around;',
|
|
145
|
-
'jc-ev': 'justify-content: space-evenly;',
|
|
146
|
-
|
|
147
|
-
// Align Items (short forms)
|
|
148
|
-
'ai-c': 'align-items: center;',
|
|
149
|
-
'ai-s': 'align-items: flex-start;',
|
|
150
|
-
'ai-e': 'align-items: flex-end;',
|
|
151
|
-
'ai-st': 'align-items: stretch;',
|
|
152
|
-
'ai-b': 'align-items: baseline;',
|
|
153
|
-
|
|
154
|
-
// Overflow (short forms)
|
|
155
|
-
'o-h': 'overflow: hidden;',
|
|
156
|
-
'o-a': 'overflow: auto;',
|
|
157
|
-
'o-s': 'overflow: scroll;',
|
|
158
|
-
'o-v': 'overflow: visible;',
|
|
159
|
-
|
|
160
|
-
// Cursor (full forms)
|
|
161
|
-
'cursor-pointer': 'cursor: pointer;',
|
|
162
|
-
'cursor-default': 'cursor: default;',
|
|
163
|
-
|
|
164
|
-
// Pointer events
|
|
165
|
-
'pe-none': 'pointer-events: none;',
|
|
166
|
-
'pe-auto': 'pointer-events: auto;',
|
|
167
|
-
|
|
168
|
-
// User select
|
|
169
|
-
'us-none': 'user-select: none;',
|
|
170
|
-
'us-auto': 'user-select: auto;',
|
|
171
|
-
'us-text': 'user-select: text;',
|
|
172
|
-
'us-all': 'user-select: all;',
|
|
173
|
-
|
|
174
|
-
// White space
|
|
175
|
-
'whs-normal': 'white-space: normal;',
|
|
176
|
-
'whs-nowrap': 'white-space: nowrap;',
|
|
177
|
-
'whs-pre': 'white-space: pre;',
|
|
178
|
-
'whs-pre-wrap': 'white-space: pre-wrap;',
|
|
179
|
-
'whs-pre-line': 'white-space: pre-line;',
|
|
180
|
-
|
|
181
|
-
// Shadow presets
|
|
182
|
-
'sh-0': 'box-shadow: none;',
|
|
183
|
-
'sh-1': 'box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);',
|
|
184
|
-
'sh-2': 'box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);',
|
|
185
|
-
'sh-3': 'box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);',
|
|
186
|
-
'sh-4': 'box-shadow: 0 12px 32px rgba(0, 0, 0, 0.2);',
|
|
187
|
-
|
|
188
|
-
// Gradient presets
|
|
189
|
-
'gr-1': 'background: linear-gradient(90deg, rgba(10,22,40,0.85) 0%, rgba(10,22,40,0.2) 100%);',
|
|
190
|
-
'gr-2': 'background: linear-gradient(0deg, #a9e8fd, #00aee8 25%, #0f1335 72%);',
|
|
191
|
-
|
|
192
|
-
// Border presets
|
|
193
|
-
'bd-0': 'border: 1px solid var(--border);',
|
|
194
|
-
'bd-1': 'border: 1px solid var(--border-light);',
|
|
195
|
-
|
|
196
|
-
// Note: CSS functions (blur, translateY, scale, rotate, repeat) are now handled
|
|
197
|
-
// dynamically via the styleValueRegistry - no need to hardcode specific values here
|
|
198
|
-
};
|
|
199
|
-
|
|
200
115
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
116
|
+
* Predefined rules for static utilities and presets (Tailwind names):
|
|
117
|
+
* `flex` → `display: flex;`, `hidden` → `display: none;`,
|
|
118
|
+
* `justify-center` → `justify-content: center;`, `shadow-2` → preset value, …
|
|
119
|
+
* Derived from the single source of truth in utilityClassConfig.ts.
|
|
203
120
|
*/
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
for (const
|
|
207
|
-
|
|
208
|
-
const classValue = className.substring(knownPrefix.length + 1);
|
|
209
|
-
const cssProp = prefixToCSSProperty[knownPrefix];
|
|
210
|
-
if (cssProp) {
|
|
211
|
-
// Hash-fallback class (e.g. `pt-h1y9pr6i`): the part after the
|
|
212
|
-
// prefix is a hash, not a real CSS value. Returning `value: 'h1y9pr6i'`
|
|
213
|
-
// would make auto-responsive scaling and other downstream consumers
|
|
214
|
-
// emit broken rules like `padding-top: h1y9pr6i;`. The class IS
|
|
215
|
-
// resolvable via the style-value registry — `generateRuleForClass`
|
|
216
|
-
// handles it directly. Skip the dynamic-extraction path here.
|
|
217
|
-
if (/^h[0-9a-z]+$/.test(classValue)) {
|
|
218
|
-
return null;
|
|
219
|
-
}
|
|
220
|
-
return { property: cssProp, value: classValue };
|
|
221
|
-
}
|
|
222
|
-
}
|
|
121
|
+
const utilityClassRules: Record<string, string> = (() => {
|
|
122
|
+
const rules: Record<string, string> = {};
|
|
123
|
+
for (const [className, entry] of Object.entries(staticUtilityReverse)) {
|
|
124
|
+
rules[className] = `${camelToKebab(entry.prop)}: ${entry.value};`;
|
|
223
125
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
126
|
+
for (const [className, entry] of Object.entries(presetClassReverse)) {
|
|
127
|
+
rules[className] = `${camelToKebab(entry.prop)}: ${entry.value};`;
|
|
128
|
+
}
|
|
129
|
+
return rules;
|
|
130
|
+
})();
|
|
227
131
|
|
|
228
132
|
/**
|
|
229
133
|
* Resolve `{ property, value }` for a utility class, consulting the style-value
|
|
230
|
-
* registry for hash-fallback classes (e.g. `
|
|
134
|
+
* registry for hash-fallback classes (e.g. `text-h1glej9a` for `222.3px`).
|
|
231
135
|
*
|
|
232
136
|
* Used by the auto-responsive scaling path so hashed classes don't silently
|
|
233
|
-
* bypass `clamp()` / `@media` rewriting.
|
|
234
|
-
* intentionally returns null for hashed classes because its other consumers
|
|
235
|
-
* would emit broken `${prop}: ${hash};` rules.
|
|
137
|
+
* bypass `clamp()` / `@media` rewriting.
|
|
236
138
|
*
|
|
237
139
|
* Returns the registered value as-is (no parsing). Downstream helpers
|
|
238
140
|
* (`buildFluidPropertyValue`, `scalePropertyValue`) already null out
|
|
@@ -241,48 +143,41 @@ function extractPropertyAndValue(className: string): { property: string; value:
|
|
|
241
143
|
* `if (!fluidValue) return rule;` guards.
|
|
242
144
|
*/
|
|
243
145
|
function resolveScalablePropertyValue(className: string): { property: string; value: string } | null {
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (registered == null || registered === '') continue;
|
|
255
|
-
return { property: cssProp, value: String(registered) };
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return null;
|
|
146
|
+
const parsed = parseUtilityClass(className);
|
|
147
|
+
if (!parsed) return null;
|
|
148
|
+
if (parsed.value != null) return { property: parsed.property, value: parsed.value };
|
|
149
|
+
|
|
150
|
+
// Hash-fallback class: real value lives in the registry
|
|
151
|
+
const registered = getStyleValue(className);
|
|
152
|
+
if (registered == null || registered === '') return null;
|
|
153
|
+
const value = String(registered);
|
|
154
|
+
const property = parsed.root ? resolveRootProperty(parsed.root, value) : parsed.property;
|
|
155
|
+
return { property, value };
|
|
259
156
|
}
|
|
260
157
|
|
|
158
|
+
/** Border-side class roots → CSS side shorthand base. */
|
|
159
|
+
const borderSideMap: Record<string, string> = {
|
|
160
|
+
'border-t': 'border-top',
|
|
161
|
+
'border-r': 'border-right',
|
|
162
|
+
'border-b': 'border-bottom',
|
|
163
|
+
'border-l': 'border-left',
|
|
164
|
+
};
|
|
165
|
+
|
|
261
166
|
/**
|
|
262
167
|
* Generate CSS rule for a utility class
|
|
263
|
-
* Handles
|
|
168
|
+
* Handles arbitrary values (p-[10px]), variables (bg-(--primary)),
|
|
169
|
+
* keywords (w-full), bare numbers (z-10) and hash-fallback classes.
|
|
264
170
|
*/
|
|
265
|
-
export function generateRuleForClass(className: string): string | null {
|
|
266
|
-
// Check if it's a predefined rule
|
|
171
|
+
export function generateRuleForClass(className: string, knownTokens?: ReadonlySet<string>): string | null {
|
|
172
|
+
// Check if it's a predefined static/preset rule
|
|
267
173
|
if (utilityClassRules[className]) {
|
|
268
174
|
return utilityClassRules[className];
|
|
269
175
|
}
|
|
270
176
|
|
|
271
|
-
|
|
272
|
-
// Try to match known prefixes first by checking longest prefixes first
|
|
273
|
-
let prefix = '';
|
|
274
|
-
let classValue = '';
|
|
275
|
-
|
|
276
|
-
for (const knownPrefix of SORTED_PREFIXES) {
|
|
277
|
-
if (className.startsWith(knownPrefix + '-')) {
|
|
278
|
-
prefix = knownPrefix;
|
|
279
|
-
classValue = className.substring(knownPrefix.length + 1); // +1 for the hyphen
|
|
280
|
-
break;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
177
|
+
const parsed = parseUtilityClass(className, knownTokens);
|
|
283
178
|
|
|
284
|
-
if (!
|
|
285
|
-
// Check dynamic registry for classes with unknown
|
|
179
|
+
if (!parsed) {
|
|
180
|
+
// Check dynamic registry for classes with unknown roots
|
|
286
181
|
const dynamicStyle = getDynamicStyle(className);
|
|
287
182
|
if (dynamicStyle) {
|
|
288
183
|
return `${dynamicStyle.property}: ${dynamicStyle.value};`;
|
|
@@ -290,181 +185,66 @@ export function generateRuleForClass(className: string): string | null {
|
|
|
290
185
|
return null;
|
|
291
186
|
}
|
|
292
187
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
// Check dynamic registry for classes with unknown prefixes
|
|
188
|
+
// Arbitrary property syntax: [clip-path:circle(50%)] — prefer the registered
|
|
189
|
+
// original value (exact), fall back to the decoded one.
|
|
190
|
+
if (parsed.kind === 'arbitrary-property') {
|
|
297
191
|
const dynamicStyle = getDynamicStyle(className);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
return null;
|
|
192
|
+
const value = dynamicStyle ? dynamicStyle.value : parsed.value;
|
|
193
|
+
return `${parsed.property}: ${value};`;
|
|
302
194
|
}
|
|
303
195
|
|
|
304
|
-
|
|
305
|
-
// Generate ONLY width and style, NOT color - allows bc- to control color independently
|
|
306
|
-
// e.g., bt-1px-solid → "border-top-width: 1px; border-top-style: solid;"
|
|
307
|
-
// This must come BEFORE registry check to ensure consistent output
|
|
308
|
-
const borderSideMap: Record<string, string> = {
|
|
309
|
-
'bt': 'border-top',
|
|
310
|
-
'bb': 'border-bottom',
|
|
311
|
-
'bl': 'border-left',
|
|
312
|
-
'border-r': 'border-right',
|
|
313
|
-
};
|
|
314
|
-
if (borderSideMap[prefix] && classValue.includes('-')) {
|
|
315
|
-
const parts = classValue.split('-');
|
|
316
|
-
const width = parts[0];
|
|
317
|
-
const borderStyle = parts[1] || 'solid';
|
|
318
|
-
const side = borderSideMap[prefix];
|
|
319
|
-
return `${side}-width: ${width}; ${side}-style: ${borderStyle};`;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Check registry for original value first (avoids lossy reverse-engineering)
|
|
323
|
-
const originalValue = getStyleValue(className);
|
|
324
|
-
if (originalValue !== undefined) {
|
|
325
|
-
return `${cssProp}: ${originalValue};`;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// FALLBACK: Reverse-engineer value from class name for backward compatibility
|
|
329
|
-
// (for classes extracted from HTML that weren't registered)
|
|
330
|
-
|
|
331
|
-
// Handle border with special syntax BEFORE hyphen conversion (e.g., b-1px-solid-text)
|
|
332
|
-
// This needs the original hyphenated format to parse correctly
|
|
333
|
-
if (prefix === 'b' && classValue.includes('-')) {
|
|
334
|
-
const parts = classValue.split('-');
|
|
335
|
-
const width = parts[0];
|
|
336
|
-
const borderStyle = parts[1] || 'solid';
|
|
337
|
-
const borderColor = parts[2] ? `var(--${parts[2]})` : 'currentColor';
|
|
338
|
-
return `border: ${width} ${borderStyle} ${borderColor};`;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Handle border-radius with CSS variables (e.g., br-background)
|
|
342
|
-
if (prefix === 'br' && classValue.includes('-') && !classValue.match(/^\d+px$/)) {
|
|
343
|
-
return `border-radius: var(--${classValue});`;
|
|
344
|
-
}
|
|
196
|
+
const root = parsed.root;
|
|
345
197
|
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
//
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// Only convert hyphens that are NOT inside parentheses (function arguments)
|
|
360
|
-
// This preserves negative values like translateY(-100vh) while still handling
|
|
361
|
-
// multi-value shorthand like "10px-20px" → "10px 20px"
|
|
362
|
-
const hasParentheses = value.includes('(');
|
|
363
|
-
|
|
364
|
-
if (hasParentheses) {
|
|
365
|
-
// Convert hyphens to spaces, handling both inside and outside parentheses
|
|
366
|
-
// Inside parentheses: convert hyphens after commas (they were space separators)
|
|
367
|
-
// - "repeat(3,-1fr)" → "repeat(3, 1fr)" (single hyphen = space)
|
|
368
|
-
// - "clamp(-10px,--5px,-10px)" → "clamp(-10px, -5px, 10px)" (double hyphen = space + negative)
|
|
369
|
-
let result = '';
|
|
370
|
-
let depth = 0;
|
|
371
|
-
let afterComma = false;
|
|
372
|
-
for (let i = 0; i < value.length; i++) {
|
|
373
|
-
const char = value[i];
|
|
374
|
-
if (char === '(') {
|
|
375
|
-
depth++;
|
|
376
|
-
result += char;
|
|
377
|
-
afterComma = false;
|
|
378
|
-
} else if (char === ')') {
|
|
379
|
-
depth--;
|
|
380
|
-
result += char;
|
|
381
|
-
afterComma = false;
|
|
382
|
-
} else if (char === ',') {
|
|
383
|
-
result += char;
|
|
384
|
-
afterComma = true;
|
|
385
|
-
} else if (char === '-' && afterComma && depth > 0) {
|
|
386
|
-
// Hyphen after comma inside parentheses - convert to space
|
|
387
|
-
// Check if next char is also hyphen (indicating actual negative number)
|
|
388
|
-
if (value[i + 1] === '-') {
|
|
389
|
-
// Double hyphen: ",--" means ", -" (space followed by negative)
|
|
390
|
-
result += ' ';
|
|
391
|
-
// Don't skip the next hyphen - let it be added normally
|
|
392
|
-
} else {
|
|
393
|
-
// Single hyphen: ",-" means ", " (just a space separator)
|
|
394
|
-
result += ' ';
|
|
395
|
-
}
|
|
396
|
-
afterComma = false;
|
|
397
|
-
} else if (char === '-' && depth > 0 && i > 0) {
|
|
398
|
-
// Hyphens inside parentheses (not after comma) - convert if followed by digit/value
|
|
399
|
-
// This handles cases like "#0f1442-94%" → "#0f1442 94%"
|
|
400
|
-
const rest = value.substring(i + 1);
|
|
401
|
-
if (valuePattern.test(rest[0] || '')) {
|
|
402
|
-
result += ' ';
|
|
403
|
-
} else {
|
|
404
|
-
result += char;
|
|
405
|
-
}
|
|
406
|
-
afterComma = false;
|
|
407
|
-
} else if (char === '-' && depth === 0 && i > 0) {
|
|
408
|
-
// Hyphens outside parentheses - convert if followed by value pattern
|
|
409
|
-
const rest = value.substring(i + 1);
|
|
410
|
-
if (valuePattern.test(rest[0] || '')) {
|
|
411
|
-
result += ' ';
|
|
412
|
-
} else {
|
|
413
|
-
result += char;
|
|
414
|
-
}
|
|
415
|
-
afterComma = false;
|
|
416
|
-
} else {
|
|
417
|
-
result += char;
|
|
418
|
-
afterComma = false;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
value = result;
|
|
422
|
-
} else if (value.startsWith('-')) {
|
|
423
|
-
// For negative values, only replace hyphens that come after the first character
|
|
424
|
-
// This way "-0.015" stays as is, but "-10px-5px" becomes "-10px 5px"
|
|
425
|
-
value = value[0] + value.substring(1).replace(new RegExp(`-(?=${valuePattern.source})`, 'g'), ' ');
|
|
198
|
+
// Resolve the CSS value. The style-value registry holds the exact original
|
|
199
|
+
// for arbitrary/hash classes; variable, keyword and numeric forms decode
|
|
200
|
+
// deterministically from the class name itself.
|
|
201
|
+
let value: string;
|
|
202
|
+
let property = parsed.property;
|
|
203
|
+
if (parsed.kind === 'hash') {
|
|
204
|
+
const registered = getStyleValue(className);
|
|
205
|
+
if (registered == null) return null;
|
|
206
|
+
value = String(registered);
|
|
207
|
+
if (root) property = resolveRootProperty(root, value);
|
|
208
|
+
} else if (parsed.kind === 'arbitrary') {
|
|
209
|
+
const registered = getStyleValue(className);
|
|
210
|
+
value = registered !== undefined ? String(registered) : parsed.value!;
|
|
426
211
|
} else {
|
|
427
|
-
|
|
428
|
-
value = value.replace(new RegExp(`-(?=${valuePattern.source})`, 'g'), ' ');
|
|
212
|
+
value = parsed.value!;
|
|
429
213
|
}
|
|
430
214
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// Handle special multi-property prefixes
|
|
435
|
-
if (prefix === 'px') {
|
|
436
|
-
// padding-left and padding-right
|
|
215
|
+
// Multi-property roots (Tailwind axis shorthands)
|
|
216
|
+
if (root === 'px') {
|
|
437
217
|
return `padding-left: ${value}; padding-right: ${value};`;
|
|
438
218
|
}
|
|
439
|
-
if (
|
|
440
|
-
// padding-top and padding-bottom
|
|
219
|
+
if (root === 'py') {
|
|
441
220
|
return `padding-top: ${value}; padding-bottom: ${value};`;
|
|
442
221
|
}
|
|
443
|
-
if (
|
|
444
|
-
// margin-left and margin-right
|
|
222
|
+
if (root === 'mx') {
|
|
445
223
|
return `margin-left: ${value}; margin-right: ${value};`;
|
|
446
224
|
}
|
|
447
|
-
if (
|
|
448
|
-
// margin-top and margin-bottom
|
|
225
|
+
if (root === 'my') {
|
|
449
226
|
return `margin-top: ${value}; margin-bottom: ${value};`;
|
|
450
227
|
}
|
|
451
228
|
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
229
|
+
// Border-side classes (border-t-, border-r-, …): emit width/style (and color
|
|
230
|
+
// when present) as LONGHANDS, so a separate border-color class can still
|
|
231
|
+
// control the color independently (longhands sort after the side class).
|
|
232
|
+
if (root && borderSideMap[root] && property !== 'border-color') {
|
|
233
|
+
const side = borderSideMap[root];
|
|
234
|
+
const tokens = value.split(/\s+/).filter(Boolean);
|
|
235
|
+
if (tokens.length >= 2) {
|
|
236
|
+
const declarations = [`${side}-width: ${tokens[0]};`, `${side}-style: ${tokens[1]};`];
|
|
237
|
+
if (tokens[2]) {
|
|
238
|
+
declarations.push(`${side}-color: ${tokens.slice(2).join(' ')};`);
|
|
461
239
|
}
|
|
462
|
-
return
|
|
240
|
+
return declarations.join(' ');
|
|
463
241
|
}
|
|
242
|
+
return `${side}: ${value};`;
|
|
464
243
|
}
|
|
465
244
|
|
|
466
|
-
// Standard case:
|
|
467
|
-
|
|
245
|
+
// Standard case: property + decoded value (e.g. `padding: 10px;`,
|
|
246
|
+
// `color: var(--primary);`, `width: 100%;`)
|
|
247
|
+
return `${property}: ${value};`;
|
|
468
248
|
}
|
|
469
249
|
|
|
470
250
|
/**
|
|
@@ -480,7 +260,7 @@ function applyFluidToUtilityRule(
|
|
|
480
260
|
rule: string,
|
|
481
261
|
className: string,
|
|
482
262
|
responsiveScales: ResponsiveScales,
|
|
483
|
-
breakpoints: BreakpointConfig
|
|
263
|
+
breakpoints: BreakpointConfig,
|
|
484
264
|
): string {
|
|
485
265
|
const propValue = resolveScalablePropertyValue(className);
|
|
486
266
|
if (!propValue) return rule;
|
|
@@ -488,9 +268,7 @@ function applyFluidToUtilityRule(
|
|
|
488
268
|
const category = AUTO_RESPONSIVE_TYPE_MAP[propValue.property];
|
|
489
269
|
if (!category) return rule;
|
|
490
270
|
|
|
491
|
-
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
492
|
-
| Record<string, number>
|
|
493
|
-
| undefined;
|
|
271
|
+
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as Record<string, number> | undefined;
|
|
494
272
|
if (!scaleConfig) return rule;
|
|
495
273
|
|
|
496
274
|
const smallest = getSmallestBreakpointName(breakpoints);
|
|
@@ -501,13 +279,7 @@ function applyFluidToUtilityRule(
|
|
|
501
279
|
|
|
502
280
|
const range = responsiveScales.fluidRange ?? DEFAULT_FLUID_RANGE;
|
|
503
281
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
504
|
-
const fluidValue = buildFluidPropertyValue(
|
|
505
|
-
propValue.value,
|
|
506
|
-
scale,
|
|
507
|
-
range.min,
|
|
508
|
-
range.max,
|
|
509
|
-
baseRef
|
|
510
|
-
);
|
|
282
|
+
const fluidValue = buildFluidPropertyValue(propValue.value, scale, range.min, range.max, baseRef);
|
|
511
283
|
if (!fluidValue) return rule;
|
|
512
284
|
|
|
513
285
|
return `${propValue.property}: ${fluidValue};`;
|
|
@@ -521,7 +293,7 @@ function applyFluidToUtilityRule(
|
|
|
521
293
|
function applyFluidToStyle(
|
|
522
294
|
style: StyleObject,
|
|
523
295
|
responsiveScales: ResponsiveScales,
|
|
524
|
-
breakpoints: BreakpointConfig
|
|
296
|
+
breakpoints: BreakpointConfig,
|
|
525
297
|
): StyleObject {
|
|
526
298
|
const range = responsiveScales.fluidRange ?? DEFAULT_FLUID_RANGE;
|
|
527
299
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
@@ -539,23 +311,13 @@ function applyFluidToStyle(
|
|
|
539
311
|
continue;
|
|
540
312
|
}
|
|
541
313
|
|
|
542
|
-
const scale = getScaleMultiplier(
|
|
543
|
-
responsiveScales,
|
|
544
|
-
prop as CSSPropertyType,
|
|
545
|
-
smallest
|
|
546
|
-
);
|
|
314
|
+
const scale = getScaleMultiplier(responsiveScales, prop as CSSPropertyType, smallest);
|
|
547
315
|
if (scale == null || scale === 1) {
|
|
548
316
|
out[prop] = value;
|
|
549
317
|
continue;
|
|
550
318
|
}
|
|
551
319
|
|
|
552
|
-
const fluidValue = buildFluidPropertyValue(
|
|
553
|
-
String(value),
|
|
554
|
-
scale,
|
|
555
|
-
range.min,
|
|
556
|
-
range.max,
|
|
557
|
-
baseRef
|
|
558
|
-
);
|
|
320
|
+
const fluidValue = buildFluidPropertyValue(String(value), scale, range.min, range.max, baseRef);
|
|
559
321
|
out[prop] = fluidValue ?? value;
|
|
560
322
|
}
|
|
561
323
|
return out;
|
|
@@ -580,10 +342,7 @@ const CONTAINER_RESERVED_VALUES = new Set(['auto', 'inherit', 'initial', 'unset'
|
|
|
580
342
|
* Margins are ALWAYS overwritten with `auto` (per user spec), even if the
|
|
581
343
|
* caller had set explicit values.
|
|
582
344
|
*/
|
|
583
|
-
export function applyContainerPattern(
|
|
584
|
-
style: StyleObject,
|
|
585
|
-
fluidActive: boolean
|
|
586
|
-
): StyleObject {
|
|
345
|
+
export function applyContainerPattern(style: StyleObject, fluidActive: boolean): StyleObject {
|
|
587
346
|
if (!fluidActive) return style;
|
|
588
347
|
const w = style.width;
|
|
589
348
|
const mw = style.maxWidth;
|
|
@@ -609,7 +368,8 @@ export function generateUtilityCSS(
|
|
|
609
368
|
usedClasses: Set<string>,
|
|
610
369
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
611
370
|
responsiveScales?: ResponsiveScales,
|
|
612
|
-
remConfig?: RemConversionConfig
|
|
371
|
+
remConfig?: RemConversionConfig,
|
|
372
|
+
knownTokens?: ReadonlySet<string>,
|
|
613
373
|
): string {
|
|
614
374
|
const css: string[] = [];
|
|
615
375
|
const baseClasses = new Set<string>();
|
|
@@ -619,40 +379,35 @@ export function generateUtilityCSS(
|
|
|
619
379
|
const breakpointValues = getBreakpointValues(breakpoints);
|
|
620
380
|
|
|
621
381
|
// Create a map for responsive breakpoint classes
|
|
622
|
-
// Map from prefix (e.g
|
|
382
|
+
// Map from variant prefix (e.g. 'tablet:', 'mobile:') to the class names and breakpoint info
|
|
623
383
|
type BreakpointClassMap = Record<string, { classes: Set<string>; breakpointName: string; value: number }>;
|
|
624
384
|
const responsiveClasses: BreakpointClassMap = {};
|
|
625
385
|
|
|
626
|
-
// Initialize responsive class sets for each breakpoint
|
|
386
|
+
// Initialize responsive class sets for each breakpoint. Register BOTH the current desktop-first
|
|
387
|
+
// prefix (`max-tablet:`) and the legacy bare form (`tablet:`) so pre-`max-` projects still bucket
|
|
388
|
+
// into their @media block; each prefix renders its own classes verbatim (selectors stay valid).
|
|
627
389
|
for (const [breakpointName, breakpointValue] of Object.entries(breakpointValues)) {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
390
|
+
for (const prefix of breakpointClassPrefixes(breakpointName)) {
|
|
391
|
+
responsiveClasses[prefix] = {
|
|
392
|
+
classes: new Set<string>(),
|
|
393
|
+
breakpointName,
|
|
394
|
+
value: breakpointValue,
|
|
395
|
+
};
|
|
633
396
|
}
|
|
634
|
-
|
|
635
|
-
responsiveClasses[prefix] = {
|
|
636
|
-
classes: new Set<string>(),
|
|
637
|
-
breakpointName,
|
|
638
|
-
value: breakpointValue
|
|
639
|
-
};
|
|
640
397
|
}
|
|
641
398
|
|
|
642
399
|
// Separate classes by breakpoint
|
|
643
400
|
for (const className of usedClasses) {
|
|
644
401
|
let matched = false;
|
|
645
402
|
|
|
646
|
-
// Try to match against any responsive breakpoint prefix
|
|
403
|
+
// Try to match against any responsive breakpoint variant prefix
|
|
647
404
|
for (const prefix of Object.keys(responsiveClasses)) {
|
|
648
|
-
if (className.startsWith(
|
|
649
|
-
const potentialClass = className.substring(prefix.length
|
|
650
|
-
const rule = generateRuleForClass(potentialClass);
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (rule && !potentialClass.match(/^(auto|0|[\d.]+px|[\d.]+p)$/)) {
|
|
655
|
-
responsiveClasses[prefix].classes.add(potentialClass);
|
|
405
|
+
if (className.startsWith(prefix) && className.length > prefix.length) {
|
|
406
|
+
const potentialClass = className.substring(prefix.length);
|
|
407
|
+
const rule = generateRuleForClass(potentialClass, knownTokens);
|
|
408
|
+
const bucket = responsiveClasses[prefix];
|
|
409
|
+
if (rule && bucket) {
|
|
410
|
+
bucket.classes.add(potentialClass);
|
|
656
411
|
matched = true;
|
|
657
412
|
break;
|
|
658
413
|
}
|
|
@@ -680,7 +435,7 @@ export function generateUtilityCSS(
|
|
|
680
435
|
|
|
681
436
|
// Generate base rules — sorted so shorthands (border) appear before longhands (border-color)
|
|
682
437
|
for (const className of sortClassesByPropertyOrder(baseClasses)) {
|
|
683
|
-
let rule = generateRuleForClass(className);
|
|
438
|
+
let rule = generateRuleForClass(className, knownTokens);
|
|
684
439
|
if (rule) {
|
|
685
440
|
// In fluid mode, replace auto-responsive raw values with clamp() expressions.
|
|
686
441
|
if (fluidActive && autoResponsiveClasses.has(className)) {
|
|
@@ -699,8 +454,15 @@ export function generateUtilityCSS(
|
|
|
699
454
|
|
|
700
455
|
// Generate auto-responsive rules for classes with enabled scaling.
|
|
701
456
|
// In fluid mode the base rule already encodes scaling via clamp(), so skip @media.
|
|
457
|
+
// Sort shorthand-before-longhand exactly like the base rules above: within a breakpoint
|
|
458
|
+
// block, a `margin` shorthand emitted AFTER a `margin-bottom` longhand would clobber it
|
|
459
|
+
// (equal specificity ⇒ source order wins), silently dropping the longhand on that
|
|
460
|
+
// breakpoint. The base section is sorted, so the cascade would flip ONLY inside @media —
|
|
461
|
+
// and the unsorted Set's order depends on collection order (SSR render vs .astro walk),
|
|
462
|
+
// so the same project could render correctly in one path and lose the longhand in the
|
|
463
|
+
// other. Sorting makes the @media cascade deterministic and consistent with base.
|
|
702
464
|
if (responsiveScales?.enabled && !fluidActive) {
|
|
703
|
-
for (const className of autoResponsiveClasses) {
|
|
465
|
+
for (const className of sortClassesByPropertyOrder(autoResponsiveClasses)) {
|
|
704
466
|
const propValue = resolveScalablePropertyValue(className);
|
|
705
467
|
if (!propValue) continue;
|
|
706
468
|
|
|
@@ -727,16 +489,17 @@ export function generateUtilityCSS(
|
|
|
727
489
|
if (!autoResponsiveMediaQueries[breakpointName]) {
|
|
728
490
|
autoResponsiveMediaQueries[breakpointName] = {
|
|
729
491
|
classes: [],
|
|
730
|
-
value: breakpointValue
|
|
492
|
+
value: breakpointValue,
|
|
731
493
|
};
|
|
732
494
|
}
|
|
733
495
|
|
|
734
|
-
const finalScaledValue =
|
|
735
|
-
|
|
736
|
-
|
|
496
|
+
const finalScaledValue =
|
|
497
|
+
remConfig?.enabled && shouldConvertProperty(propValue.property)
|
|
498
|
+
? convertPxToRem(scaledValue, remConfig.baseFontSize)
|
|
499
|
+
: scaledValue;
|
|
737
500
|
autoResponsiveMediaQueries[breakpointName].classes.push({
|
|
738
501
|
className: escapedClassName,
|
|
739
|
-
rule: `${propValue.property}: ${finalScaledValue}
|
|
502
|
+
rule: `${propValue.property}: ${finalScaledValue};`,
|
|
740
503
|
});
|
|
741
504
|
}
|
|
742
505
|
}
|
|
@@ -745,14 +508,13 @@ export function generateUtilityCSS(
|
|
|
745
508
|
// Generate media queries in two separate sections:
|
|
746
509
|
// 1. Auto-responsive classes FIRST (baseline responsive behavior)
|
|
747
510
|
// 2. Manual breakpoint classes AFTER (explicit overrides)
|
|
748
|
-
// This ensures manual
|
|
511
|
+
// This ensures manual tablet:/mobile: classes always override auto-responsive rules
|
|
749
512
|
|
|
750
513
|
// Sort auto-responsive media queries by breakpoint value (descending)
|
|
751
|
-
const sortedAutoResponsive = Object.entries(autoResponsiveMediaQueries)
|
|
752
|
-
.sort(([, a], [, b]) => b.value - a.value);
|
|
514
|
+
const sortedAutoResponsive = Object.entries(autoResponsiveMediaQueries).sort(([, a], [, b]) => b.value - a.value);
|
|
753
515
|
|
|
754
516
|
// Generate auto-responsive media queries first
|
|
755
|
-
for (const [
|
|
517
|
+
for (const [, mq] of sortedAutoResponsive) {
|
|
756
518
|
if (mq.classes.length === 0) continue;
|
|
757
519
|
|
|
758
520
|
css.push(`@media (max-width: ${mq.value}px) {`);
|
|
@@ -771,12 +533,12 @@ export function generateUtilityCSS(
|
|
|
771
533
|
for (const [prefix, breakpointInfo] of sortedManualResponsive) {
|
|
772
534
|
const rules: string[] = [];
|
|
773
535
|
|
|
774
|
-
for (const className of breakpointInfo.classes) {
|
|
775
|
-
const rule = generateRuleForClass(className);
|
|
536
|
+
for (const className of sortClassesByPropertyOrder(breakpointInfo.classes)) {
|
|
537
|
+
const rule = generateRuleForClass(className, knownTokens);
|
|
776
538
|
if (rule) {
|
|
777
|
-
const escapedClassName = escapeCSSClassName(className);
|
|
539
|
+
const escapedClassName = escapeCSSClassName(`${prefix}${className}`);
|
|
778
540
|
const finalRule = applyRemConversion(rule, remConfig);
|
|
779
|
-
rules.push(` .${
|
|
541
|
+
rules.push(` .${escapedClassName} { ${finalRule} }`);
|
|
780
542
|
}
|
|
781
543
|
}
|
|
782
544
|
|
|
@@ -802,35 +564,33 @@ export function generateSingleClassCSS(
|
|
|
802
564
|
className: string,
|
|
803
565
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
804
566
|
responsiveScales?: ResponsiveScales,
|
|
805
|
-
remConfig?: RemConversionConfig
|
|
567
|
+
remConfig?: RemConversionConfig,
|
|
568
|
+
knownTokens?: ReadonlySet<string>,
|
|
806
569
|
): string {
|
|
807
570
|
const breakpointValues = getBreakpointValues(breakpoints);
|
|
808
571
|
const css: string[] = [];
|
|
809
572
|
|
|
810
|
-
// Build responsive prefix map: prefix → { breakpointName, value }
|
|
573
|
+
// Build responsive variant prefix map: prefix → { breakpointName, value }. Both the current
|
|
574
|
+
// desktop-first (`max-tablet:`) and legacy bare (`tablet:`) forms map to the same breakpoint.
|
|
811
575
|
const responsivePrefixes: Record<string, { breakpointName: string; value: number }> = {};
|
|
812
576
|
for (const [breakpointName, breakpointValue] of Object.entries(breakpointValues)) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
prefix = 'mob';
|
|
577
|
+
for (const prefix of breakpointClassPrefixes(breakpointName)) {
|
|
578
|
+
responsivePrefixes[prefix] = { breakpointName, value: breakpointValue };
|
|
816
579
|
}
|
|
817
|
-
responsivePrefixes[prefix] = { breakpointName, value: breakpointValue };
|
|
818
580
|
}
|
|
819
581
|
|
|
820
|
-
// Check if class has a responsive prefix (e.g. "
|
|
582
|
+
// Check if class has a responsive variant prefix (e.g. "tablet:text-[16px]", "mobile:p-[10px]")
|
|
821
583
|
let matched = false;
|
|
822
584
|
for (const prefix of Object.keys(responsivePrefixes)) {
|
|
823
|
-
if (className.startsWith(
|
|
824
|
-
const potentialClass = className.substring(prefix.length
|
|
825
|
-
const rule = generateRuleForClass(potentialClass);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
if (rule && !potentialClass.match(/^(auto|0|[\d.]+px|[\d.]+p)$/)) {
|
|
585
|
+
if (className.startsWith(prefix) && className.length > prefix.length) {
|
|
586
|
+
const potentialClass = className.substring(prefix.length);
|
|
587
|
+
const rule = generateRuleForClass(potentialClass, knownTokens);
|
|
588
|
+
const responsivePrefix = responsivePrefixes[prefix];
|
|
589
|
+
if (rule && responsivePrefix) {
|
|
829
590
|
const escapedClassName = escapeCSSClassName(className);
|
|
830
|
-
const
|
|
831
|
-
const bpValue = responsivePrefixes[prefix].value;
|
|
591
|
+
const bpValue = responsivePrefix.value;
|
|
832
592
|
const finalRule = applyRemConversion(rule, remConfig);
|
|
833
|
-
css.push(`@media (max-width: ${bpValue}px) {\n .${
|
|
593
|
+
css.push(`@media (max-width: ${bpValue}px) {\n .${escapedClassName} { ${finalRule} }\n}`);
|
|
834
594
|
matched = true;
|
|
835
595
|
break;
|
|
836
596
|
}
|
|
@@ -839,7 +599,7 @@ export function generateSingleClassCSS(
|
|
|
839
599
|
|
|
840
600
|
if (!matched) {
|
|
841
601
|
// Base (non-responsive) class
|
|
842
|
-
let rule = generateRuleForClass(className);
|
|
602
|
+
let rule = generateRuleForClass(className, knownTokens);
|
|
843
603
|
if (!rule) return '';
|
|
844
604
|
|
|
845
605
|
const mode = getResponsiveMode(responsiveScales);
|
|
@@ -863,13 +623,14 @@ export function generateSingleClassCSS(
|
|
|
863
623
|
if (propValue) {
|
|
864
624
|
const category = AUTO_RESPONSIVE_TYPE_MAP[propValue.property];
|
|
865
625
|
if (category) {
|
|
866
|
-
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
626
|
+
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
627
|
+
| Record<string, number>
|
|
628
|
+
| undefined;
|
|
867
629
|
if (scaleConfig) {
|
|
868
630
|
const baseRef = responsiveScales.baseReference || 16;
|
|
869
631
|
|
|
870
632
|
// Sort breakpoints descending (largest first) for proper cascade
|
|
871
|
-
const sortedBps = Object.entries(breakpointValues)
|
|
872
|
-
.sort(([, a], [, b]) => b - a);
|
|
633
|
+
const sortedBps = Object.entries(breakpointValues).sort(([, a], [, b]) => b - a);
|
|
873
634
|
|
|
874
635
|
for (const [breakpointName, breakpointValue] of sortedBps) {
|
|
875
636
|
const scale = scaleConfig[breakpointName];
|
|
@@ -878,10 +639,13 @@ export function generateSingleClassCSS(
|
|
|
878
639
|
const scaledValue = scalePropertyValue(propValue.value, baseRef, scale);
|
|
879
640
|
if (!scaledValue) continue;
|
|
880
641
|
|
|
881
|
-
const finalScaledValue =
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
642
|
+
const finalScaledValue =
|
|
643
|
+
remConfig?.enabled && shouldConvertProperty(propValue.property)
|
|
644
|
+
? convertPxToRem(scaledValue, remConfig.baseFontSize)
|
|
645
|
+
: scaledValue;
|
|
646
|
+
css.push(
|
|
647
|
+
`@media (max-width: ${breakpointValue}px) {\n .${escapedClassName} { ${propValue.property}: ${finalScaledValue}; }\n}`,
|
|
648
|
+
);
|
|
885
649
|
}
|
|
886
650
|
}
|
|
887
651
|
}
|
|
@@ -893,99 +657,34 @@ export function generateSingleClassCSS(
|
|
|
893
657
|
}
|
|
894
658
|
|
|
895
659
|
/**
|
|
896
|
-
* Extract all utility classes from a rendered HTML string
|
|
897
|
-
*
|
|
660
|
+
* Extract all utility classes from a rendered HTML string.
|
|
661
|
+
* A class counts as a utility when (after stripping an optional `breakpoint:`
|
|
662
|
+
* variant prefix) it parses as a Meno utility class — static (`flex`,
|
|
663
|
+
* `justify-center`), arbitrary (`p-[10px]`), variable (`bg-(--primary)`),
|
|
664
|
+
* keyword (`w-full`), numeric (`z-10`), preset (`shadow-2`),
|
|
665
|
+
* arbitrary-property (`[clip-path:…]`) or hash-fallback — or when it was
|
|
666
|
+
* registered in the dynamic style registry.
|
|
898
667
|
*/
|
|
899
|
-
export function extractUtilityClassesFromHTML(html: string): Set<string> {
|
|
668
|
+
export function extractUtilityClassesFromHTML(html: string, knownTokens?: ReadonlySet<string>): Set<string> {
|
|
900
669
|
const classes = new Set<string>();
|
|
901
670
|
const classRegex = /class="([^"]*)"/g;
|
|
902
|
-
let match;
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const specialPrefixes = new Set([
|
|
908
|
-
// Display short forms
|
|
909
|
-
'f', 'fd-col', 'fd-row', 'g', 'b', 'i', 'ib', 'h', 'if', 'ig',
|
|
910
|
-
// Justify content
|
|
911
|
-
'jc-c', 'jc-s', 'jc-e', 'jc-b', 'jc-a',
|
|
912
|
-
// Align items
|
|
913
|
-
'ai-c', 'ai-s', 'ai-e', 'ai-b',
|
|
914
|
-
// Overflow
|
|
915
|
-
'o-h', 'o-a', 'o-s', 'o-v',
|
|
916
|
-
// Cursor
|
|
917
|
-
'cursor-pointer', 'cursor-default',
|
|
918
|
-
// White space
|
|
919
|
-
'whs-normal', 'whs-nowrap', 'whs-pre', 'whs-pre-wrap', 'whs-pre-line',
|
|
920
|
-
]);
|
|
921
|
-
|
|
922
|
-
while ((match = classRegex.exec(html)) !== null) {
|
|
923
|
-
const classString = match[1];
|
|
924
|
-
const classList = classString.split(/\s+/);
|
|
671
|
+
let match: RegExpExecArray | null = classRegex.exec(html);
|
|
672
|
+
|
|
673
|
+
while (match !== null) {
|
|
674
|
+
const classList = (match[1] ?? '').split(/\s+/);
|
|
675
|
+
match = classRegex.exec(html);
|
|
925
676
|
|
|
926
677
|
for (const className of classList) {
|
|
927
|
-
|
|
928
|
-
if (!className || className.length === 0) continue;
|
|
678
|
+
if (!className) continue;
|
|
929
679
|
|
|
930
|
-
|
|
931
|
-
if (
|
|
680
|
+
const { base } = splitVariantPrefix(className);
|
|
681
|
+
if (parseUtilityClass(base, knownTokens)) {
|
|
932
682
|
classes.add(className);
|
|
933
683
|
continue;
|
|
934
684
|
}
|
|
935
685
|
|
|
936
|
-
//
|
|
937
|
-
|
|
938
|
-
let hasResponsivePrefix = false;
|
|
939
|
-
|
|
940
|
-
// Extract responsive prefix if present
|
|
941
|
-
// Check for multi-letter prefix first (e.g., 'mob-' for mobile)
|
|
942
|
-
if (className.startsWith('mob-') && className.length > 4) {
|
|
943
|
-
classToCheck = className.substring(4); // Remove 'mob-' prefix
|
|
944
|
-
hasResponsivePrefix = true;
|
|
945
|
-
}
|
|
946
|
-
// Then check for single-letter prefixes (t-, s-, l-, x-, u-)
|
|
947
|
-
else if (className.length > 2 && className.charAt(1) === '-' && className.match(/^[a-z]-/)) {
|
|
948
|
-
const firstChar = className.charAt(0);
|
|
949
|
-
// Only treat as responsive prefix if it looks like a breakpoint indicator
|
|
950
|
-
// Common breakpoint prefixes: t (tablet), s (small), x (extra), u (ultra)
|
|
951
|
-
// NOTE: We exclude 'm' because it conflicts with margin prefix - use 'mob' instead
|
|
952
|
-
// NOTE: We exclude 'l' because it conflicts with left property prefix - use 'lg' instead
|
|
953
|
-
if (['t', 's', 'x', 'u'].includes(firstChar)) {
|
|
954
|
-
classToCheck = className.substring(2); // Remove responsive prefix
|
|
955
|
-
hasResponsivePrefix = true;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// Check if it starts with a known prefix
|
|
960
|
-
// Handle both single-letter (like 'p-', 'w-') and multi-letter (like 'jc-', 'ai-')
|
|
961
|
-
for (const prefix of knownPrefixes) {
|
|
962
|
-
if (classToCheck === prefix) {
|
|
963
|
-
// Exact match (like 'f' for flex, 'g' for grid)
|
|
964
|
-
classes.add(className);
|
|
965
|
-
break;
|
|
966
|
-
} else if (classToCheck.startsWith(prefix + '-')) {
|
|
967
|
-
// Prefix with hyphen separator (like 'p-10px', 'fs-16px', or 't-p-10px')
|
|
968
|
-
classes.add(className);
|
|
969
|
-
break;
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
// Also check special prefixes for responsive classes
|
|
974
|
-
if (hasResponsivePrefix && !classes.has(className)) {
|
|
975
|
-
for (const specialPrefix of specialPrefixes) {
|
|
976
|
-
if (classToCheck === specialPrefix) {
|
|
977
|
-
classes.add(className);
|
|
978
|
-
break;
|
|
979
|
-
} else if (classToCheck.startsWith(specialPrefix + '-')) {
|
|
980
|
-
classes.add(className);
|
|
981
|
-
break;
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
// Check if it's a dynamic class (registered in the dynamic registry)
|
|
987
|
-
// This handles classes for properties not in propertyMap
|
|
988
|
-
if (!classes.has(className) && isDynamicClass(className)) {
|
|
686
|
+
// Dynamic classes (registered for properties without a known root)
|
|
687
|
+
if (isDynamicClass(className) || isDynamicClass(base)) {
|
|
989
688
|
classes.add(className);
|
|
990
689
|
}
|
|
991
690
|
}
|
|
@@ -1002,18 +701,20 @@ export function extractUtilityClassesFromHTML(html: string): Set<string> {
|
|
|
1002
701
|
* Check if a style object is responsive (has base/breakpoint structure)
|
|
1003
702
|
*/
|
|
1004
703
|
function isResponsiveStyle(style: StyleValue): style is ResponsiveStyleObject {
|
|
1005
|
-
return (
|
|
1006
|
-
typeof style === 'object' &&
|
|
1007
|
-
style !== null &&
|
|
1008
|
-
('base' in style || 'tablet' in style || 'mobile' in style)
|
|
1009
|
-
);
|
|
704
|
+
return typeof style === 'object' && style !== null && ('base' in style || 'tablet' in style || 'mobile' in style);
|
|
1010
705
|
}
|
|
1011
706
|
|
|
1012
707
|
/**
|
|
1013
708
|
* Convert a flat style object to CSS property declarations
|
|
1014
709
|
* Returns string like "padding: 10px; background-color: var(--primary);"
|
|
710
|
+
*
|
|
711
|
+
* Exported for the studio's astro-play optimistic style preview: it converts a
|
|
712
|
+
* single changed declaration to the same kebab-case + `var(--name)` color form
|
|
713
|
+
* the server's utility/theme CSS uses, so the inline preview matches the value
|
|
714
|
+
* the recompiled stylesheet will land with. `_mapping` (binding) values are
|
|
715
|
+
* skipped, so a bound property yields an empty string (no optimistic preview).
|
|
1015
716
|
*/
|
|
1016
|
-
function styleObjectToCSS(style: StyleObject): string {
|
|
717
|
+
export function styleObjectToCSS(style: StyleObject): string {
|
|
1017
718
|
const declarations: string[] = [];
|
|
1018
719
|
|
|
1019
720
|
for (const [prop, value] of Object.entries(style)) {
|
|
@@ -1065,7 +766,7 @@ export function generateInteractiveCSS(
|
|
|
1065
766
|
interactiveStyles: InteractiveStyles,
|
|
1066
767
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
1067
768
|
remConfig?: RemConversionConfig,
|
|
1068
|
-
responsiveScales?: ResponsiveScales
|
|
769
|
+
responsiveScales?: ResponsiveScales,
|
|
1069
770
|
): string {
|
|
1070
771
|
const css: string[] = [];
|
|
1071
772
|
|
|
@@ -1082,9 +783,7 @@ export function generateInteractiveCSS(
|
|
|
1082
783
|
const fullSelector = `${prefix || ''}.${elementClass}${postfix || ''}`;
|
|
1083
784
|
|
|
1084
785
|
// Breakpoint styles (sorted by value descending)
|
|
1085
|
-
const sortedBreakpoints = Object.entries(breakpointValues).sort(
|
|
1086
|
-
([, a], [, b]) => b - a
|
|
1087
|
-
);
|
|
786
|
+
const sortedBreakpoints = Object.entries(breakpointValues).sort(([, a], [, b]) => b - a);
|
|
1088
787
|
|
|
1089
788
|
if (isResponsiveStyle(style)) {
|
|
1090
789
|
// Generate responsive rules
|
|
@@ -1120,7 +819,7 @@ export function generateInteractiveCSS(
|
|
|
1120
819
|
responsive.base,
|
|
1121
820
|
responsiveScales!,
|
|
1122
821
|
breakpointName,
|
|
1123
|
-
new Set(Object.keys(explicit ?? {}))
|
|
822
|
+
new Set(Object.keys(explicit ?? {})),
|
|
1124
823
|
);
|
|
1125
824
|
if (Object.keys(scaled).length > 0) {
|
|
1126
825
|
merged = { ...(merged ?? {}), ...scaled };
|
|
@@ -1130,9 +829,7 @@ export function generateInteractiveCSS(
|
|
|
1130
829
|
if (merged && Object.keys(merged).length > 0) {
|
|
1131
830
|
const properties = applyRemConversion(styleObjectToCSS(merged), remConfig);
|
|
1132
831
|
if (properties) {
|
|
1133
|
-
css.push(
|
|
1134
|
-
`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${properties}; } }`
|
|
1135
|
-
);
|
|
832
|
+
css.push(`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${properties}; } }`);
|
|
1136
833
|
}
|
|
1137
834
|
}
|
|
1138
835
|
}
|
|
@@ -1153,18 +850,11 @@ export function generateInteractiveCSS(
|
|
|
1153
850
|
// Auto-scale the flat style into each enabled breakpoint — breakpoints mode only.
|
|
1154
851
|
if (scalingEnabled && !fluidActive) {
|
|
1155
852
|
for (const [breakpointName, breakpointValue] of sortedBreakpoints) {
|
|
1156
|
-
const scaled = scaleStyleForBreakpoint(
|
|
1157
|
-
flatStyle,
|
|
1158
|
-
responsiveScales!,
|
|
1159
|
-
breakpointName,
|
|
1160
|
-
new Set()
|
|
1161
|
-
);
|
|
853
|
+
const scaled = scaleStyleForBreakpoint(flatStyle, responsiveScales!, breakpointName, new Set());
|
|
1162
854
|
if (Object.keys(scaled).length === 0) continue;
|
|
1163
855
|
const scaledProps = applyRemConversion(styleObjectToCSS(scaled), remConfig);
|
|
1164
856
|
if (!scaledProps) continue;
|
|
1165
|
-
css.push(
|
|
1166
|
-
`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${scaledProps}; } }`
|
|
1167
|
-
);
|
|
857
|
+
css.push(`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${scaledProps}; } }`);
|
|
1168
858
|
}
|
|
1169
859
|
}
|
|
1170
860
|
}
|
|
@@ -1184,7 +874,7 @@ function scaleStyleForBreakpoint(
|
|
|
1184
874
|
baseStyle: StyleObject,
|
|
1185
875
|
responsiveScales: ResponsiveScales,
|
|
1186
876
|
breakpointName: string,
|
|
1187
|
-
skipProperties: Set<string
|
|
877
|
+
skipProperties: Set<string>,
|
|
1188
878
|
): StyleObject {
|
|
1189
879
|
const result: StyleObject = {};
|
|
1190
880
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
@@ -1197,11 +887,7 @@ function scaleStyleForBreakpoint(
|
|
|
1197
887
|
const strValue = String(value);
|
|
1198
888
|
if (strValue === '') continue;
|
|
1199
889
|
|
|
1200
|
-
const scale = getScaleMultiplier(
|
|
1201
|
-
responsiveScales,
|
|
1202
|
-
property as CSSPropertyType,
|
|
1203
|
-
breakpointName
|
|
1204
|
-
);
|
|
890
|
+
const scale = getScaleMultiplier(responsiveScales, property as CSSPropertyType, breakpointName);
|
|
1205
891
|
if (scale == null) continue;
|
|
1206
892
|
|
|
1207
893
|
const scaledValue = scalePropertyValue(strValue, baseRef, scale);
|
|
@@ -1224,7 +910,7 @@ export function generateAllInteractiveCSS(
|
|
|
1224
910
|
interactiveStylesMap: Map<string, InteractiveStyles>,
|
|
1225
911
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
1226
912
|
remConfig?: RemConversionConfig,
|
|
1227
|
-
responsiveScales?: ResponsiveScales
|
|
913
|
+
responsiveScales?: ResponsiveScales,
|
|
1228
914
|
): string {
|
|
1229
915
|
const cssBlocks: string[] = [];
|
|
1230
916
|
|