meno-core 1.0.54 → 1.1.1
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/bin/cli.ts +19 -205
- package/build-astro.ts +31 -27
- package/dist/bin/cli.js +15 -154
- package/dist/bin/cli.js.map +2 -2
- package/dist/chunks/{chunk-3XER4E5W.js → chunk-4ZRU52J2.js} +6 -5
- package/dist/chunks/{chunk-3XER4E5W.js.map → chunk-4ZRU52J2.js.map} +2 -2
- package/dist/chunks/{chunk-NVRBTSQG.js → chunk-5X4HCN7J.js} +4 -4
- package/dist/chunks/chunk-5X4HCN7J.js.map +7 -0
- package/dist/chunks/{chunk-FZITJSSS.js → chunk-7CCVOL5M.js} +14 -14
- package/dist/chunks/chunk-7CCVOL5M.js.map +7 -0
- package/dist/chunks/{chunk-AE3QK5QW.js → chunk-7ZLF4NE5.js} +31 -26
- package/dist/chunks/chunk-7ZLF4NE5.js.map +7 -0
- package/dist/chunks/{chunk-HIZMY3EP.js → chunk-AR7LHIFY.js} +8 -2
- package/dist/chunks/chunk-AR7LHIFY.js.map +7 -0
- package/dist/chunks/{chunk-7HWQUVTU.js → chunk-J4IPTP5X.js} +1839 -443
- package/dist/chunks/chunk-J4IPTP5X.js.map +7 -0
- package/dist/chunks/{chunk-5ETZFREW.js → chunk-QWTQZHG3.js} +186 -79
- package/dist/chunks/chunk-QWTQZHG3.js.map +7 -0
- package/dist/chunks/{chunk-STDY3OVM.js → chunk-XTKNX4FW.js} +3 -3
- package/dist/chunks/{chunk-STDY3OVM.js.map → chunk-XTKNX4FW.js.map} +2 -2
- package/dist/chunks/{fs-ZI5JEU7V.js → fs-G3ANWEFP.js} +2 -2
- package/dist/lib/client/index.js +381 -159
- package/dist/lib/client/index.js.map +3 -3
- package/dist/lib/server/index.js +14365 -6276
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +296 -87
- 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 +27 -21
- package/dist/lib/test-utils/index.js.map +2 -2
- package/lib/client/ErrorBoundary.test.tsx +147 -90
- package/lib/client/ErrorBoundary.tsx +8 -5
- package/lib/client/componentRegistry.ts +0 -1
- package/lib/client/core/ComponentBuilder.test.ts +150 -0
- package/lib/client/core/ComponentBuilder.ts +112 -12
- package/lib/client/core/ComponentRenderer.test.tsx +1 -2
- package/lib/client/core/builders/embedBuilder.ts +1 -1
- package/lib/client/core/builders/linkBuilder.ts +2 -2
- package/lib/client/core/builders/linkNodeBuilder.ts +1 -1
- package/lib/client/core/builders/listBuilder.ts +41 -4
- package/lib/client/core/builders/localeListBuilder.ts +2 -2
- package/lib/client/core/cmsTemplateProcessor.ts +1 -2
- package/lib/client/hmr/HMRManager.tsx +39 -30
- package/lib/client/hmrCssReload.ts +35 -6
- package/lib/client/hmrWebSocket.ts +5 -5
- package/lib/client/hooks/useColorVariables.ts +12 -9
- package/lib/client/hooks/usePropertyAutocomplete.ts +1 -1
- package/lib/client/hooks/useVariables.ts +3 -1
- package/lib/client/meno-filter/MenoFilter.test.ts +28 -28
- package/lib/client/meno-filter/MenoFilter.ts +9 -6
- package/lib/client/meno-filter/bindings.ts +3 -3
- package/lib/client/meno-filter/init.ts +3 -3
- package/lib/client/meno-filter/renderer.ts +8 -4
- package/lib/client/meno-filter/ui.ts +1 -1
- package/lib/client/meno-filter/updates.ts +3 -2
- package/lib/client/meno-filter/utils.ts +1 -1
- package/lib/client/navigation.test.ts +40 -44
- package/lib/client/responsiveStyleResolver.knownTokens.test.ts +71 -0
- package/lib/client/responsiveStyleResolver.test.ts +5 -5
- package/lib/client/responsiveStyleResolver.ts +61 -4
- package/lib/client/routing/RouteLoader.test.ts +1 -1
- package/lib/client/routing/RouteLoader.ts +8 -3
- package/lib/client/routing/Router.tsx +181 -152
- package/lib/client/scripts/ScriptExecutor.ts +3 -3
- package/lib/client/services/PrefetchService.test.ts +1 -1
- package/lib/client/services/PrefetchService.ts +13 -5
- package/lib/client/styleProcessor.ts +9 -3
- package/lib/client/styles/StyleInjector.ts +1 -1
- package/lib/client/styles/UtilityClassCollector.ts +54 -13
- package/lib/client/templateEngine.test.ts +17 -23
- package/lib/client/templateEngine.ts +7 -13
- package/lib/client/theme.test.ts +1 -1
- package/lib/server/__integration__/server-lifecycle.test.ts +3 -1
- package/lib/server/__integration__/static-assets.test.ts +2 -2
- package/lib/server/__integration__/test-helpers.ts +3 -4
- package/lib/server/astro/cmsPageEmitter.ts +4 -5
- package/lib/server/astro/componentEmitter.ts +11 -8
- package/lib/server/astro/nodeToAstro.test.ts +4 -4
- package/lib/server/astro/nodeToAstro.ts +41 -36
- package/lib/server/astro/normalizeOrphanTemplateProps.test.ts +11 -11
- package/lib/server/astro/normalizeOrphanTemplateProps.ts +7 -6
- package/lib/server/astro/pageEmitter.ts +1 -1
- package/lib/server/astro/templateTransformer.ts +3 -3
- package/lib/server/createServer.ts +2 -2
- package/lib/server/cssGenerator.ts +2 -2
- package/lib/server/fileWatcher.test.ts +41 -4
- package/lib/server/fileWatcher.ts +102 -23
- package/lib/server/index.ts +7 -16
- package/lib/server/jsonLoader.test.ts +3 -3
- package/lib/server/jsonLoader.ts +8 -5
- package/lib/server/middleware/cors.test.ts +1 -1
- package/lib/server/middleware/cors.ts +2 -2
- package/lib/server/middleware/errorHandler.test.ts +2 -2
- package/lib/server/middleware/logger.test.ts +3 -3
- package/lib/server/middleware/logger.ts +7 -7
- package/lib/server/migrateTemplates.ts +3 -3
- package/lib/server/projectContext.ts +1 -1
- package/lib/server/providers/fileSystemCMSProvider.test.ts +18 -4
- package/lib/server/providers/fileSystemCMSProvider.ts +27 -18
- package/lib/server/routes/api/cms.ts +1 -1
- package/lib/server/routes/api/colors.test.ts +2 -2
- package/lib/server/routes/api/components.ts +1 -1
- package/lib/server/routes/api/config.ts +3 -3
- package/lib/server/routes/api/core-routes.ts +2 -2
- package/lib/server/routes/api/enums.test.ts +2 -2
- package/lib/server/routes/api/functions.ts +1 -1
- package/lib/server/routes/api/variables.test.ts +2 -2
- package/lib/server/routes/index.ts +2 -2
- package/lib/server/routes/pages.ts +1 -2
- package/lib/server/routes/static.ts +1 -2
- package/lib/server/runtime/bundler.ts +65 -43
- package/lib/server/runtime/fs.ts +13 -13
- package/lib/server/runtime/httpServer.ts +3 -3
- package/lib/server/services/ColorService.test.ts +3 -3
- package/lib/server/services/ColorService.ts +5 -2
- package/lib/server/services/EnumService.test.ts +2 -2
- package/lib/server/services/EnumService.ts +4 -1
- package/lib/server/services/VariableService.test.ts +7 -7
- package/lib/server/services/VariableService.ts +5 -2
- package/lib/server/services/cmsService.test.ts +44 -44
- package/lib/server/services/cmsService.ts +17 -97
- package/lib/server/services/componentService.test.ts +10 -16
- package/lib/server/services/componentService.ts +20 -12
- package/lib/server/services/configService.test.ts +13 -14
- package/lib/server/services/configService.ts +30 -0
- package/lib/server/services/fileWatcherService.ts +2 -2
- package/lib/server/services/pageService.test.ts +7 -7
- package/lib/server/services/pageService.ts +18 -5
- package/lib/server/ssr/attributeBuilder.ts +2 -2
- package/lib/server/ssr/cmsSSRProcessor.ts +1 -2
- package/lib/server/ssr/errorOverlay.ts +1 -1
- package/lib/server/ssr/htmlGenerator.nonce.test.ts +6 -5
- package/lib/server/ssr/htmlGenerator.test.ts +0 -1
- package/lib/server/ssr/htmlGenerator.ts +25 -9
- package/lib/server/ssr/imageMetadata.ts +2 -2
- package/lib/server/ssr/jsCollector.test.ts +5 -5
- package/lib/server/ssr/liveReloadIntegration.test.ts +7 -8
- package/lib/server/ssr/ssrRenderer.branches.test.ts +9 -9
- package/lib/server/ssr/ssrRenderer.test.ts +150 -15
- package/lib/server/ssr/ssrRenderer.ts +218 -40
- package/lib/server/ssrRenderer.test.ts +6 -6
- package/lib/server/utils/jsonLineMapper.ts +7 -7
- package/lib/server/validateStyleCoverage.ts +0 -1
- package/lib/server/websocketManager.ts +12 -0
- package/lib/shared/breakpoints.test.ts +5 -5
- package/lib/shared/cmsQuery.test.ts +81 -0
- package/lib/shared/cmsQuery.ts +92 -0
- package/lib/shared/cmsQueryParser.ts +11 -7
- package/lib/shared/colorConversions.ts +9 -9
- package/lib/shared/colorVariableUtils.test.ts +22 -0
- package/lib/shared/colorVariableUtils.ts +19 -5
- package/lib/shared/constants.ts +4 -0
- package/lib/shared/cssGeneration.test.ts +33 -0
- package/lib/shared/cssGeneration.ts +50 -35
- package/lib/shared/cssProperties.test.ts +64 -64
- package/lib/shared/cssProperties.ts +2 -2
- package/lib/shared/elementClassName.ts +2 -2
- package/lib/shared/errorLogger.test.ts +12 -12
- package/lib/shared/expressionEvaluator.test.ts +27 -1
- package/lib/shared/expressionEvaluator.ts +9 -0
- package/lib/shared/fontLoader.test.ts +13 -13
- package/lib/shared/fontLoader.ts +2 -2
- package/lib/shared/gradientUtils.test.ts +2 -2
- package/lib/shared/gradientUtils.ts +15 -8
- package/lib/shared/hrefRefs.test.ts +2 -2
- package/lib/shared/hrefRefs.ts +1 -1
- package/lib/shared/i18n.test.ts +3 -3
- package/lib/shared/i18n.ts +5 -4
- package/lib/shared/index.ts +29 -0
- package/lib/shared/inlineSvgStyleRules.ts +12 -9
- package/lib/shared/interactiveStyleMappings.test.ts +21 -21
- package/lib/shared/interactiveStyleMappings.ts +2 -8
- package/lib/shared/interactiveStyles.test.ts +3 -1
- package/lib/shared/itemTemplateUtils.test.ts +5 -5
- package/lib/shared/itemTemplateUtils.ts +9 -5
- package/lib/shared/jsonRepair.ts +8 -8
- package/lib/shared/libraryLoader.test.ts +6 -6
- 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/nodeUtils.test.ts +13 -9
- package/lib/shared/nodeUtils.ts +27 -4
- package/lib/shared/pathSecurity.ts +1 -1
- package/lib/shared/pathUtils.ts +1 -2
- package/lib/shared/paths/PathConverter.ts +3 -3
- package/lib/shared/paths/PathUtils.ts +4 -3
- package/lib/shared/paths/PathValidator.ts +1 -1
- package/lib/shared/permissions.test.ts +168 -0
- package/lib/shared/permissions.ts +162 -0
- package/lib/shared/propResolver.test.ts +1 -1
- package/lib/shared/propResolver.ts +1 -1
- package/lib/shared/registry/BaseNodeTypeRegistry.test.ts +5 -3
- package/lib/shared/registry/ClientRegistry.ts +0 -1
- package/lib/shared/registry/ComponentRegistry.test.ts +3 -3
- package/lib/shared/registry/ComponentRegistry.ts +6 -1
- package/lib/shared/registry/NodeTypeDefinition.ts +2 -2
- package/lib/shared/registry/SSRRegistry.ts +0 -1
- package/lib/shared/registry/fieldPresets.ts +3 -1
- package/lib/shared/registry/nodeTypes/CustomNodeType.ts +103 -0
- package/lib/shared/registry/nodeTypes/HtmlNodeType.ts +1 -1
- package/lib/shared/registry/nodeTypes/IslandNodeType.ts +129 -0
- package/lib/shared/registry/nodeTypes/ListNodeType.ts +43 -7
- package/lib/shared/registry/nodeTypes/MarkdownNodeType.ts +78 -0
- package/lib/shared/registry/nodeTypes/SlotMarkerType.ts +6 -1
- package/lib/shared/registry/nodeTypes/index.ts +16 -1
- package/lib/shared/responsiveScaling.test.ts +1 -3
- package/lib/shared/responsiveScaling.ts +6 -5
- package/lib/shared/responsiveStyleUtils.test.ts +0 -1
- package/lib/shared/richtext/htmlToTiptap.test.ts +116 -116
- package/lib/shared/richtext/htmlToTiptap.ts +1 -1
- package/lib/shared/styleNodeUtils.ts +17 -14
- package/lib/shared/tailwindThemeScale.ts +256 -0
- package/lib/shared/themeDefaults.test.ts +0 -3
- package/lib/shared/tree/PathBuilder.test.ts +1 -7
- package/lib/shared/tree/PathBuilder.ts +15 -13
- package/lib/shared/treePathUtils.test.ts +49 -2
- package/lib/shared/treePathUtils.ts +40 -4
- package/lib/shared/types/api.ts +105 -7
- package/lib/shared/types/cms.test.ts +34 -0
- package/lib/shared/types/cms.ts +65 -13
- package/lib/shared/types/components.ts +24 -0
- package/lib/shared/types/errors.test.ts +1 -1
- package/lib/shared/types/index.ts +23 -0
- package/lib/shared/types/permissions.ts +139 -0
- package/lib/shared/types/variables.ts +3 -3
- package/lib/shared/utilityClassConfig.ts +219 -0
- package/lib/shared/utilityClassMapper.durability.test.ts +117 -0
- package/lib/shared/utilityClassMapper.test.ts +401 -26
- package/lib/shared/utilityClassMapper.ts +193 -29
- package/lib/shared/utilityClassNames.test.ts +38 -0
- package/lib/shared/utilityClassNames.ts +301 -23
- 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 +1 -1
- package/lib/shared/validation/propValidator.ts +1 -1
- package/lib/shared/validation/schemas.test.ts +41 -0
- package/lib/shared/validation/schemas.ts +159 -13
- package/lib/shared/validation/validators.test.ts +2 -2
- package/lib/shared/viewportUnits.test.ts +13 -0
- package/lib/shared/viewportUnits.ts +12 -5
- package/lib/test-utils/factories/ConsoleMockFactory.ts +7 -7
- package/lib/test-utils/factories/DomMockFactory.ts +10 -4
- package/lib/test-utils/factories/ServerMockFactory.ts +1 -1
- package/lib/test-utils/factories/StoreMockFactory.ts +5 -5
- package/lib/test-utils/mockFactories.ts +1 -1
- package/lib/test-utils/mocks.ts +3 -3
- package/package.json +3 -5
- package/scripts/build-for-publish.mjs +2 -4
- package/scripts/build-meno-filter.ts +3 -3
- package/tsconfig.json +1 -0
- package/vite.config.ts +1 -1
- package/build-next.ts +0 -1374
- package/build-static.test.ts +0 -453
- package/build-static.ts +0 -1072
- package/dist/build-static.js +0 -37
- package/dist/chunks/chunk-2FN4UOVO.js +0 -6465
- package/dist/chunks/chunk-2FN4UOVO.js.map +0 -7
- package/dist/chunks/chunk-5ETZFREW.js.map +0 -7
- package/dist/chunks/chunk-7E4IF5L7.js +0 -245
- package/dist/chunks/chunk-7E4IF5L7.js.map +0 -7
- package/dist/chunks/chunk-7HWQUVTU.js.map +0 -7
- package/dist/chunks/chunk-AE3QK5QW.js.map +0 -7
- package/dist/chunks/chunk-F6KTJYGV.js +0 -322
- package/dist/chunks/chunk-F6KTJYGV.js.map +0 -7
- package/dist/chunks/chunk-FZITJSSS.js.map +0 -7
- package/dist/chunks/chunk-GSYYA5GX.js +0 -77
- package/dist/chunks/chunk-GSYYA5GX.js.map +0 -7
- package/dist/chunks/chunk-HIZMY3EP.js.map +0 -7
- package/dist/chunks/chunk-I2WEGYA7.js +0 -424
- package/dist/chunks/chunk-I2WEGYA7.js.map +0 -7
- package/dist/chunks/chunk-JNO3CNLJ.js +0 -464
- package/dist/chunks/chunk-JNO3CNLJ.js.map +0 -7
- package/dist/chunks/chunk-NVRBTSQG.js.map +0 -7
- package/dist/chunks/chunk-Q4OBWKXG.js +0 -1143
- package/dist/chunks/chunk-Q4OBWKXG.js.map +0 -7
- package/dist/chunks/chunk-QTE32Y53.js +0 -3829
- package/dist/chunks/chunk-QTE32Y53.js.map +0 -7
- package/dist/chunks/configService-PRJZF7Y6.js +0 -14
- package/dist/chunks/configService-PRJZF7Y6.js.map +0 -7
- package/dist/chunks/constants-KIQEYMAM.js +0 -46
- package/dist/chunks/constants-KIQEYMAM.js.map +0 -7
- package/dist/chunks/fs-ZI5JEU7V.js.map +0 -7
- package/dist/entries/server-router.js +0 -62
- package/dist/entries/server-router.js.map +0 -7
- package/entries/server-router.tsx +0 -75
- package/lib/server/providers/fileSystemPageProvider.test.ts +0 -84
- package/lib/server/providers/fileSystemPageProvider.ts +0 -179
- package/lib/server/webflow/buildWebflow.ts +0 -623
- package/lib/server/webflow/index.ts +0 -23
- package/lib/server/webflow/nodeToWebflow.test.ts +0 -3102
- package/lib/server/webflow/nodeToWebflow.ts +0 -2372
- package/lib/server/webflow/styleMapper.test.ts +0 -348
- package/lib/server/webflow/styleMapper.ts +0 -687
- package/lib/server/webflow/templateWrapper.ts +0 -49
- package/lib/server/webflow/types.ts +0 -389
- /package/dist/{build-static.js.map → chunks/fs-G3ANWEFP.js.map} +0 -0
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Uses centralized configuration from utilityClassConfig.ts
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ResponsiveStyleObject, StyleObject, StyleMapping } from './types';
|
|
7
|
+
import type { ResponsiveStyleObject, StyleObject, StyleMapping, StyleValue } from './types';
|
|
8
|
+
import { isResponsiveStyle } from './styleUtils';
|
|
8
9
|
import {
|
|
9
10
|
propertyMap,
|
|
10
11
|
propertyOrder,
|
|
@@ -18,6 +19,8 @@ import {
|
|
|
18
19
|
shadowPresets,
|
|
19
20
|
gradientPresets,
|
|
20
21
|
borderPresets,
|
|
22
|
+
SPACING_SCALE_ROOTS,
|
|
23
|
+
SPACING_SCALE_REVERSE,
|
|
21
24
|
} from './utilityClassConfig';
|
|
22
25
|
import {
|
|
23
26
|
camelToKebab,
|
|
@@ -26,11 +29,13 @@ import {
|
|
|
26
29
|
parseUtilityClass,
|
|
27
30
|
splitVariantPrefix,
|
|
28
31
|
breakpointClassPrefix,
|
|
32
|
+
normalizeBreakpointVariant,
|
|
29
33
|
resolveRootProperty,
|
|
30
34
|
ROOT_CAMEL_OVERRIDES,
|
|
31
|
-
|
|
35
|
+
isBareColorTokenName,
|
|
32
36
|
} from './utilityClassNames';
|
|
33
37
|
import { registerStyleValue, registerDynamicStyle, getStyleValue, getDynamicStyle } from './styleValueRegistry';
|
|
38
|
+
import { THEME_SCALE_BY_CSSVAR } from './tailwindThemeScale';
|
|
34
39
|
import {
|
|
35
40
|
SCALABLE_CSS_PROPERTIES,
|
|
36
41
|
buildFluidClampWithExplicitMin,
|
|
@@ -63,22 +68,29 @@ function shortHash(input: string): string {
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
71
|
+
* Whether the natural `root-(--x)` / `root-[value]` form would round-trip to a DIFFERENT property
|
|
72
|
+
* than `prop`. Roots shared by a shorthand and a color/width longhand (`border` → border/borderColor,
|
|
73
|
+
* `outline` → outline/outlineWidth/outlineColor) are disambiguated on parse by value type via
|
|
74
|
+
* `resolveRootProperty` — so a color-looking value (notably any opaque `var(--x)`) bound to the
|
|
75
|
+
* SHORTHAND reads back as the color longhand. Callers fall back to the explicit arbitrary-property
|
|
76
|
+
* form (`[border:var(--x)]`) when this returns true.
|
|
77
|
+
*
|
|
78
|
+
* Mirrors the inverse parser's property derivation EXACTLY (`classToStyle`): `ROOT_CAMEL_OVERRIDES`
|
|
79
|
+
* wins first (px → paddingInline, …), else `resolveRootProperty`. Comparing kebab CSS names instead
|
|
80
|
+
* would false-positive on those overridden roots (`px` resolves to `padding`, not `padding-inline`).
|
|
69
81
|
*/
|
|
70
|
-
function
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return true;
|
|
82
|
+
function rootFormMisresolves(root: string, prop: string, value: string): boolean {
|
|
83
|
+
const parsedProp = ROOT_CAMEL_OVERRIDES[root] ?? cssPropertyToCamel(resolveRootProperty(root, value));
|
|
84
|
+
return parsedProp !== prop;
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
/**
|
|
77
88
|
* Converts a CSS property value to a Tailwind-style utility class name.
|
|
78
89
|
* Examples:
|
|
79
|
-
* { prop: "padding", value: "
|
|
90
|
+
* { prop: "padding", value: "16px" } → "p-4" (exact scale step)
|
|
91
|
+
* { prop: "padding", value: "13px" } → "p-[13px]" (off-scale → arbitrary)
|
|
80
92
|
* { prop: "display", value: "flex" } → "flex"
|
|
81
|
-
* { prop: "color", value: "primary" } → "text-
|
|
93
|
+
* { prop: "color", value: "primary" } → "text-primary"
|
|
82
94
|
* { prop: "width", value: "100%" } → "w-full"
|
|
83
95
|
* { prop: "textOverflow", value: "ellipsis" } → "[text-overflow:ellipsis]"
|
|
84
96
|
*
|
|
@@ -88,6 +100,19 @@ function isBareColorToken(value: string): boolean {
|
|
|
88
100
|
function propertyValueToClass(prop: string, value: string | number): string | null {
|
|
89
101
|
let stringValue = String(value);
|
|
90
102
|
|
|
103
|
+
// Tailwind theme token: a `var(--token)` value (or a legacy `var(--token, …)` with fallback)
|
|
104
|
+
// whose token is a known Tailwind scale var (and whose property matches) emits the compact class —
|
|
105
|
+
// `var(--text-lg)` → `text-lg`. Runs before the fontFamily comma-split below (which would shred a
|
|
106
|
+
// multi-family `var()` fallback). The parser reconstructs the value from the class, so no registry
|
|
107
|
+
// entry is needed.
|
|
108
|
+
const varRef = stringValue.match(/^var\((--[\w-]+)(?:\s*,[\s\S]*)?\)$/);
|
|
109
|
+
if (varRef) {
|
|
110
|
+
const entry = THEME_SCALE_BY_CSSVAR.get(varRef[1] ?? '');
|
|
111
|
+
if (entry && entry.property === camelToKebab(prop)) {
|
|
112
|
+
return entry.className;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
91
116
|
// Strip outer quotes from fontFamily values (e.g. "Space Grotesk" → Space Grotesk)
|
|
92
117
|
if (prop === 'fontFamily') {
|
|
93
118
|
stringValue = stringValue
|
|
@@ -110,19 +135,22 @@ function propertyValueToClass(prop: string, value: string | number): string | nu
|
|
|
110
135
|
}
|
|
111
136
|
|
|
112
137
|
// Preset values (e.g., boxShadow: "2" → shadow-2)
|
|
113
|
-
|
|
138
|
+
const shadowPreset = shadowPresets[stringValue];
|
|
139
|
+
if (prop === 'boxShadow' && shadowPreset) {
|
|
114
140
|
const className = `shadow-${stringValue}`;
|
|
115
|
-
registerStyleValue(className,
|
|
141
|
+
registerStyleValue(className, shadowPreset);
|
|
116
142
|
return className;
|
|
117
143
|
}
|
|
118
|
-
|
|
144
|
+
const gradientPreset = gradientPresets[stringValue];
|
|
145
|
+
if ((prop === 'background' || prop === 'backgroundImage') && gradientPreset) {
|
|
119
146
|
const className = `bg-gradient-${stringValue}`;
|
|
120
|
-
registerStyleValue(className,
|
|
147
|
+
registerStyleValue(className, gradientPreset);
|
|
121
148
|
return className;
|
|
122
149
|
}
|
|
123
|
-
|
|
150
|
+
const borderPreset = borderPresets[stringValue];
|
|
151
|
+
if (prop === 'border' && borderPreset) {
|
|
124
152
|
const className = `border-preset-${stringValue}`;
|
|
125
|
-
registerStyleValue(className,
|
|
153
|
+
registerStyleValue(className, borderPreset);
|
|
126
154
|
return className;
|
|
127
155
|
}
|
|
128
156
|
|
|
@@ -170,6 +198,30 @@ function propertyValueToClass(prop: string, value: string | number): string | nu
|
|
|
170
198
|
}
|
|
171
199
|
}
|
|
172
200
|
|
|
201
|
+
// Named spacing scale (Tailwind steps): padding 16px → p-4, gap 24px → gap-6.
|
|
202
|
+
// Only EXACT steps; off-scale values fall through to the arbitrary form below.
|
|
203
|
+
// Keyword values (w-full, m-auto) are matched above, so they still win.
|
|
204
|
+
if (!className && SPACING_SCALE_ROOTS.has(root)) {
|
|
205
|
+
const step = SPACING_SCALE_REVERSE[stringValue];
|
|
206
|
+
if (step !== undefined) {
|
|
207
|
+
className = `${root}-${step}`;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Tailwind grid track scale: gridTemplateColumns "repeat(3, minmax(0, 1fr))" → grid-cols-3
|
|
212
|
+
// (and gridTemplateRows → grid-rows-N, plus the none/subgrid keywords). Keeps grid-cols-N that
|
|
213
|
+
// came in from hand/AI-authored class strings round-tripping to the compact form rather than
|
|
214
|
+
// expanding to grid-cols-[repeat(3,_minmax(0,_1fr))]. Other track values ("1fr 1fr 1fr",
|
|
215
|
+
// "repeat(3, 1fr)") have no compact Tailwind spelling and stay arbitrary.
|
|
216
|
+
if (!className && (root === 'grid-cols' || root === 'grid-rows')) {
|
|
217
|
+
const repeatMatch = stringValue.match(/^repeat\(\s*([1-9]\d*)\s*,\s*minmax\(\s*0\s*,\s*1fr\s*\)\s*\)$/);
|
|
218
|
+
if (repeatMatch) {
|
|
219
|
+
className = `${root}-${repeatMatch[1]}`;
|
|
220
|
+
} else if (stringValue === 'none' || stringValue === 'subgrid') {
|
|
221
|
+
className = `${root}-${stringValue}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
173
225
|
// CSS variables: var(--x) → root-(--x). The `font-*` root is shared by font-weight and
|
|
174
226
|
// font-family and is disambiguated by value type — but a `var(--x)` value is ambiguous, so
|
|
175
227
|
// weight/size vars carry a Tailwind data-type hint (`number:` / `length:`) the parser reads
|
|
@@ -178,22 +230,41 @@ function propertyValueToClass(prop: string, value: string | number): string | nu
|
|
|
178
230
|
if (!className) {
|
|
179
231
|
const varMatch = stringValue.match(/^var\((--[\w-]+)\)$/);
|
|
180
232
|
if (varMatch) {
|
|
233
|
+
const varName = (varMatch[1] ?? '').slice(2); // strip the leading `--`
|
|
181
234
|
if (prop === 'fontSize') className = `text-(length:${varMatch[1]})`;
|
|
182
235
|
else if (prop === 'fontWeight') className = `font-(number:${varMatch[1]})`;
|
|
183
|
-
|
|
236
|
+
// The `border`/`outline` roots are shared by a shorthand and a color longhand
|
|
237
|
+
// (`border` vs `borderColor`). The inverse parser disambiguates by value type, but a
|
|
238
|
+
// `var(--x)` value is opaque and reads as a color — so `border-(--x)` would round-trip
|
|
239
|
+
// to `borderColor`. When the prop is the shorthand/width longhand, fall back to the
|
|
240
|
+
// unambiguous arbitrary-property form (`[border:var(--x)]`), which names it explicitly.
|
|
241
|
+
else if (rootFormMisresolves(root, prop, stringValue)) {
|
|
242
|
+
className = `[${camelToKebab(prop)}:${stringValue}]`;
|
|
243
|
+
}
|
|
244
|
+
// A `var(--token)` on a color-token prop IS a Meno token reference → emit the bare token
|
|
245
|
+
// form (`bg-muted`), matching the bare-value path so `var(--muted)` and `muted` agree.
|
|
246
|
+
// (Token-aware inverse recovers it; palette-shaped names fall through to the var shorthand.)
|
|
247
|
+
else if (colorTokenProps.has(prop) && isBareColorTokenName(varName)) {
|
|
248
|
+
className = `${root}-${varName}`;
|
|
249
|
+
} else className = `${root}-(${varMatch[1]})`;
|
|
184
250
|
}
|
|
185
251
|
}
|
|
186
252
|
|
|
187
|
-
// Bare color tokens (Meno convention): color: "primary" → text-
|
|
188
|
-
if (!className && colorTokenProps.has(prop) &&
|
|
189
|
-
className = `${root}
|
|
253
|
+
// Bare color tokens (Meno convention): color: "primary" → text-primary
|
|
254
|
+
if (!className && colorTokenProps.has(prop) && isBareColorTokenName(stringValue)) {
|
|
255
|
+
className = `${root}-${stringValue}`;
|
|
190
256
|
}
|
|
191
257
|
|
|
192
258
|
// Arbitrary value (Tailwind bracket syntax): padding: "92px 0" → p-[92px_0]
|
|
193
259
|
if (!className) {
|
|
194
260
|
const encoded = encodeArbitraryValue(stringValue);
|
|
195
261
|
if (encoded != null) {
|
|
196
|
-
|
|
262
|
+
// Same shorthand/longhand guard as the var branch: a bare color value bound to the
|
|
263
|
+
// `border`/`outline` shorthand would parse back as the color longhand, so name the
|
|
264
|
+
// property explicitly via the arbitrary-property form instead of `root-[…]`.
|
|
265
|
+
className = rootFormMisresolves(root, prop, stringValue)
|
|
266
|
+
? `[${camelToKebab(prop)}:${encoded}]`
|
|
267
|
+
: `${root}-[${encoded}]`;
|
|
197
268
|
}
|
|
198
269
|
}
|
|
199
270
|
|
|
@@ -235,6 +306,7 @@ export function stylesToClasses(styles: StyleObject | Record<string, string | nu
|
|
|
235
306
|
const classes: string[] = [];
|
|
236
307
|
for (const prop of sortedProps) {
|
|
237
308
|
const value = styles[prop];
|
|
309
|
+
if (value === undefined) continue;
|
|
238
310
|
// Skip StyleMapping values - they are resolved at render time, not converted to classes
|
|
239
311
|
if (isStyleMapping(value)) continue;
|
|
240
312
|
const className = propertyValueToClass(prop, value);
|
|
@@ -395,7 +467,10 @@ export function responsiveStylesToClasses(
|
|
|
395
467
|
* Parse a utility class back to CSS property and value
|
|
396
468
|
* Example: "p-[10px]" → { prop: "padding", value: "10px" }
|
|
397
469
|
*/
|
|
398
|
-
export function classToStyle(
|
|
470
|
+
export function classToStyle(
|
|
471
|
+
className: string,
|
|
472
|
+
knownTokens?: ReadonlySet<string>,
|
|
473
|
+
): { prop: string; value: string } | null {
|
|
399
474
|
// Remove responsive variant prefix for parsing (tablet:p-[10px] → p-[10px])
|
|
400
475
|
const { base: cleanClass } = splitVariantPrefix(className);
|
|
401
476
|
|
|
@@ -407,7 +482,7 @@ export function classToStyle(className: string): { prop: string; value: string }
|
|
|
407
482
|
return presetClassReverse[cleanClass];
|
|
408
483
|
}
|
|
409
484
|
|
|
410
|
-
const parsed = parseUtilityClass(cleanClass);
|
|
485
|
+
const parsed = parseUtilityClass(cleanClass, knownTokens);
|
|
411
486
|
|
|
412
487
|
if (!parsed) {
|
|
413
488
|
// Dynamic (arbitrary-property hash) classes registered for unknown roots
|
|
@@ -451,20 +526,26 @@ export function classToStyle(className: string): { prop: string; value: string }
|
|
|
451
526
|
* Convert utility classes back to a style object
|
|
452
527
|
* Example: ["p-[10px]", "tablet:p-[24px]"] → { base: { padding: "10px" }, tablet: { padding: "24px" } }
|
|
453
528
|
*/
|
|
454
|
-
export function classesToStyles(classes: string[]): ResponsiveStyleObject | null {
|
|
529
|
+
export function classesToStyles(classes: string[], knownTokens?: ReadonlySet<string>): ResponsiveStyleObject | null {
|
|
455
530
|
const styles: ResponsiveStyleObject = { base: {}, tablet: {}, mobile: {} };
|
|
456
531
|
|
|
457
532
|
for (const className of classes) {
|
|
458
533
|
const { breakpoint: variant, base: cleanClass } = splitVariantPrefix(className);
|
|
459
|
-
|
|
460
|
-
|
|
534
|
+
// Accept both the current desktop-first form (`max-tablet:`) and the legacy bare form (`tablet:`).
|
|
535
|
+
const bp = normalizeBreakpointVariant(variant);
|
|
536
|
+
const breakpoint: 'base' | 'tablet' | 'mobile' = bp === 'tablet' ? 'tablet' : bp === 'mobile' ? 'mobile' : 'base';
|
|
461
537
|
|
|
462
|
-
const styleEntry = classToStyle(cleanClass);
|
|
538
|
+
const styleEntry = classToStyle(cleanClass, knownTokens);
|
|
463
539
|
if (styleEntry) {
|
|
464
540
|
if (!styles[breakpoint]) {
|
|
465
541
|
styles[breakpoint] = {};
|
|
466
542
|
}
|
|
467
543
|
styles[breakpoint]![styleEntry.prop] = styleEntry.value;
|
|
544
|
+
// `size-*` is width AND height (no CSS shorthand) — restore both so the editor doesn't
|
|
545
|
+
// drop height on the next save. classToStyle returns the width half; mirror it to height.
|
|
546
|
+
if (cleanClass.startsWith('size-') && styleEntry.prop === 'width') {
|
|
547
|
+
styles[breakpoint]!.height = styleEntry.value;
|
|
548
|
+
}
|
|
468
549
|
}
|
|
469
550
|
}
|
|
470
551
|
|
|
@@ -472,5 +553,88 @@ export function classesToStyles(classes: string[]): ResponsiveStyleObject | null
|
|
|
472
553
|
if (Object.keys(styles.tablet || {}).length === 0) delete styles.tablet;
|
|
473
554
|
if (Object.keys(styles.mobile || {}).length === 0) delete styles.mobile;
|
|
474
555
|
|
|
475
|
-
|
|
556
|
+
// Return the object when ANY breakpoint carries a declaration — not only base. A class string
|
|
557
|
+
// can legitimately style just a narrower breakpoint (e.g. "tablet:p-[16px]"), and dropping it to
|
|
558
|
+
// null there would lose class-string styling for that node (class-styling adapter, §11).
|
|
559
|
+
const hasAny =
|
|
560
|
+
Object.keys(styles.base || {}).length > 0 ||
|
|
561
|
+
Object.keys(styles.tablet || {}).length > 0 ||
|
|
562
|
+
Object.keys(styles.mobile || {}).length > 0;
|
|
563
|
+
return hasAny ? styles : null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Whether a single style value can be stored as a STATIC utility class. Static strings (no
|
|
568
|
+
* `{{template}}`) and numbers can; template-bearing strings and `_mapping` objects cannot — a
|
|
569
|
+
* template would mint a broken `p-[{{x}}px]`, and a mapping is prop-driven. The latter must stay in
|
|
570
|
+
* the `style` object (rendered via the runtime var-bridge / `class:list`). This is the boundary that
|
|
571
|
+
* lets styling default to class storage while keeping dynamic values correct.
|
|
572
|
+
*/
|
|
573
|
+
export function isClassableStyleValue(value: unknown): boolean {
|
|
574
|
+
if (typeof value === 'number') return true;
|
|
575
|
+
if (typeof value === 'string') return !value.includes('{{');
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function splitStyleBucket(bucket: StyleObject): {
|
|
580
|
+
classable: StyleObject;
|
|
581
|
+
residual: StyleObject;
|
|
582
|
+
} {
|
|
583
|
+
const classable: StyleObject = {};
|
|
584
|
+
const residual: StyleObject = {};
|
|
585
|
+
for (const [k, v] of Object.entries(bucket)) {
|
|
586
|
+
if (isClassableStyleValue(v)) classable[k] = v as string | number;
|
|
587
|
+
else residual[k] = v as StyleMapping;
|
|
588
|
+
}
|
|
589
|
+
return { classable, residual };
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Split a style value into the part storable as a utility-class string (static values) and the
|
|
594
|
+
* residual that must stay in the `style` object (templates + `_mapping`s). Preserves the responsive
|
|
595
|
+
* shape; a flat object is treated as `base`. Either side is null when it has no declarations. The
|
|
596
|
+
* canonical home (shared by the studio persistence boundary and the project-wide migration) for the
|
|
597
|
+
* "what can be a class" decision.
|
|
598
|
+
*/
|
|
599
|
+
export function splitStyleByClassability(style: StyleValue | null | undefined): {
|
|
600
|
+
classable: ResponsiveStyleObject | null;
|
|
601
|
+
residual: ResponsiveStyleObject | null;
|
|
602
|
+
} {
|
|
603
|
+
if (!style || typeof style !== 'object') return { classable: null, residual: null };
|
|
604
|
+
|
|
605
|
+
if (isResponsiveStyle(style)) {
|
|
606
|
+
const classable: ResponsiveStyleObject = {};
|
|
607
|
+
const residual: ResponsiveStyleObject = {};
|
|
608
|
+
for (const [bp, bucket] of Object.entries(style)) {
|
|
609
|
+
if (!bucket || typeof bucket !== 'object') continue;
|
|
610
|
+
const split = splitStyleBucket(bucket);
|
|
611
|
+
if (Object.keys(split.classable).length > 0) classable[bp] = split.classable;
|
|
612
|
+
if (Object.keys(split.residual).length > 0) residual[bp] = split.residual;
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
classable: Object.keys(classable).length > 0 ? classable : null,
|
|
616
|
+
residual: Object.keys(residual).length > 0 ? residual : null,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const split = splitStyleBucket(style as StyleObject);
|
|
621
|
+
return {
|
|
622
|
+
classable: Object.keys(split.classable).length > 0 ? { base: split.classable } : null,
|
|
623
|
+
residual: Object.keys(split.residual).length > 0 ? { base: split.residual } : null,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Whether a style value contains at least one prop `_mapping` (in any breakpoint) — flat or
|
|
629
|
+
* responsive. Prop-driven styling can't be a static utility class, so callers that gate class
|
|
630
|
+
* conversion (the studio panel, the project migration) check this. Single source of truth.
|
|
631
|
+
*/
|
|
632
|
+
export function styleHasMapping(style: StyleValue | null | undefined): boolean {
|
|
633
|
+
if (!style || typeof style !== 'object') return false;
|
|
634
|
+
const buckets = isResponsiveStyle(style) ? Object.values(style) : [style as Record<string, unknown>];
|
|
635
|
+
for (const bucket of buckets) {
|
|
636
|
+
if (!bucket || typeof bucket !== 'object') continue;
|
|
637
|
+
for (const v of Object.values(bucket)) if (isStyleMapping(v)) return true;
|
|
638
|
+
}
|
|
639
|
+
return false;
|
|
476
640
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { encodeArbitraryValue, decodeArbitraryValue } from './utilityClassNames';
|
|
3
|
+
|
|
4
|
+
describe('encodeArbitraryValue / decodeArbitraryValue', () => {
|
|
5
|
+
describe('reversible (decode ∘ encode === identity)', () => {
|
|
6
|
+
const cases: Array<[string, string]> = [
|
|
7
|
+
['single token', '24px'],
|
|
8
|
+
['spaces → underscores', '92px 0'],
|
|
9
|
+
['multi-token shorthand', '1px solid red'],
|
|
10
|
+
['literal underscore escaped', 'slide_in'],
|
|
11
|
+
['underscore + spaces', 'fade_in 2s ease'],
|
|
12
|
+
['underscore inside url() preserved', 'url(a_b.png)'],
|
|
13
|
+
['calc with spaces', 'calc(100% - 24px)'],
|
|
14
|
+
];
|
|
15
|
+
for (const [name, value] of cases) {
|
|
16
|
+
test(name, () => {
|
|
17
|
+
const encoded = encodeArbitraryValue(value);
|
|
18
|
+
expect(encoded).not.toBeNull();
|
|
19
|
+
// no raw spaces / quotes leak into the class token
|
|
20
|
+
expect(encoded!).not.toMatch(/[\s"']/);
|
|
21
|
+
expect(decodeArbitraryValue(encoded!)).toBe(value);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('literal underscore encodes via the \\_ escape', () => {
|
|
26
|
+
expect(encodeArbitraryValue('slide_in')).toBe('slide\\_in');
|
|
27
|
+
expect(encodeArbitraryValue('fade_in 2s ease')).toBe('fade\\_in_2s_ease');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('un-encodable → null (defer to side-channel)', () => {
|
|
32
|
+
for (const value of ['"a a"', "'x'", '[grid]', 'a\\b', 'url("a b.png")']) {
|
|
33
|
+
test(JSON.stringify(value), () => {
|
|
34
|
+
expect(encodeArbitraryValue(value)).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|