meno-core 1.0.53 → 1.0.54
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 +48 -57
- package/build-astro.ts +131 -113
- package/build-next.ts +109 -96
- package/build-static.test.ts +39 -10
- package/build-static.ts +120 -122
- package/dist/bin/cli.js +34 -38
- package/dist/bin/cli.js.map +2 -2
- package/dist/build-static.js +12 -11
- package/dist/chunks/chunk-2AR55GYH.js +42 -0
- package/dist/chunks/chunk-2AR55GYH.js.map +7 -0
- package/dist/chunks/{chunk-IGYR22T6.js → chunk-2FN4UOVO.js} +512 -239
- package/dist/chunks/chunk-2FN4UOVO.js.map +7 -0
- package/dist/chunks/chunk-3XER4E5W.js +168 -0
- package/dist/chunks/chunk-3XER4E5W.js.map +7 -0
- package/dist/chunks/{chunk-X754AHS5.js → chunk-5ETZFREW.js} +9 -12
- package/dist/chunks/chunk-5ETZFREW.js.map +7 -0
- package/dist/chunks/{chunk-2MHDV5BF.js → chunk-7E4IF5L7.js} +15 -21
- package/dist/chunks/chunk-7E4IF5L7.js.map +7 -0
- package/dist/chunks/{chunk-JGWFTO6P.js → chunk-7HWQUVTU.js} +1578 -1349
- package/dist/chunks/chunk-7HWQUVTU.js.map +7 -0
- package/dist/chunks/{chunk-O3NAGJP4.js → chunk-AE3QK5QW.js} +110 -21
- package/dist/chunks/chunk-AE3QK5QW.js.map +7 -0
- package/dist/chunks/{chunk-GZHGVVW3.js → chunk-F6KTJYGV.js} +7 -7
- package/dist/chunks/chunk-F6KTJYGV.js.map +7 -0
- package/dist/chunks/{chunk-WQFG7PAH.js → chunk-FZITJSSS.js} +2 -6
- package/dist/chunks/chunk-FZITJSSS.js.map +7 -0
- package/dist/chunks/{chunk-QB2LNO4W.js → chunk-GSYYA5GX.js} +2 -2
- package/dist/chunks/chunk-GSYYA5GX.js.map +7 -0
- package/dist/chunks/{chunk-YBLHKYFF.js → chunk-HIZMY3EP.js} +1 -1
- package/dist/chunks/chunk-HIZMY3EP.js.map +7 -0
- package/dist/chunks/{chunk-AZQYF6KE.js → chunk-I2WEGYA7.js} +41 -176
- package/dist/chunks/chunk-I2WEGYA7.js.map +7 -0
- package/dist/chunks/{chunk-I7YIGZXT.js → chunk-JNO3CNLJ.js} +6 -9
- package/dist/chunks/chunk-JNO3CNLJ.js.map +7 -0
- package/dist/chunks/{chunk-UB44F4Z2.js → chunk-NVRBTSQG.js} +2 -4
- package/dist/chunks/chunk-NVRBTSQG.js.map +7 -0
- package/dist/chunks/{chunk-JGP5A3Y5.js → chunk-Q4OBWKXG.js} +40 -33
- package/dist/chunks/chunk-Q4OBWKXG.js.map +7 -0
- package/dist/chunks/{chunk-R6XHAFBF.js → chunk-QTE32Y53.js} +250 -242
- package/dist/chunks/chunk-QTE32Y53.js.map +7 -0
- package/dist/chunks/{chunk-H3GJ4H2U.js → chunk-STDY3OVM.js} +214 -85
- package/dist/chunks/chunk-STDY3OVM.js.map +7 -0
- package/dist/chunks/configService-PRJZF7Y6.js +14 -0
- package/dist/chunks/{constants-STK2YBIW.js → constants-KIQEYMAM.js} +2 -2
- package/dist/chunks/{fs-JGINUXGL.js → fs-ZI5JEU7V.js} +2 -2
- package/dist/entries/server-router.js +14 -19
- package/dist/entries/server-router.js.map +2 -2
- package/dist/lib/client/index.js +638 -332
- package/dist/lib/client/index.js.map +2 -2
- package/dist/lib/server/index.js +177 -235
- package/dist/lib/server/index.js.map +2 -2
- package/dist/lib/shared/index.js +80 -44
- package/dist/lib/shared/index.js.map +2 -2
- package/dist/lib/shared/richtext/index.js +1 -1
- package/dist/lib/test-utils/index.js +38 -60
- package/dist/lib/test-utils/index.js.map +2 -2
- package/entries/client-router.tsx +10 -8
- package/entries/server-router.tsx +1 -7
- package/lib/client/ClientInitializer.ts +8 -8
- package/lib/client/ErrorBoundary.test.tsx +146 -143
- package/lib/client/ErrorBoundary.tsx +175 -127
- package/lib/client/componentRegistry.test.ts +96 -108
- package/lib/client/componentRegistry.ts +1 -2
- package/lib/client/contexts/ThemeContext.tsx +3 -2
- package/lib/client/core/ComponentBuilder.test.ts +513 -560
- package/lib/client/core/ComponentBuilder.ts +318 -146
- package/lib/client/core/ComponentRenderer.test.tsx +1 -2
- package/lib/client/core/ComponentRenderer.tsx +46 -33
- package/lib/client/core/builders/embedBuilder.ts +241 -53
- package/lib/client/core/builders/linkBuilder.ts +71 -44
- package/lib/client/core/builders/linkNodeBuilder.ts +78 -53
- package/lib/client/core/builders/listBuilder.ts +115 -86
- package/lib/client/core/builders/localeListBuilder.ts +95 -60
- package/lib/client/core/builders/types.ts +5 -5
- package/lib/client/core/cmsTemplateProcessor.ts +7 -7
- 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 +8 -0
- package/lib/client/hmrCssReload.ts +14 -8
- package/lib/client/hmrWebSocket.ts +9 -14
- package/lib/client/hooks/useColorVariables.test.ts +21 -21
- package/lib/client/hooks/useColorVariables.ts +12 -10
- package/lib/client/hooks/usePropertyAutocomplete.ts +3 -5
- package/lib/client/hooks/useVariables.ts +4 -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 +19 -21
- package/lib/client/meno-filter/MenoFilter.ts +5 -9
- package/lib/client/meno-filter/bindings.ts +15 -40
- package/lib/client/meno-filter/init.ts +1 -1
- package/lib/client/meno-filter/renderer.ts +23 -29
- package/lib/client/meno-filter/script.generated.ts +1 -3
- package/lib/client/meno-filter/ui.ts +3 -5
- package/lib/client/meno-filter/updates.ts +15 -21
- package/lib/client/navigation.test.ts +159 -159
- package/lib/client/navigation.ts +0 -1
- package/lib/client/responsiveStyleResolver.test.ts +230 -228
- package/lib/client/responsiveStyleResolver.ts +13 -16
- package/lib/client/routing/RouteLoader.test.ts +23 -24
- package/lib/client/routing/RouteLoader.ts +22 -35
- package/lib/client/routing/Router.tsx +31 -3
- package/lib/client/scripts/ScriptExecutor.test.ts +157 -158
- package/lib/client/scripts/ScriptExecutor.ts +15 -33
- package/lib/client/services/PrefetchService.test.ts +2 -2
- package/lib/client/services/PrefetchService.ts +10 -24
- package/lib/client/styleProcessor.test.ts +9 -9
- package/lib/client/styleProcessor.ts +18 -15
- package/lib/client/styles/StyleInjector.test.ts +122 -115
- package/lib/client/styles/StyleInjector.ts +9 -6
- package/lib/client/styles/UtilityClassCollector.ts +19 -26
- package/lib/client/styles/cspNonce.test.ts +2 -5
- package/lib/client/templateEngine.test.ts +554 -482
- package/lib/client/templateEngine.ts +200 -64
- 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 +2 -5
- package/lib/server/__integration__/ssr-rendering.test.ts +47 -37
- package/lib/server/__integration__/static-assets.test.ts +1 -1
- package/lib/server/__integration__/test-helpers.ts +84 -70
- package/lib/server/ab/generateFunctions.ts +12 -10
- package/lib/server/astro/cmsPageEmitter.ts +23 -38
- package/lib/server/astro/componentEmitter.ts +25 -36
- package/lib/server/astro/cssCollector.ts +10 -26
- package/lib/server/astro/nodeToAstro.test.ts +1750 -30
- package/lib/server/astro/nodeToAstro.ts +198 -187
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +105 -109
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +4 -12
- 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 +14 -17
- package/lib/server/createServer.ts +5 -17
- package/lib/server/cssGenerator.test.ts +35 -44
- package/lib/server/cssGenerator.ts +6 -17
- package/lib/server/fileWatcher.test.ts +124 -10
- package/lib/server/fileWatcher.ts +124 -118
- package/lib/server/index.ts +7 -1
- package/lib/server/jsonLoader.test.ts +39 -2
- package/lib/server/jsonLoader.ts +33 -31
- package/lib/server/middleware/cors.test.ts +20 -20
- package/lib/server/middleware/cors.ts +28 -4
- package/lib/server/middleware/errorHandler.test.ts +5 -3
- package/lib/server/middleware/errorHandler.ts +3 -8
- package/lib/server/middleware/index.ts +0 -1
- package/lib/server/middleware/logger.test.ts +7 -5
- package/lib/server/middleware/logger.ts +10 -22
- package/lib/server/pageCache.test.ts +76 -77
- package/lib/server/pageCache.ts +0 -1
- package/lib/server/projectContext.ts +4 -3
- package/lib/server/providers/fileSystemCMSProvider.test.ts +124 -95
- package/lib/server/providers/fileSystemCMSProvider.ts +35 -20
- package/lib/server/providers/fileSystemPageProvider.test.ts +84 -0
- package/lib/server/providers/fileSystemPageProvider.ts +31 -12
- package/lib/server/routes/api/cms.test.ts +26 -14
- package/lib/server/routes/api/cms.ts +9 -14
- package/lib/server/routes/api/components.ts +30 -34
- package/lib/server/routes/api/config.ts +0 -1
- package/lib/server/routes/api/core-routes.ts +47 -74
- package/lib/server/routes/api/functions.ts +8 -16
- 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 +1 -3
- package/lib/server/routes/api/variables.ts +1 -1
- package/lib/server/routes/index.ts +23 -26
- package/lib/server/routes/pages.ts +34 -29
- package/lib/server/routes/static.ts +16 -4
- package/lib/server/runtime/bundler.ts +47 -32
- package/lib/server/runtime/fs.ts +3 -13
- package/lib/server/runtime/httpServer.ts +5 -9
- package/lib/server/services/ColorService.ts +32 -27
- package/lib/server/services/EnumService.test.ts +2 -6
- package/lib/server/services/EnumService.ts +7 -2
- package/lib/server/services/VariableService.test.ts +1 -5
- package/lib/server/services/VariableService.ts +6 -1
- package/lib/server/services/cmsService.test.ts +116 -78
- package/lib/server/services/cmsService.ts +24 -54
- package/lib/server/services/componentService.test.ts +122 -36
- package/lib/server/services/componentService.ts +54 -38
- package/lib/server/services/configService.test.ts +9 -31
- package/lib/server/services/configService.ts +20 -27
- package/lib/server/services/fileWatcherService.ts +40 -6
- package/lib/server/services/index.ts +0 -1
- package/lib/server/services/pageService.test.ts +1 -3
- package/lib/server/services/pageService.ts +14 -13
- package/lib/server/ssr/attributeBuilder.ts +16 -6
- package/lib/server/ssr/buildErrorOverlay.ts +11 -12
- package/lib/server/ssr/clientDataInjector.ts +7 -21
- package/lib/server/ssr/cmsSSRProcessor.ts +3 -6
- 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 +3 -9
- package/lib/server/ssr/htmlGenerator.test.ts +120 -43
- package/lib/server/ssr/htmlGenerator.ts +120 -85
- package/lib/server/ssr/imageMetadata.test.ts +3 -1
- package/lib/server/ssr/imageMetadata.ts +25 -19
- package/lib/server/ssr/jsCollector.test.ts +3 -13
- package/lib/server/ssr/jsCollector.ts +3 -8
- package/lib/server/ssr/liveReloadIntegration.test.ts +69 -28
- 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 +196 -246
- package/lib/server/ssr/ssrRenderer.ts +609 -225
- package/lib/server/ssrRenderer.test.ts +1044 -950
- package/lib/server/utils/jsonLineMapper.test.ts +28 -28
- package/lib/server/utils/jsonLineMapper.ts +1 -1
- package/lib/server/validateStyleCoverage.ts +18 -20
- package/lib/server/webflow/buildWebflow.ts +41 -53
- package/lib/server/webflow/nodeToWebflow.test.ts +150 -218
- package/lib/server/webflow/nodeToWebflow.ts +195 -258
- package/lib/server/webflow/styleMapper.test.ts +15 -56
- package/lib/server/webflow/styleMapper.ts +33 -41
- package/lib/server/webflow/types.ts +1 -8
- package/lib/server/websocketManager.ts +16 -19
- package/lib/shared/attributeNodeUtils.test.ts +15 -15
- package/lib/shared/attributeNodeUtils.ts +5 -12
- package/lib/shared/breakpoints.ts +4 -11
- package/lib/shared/cmsQueryParser.test.ts +50 -42
- package/lib/shared/cmsQueryParser.ts +49 -31
- package/lib/shared/colorVariableUtils.test.ts +5 -5
- package/lib/shared/colorVariableUtils.ts +3 -8
- package/lib/shared/componentRefs.ts +1 -5
- package/lib/shared/constants.test.ts +3 -3
- package/lib/shared/constants.ts +4 -8
- package/lib/shared/cssGeneration.test.ts +262 -144
- package/lib/shared/cssGeneration.ts +189 -516
- package/lib/shared/cssNamedColors.ts +152 -30
- package/lib/shared/cssProperties.test.ts +5 -6
- package/lib/shared/cssProperties.ts +296 -112
- package/lib/shared/elementClassName.test.ts +109 -109
- package/lib/shared/elementClassName.ts +1 -1
- package/lib/shared/elementUtils.ts +12 -16
- 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 +119 -0
- package/lib/shared/expressionEvaluator.ts +43 -24
- package/lib/shared/fontLoader.test.ts +19 -5
- package/lib/shared/friendlyError.ts +2 -3
- package/lib/shared/gradientUtils.test.ts +1 -5
- package/lib/shared/gradientUtils.ts +2 -6
- package/lib/shared/hrefRefs.ts +2 -10
- package/lib/shared/i18n.test.ts +1 -1
- package/lib/shared/i18n.ts +13 -34
- package/lib/shared/index.ts +4 -0
- package/lib/shared/inlineSvgStyleRules.ts +2 -4
- package/lib/shared/interactiveStyleMappings.test.ts +11 -33
- package/lib/shared/interactiveStyleMappings.ts +9 -16
- package/lib/shared/interactiveStyles.test.ts +165 -188
- package/lib/shared/interfaces/contentProvider.ts +1 -1
- package/lib/shared/itemTemplateUtils.test.ts +6 -12
- package/lib/shared/itemTemplateUtils.ts +19 -35
- package/lib/shared/jsonRepair.ts +8 -2
- package/lib/shared/libraryLoader.test.ts +15 -49
- package/lib/shared/libraryLoader.ts +7 -22
- package/lib/shared/netlifyLocale404.test.ts +179 -0
- package/lib/shared/netlifyLocale404.ts +110 -0
- package/lib/shared/nodeUtils.test.ts +24 -16
- package/lib/shared/nodeUtils.ts +49 -19
- package/lib/shared/pathArrayUtils.test.ts +1 -2
- package/lib/shared/pathArrayUtils.ts +1 -1
- package/lib/shared/pathSecurity.ts +1 -1
- package/lib/shared/pathUtils.test.ts +4 -6
- package/lib/shared/pathUtils.ts +42 -48
- 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 +14 -17
- package/lib/shared/paths/PathUtils.ts +9 -10
- package/lib/shared/paths/PathValidator.test.ts +2 -15
- package/lib/shared/paths/PathValidator.ts +11 -9
- package/lib/shared/paths/index.ts +1 -2
- package/lib/shared/propResolver.test.ts +240 -244
- package/lib/shared/propResolver.ts +14 -25
- package/lib/shared/pxToRem.test.ts +7 -6
- package/lib/shared/pxToRem.ts +2 -5
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +9 -5
- package/lib/shared/registry/ClientRegistry.ts +0 -1
- package/lib/shared/registry/ComponentRegistry.test.ts +43 -29
- package/lib/shared/registry/ComponentRegistry.ts +9 -11
- package/lib/shared/registry/NodeTypeDefinition.ts +15 -8
- package/lib/shared/registry/RegistryManager.ts +1 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -1
- package/lib/shared/registry/createNodeType.ts +7 -9
- package/lib/shared/registry/defineNodeType.ts +2 -6
- package/lib/shared/registry/index.ts +0 -1
- package/lib/shared/registry/nodeTypes/ComponentInstanceNodeType.ts +14 -15
- package/lib/shared/registry/nodeTypes/EmbedNodeType.ts +18 -11
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +47 -18
- package/lib/shared/registry/nodeTypes/LinkNodeType.ts +21 -19
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +78 -74
- package/lib/shared/registry/nodeTypes/LocaleListNodeType.ts +27 -21
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -7
- package/lib/shared/registry/nodeTypes/index.ts +10 -2
- package/lib/shared/responsiveScaling.test.ts +15 -31
- package/lib/shared/responsiveScaling.ts +55 -37
- package/lib/shared/responsiveStyleUtils.ts +11 -13
- package/lib/shared/richtext/htmlToTiptap.test.ts +23 -14
- package/lib/shared/richtext/htmlToTiptap.ts +1 -3
- 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 +5 -10
- package/lib/shared/styleUtils.test.ts +87 -61
- package/lib/shared/styleUtils.ts +5 -6
- package/lib/shared/themeDefaults.test.ts +11 -11
- package/lib/shared/themeDefaults.ts +3 -4
- package/lib/shared/tree/PathBuilder.test.ts +62 -138
- package/lib/shared/tree/PathBuilder.ts +49 -39
- package/lib/shared/treePathUtils.test.ts +2 -10
- package/lib/shared/treePathUtils.ts +54 -59
- package/lib/shared/types/api.ts +1 -2
- package/lib/shared/types/cms.ts +25 -21
- package/lib/shared/types/comment.ts +49 -16
- package/lib/shared/types/components.ts +27 -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 +2 -2
- package/lib/shared/types/styles.ts +0 -1
- package/lib/shared/types/variables.test.ts +4 -13
- package/lib/shared/types/variables.ts +48 -27
- package/lib/shared/types.ts +1 -2
- package/lib/shared/utilityClassConfig.ts +648 -321
- package/lib/shared/utilityClassMapper.test.ts +203 -92
- package/lib/shared/utilityClassMapper.ts +188 -246
- package/lib/shared/utilityClassNames.ts +326 -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/propValidator.test.ts +18 -20
- package/lib/shared/validation/propValidator.ts +12 -17
- package/lib/shared/validation/schemas.test.ts +24 -33
- package/lib/shared/validation/schemas.ts +469 -344
- package/lib/shared/validation/validators.test.ts +1 -6
- package/lib/shared/validation/validators.ts +89 -68
- package/lib/shared/viewportUnits.integration.test.ts +14 -10
- package/lib/shared/viewportUnits.test.ts +11 -23
- package/lib/test-utils/dom-setup.ts +1 -1
- package/lib/test-utils/factories/ConsoleMockFactory.ts +3 -7
- package/lib/test-utils/factories/DomMockFactory.ts +7 -19
- 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 +5 -9
- package/lib/test-utils/factories/StoreMockFactory.ts +14 -25
- 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 +12 -18
- package/lib/test-utils/mocks.ts +4 -2
- package/package.json +1 -1
- package/scripts/build-meno-filter.ts +1 -4
- package/vite.config.ts +4 -4
- package/dist/chunks/chunk-2MHDV5BF.js.map +0 -7
- package/dist/chunks/chunk-AZQYF6KE.js.map +0 -7
- package/dist/chunks/chunk-GZHGVVW3.js.map +0 -7
- package/dist/chunks/chunk-H3GJ4H2U.js.map +0 -7
- package/dist/chunks/chunk-I7YIGZXT.js.map +0 -7
- package/dist/chunks/chunk-IGYR22T6.js.map +0 -7
- 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.map +0 -7
- 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 → configService-PRJZF7Y6.js.map} +0 -0
- /package/dist/chunks/{constants-STK2YBIW.js.map → constants-KIQEYMAM.js.map} +0 -0
- /package/dist/chunks/{fs-JGINUXGL.js.map → fs-ZI5JEU7V.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
|
+
breakpointClassPrefix,
|
|
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,32 @@ 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
|
-
for (let i = 0; i < props.length; i++) {
|
|
37
|
+
for (let i = 0; i < propertyOrder.length; i++) {
|
|
37
38
|
// Convert camelCase key to kebab-case CSS property
|
|
38
|
-
const kebab =
|
|
39
|
-
map.set(kebab, i);
|
|
39
|
+
const kebab = camelToKebab(propertyOrder[i]);
|
|
40
|
+
if (!map.has(kebab)) map.set(kebab, i);
|
|
40
41
|
}
|
|
41
42
|
return map;
|
|
42
43
|
})();
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Get the property-order index for a utility class name.
|
|
46
|
-
* Returns the position of the class's CSS property in
|
|
47
|
+
* Returns the position of the class's CSS property in propertyOrder,
|
|
47
48
|
* or Infinity for unknown classes (placed last).
|
|
48
49
|
*/
|
|
49
50
|
function getClassPropertyOrder(className: string): number {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const idx = cssPropertyOrderMap.get(extracted.property);
|
|
51
|
+
const parsed = parseUtilityClass(className);
|
|
52
|
+
if (parsed) {
|
|
53
|
+
const idx = cssPropertyOrderMap.get(parsed.property);
|
|
54
54
|
if (idx !== undefined) return idx;
|
|
55
55
|
}
|
|
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
56
|
return Infinity;
|
|
68
57
|
}
|
|
69
58
|
|
|
@@ -85,18 +74,18 @@ function getResponsiveMode(scales: ResponsiveScales | undefined | null): Respons
|
|
|
85
74
|
|
|
86
75
|
/** CSS property → responsive scale category mapping (used by generateUtilityCSS and generateSingleClassCSS) */
|
|
87
76
|
const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
88
|
-
|
|
77
|
+
padding: 'padding',
|
|
89
78
|
'padding-left': 'padding',
|
|
90
79
|
'padding-right': 'padding',
|
|
91
80
|
'padding-top': 'padding',
|
|
92
81
|
'padding-bottom': 'padding',
|
|
93
|
-
|
|
82
|
+
margin: 'margin',
|
|
94
83
|
'margin-left': 'margin',
|
|
95
84
|
'margin-right': 'margin',
|
|
96
85
|
'margin-top': 'margin',
|
|
97
86
|
'margin-bottom': 'margin',
|
|
98
87
|
'font-size': 'fontSize',
|
|
99
|
-
|
|
88
|
+
gap: 'gap',
|
|
100
89
|
'row-gap': 'gap',
|
|
101
90
|
'column-gap': 'gap',
|
|
102
91
|
'border-radius': 'borderRadius',
|
|
@@ -104,8 +93,8 @@ const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
|
104
93
|
'border-top-right-radius': 'borderRadius',
|
|
105
94
|
'border-bottom-left-radius': 'borderRadius',
|
|
106
95
|
'border-bottom-right-radius': 'borderRadius',
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
width: 'size',
|
|
97
|
+
height: 'size',
|
|
109
98
|
'max-width': 'size',
|
|
110
99
|
'max-height': 'size',
|
|
111
100
|
'min-width': 'size',
|
|
@@ -113,126 +102,37 @@ const AUTO_RESPONSIVE_TYPE_MAP: Record<string, string> = {
|
|
|
113
102
|
};
|
|
114
103
|
|
|
115
104
|
/**
|
|
116
|
-
* Escape special characters in CSS class names using backslash notation
|
|
117
|
-
*
|
|
105
|
+
* Escape special characters in CSS class names using backslash notation.
|
|
106
|
+
* Tailwind-style class names carry `[ ] ( ) : % # . , / *` and more — escape
|
|
107
|
+
* everything outside the safe identifier charset.
|
|
118
108
|
*/
|
|
119
109
|
function escapeCSSClassName(className: string): string {
|
|
120
|
-
|
|
121
|
-
// This includes: . # [ ] ( ) { } ; : , > + ~ % etc.
|
|
122
|
-
return className.replace(/[.#[\](){};<>+~:,%\/\s]/g, '\\$&');
|
|
110
|
+
return className.replace(/[^a-zA-Z0-9_-]/g, '\\$&');
|
|
123
111
|
}
|
|
124
112
|
|
|
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
113
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
114
|
+
* Predefined rules for static utilities and presets (Tailwind names):
|
|
115
|
+
* `flex` → `display: flex;`, `hidden` → `display: none;`,
|
|
116
|
+
* `justify-center` → `justify-content: center;`, `shadow-2` → preset value, …
|
|
117
|
+
* Derived from the single source of truth in utilityClassConfig.ts.
|
|
203
118
|
*/
|
|
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
|
-
}
|
|
119
|
+
const utilityClassRules: Record<string, string> = (() => {
|
|
120
|
+
const rules: Record<string, string> = {};
|
|
121
|
+
for (const [className, entry] of Object.entries(staticUtilityReverse)) {
|
|
122
|
+
rules[className] = `${camelToKebab(entry.prop)}: ${entry.value};`;
|
|
223
123
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
124
|
+
for (const [className, entry] of Object.entries(presetClassReverse)) {
|
|
125
|
+
rules[className] = `${camelToKebab(entry.prop)}: ${entry.value};`;
|
|
126
|
+
}
|
|
127
|
+
return rules;
|
|
128
|
+
})();
|
|
227
129
|
|
|
228
130
|
/**
|
|
229
131
|
* Resolve `{ property, value }` for a utility class, consulting the style-value
|
|
230
|
-
* registry for hash-fallback classes (e.g. `
|
|
132
|
+
* registry for hash-fallback classes (e.g. `text-h1glej9a` for `222.3px`).
|
|
231
133
|
*
|
|
232
134
|
* 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.
|
|
135
|
+
* bypass `clamp()` / `@media` rewriting.
|
|
236
136
|
*
|
|
237
137
|
* Returns the registered value as-is (no parsing). Downstream helpers
|
|
238
138
|
* (`buildFluidPropertyValue`, `scalePropertyValue`) already null out
|
|
@@ -241,48 +141,41 @@ function extractPropertyAndValue(className: string): { property: string; value:
|
|
|
241
141
|
* `if (!fluidValue) return rule;` guards.
|
|
242
142
|
*/
|
|
243
143
|
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;
|
|
144
|
+
const parsed = parseUtilityClass(className);
|
|
145
|
+
if (!parsed) return null;
|
|
146
|
+
if (parsed.value != null) return { property: parsed.property, value: parsed.value };
|
|
147
|
+
|
|
148
|
+
// Hash-fallback class: real value lives in the registry
|
|
149
|
+
const registered = getStyleValue(className);
|
|
150
|
+
if (registered == null || registered === '') return null;
|
|
151
|
+
const value = String(registered);
|
|
152
|
+
const property = parsed.root ? resolveRootProperty(parsed.root, value) : parsed.property;
|
|
153
|
+
return { property, value };
|
|
259
154
|
}
|
|
260
155
|
|
|
156
|
+
/** Border-side class roots → CSS side shorthand base. */
|
|
157
|
+
const borderSideMap: Record<string, string> = {
|
|
158
|
+
'border-t': 'border-top',
|
|
159
|
+
'border-r': 'border-right',
|
|
160
|
+
'border-b': 'border-bottom',
|
|
161
|
+
'border-l': 'border-left',
|
|
162
|
+
};
|
|
163
|
+
|
|
261
164
|
/**
|
|
262
165
|
* Generate CSS rule for a utility class
|
|
263
|
-
* Handles
|
|
166
|
+
* Handles arbitrary values (p-[10px]), variables (bg-(--primary)),
|
|
167
|
+
* keywords (w-full), bare numbers (z-10) and hash-fallback classes.
|
|
264
168
|
*/
|
|
265
169
|
export function generateRuleForClass(className: string): string | null {
|
|
266
|
-
// Check if it's a predefined rule
|
|
170
|
+
// Check if it's a predefined static/preset rule
|
|
267
171
|
if (utilityClassRules[className]) {
|
|
268
172
|
return utilityClassRules[className];
|
|
269
173
|
}
|
|
270
174
|
|
|
271
|
-
|
|
272
|
-
// Try to match known prefixes first by checking longest prefixes first
|
|
273
|
-
let prefix = '';
|
|
274
|
-
let classValue = '';
|
|
175
|
+
const parsed = parseUtilityClass(className);
|
|
275
176
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
prefix = knownPrefix;
|
|
279
|
-
classValue = className.substring(knownPrefix.length + 1); // +1 for the hyphen
|
|
280
|
-
break;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (!prefix || !classValue) {
|
|
285
|
-
// Check dynamic registry for classes with unknown prefixes
|
|
177
|
+
if (!parsed) {
|
|
178
|
+
// Check dynamic registry for classes with unknown roots
|
|
286
179
|
const dynamicStyle = getDynamicStyle(className);
|
|
287
180
|
if (dynamicStyle) {
|
|
288
181
|
return `${dynamicStyle.property}: ${dynamicStyle.value};`;
|
|
@@ -290,181 +183,66 @@ export function generateRuleForClass(className: string): string | null {
|
|
|
290
183
|
return null;
|
|
291
184
|
}
|
|
292
185
|
|
|
293
|
-
//
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
// Check dynamic registry for classes with unknown prefixes
|
|
186
|
+
// Arbitrary property syntax: [clip-path:circle(50%)] — prefer the registered
|
|
187
|
+
// original value (exact), fall back to the decoded one.
|
|
188
|
+
if (parsed.kind === 'arbitrary-property') {
|
|
297
189
|
const dynamicStyle = getDynamicStyle(className);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Handle border-side classes (bt-, bb-, bl-, border-r-) with special syntax FIRST
|
|
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};`;
|
|
190
|
+
const value = dynamicStyle ? dynamicStyle.value : parsed.value;
|
|
191
|
+
return `${parsed.property}: ${value};`;
|
|
320
192
|
}
|
|
321
193
|
|
|
322
|
-
|
|
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
|
-
}
|
|
194
|
+
const root = parsed.root;
|
|
340
195
|
|
|
341
|
-
//
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
// Pattern to match CSS values that should be preceded by a space instead of hyphen
|
|
356
|
-
// Includes: digits, auto, inherit, initial, unset, border style keywords, and CSS function names
|
|
357
|
-
const valuePattern = /\d|auto|inherit|initial|unset|solid|dashed|dotted|double|groove|ridge|inset|outset|none|hidden|minmax|repeat|clamp|calc|min|max|fit-content|var/;
|
|
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'), ' ');
|
|
196
|
+
// Resolve the CSS value. The style-value registry holds the exact original
|
|
197
|
+
// for arbitrary/hash classes; variable, keyword and numeric forms decode
|
|
198
|
+
// deterministically from the class name itself.
|
|
199
|
+
let value: string;
|
|
200
|
+
let property = parsed.property;
|
|
201
|
+
if (parsed.kind === 'hash') {
|
|
202
|
+
const registered = getStyleValue(className);
|
|
203
|
+
if (registered == null) return null;
|
|
204
|
+
value = String(registered);
|
|
205
|
+
if (root) property = resolveRootProperty(root, value);
|
|
206
|
+
} else if (parsed.kind === 'arbitrary') {
|
|
207
|
+
const registered = getStyleValue(className);
|
|
208
|
+
value = registered !== undefined ? String(registered) : parsed.value!;
|
|
426
209
|
} else {
|
|
427
|
-
|
|
428
|
-
value = value.replace(new RegExp(`-(?=${valuePattern.source})`, 'g'), ' ');
|
|
210
|
+
value = parsed.value!;
|
|
429
211
|
}
|
|
430
212
|
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
// Handle special multi-property prefixes
|
|
435
|
-
if (prefix === 'px') {
|
|
436
|
-
// padding-left and padding-right
|
|
213
|
+
// Multi-property roots (Tailwind axis shorthands)
|
|
214
|
+
if (root === 'px') {
|
|
437
215
|
return `padding-left: ${value}; padding-right: ${value};`;
|
|
438
216
|
}
|
|
439
|
-
if (
|
|
440
|
-
// padding-top and padding-bottom
|
|
217
|
+
if (root === 'py') {
|
|
441
218
|
return `padding-top: ${value}; padding-bottom: ${value};`;
|
|
442
219
|
}
|
|
443
|
-
if (
|
|
444
|
-
// margin-left and margin-right
|
|
220
|
+
if (root === 'mx') {
|
|
445
221
|
return `margin-left: ${value}; margin-right: ${value};`;
|
|
446
222
|
}
|
|
447
|
-
if (
|
|
448
|
-
// margin-top and margin-bottom
|
|
223
|
+
if (root === 'my') {
|
|
449
224
|
return `margin-top: ${value}; margin-bottom: ${value};`;
|
|
450
225
|
}
|
|
451
226
|
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
//
|
|
455
|
-
if (
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
227
|
+
// Border-side classes (border-t-, border-r-, …): emit width/style (and color
|
|
228
|
+
// when present) as LONGHANDS, so a separate border-color class can still
|
|
229
|
+
// control the color independently (longhands sort after the side class).
|
|
230
|
+
if (root && borderSideMap[root] && property !== 'border-color') {
|
|
231
|
+
const side = borderSideMap[root];
|
|
232
|
+
const tokens = value.split(/\s+/).filter(Boolean);
|
|
233
|
+
if (tokens.length >= 2) {
|
|
234
|
+
const declarations = [`${side}-width: ${tokens[0]};`, `${side}-style: ${tokens[1]};`];
|
|
235
|
+
if (tokens[2]) {
|
|
236
|
+
declarations.push(`${side}-color: ${tokens.slice(2).join(' ')};`);
|
|
461
237
|
}
|
|
462
|
-
return
|
|
238
|
+
return declarations.join(' ');
|
|
463
239
|
}
|
|
240
|
+
return `${side}: ${value};`;
|
|
464
241
|
}
|
|
465
242
|
|
|
466
|
-
// Standard case:
|
|
467
|
-
|
|
243
|
+
// Standard case: property + decoded value (e.g. `padding: 10px;`,
|
|
244
|
+
// `color: var(--primary);`, `width: 100%;`)
|
|
245
|
+
return `${property}: ${value};`;
|
|
468
246
|
}
|
|
469
247
|
|
|
470
248
|
/**
|
|
@@ -480,7 +258,7 @@ function applyFluidToUtilityRule(
|
|
|
480
258
|
rule: string,
|
|
481
259
|
className: string,
|
|
482
260
|
responsiveScales: ResponsiveScales,
|
|
483
|
-
breakpoints: BreakpointConfig
|
|
261
|
+
breakpoints: BreakpointConfig,
|
|
484
262
|
): string {
|
|
485
263
|
const propValue = resolveScalablePropertyValue(className);
|
|
486
264
|
if (!propValue) return rule;
|
|
@@ -488,9 +266,7 @@ function applyFluidToUtilityRule(
|
|
|
488
266
|
const category = AUTO_RESPONSIVE_TYPE_MAP[propValue.property];
|
|
489
267
|
if (!category) return rule;
|
|
490
268
|
|
|
491
|
-
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
492
|
-
| Record<string, number>
|
|
493
|
-
| undefined;
|
|
269
|
+
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as Record<string, number> | undefined;
|
|
494
270
|
if (!scaleConfig) return rule;
|
|
495
271
|
|
|
496
272
|
const smallest = getSmallestBreakpointName(breakpoints);
|
|
@@ -501,13 +277,7 @@ function applyFluidToUtilityRule(
|
|
|
501
277
|
|
|
502
278
|
const range = responsiveScales.fluidRange ?? DEFAULT_FLUID_RANGE;
|
|
503
279
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
504
|
-
const fluidValue = buildFluidPropertyValue(
|
|
505
|
-
propValue.value,
|
|
506
|
-
scale,
|
|
507
|
-
range.min,
|
|
508
|
-
range.max,
|
|
509
|
-
baseRef
|
|
510
|
-
);
|
|
280
|
+
const fluidValue = buildFluidPropertyValue(propValue.value, scale, range.min, range.max, baseRef);
|
|
511
281
|
if (!fluidValue) return rule;
|
|
512
282
|
|
|
513
283
|
return `${propValue.property}: ${fluidValue};`;
|
|
@@ -521,7 +291,7 @@ function applyFluidToUtilityRule(
|
|
|
521
291
|
function applyFluidToStyle(
|
|
522
292
|
style: StyleObject,
|
|
523
293
|
responsiveScales: ResponsiveScales,
|
|
524
|
-
breakpoints: BreakpointConfig
|
|
294
|
+
breakpoints: BreakpointConfig,
|
|
525
295
|
): StyleObject {
|
|
526
296
|
const range = responsiveScales.fluidRange ?? DEFAULT_FLUID_RANGE;
|
|
527
297
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
@@ -539,23 +309,13 @@ function applyFluidToStyle(
|
|
|
539
309
|
continue;
|
|
540
310
|
}
|
|
541
311
|
|
|
542
|
-
const scale = getScaleMultiplier(
|
|
543
|
-
responsiveScales,
|
|
544
|
-
prop as CSSPropertyType,
|
|
545
|
-
smallest
|
|
546
|
-
);
|
|
312
|
+
const scale = getScaleMultiplier(responsiveScales, prop as CSSPropertyType, smallest);
|
|
547
313
|
if (scale == null || scale === 1) {
|
|
548
314
|
out[prop] = value;
|
|
549
315
|
continue;
|
|
550
316
|
}
|
|
551
317
|
|
|
552
|
-
const fluidValue = buildFluidPropertyValue(
|
|
553
|
-
String(value),
|
|
554
|
-
scale,
|
|
555
|
-
range.min,
|
|
556
|
-
range.max,
|
|
557
|
-
baseRef
|
|
558
|
-
);
|
|
318
|
+
const fluidValue = buildFluidPropertyValue(String(value), scale, range.min, range.max, baseRef);
|
|
559
319
|
out[prop] = fluidValue ?? value;
|
|
560
320
|
}
|
|
561
321
|
return out;
|
|
@@ -580,10 +340,7 @@ const CONTAINER_RESERVED_VALUES = new Set(['auto', 'inherit', 'initial', 'unset'
|
|
|
580
340
|
* Margins are ALWAYS overwritten with `auto` (per user spec), even if the
|
|
581
341
|
* caller had set explicit values.
|
|
582
342
|
*/
|
|
583
|
-
export function applyContainerPattern(
|
|
584
|
-
style: StyleObject,
|
|
585
|
-
fluidActive: boolean
|
|
586
|
-
): StyleObject {
|
|
343
|
+
export function applyContainerPattern(style: StyleObject, fluidActive: boolean): StyleObject {
|
|
587
344
|
if (!fluidActive) return style;
|
|
588
345
|
const w = style.width;
|
|
589
346
|
const mw = style.maxWidth;
|
|
@@ -609,7 +366,7 @@ export function generateUtilityCSS(
|
|
|
609
366
|
usedClasses: Set<string>,
|
|
610
367
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
611
368
|
responsiveScales?: ResponsiveScales,
|
|
612
|
-
remConfig?: RemConversionConfig
|
|
369
|
+
remConfig?: RemConversionConfig,
|
|
613
370
|
): string {
|
|
614
371
|
const css: string[] = [];
|
|
615
372
|
const baseClasses = new Set<string>();
|
|
@@ -619,23 +376,17 @@ export function generateUtilityCSS(
|
|
|
619
376
|
const breakpointValues = getBreakpointValues(breakpoints);
|
|
620
377
|
|
|
621
378
|
// Create a map for responsive breakpoint classes
|
|
622
|
-
// Map from prefix (e.g
|
|
379
|
+
// Map from variant prefix (e.g. 'tablet:', 'mobile:') to the class names and breakpoint info
|
|
623
380
|
type BreakpointClassMap = Record<string, { classes: Set<string>; breakpointName: string; value: number }>;
|
|
624
381
|
const responsiveClasses: BreakpointClassMap = {};
|
|
625
382
|
|
|
626
383
|
// Initialize responsive class sets for each breakpoint
|
|
627
384
|
for (const [breakpointName, breakpointValue] of Object.entries(breakpointValues)) {
|
|
628
|
-
|
|
629
|
-
// For 'mobile', use 'mob' to avoid conflict with 'margin' (m-), etc.
|
|
630
|
-
let prefix = breakpointName.charAt(0).toLowerCase();
|
|
631
|
-
if (breakpointName.toLowerCase() === 'mobile') {
|
|
632
|
-
prefix = 'mob'; // Use 'mob' instead of 'm' to avoid margin conflict
|
|
633
|
-
}
|
|
634
|
-
|
|
385
|
+
const prefix = breakpointClassPrefix(breakpointName);
|
|
635
386
|
responsiveClasses[prefix] = {
|
|
636
387
|
classes: new Set<string>(),
|
|
637
388
|
breakpointName,
|
|
638
|
-
value: breakpointValue
|
|
389
|
+
value: breakpointValue,
|
|
639
390
|
};
|
|
640
391
|
}
|
|
641
392
|
|
|
@@ -643,15 +394,12 @@ export function generateUtilityCSS(
|
|
|
643
394
|
for (const className of usedClasses) {
|
|
644
395
|
let matched = false;
|
|
645
396
|
|
|
646
|
-
// Try to match against any responsive breakpoint prefix
|
|
397
|
+
// Try to match against any responsive breakpoint variant prefix
|
|
647
398
|
for (const prefix of Object.keys(responsiveClasses)) {
|
|
648
|
-
if (className.startsWith(
|
|
649
|
-
const potentialClass = className.substring(prefix.length
|
|
399
|
+
if (className.startsWith(prefix) && className.length > prefix.length) {
|
|
400
|
+
const potentialClass = className.substring(prefix.length);
|
|
650
401
|
const rule = generateRuleForClass(potentialClass);
|
|
651
|
-
|
|
652
|
-
// If it generates a valid rule and doesn't look like a margin value, treat as breakpoint class
|
|
653
|
-
// Margin values are typically: auto, 0, or pixel/percentage values (like 10px, 92px, 50p)
|
|
654
|
-
if (rule && !potentialClass.match(/^(auto|0|[\d.]+px|[\d.]+p)$/)) {
|
|
402
|
+
if (rule) {
|
|
655
403
|
responsiveClasses[prefix].classes.add(potentialClass);
|
|
656
404
|
matched = true;
|
|
657
405
|
break;
|
|
@@ -699,8 +447,15 @@ export function generateUtilityCSS(
|
|
|
699
447
|
|
|
700
448
|
// Generate auto-responsive rules for classes with enabled scaling.
|
|
701
449
|
// In fluid mode the base rule already encodes scaling via clamp(), so skip @media.
|
|
450
|
+
// Sort shorthand-before-longhand exactly like the base rules above: within a breakpoint
|
|
451
|
+
// block, a `margin` shorthand emitted AFTER a `margin-bottom` longhand would clobber it
|
|
452
|
+
// (equal specificity ⇒ source order wins), silently dropping the longhand on that
|
|
453
|
+
// breakpoint. The base section is sorted, so the cascade would flip ONLY inside @media —
|
|
454
|
+
// and the unsorted Set's order depends on collection order (SSR render vs .astro walk),
|
|
455
|
+
// so the same project could render correctly in one path and lose the longhand in the
|
|
456
|
+
// other. Sorting makes the @media cascade deterministic and consistent with base.
|
|
702
457
|
if (responsiveScales?.enabled && !fluidActive) {
|
|
703
|
-
for (const className of autoResponsiveClasses) {
|
|
458
|
+
for (const className of sortClassesByPropertyOrder(autoResponsiveClasses)) {
|
|
704
459
|
const propValue = resolveScalablePropertyValue(className);
|
|
705
460
|
if (!propValue) continue;
|
|
706
461
|
|
|
@@ -727,16 +482,17 @@ export function generateUtilityCSS(
|
|
|
727
482
|
if (!autoResponsiveMediaQueries[breakpointName]) {
|
|
728
483
|
autoResponsiveMediaQueries[breakpointName] = {
|
|
729
484
|
classes: [],
|
|
730
|
-
value: breakpointValue
|
|
485
|
+
value: breakpointValue,
|
|
731
486
|
};
|
|
732
487
|
}
|
|
733
488
|
|
|
734
|
-
const finalScaledValue =
|
|
735
|
-
|
|
736
|
-
|
|
489
|
+
const finalScaledValue =
|
|
490
|
+
remConfig?.enabled && shouldConvertProperty(propValue.property)
|
|
491
|
+
? convertPxToRem(scaledValue, remConfig.baseFontSize)
|
|
492
|
+
: scaledValue;
|
|
737
493
|
autoResponsiveMediaQueries[breakpointName].classes.push({
|
|
738
494
|
className: escapedClassName,
|
|
739
|
-
rule: `${propValue.property}: ${finalScaledValue}
|
|
495
|
+
rule: `${propValue.property}: ${finalScaledValue};`,
|
|
740
496
|
});
|
|
741
497
|
}
|
|
742
498
|
}
|
|
@@ -745,14 +501,13 @@ export function generateUtilityCSS(
|
|
|
745
501
|
// Generate media queries in two separate sections:
|
|
746
502
|
// 1. Auto-responsive classes FIRST (baseline responsive behavior)
|
|
747
503
|
// 2. Manual breakpoint classes AFTER (explicit overrides)
|
|
748
|
-
// This ensures manual
|
|
504
|
+
// This ensures manual tablet:/mobile: classes always override auto-responsive rules
|
|
749
505
|
|
|
750
506
|
// Sort auto-responsive media queries by breakpoint value (descending)
|
|
751
|
-
const sortedAutoResponsive = Object.entries(autoResponsiveMediaQueries)
|
|
752
|
-
.sort(([, a], [, b]) => b.value - a.value);
|
|
507
|
+
const sortedAutoResponsive = Object.entries(autoResponsiveMediaQueries).sort(([, a], [, b]) => b.value - a.value);
|
|
753
508
|
|
|
754
509
|
// Generate auto-responsive media queries first
|
|
755
|
-
for (const [
|
|
510
|
+
for (const [, mq] of sortedAutoResponsive) {
|
|
756
511
|
if (mq.classes.length === 0) continue;
|
|
757
512
|
|
|
758
513
|
css.push(`@media (max-width: ${mq.value}px) {`);
|
|
@@ -771,12 +526,12 @@ export function generateUtilityCSS(
|
|
|
771
526
|
for (const [prefix, breakpointInfo] of sortedManualResponsive) {
|
|
772
527
|
const rules: string[] = [];
|
|
773
528
|
|
|
774
|
-
for (const className of breakpointInfo.classes) {
|
|
529
|
+
for (const className of sortClassesByPropertyOrder(breakpointInfo.classes)) {
|
|
775
530
|
const rule = generateRuleForClass(className);
|
|
776
531
|
if (rule) {
|
|
777
|
-
const escapedClassName = escapeCSSClassName(className);
|
|
532
|
+
const escapedClassName = escapeCSSClassName(`${prefix}${className}`);
|
|
778
533
|
const finalRule = applyRemConversion(rule, remConfig);
|
|
779
|
-
rules.push(` .${
|
|
534
|
+
rules.push(` .${escapedClassName} { ${finalRule} }`);
|
|
780
535
|
}
|
|
781
536
|
}
|
|
782
537
|
|
|
@@ -802,35 +557,28 @@ export function generateSingleClassCSS(
|
|
|
802
557
|
className: string,
|
|
803
558
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
804
559
|
responsiveScales?: ResponsiveScales,
|
|
805
|
-
remConfig?: RemConversionConfig
|
|
560
|
+
remConfig?: RemConversionConfig,
|
|
806
561
|
): string {
|
|
807
562
|
const breakpointValues = getBreakpointValues(breakpoints);
|
|
808
563
|
const css: string[] = [];
|
|
809
564
|
|
|
810
|
-
// Build responsive prefix map: prefix → { breakpointName, value }
|
|
565
|
+
// Build responsive variant prefix map: prefix → { breakpointName, value }
|
|
811
566
|
const responsivePrefixes: Record<string, { breakpointName: string; value: number }> = {};
|
|
812
567
|
for (const [breakpointName, breakpointValue] of Object.entries(breakpointValues)) {
|
|
813
|
-
|
|
814
|
-
if (breakpointName.toLowerCase() === 'mobile') {
|
|
815
|
-
prefix = 'mob';
|
|
816
|
-
}
|
|
817
|
-
responsivePrefixes[prefix] = { breakpointName, value: breakpointValue };
|
|
568
|
+
responsivePrefixes[breakpointClassPrefix(breakpointName)] = { breakpointName, value: breakpointValue };
|
|
818
569
|
}
|
|
819
570
|
|
|
820
|
-
// Check if class has a responsive prefix (e.g. "
|
|
571
|
+
// Check if class has a responsive variant prefix (e.g. "tablet:text-[16px]", "mobile:p-[10px]")
|
|
821
572
|
let matched = false;
|
|
822
573
|
for (const prefix of Object.keys(responsivePrefixes)) {
|
|
823
|
-
if (className.startsWith(
|
|
824
|
-
const potentialClass = className.substring(prefix.length
|
|
574
|
+
if (className.startsWith(prefix) && className.length > prefix.length) {
|
|
575
|
+
const potentialClass = className.substring(prefix.length);
|
|
825
576
|
const rule = generateRuleForClass(potentialClass);
|
|
826
|
-
|
|
827
|
-
// Only treat as responsive if it generates a valid rule and isn't a margin value
|
|
828
|
-
if (rule && !potentialClass.match(/^(auto|0|[\d.]+px|[\d.]+p)$/)) {
|
|
577
|
+
if (rule) {
|
|
829
578
|
const escapedClassName = escapeCSSClassName(className);
|
|
830
|
-
const escapedInnerClass = escapeCSSClassName(potentialClass);
|
|
831
579
|
const bpValue = responsivePrefixes[prefix].value;
|
|
832
580
|
const finalRule = applyRemConversion(rule, remConfig);
|
|
833
|
-
css.push(`@media (max-width: ${bpValue}px) {\n .${
|
|
581
|
+
css.push(`@media (max-width: ${bpValue}px) {\n .${escapedClassName} { ${finalRule} }\n}`);
|
|
834
582
|
matched = true;
|
|
835
583
|
break;
|
|
836
584
|
}
|
|
@@ -863,13 +611,14 @@ export function generateSingleClassCSS(
|
|
|
863
611
|
if (propValue) {
|
|
864
612
|
const category = AUTO_RESPONSIVE_TYPE_MAP[propValue.property];
|
|
865
613
|
if (category) {
|
|
866
|
-
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
614
|
+
const scaleConfig = responsiveScales[category as keyof ResponsiveScales] as
|
|
615
|
+
| Record<string, number>
|
|
616
|
+
| undefined;
|
|
867
617
|
if (scaleConfig) {
|
|
868
618
|
const baseRef = responsiveScales.baseReference || 16;
|
|
869
619
|
|
|
870
620
|
// Sort breakpoints descending (largest first) for proper cascade
|
|
871
|
-
const sortedBps = Object.entries(breakpointValues)
|
|
872
|
-
.sort(([, a], [, b]) => b - a);
|
|
621
|
+
const sortedBps = Object.entries(breakpointValues).sort(([, a], [, b]) => b - a);
|
|
873
622
|
|
|
874
623
|
for (const [breakpointName, breakpointValue] of sortedBps) {
|
|
875
624
|
const scale = scaleConfig[breakpointName];
|
|
@@ -878,10 +627,13 @@ export function generateSingleClassCSS(
|
|
|
878
627
|
const scaledValue = scalePropertyValue(propValue.value, baseRef, scale);
|
|
879
628
|
if (!scaledValue) continue;
|
|
880
629
|
|
|
881
|
-
const finalScaledValue =
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
630
|
+
const finalScaledValue =
|
|
631
|
+
remConfig?.enabled && shouldConvertProperty(propValue.property)
|
|
632
|
+
? convertPxToRem(scaledValue, remConfig.baseFontSize)
|
|
633
|
+
: scaledValue;
|
|
634
|
+
css.push(
|
|
635
|
+
`@media (max-width: ${breakpointValue}px) {\n .${escapedClassName} { ${propValue.property}: ${finalScaledValue}; }\n}`,
|
|
636
|
+
);
|
|
885
637
|
}
|
|
886
638
|
}
|
|
887
639
|
}
|
|
@@ -893,99 +645,33 @@ export function generateSingleClassCSS(
|
|
|
893
645
|
}
|
|
894
646
|
|
|
895
647
|
/**
|
|
896
|
-
* Extract all utility classes from a rendered HTML string
|
|
897
|
-
*
|
|
648
|
+
* Extract all utility classes from a rendered HTML string.
|
|
649
|
+
* A class counts as a utility when (after stripping an optional `breakpoint:`
|
|
650
|
+
* variant prefix) it parses as a Meno utility class — static (`flex`,
|
|
651
|
+
* `justify-center`), arbitrary (`p-[10px]`), variable (`bg-(--primary)`),
|
|
652
|
+
* keyword (`w-full`), numeric (`z-10`), preset (`shadow-2`),
|
|
653
|
+
* arbitrary-property (`[clip-path:…]`) or hash-fallback — or when it was
|
|
654
|
+
* registered in the dynamic style registry.
|
|
898
655
|
*/
|
|
899
656
|
export function extractUtilityClassesFromHTML(html: string): Set<string> {
|
|
900
657
|
const classes = new Set<string>();
|
|
901
658
|
const classRegex = /class="([^"]*)"/g;
|
|
902
659
|
let match;
|
|
903
660
|
|
|
904
|
-
// Get all known utility prefixes from propertyMap
|
|
905
|
-
const knownPrefixes = new Set(Object.values(propertyMap));
|
|
906
|
-
// Also include special/short form prefixes from utilityClassRules
|
|
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
661
|
while ((match = classRegex.exec(html)) !== null) {
|
|
923
|
-
const
|
|
924
|
-
const classList = classString.split(/\s+/);
|
|
662
|
+
const classList = match[1].split(/\s+/);
|
|
925
663
|
|
|
926
664
|
for (const className of classList) {
|
|
927
|
-
|
|
928
|
-
if (!className || className.length === 0) continue;
|
|
665
|
+
if (!className) continue;
|
|
929
666
|
|
|
930
|
-
|
|
931
|
-
if (
|
|
667
|
+
const { base } = splitVariantPrefix(className);
|
|
668
|
+
if (parseUtilityClass(base)) {
|
|
932
669
|
classes.add(className);
|
|
933
670
|
continue;
|
|
934
671
|
}
|
|
935
672
|
|
|
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)) {
|
|
673
|
+
// Dynamic classes (registered for properties without a known root)
|
|
674
|
+
if (isDynamicClass(className) || isDynamicClass(base)) {
|
|
989
675
|
classes.add(className);
|
|
990
676
|
}
|
|
991
677
|
}
|
|
@@ -1002,18 +688,20 @@ export function extractUtilityClassesFromHTML(html: string): Set<string> {
|
|
|
1002
688
|
* Check if a style object is responsive (has base/breakpoint structure)
|
|
1003
689
|
*/
|
|
1004
690
|
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
|
-
);
|
|
691
|
+
return typeof style === 'object' && style !== null && ('base' in style || 'tablet' in style || 'mobile' in style);
|
|
1010
692
|
}
|
|
1011
693
|
|
|
1012
694
|
/**
|
|
1013
695
|
* Convert a flat style object to CSS property declarations
|
|
1014
696
|
* Returns string like "padding: 10px; background-color: var(--primary);"
|
|
697
|
+
*
|
|
698
|
+
* Exported for the studio's astro-play optimistic style preview: it converts a
|
|
699
|
+
* single changed declaration to the same kebab-case + `var(--name)` color form
|
|
700
|
+
* the server's utility/theme CSS uses, so the inline preview matches the value
|
|
701
|
+
* the recompiled stylesheet will land with. `_mapping` (binding) values are
|
|
702
|
+
* skipped, so a bound property yields an empty string (no optimistic preview).
|
|
1015
703
|
*/
|
|
1016
|
-
function styleObjectToCSS(style: StyleObject): string {
|
|
704
|
+
export function styleObjectToCSS(style: StyleObject): string {
|
|
1017
705
|
const declarations: string[] = [];
|
|
1018
706
|
|
|
1019
707
|
for (const [prop, value] of Object.entries(style)) {
|
|
@@ -1065,7 +753,7 @@ export function generateInteractiveCSS(
|
|
|
1065
753
|
interactiveStyles: InteractiveStyles,
|
|
1066
754
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
1067
755
|
remConfig?: RemConversionConfig,
|
|
1068
|
-
responsiveScales?: ResponsiveScales
|
|
756
|
+
responsiveScales?: ResponsiveScales,
|
|
1069
757
|
): string {
|
|
1070
758
|
const css: string[] = [];
|
|
1071
759
|
|
|
@@ -1082,9 +770,7 @@ export function generateInteractiveCSS(
|
|
|
1082
770
|
const fullSelector = `${prefix || ''}.${elementClass}${postfix || ''}`;
|
|
1083
771
|
|
|
1084
772
|
// Breakpoint styles (sorted by value descending)
|
|
1085
|
-
const sortedBreakpoints = Object.entries(breakpointValues).sort(
|
|
1086
|
-
([, a], [, b]) => b - a
|
|
1087
|
-
);
|
|
773
|
+
const sortedBreakpoints = Object.entries(breakpointValues).sort(([, a], [, b]) => b - a);
|
|
1088
774
|
|
|
1089
775
|
if (isResponsiveStyle(style)) {
|
|
1090
776
|
// Generate responsive rules
|
|
@@ -1120,7 +806,7 @@ export function generateInteractiveCSS(
|
|
|
1120
806
|
responsive.base,
|
|
1121
807
|
responsiveScales!,
|
|
1122
808
|
breakpointName,
|
|
1123
|
-
new Set(Object.keys(explicit ?? {}))
|
|
809
|
+
new Set(Object.keys(explicit ?? {})),
|
|
1124
810
|
);
|
|
1125
811
|
if (Object.keys(scaled).length > 0) {
|
|
1126
812
|
merged = { ...(merged ?? {}), ...scaled };
|
|
@@ -1130,9 +816,7 @@ export function generateInteractiveCSS(
|
|
|
1130
816
|
if (merged && Object.keys(merged).length > 0) {
|
|
1131
817
|
const properties = applyRemConversion(styleObjectToCSS(merged), remConfig);
|
|
1132
818
|
if (properties) {
|
|
1133
|
-
css.push(
|
|
1134
|
-
`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${properties}; } }`
|
|
1135
|
-
);
|
|
819
|
+
css.push(`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${properties}; } }`);
|
|
1136
820
|
}
|
|
1137
821
|
}
|
|
1138
822
|
}
|
|
@@ -1153,18 +837,11 @@ export function generateInteractiveCSS(
|
|
|
1153
837
|
// Auto-scale the flat style into each enabled breakpoint — breakpoints mode only.
|
|
1154
838
|
if (scalingEnabled && !fluidActive) {
|
|
1155
839
|
for (const [breakpointName, breakpointValue] of sortedBreakpoints) {
|
|
1156
|
-
const scaled = scaleStyleForBreakpoint(
|
|
1157
|
-
flatStyle,
|
|
1158
|
-
responsiveScales!,
|
|
1159
|
-
breakpointName,
|
|
1160
|
-
new Set()
|
|
1161
|
-
);
|
|
840
|
+
const scaled = scaleStyleForBreakpoint(flatStyle, responsiveScales!, breakpointName, new Set());
|
|
1162
841
|
if (Object.keys(scaled).length === 0) continue;
|
|
1163
842
|
const scaledProps = applyRemConversion(styleObjectToCSS(scaled), remConfig);
|
|
1164
843
|
if (!scaledProps) continue;
|
|
1165
|
-
css.push(
|
|
1166
|
-
`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${scaledProps}; } }`
|
|
1167
|
-
);
|
|
844
|
+
css.push(`@media (max-width: ${breakpointValue}px) { ${fullSelector} { ${scaledProps}; } }`);
|
|
1168
845
|
}
|
|
1169
846
|
}
|
|
1170
847
|
}
|
|
@@ -1184,7 +861,7 @@ function scaleStyleForBreakpoint(
|
|
|
1184
861
|
baseStyle: StyleObject,
|
|
1185
862
|
responsiveScales: ResponsiveScales,
|
|
1186
863
|
breakpointName: string,
|
|
1187
|
-
skipProperties: Set<string
|
|
864
|
+
skipProperties: Set<string>,
|
|
1188
865
|
): StyleObject {
|
|
1189
866
|
const result: StyleObject = {};
|
|
1190
867
|
const baseRef = responsiveScales.baseReference ?? 16;
|
|
@@ -1197,11 +874,7 @@ function scaleStyleForBreakpoint(
|
|
|
1197
874
|
const strValue = String(value);
|
|
1198
875
|
if (strValue === '') continue;
|
|
1199
876
|
|
|
1200
|
-
const scale = getScaleMultiplier(
|
|
1201
|
-
responsiveScales,
|
|
1202
|
-
property as CSSPropertyType,
|
|
1203
|
-
breakpointName
|
|
1204
|
-
);
|
|
877
|
+
const scale = getScaleMultiplier(responsiveScales, property as CSSPropertyType, breakpointName);
|
|
1205
878
|
if (scale == null) continue;
|
|
1206
879
|
|
|
1207
880
|
const scaledValue = scalePropertyValue(strValue, baseRef, scale);
|
|
@@ -1224,7 +897,7 @@ export function generateAllInteractiveCSS(
|
|
|
1224
897
|
interactiveStylesMap: Map<string, InteractiveStyles>,
|
|
1225
898
|
breakpoints: BreakpointConfig = DEFAULT_BREAKPOINTS,
|
|
1226
899
|
remConfig?: RemConversionConfig,
|
|
1227
|
-
responsiveScales?: ResponsiveScales
|
|
900
|
+
responsiveScales?: ResponsiveScales,
|
|
1228
901
|
): string {
|
|
1229
902
|
const cssBlocks: string[] = [];
|
|
1230
903
|
|