@tenphi/eslint-plugin-tasty 1.7.1 → 1.7.2

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/README.md CHANGED
@@ -183,7 +183,7 @@ for typing shared signatures.
183
183
  | `tasty/valid-radius-shape` | error | Unknown radius shape keywords |
184
184
  | `tasty/valid-preset` | error | Unknown preset names |
185
185
  | `tasty/valid-recipe` | error | Unknown recipe names |
186
- | `tasty/valid-transition` | warn | Unknown transition property names |
186
+ | `tasty/valid-transition` | warn | Unknown transition names, and camelCase CSS names that Tasty would emit as no property (`gridTemplateColumns` → `grid-template-columns`) |
187
187
  | `tasty/no-nested-selector` | warn | `&`-prefixed nested selectors (use sub-elements) |
188
188
  | `tasty/no-state-in-selector` | warn | Pseudo-class and attribute conditions in sub-element `$` selectors; put conditions in property state maps ([details](docs/rules/no-state-in-selector.md)) |
189
189
  | `tasty/prefer-element-selector` | warn | Prefer element names such as `Primary > Search` over exact `data-element` attributes in sub-element `$` selectors (safely autofixable; [details](docs/rules/prefer-element-selector.md)) |
package/dist/constants.js CHANGED
@@ -1033,6 +1033,23 @@ const BUILT_IN_UNITS = new Set([
1033
1033
  "sf"
1034
1034
  ]);
1035
1035
  /**
1036
+ * Custom properties Tasty registers as default `@property` rules, so they are
1037
+ * always defined whatever the config's `tokens` list says. The scale tokens
1038
+ * match their built-in units; style handlers read the rest as bare `var()`s
1039
+ * (`radius: 'leaf'`, `preset: 'h1 / strong'`, the font fallbacks, timings).
1040
+ */
1041
+ const BUILT_IN_CUSTOM_PROPERTIES = new Set([
1042
+ "$gap",
1043
+ "$radius",
1044
+ "$border-width",
1045
+ "$outline-width",
1046
+ "$transition",
1047
+ "$sharp-radius",
1048
+ "$bold-font-weight",
1049
+ "$font-sans-fallback",
1050
+ "$font-mono-fallback"
1051
+ ]);
1052
+ /**
1036
1053
  * Standard CSS units (always valid).
1037
1054
  */
1038
1055
  const CSS_UNITS = new Set([
@@ -1267,7 +1284,9 @@ const RADIUS_SHAPES = new Set([
1267
1284
  "backleaf"
1268
1285
  ]);
1269
1286
  /**
1270
- * Known semantic transition names.
1287
+ * Known semantic transition names — the keys of Tasty's transition map, plus
1288
+ * `zIndex`, which Tasty compiles to `z-index`. Any other name is emitted
1289
+ * verbatim as a CSS property.
1271
1290
  */
1272
1291
  const SEMANTIC_TRANSITIONS = new Set([
1273
1292
  "fade",
@@ -1280,8 +1299,6 @@ const SEMANTIC_TRANSITIONS = new Set([
1280
1299
  "theme",
1281
1300
  "color",
1282
1301
  "outline",
1283
- "dimension",
1284
- "flow",
1285
1302
  "inset",
1286
1303
  "text",
1287
1304
  "opacity",
@@ -1739,14 +1756,6 @@ const TRANSITION_SEMANTIC_MAPPING = {
1739
1756
  "outline-width": "outline",
1740
1757
  outlineOffset: "outline",
1741
1758
  "outline-offset": "outline",
1742
- gridTemplateColumns: "gridColumns",
1743
- "grid-template-columns": "gridColumns",
1744
- gridTemplateRows: "gridRows",
1745
- "grid-template-rows": "gridRows",
1746
- flexDirection: "flow",
1747
- "flex-direction": "flow",
1748
- gridAutoFlow: "flow",
1749
- "grid-auto-flow": "flow",
1750
1759
  fontSize: "preset",
1751
1760
  "font-size": "preset",
1752
1761
  lineHeight: "preset",
@@ -1755,14 +1764,16 @@ const TRANSITION_SEMANTIC_MAPPING = {
1755
1764
  "letter-spacing": "preset"
1756
1765
  };
1757
1766
  /**
1758
- * Known preset modifiers.
1767
+ * Known preset modifiers, mirroring Tasty's preset handler. `normal` sets
1768
+ * `line-height: normal`.
1759
1769
  */
1760
1770
  const PRESET_MODIFIERS = new Set([
1761
1771
  "strong",
1762
1772
  "bold",
1763
1773
  "italic",
1764
1774
  "icon",
1765
- "tight"
1775
+ "tight",
1776
+ "normal"
1766
1777
  ]);
1767
1778
  /**
1768
1779
  * Default import sources for tasty.
@@ -1839,6 +1850,24 @@ const KNOWN_PSEUDO_CLASSES = new Set([
1839
1850
  "::backdrop"
1840
1851
  ]);
1841
1852
  /**
1853
+ * Color tokens Tasty provides without any project configuration, so they are
1854
+ * always defined whatever the config's `tokens` list says.
1855
+ *
1856
+ * `#current` compiles to the `currentcolor` keyword, `#clear` is a default
1857
+ * replace token for `transparent`, and the rest are registered as default
1858
+ * `@property` rules (`#white`, `#black`, and `#border`/`#outline`, which fall
1859
+ * back to `currentcolor`). `prefer-custom-property-syntax` rewrites
1860
+ * `transparent` / `currentColor` to `#clear` / `#current`, so both must stay here.
1861
+ */
1862
+ const BUILT_IN_COLOR_TOKENS = new Set([
1863
+ "#current",
1864
+ "#clear",
1865
+ "#white",
1866
+ "#black",
1867
+ "#border",
1868
+ "#outline"
1869
+ ]);
1870
+ /**
1842
1871
  * CSS named colors (level 1–4) that should be replaced by `#color` tokens.
1843
1872
  * `transparent` and `currentcolor` are excluded — they have their own token
1844
1873
  * suggestions (`#clear`, `#current`) handled by `prefer-custom-property-syntax`.
@@ -2097,5 +2126,5 @@ const PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION = new Set([
2097
2126
  ]);
2098
2127
 
2099
2128
  //#endregion
2100
- export { BOOLEAN_TRUE_PROPERTIES, BUILT_IN_STATE_PREFIXES, BUILT_IN_UNITS, COLOR_BEARING_PROPERTIES, CSS_UNITS, DEFAULT_IMPORT_SOURCES, DIRECTIONAL_BOX, DIRECTIONAL_MODIFIERS, DOCK_PROPERTIES, KNOWN_CSS_PROPERTIES, KNOWN_PSEUDO_CLASSES, KNOWN_TASTY_PROPERTIES, LOGICAL_BORDER_STYLES, LOGICAL_STYLES, LONGHAND_MAPPING, NAMED_CSS_COLORS, PRESET_MODIFIERS, PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION, PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION, RADIUS_SHAPES, RENAMED_SPECIAL_STYLE_KEYS, SEMANTIC_TRANSITIONS, SHORTHAND_MAPPING, SINGLE_VALUE_DIRECTIONAL_PROPERTIES, SPECIAL_STYLE_KEYS, TRANSITION_SEMANTIC_MAPPING };
2129
+ export { BOOLEAN_TRUE_PROPERTIES, BUILT_IN_COLOR_TOKENS, BUILT_IN_CUSTOM_PROPERTIES, BUILT_IN_STATE_PREFIXES, BUILT_IN_UNITS, COLOR_BEARING_PROPERTIES, CSS_UNITS, DEFAULT_IMPORT_SOURCES, DIRECTIONAL_BOX, DIRECTIONAL_MODIFIERS, DOCK_PROPERTIES, KNOWN_CSS_PROPERTIES, KNOWN_PSEUDO_CLASSES, KNOWN_TASTY_PROPERTIES, LOGICAL_BORDER_STYLES, LOGICAL_STYLES, LONGHAND_MAPPING, NAMED_CSS_COLORS, PRESET_MODIFIERS, PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION, PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION, RADIUS_SHAPES, RENAMED_SPECIAL_STYLE_KEYS, SEMANTIC_TRANSITIONS, SHORTHAND_MAPPING, SINGLE_VALUE_DIRECTIONAL_PROPERTIES, SPECIAL_STYLE_KEYS, TRANSITION_SEMANTIC_MAPPING };
2101
2130
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Enhanced logical style families, mirroring `@tenphi/tasty`'s `logical-list.ts`.\n//\n// Tasty v3.8 gave each logical axis/category pair its own handler, named\n// `<axis><Category>` — `blockPadding`, `inlineBorder` — which emits native\n// logical CSS (`padding-block`, `border-inline`) rather than converting the\n// value to a physical edge. The browser resolves `start`/`end` from the\n// element's `writingMode` and `direction`.\n//\n// The native CSS spellings (`paddingBlock`, `insetInlineStart`, …) stay valid\n// keys, but they are now *ordinary* CSS properties: tasty stopped reading\n// `paddingBlock`/`paddingInline`/`insetBlock`/`insetInline` in its physical\n// handlers, so they no longer get category defaults or directional behaviour.\n// That is why they are absent from `KNOWN_TASTY_PROPERTIES` (they are still\n// recognised, via `KNOWN_CSS_PROPERTIES`) and why `SHORTHAND_MAPPING` points\n// them at the enhanced style instead.\n//\n// The groups are split by *vocabulary*, not for tidiness: each one is spread\n// into a different set below, and the splits are what keep `true` off the size\n// constraints and the single-value rule off the borders.\n// ---------------------------------------------------------------------------\n\n/** Logical edge modifiers, the `start`/`end` counterpart of {@link BOX_SIDES}. */\nexport const LOGICAL_EDGES = ['start', 'end'] as const;\n\n/** Enhanced logical sizes. `min`/`max` variants are read by the same handler. */\nexport const LOGICAL_SIZE_STYLES = ['blockSize', 'inlineSize'] as const;\nexport const LOGICAL_SIZE_CONSTRAINT_STYLES = [\n 'minBlockSize',\n 'maxBlockSize',\n 'minInlineSize',\n 'maxInlineSize',\n] as const;\n\n/**\n * Enhanced logical spacing and inset axes — the categories that take a value\n * per logical edge, so they share the `start`/`end` modifier vocabulary and the\n * one-value-per-directional-group rule.\n */\nexport const LOGICAL_SPACING_STYLES = [\n 'blockPadding',\n 'inlinePadding',\n 'blockMargin',\n 'inlineMargin',\n 'blockInset',\n 'inlineInset',\n 'blockScrollMargin',\n 'inlineScrollMargin',\n 'blockScrollPadding',\n 'inlineScrollPadding',\n] as const;\n\n/**\n * Enhanced logical borders. Kept apart from {@link LOGICAL_SPACING_STYLES}\n * because a border group legitimately carries width + style + colour, exactly\n * as physical `border` does, so it is not subject to the single-value rule.\n */\nexport const LOGICAL_BORDER_STYLES = ['blockBorder', 'inlineBorder'] as const;\n\n/** Every enhanced logical style, in the order tasty's own lists declare them. */\nexport const LOGICAL_STYLES = [\n ...LOGICAL_SIZE_STYLES,\n ...LOGICAL_SIZE_CONSTRAINT_STYLES,\n ...LOGICAL_SPACING_STYLES,\n ...LOGICAL_BORDER_STYLES,\n] as const;\n\n/**\n * Built-in tasty style properties that are always valid as style keys.\n */\nexport const KNOWN_TASTY_PROPERTIES = new Set([\n 'display',\n 'font',\n 'preset',\n 'hide',\n 'whiteSpace',\n 'opacity',\n 'transition',\n 'gridArea',\n 'order',\n 'gridColumn',\n 'gridRow',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'zIndex',\n 'margin',\n 'inset',\n 'position',\n 'padding',\n 'overflow',\n 'scrollbar',\n 'textAlign',\n 'border',\n 'radius',\n 'shadow',\n 'outline',\n 'color',\n 'fill',\n 'fade',\n 'image',\n 'textTransform',\n 'fontWeight',\n 'fontStyle',\n 'width',\n 'height',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n 'flow',\n 'place',\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align',\n 'justify',\n 'gap',\n 'columnGap',\n 'rowGap',\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'recipe',\n 'textOverflow',\n 'textDecoration',\n 'animation',\n 'cursor',\n 'pointerEvents',\n 'userSelect',\n 'transform',\n 'transformOrigin',\n 'filter',\n 'backdropFilter',\n 'mixBlendMode',\n 'objectFit',\n 'objectPosition',\n 'resize',\n 'appearance',\n 'listStyle',\n 'listStyleType',\n 'content',\n 'boxSizing',\n 'verticalAlign',\n 'wordBreak',\n 'overflowWrap',\n 'hyphens',\n 'tabSize',\n 'direction',\n 'unicodeBidi',\n 'writingMode',\n 'lineClamp',\n 'aspectRatio',\n 'contain',\n 'container',\n 'containerType',\n 'containerName',\n 'interpolateSize',\n 'willChange',\n 'isolation',\n 'touchAction',\n 'scrollBehavior',\n 'scrollSnapType',\n 'scrollSnapAlign',\n 'caretColor',\n 'accentColor',\n 'colorScheme',\n // Physical scroll shorthands: tasty v3.8 gave `scrollPadding` its own\n // directional handler alongside the `scrollMargin` one, so both take the\n // padding vocabulary (modifiers, `true`, one value per directional group).\n 'scrollMargin',\n 'scrollPadding',\n ...LOGICAL_STYLES,\n]);\n\n/**\n * Special top-level keys that are valid but not regular style properties.\n *\n * These match the real CSS at-rule names Tasty emits, so they are kebab-case.\n */\nexport const SPECIAL_STYLE_KEYS = new Set([\n '@keyframes',\n '@property',\n '@font-face',\n '@counter-style',\n '@function',\n]);\n\n/**\n * At-rule style keys renamed in Tasty v3, mapped old spelling -> new.\n *\n * The v2 spellings were camelCase; v3 uses the CSS at-rule names Tasty already\n * emitted. Reported (and auto-fixed) so upgrading is mechanical.\n */\nexport const RENAMED_SPECIAL_STYLE_KEYS: Record<string, string> = {\n '@properties': '@property',\n '@fontFace': '@font-face',\n '@counterStyle': '@counter-style',\n};\n\n/**\n * CSS property names, camelCase, derived from `known-css-properties` — the\n * MDN/W3C-sourced dataset stylelint uses for `property-no-unknown`.\n *\n * Generated. Run `pnpm generate:css-properties` after bumping the dataset rather\n * than editing this list; `constants.css-properties.test.ts` fails on drift.\n *\n * Tasty renders any camelCase style key straight through to its kebab-case CSS\n * declaration (`maskSize` -> `mask-size`), so anything absent here is a false\n * positive from `known-property` on valid CSS — which is why the list is derived\n * from data instead of maintained by hand.\n *\n * The dataset also carries at-rule descriptors (`src`, `syntax`, `symbols`, `size`),\n * which are kept, as stylelint keeps them. They are only reachable as top-level\n * style keys, where the cost is a declaration browsers drop rather than a report —\n * and separating them would mean hand-maintaining the exclusion list this change\n * exists to delete. Inside an at-rule block nothing validates them: `known-property`\n * exempts every `@`-prefixed key and does not descend.\n */\nconst DATASET_CSS_PROPERTIES = [\n 'accelerator',\n 'accentColor',\n 'additiveSymbols',\n 'alignContent',\n 'alignItems',\n 'alignSelf',\n 'alignmentBaseline',\n 'all',\n 'alt',\n 'anchorName',\n 'anchorScope',\n 'animation',\n 'animationComposition',\n 'animationDelay',\n 'animationDirection',\n 'animationDuration',\n 'animationFillMode',\n 'animationIterationCount',\n 'animationName',\n 'animationPlayState',\n 'animationRange',\n 'animationRangeEnd',\n 'animationRangeStart',\n 'animationTimeline',\n 'animationTimingFunction',\n 'animationTrigger',\n 'animationTriggerBehavior',\n 'animationTriggerExitRange',\n 'animationTriggerExitRangeEnd',\n 'animationTriggerExitRangeStart',\n 'animationTriggerRange',\n 'animationTriggerRangeEnd',\n 'animationTriggerRangeStart',\n 'animationTriggerTimeline',\n 'appRegion',\n 'appearance',\n 'ascentOverride',\n 'aspectRatio',\n 'audioLevel',\n 'azimuth',\n 'backdropFilter',\n 'backfaceVisibility',\n 'background',\n 'backgroundAttachment',\n 'backgroundBlendMode',\n 'backgroundClip',\n 'backgroundColor',\n 'backgroundImage',\n 'backgroundOrigin',\n 'backgroundPosition',\n 'backgroundPositionX',\n 'backgroundPositionY',\n 'backgroundRepeat',\n 'backgroundRepeatX',\n 'backgroundRepeatY',\n 'backgroundSize',\n 'basePalette',\n 'baselineShift',\n 'baselineSource',\n 'behavior',\n 'blockEllipsis',\n 'blockSize',\n 'blockStep',\n 'blockStepAlign',\n 'blockStepInsert',\n 'blockStepRound',\n 'blockStepSize',\n 'bookmarkLabel',\n 'bookmarkLevel',\n 'bookmarkState',\n 'border',\n 'borderBlock',\n 'borderBlockColor',\n 'borderBlockEnd',\n 'borderBlockEndColor',\n 'borderBlockEndStyle',\n 'borderBlockEndWidth',\n 'borderBlockStart',\n 'borderBlockStartColor',\n 'borderBlockStartStyle',\n 'borderBlockStartWidth',\n 'borderBlockStyle',\n 'borderBlockWidth',\n 'borderBottom',\n 'borderBottomColor',\n 'borderBottomLeftRadius',\n 'borderBottomRightRadius',\n 'borderBottomStyle',\n 'borderBottomWidth',\n 'borderBoundary',\n 'borderCollapse',\n 'borderColor',\n 'borderEndEndRadius',\n 'borderEndStartRadius',\n 'borderImage',\n 'borderImageOutset',\n 'borderImageRepeat',\n 'borderImageSlice',\n 'borderImageSource',\n 'borderImageWidth',\n 'borderInline',\n 'borderInlineColor',\n 'borderInlineEnd',\n 'borderInlineEndColor',\n 'borderInlineEndStyle',\n 'borderInlineEndWidth',\n 'borderInlineStart',\n 'borderInlineStartColor',\n 'borderInlineStartStyle',\n 'borderInlineStartWidth',\n 'borderInlineStyle',\n 'borderInlineWidth',\n 'borderLeft',\n 'borderLeftColor',\n 'borderLeftStyle',\n 'borderLeftWidth',\n 'borderRadius',\n 'borderRight',\n 'borderRightColor',\n 'borderRightStyle',\n 'borderRightWidth',\n 'borderSpacing',\n 'borderStartEndRadius',\n 'borderStartStartRadius',\n 'borderStyle',\n 'borderTop',\n 'borderTopColor',\n 'borderTopLeftRadius',\n 'borderTopRightRadius',\n 'borderTopStyle',\n 'borderTopWidth',\n 'borderWidth',\n 'bottom',\n 'boxDecorationBreak',\n 'boxShadow',\n 'boxSizing',\n 'boxSnap',\n 'breakAfter',\n 'breakBefore',\n 'breakInside',\n 'bufferedRendering',\n 'captionSide',\n 'caret',\n 'caretAnimation',\n 'caretColor',\n 'caretShape',\n 'chains',\n 'clear',\n 'clip',\n 'clipPath',\n 'clipRule',\n 'color',\n 'colorAdjust',\n 'colorInterpolation',\n 'colorInterpolationFilters',\n 'colorProfile',\n 'colorRendering',\n 'colorScheme',\n 'columnCount',\n 'columnFill',\n 'columnGap',\n 'columnHeight',\n 'columnProgression',\n 'columnRule',\n 'columnRuleBreak',\n 'columnRuleColor',\n 'columnRuleOutset',\n 'columnRuleStyle',\n 'columnRuleWidth',\n 'columnSpan',\n 'columnWidth',\n 'columnWrap',\n 'columns',\n 'contain',\n 'containIntrinsicBlockSize',\n 'containIntrinsicHeight',\n 'containIntrinsicInlineSize',\n 'containIntrinsicSize',\n 'containIntrinsicWidth',\n 'container',\n 'containerName',\n 'containerType',\n 'content',\n 'contentVisibility',\n 'continue',\n 'counterIncrement',\n 'counterReset',\n 'counterSet',\n 'cue',\n 'cueAfter',\n 'cueBefore',\n 'cursor',\n 'cx',\n 'cy',\n 'd',\n 'descentOverride',\n 'direction',\n 'display',\n 'displayAlign',\n 'dominantBaseline',\n 'dynamicRangeLimit',\n 'elevation',\n 'emptyCells',\n 'enableBackground',\n 'epubCaptionSide',\n 'epubHyphens',\n 'epubTextCombine',\n 'epubTextEmphasis',\n 'epubTextEmphasisColor',\n 'epubTextEmphasisStyle',\n 'epubTextOrientation',\n 'epubTextTransform',\n 'epubWordBreak',\n 'epubWritingMode',\n 'fallback',\n 'fieldSizing',\n 'fill',\n 'fillBreak',\n 'fillColor',\n 'fillImage',\n 'fillOpacity',\n 'fillOrigin',\n 'fillPosition',\n 'fillRepeat',\n 'fillRule',\n 'fillSize',\n 'filter',\n 'flex',\n 'flexBasis',\n 'flexDirection',\n 'flexFlow',\n 'flexGrow',\n 'flexShrink',\n 'flexWrap',\n 'float',\n 'floatDefer',\n 'floatOffset',\n 'floatReference',\n 'floodColor',\n 'floodOpacity',\n 'flow',\n 'flowFrom',\n 'flowInto',\n 'font',\n 'fontDisplay',\n 'fontFamily',\n 'fontFeatureSettings',\n 'fontKerning',\n 'fontLanguageOverride',\n 'fontOpticalSizing',\n 'fontPalette',\n 'fontSize',\n 'fontSizeAdjust',\n 'fontStretch',\n 'fontStyle',\n 'fontSynthesis',\n 'fontSynthesisPosition',\n 'fontSynthesisSmallCaps',\n 'fontSynthesisStyle',\n 'fontSynthesisWeight',\n 'fontVariant',\n 'fontVariantAlternates',\n 'fontVariantCaps',\n 'fontVariantEastAsian',\n 'fontVariantEmoji',\n 'fontVariantLigatures',\n 'fontVariantNumeric',\n 'fontVariantPosition',\n 'fontVariationSettings',\n 'fontWeight',\n 'fontWidth',\n 'footnoteDisplay',\n 'footnotePolicy',\n 'forcedColorAdjust',\n 'gap',\n 'glyphOrientationHorizontal',\n 'glyphOrientationVertical',\n 'grid',\n 'gridArea',\n 'gridAutoColumns',\n 'gridAutoFlow',\n 'gridAutoRows',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnGap',\n 'gridColumnStart',\n 'gridGap',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowGap',\n 'gridRowStart',\n 'gridTemplate',\n 'gridTemplateAreas',\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'hangingPunctuation',\n 'height',\n 'hyphenateCharacter',\n 'hyphenateLimitChars',\n 'hyphenateLimitLast',\n 'hyphenateLimitLines',\n 'hyphenateLimitZone',\n 'hyphens',\n 'imageOrientation',\n 'imageRendering',\n 'imageResolution',\n 'imeMode',\n 'inherits',\n 'initialLetter',\n 'initialLetterAlign',\n 'initialLetterWrap',\n 'initialValue',\n 'inlineSize',\n 'inlineSizing',\n 'inputFormat',\n 'inputSecurity',\n 'inset',\n 'insetArea',\n 'insetBlock',\n 'insetBlockEnd',\n 'insetBlockStart',\n 'insetInline',\n 'insetInlineEnd',\n 'insetInlineStart',\n 'interactivity',\n 'interpolateSize',\n 'isolation',\n 'itemCross',\n 'itemDirection',\n 'itemFlow',\n 'itemPack',\n 'itemSlack',\n 'itemTrack',\n 'itemWrap',\n 'justifyContent',\n 'justifyItems',\n 'justifySelf',\n 'kerning',\n 'layoutFlow',\n 'layoutGrid',\n 'layoutGridChar',\n 'layoutGridLine',\n 'layoutGridMode',\n 'layoutGridType',\n 'left',\n 'letterSpacing',\n 'lightingColor',\n 'lineBreak',\n 'lineClamp',\n 'lineFitEdge',\n 'lineGapOverride',\n 'lineGrid',\n 'lineHeight',\n 'lineHeightStep',\n 'lineIncrement',\n 'linePadding',\n 'lineSnap',\n 'listStyle',\n 'listStyleImage',\n 'listStylePosition',\n 'listStyleType',\n 'margin',\n 'marginBlock',\n 'marginBlockEnd',\n 'marginBlockStart',\n 'marginBottom',\n 'marginBreak',\n 'marginInline',\n 'marginInlineEnd',\n 'marginInlineStart',\n 'marginLeft',\n 'marginRight',\n 'marginTop',\n 'marginTrim',\n 'marker',\n 'markerEnd',\n 'markerKnockoutLeft',\n 'markerKnockoutRight',\n 'markerMid',\n 'markerOffset',\n 'markerPattern',\n 'markerSegment',\n 'markerSide',\n 'markerStart',\n 'marks',\n 'mask',\n 'maskBorder',\n 'maskBorderMode',\n 'maskBorderOutset',\n 'maskBorderRepeat',\n 'maskBorderSlice',\n 'maskBorderSource',\n 'maskBorderWidth',\n 'maskClip',\n 'maskComposite',\n 'maskImage',\n 'maskMode',\n 'maskOrigin',\n 'maskPosition',\n 'maskPositionX',\n 'maskPositionY',\n 'maskRepeat',\n 'maskSize',\n 'maskSourceType',\n 'maskType',\n 'mathDepth',\n 'mathShift',\n 'mathStyle',\n 'maxBlockSize',\n 'maxHeight',\n 'maxInlineSize',\n 'maxLines',\n 'maxWidth',\n 'maxZoom',\n 'minBlockSize',\n 'minHeight',\n 'minInlineSize',\n 'minIntrinsicSizing',\n 'minWidth',\n 'minZoom',\n 'mixBlendMode',\n 'motion',\n 'motionOffset',\n 'motionPath',\n 'motionRotation',\n 'navDown',\n 'navIndex',\n 'navLeft',\n 'navRight',\n 'navUp',\n 'navigation',\n 'negative',\n 'objectFit',\n 'objectPosition',\n 'objectViewBox',\n 'offset',\n 'offsetAnchor',\n 'offsetBlockEnd',\n 'offsetBlockStart',\n 'offsetDistance',\n 'offsetInlineEnd',\n 'offsetInlineStart',\n 'offsetPath',\n 'offsetPosition',\n 'offsetRotate',\n 'offsetRotation',\n 'opacity',\n 'order',\n 'orientation',\n 'orphans',\n 'outline',\n 'outlineColor',\n 'outlineOffset',\n 'outlineStyle',\n 'outlineWidth',\n 'overflow',\n 'overflowAnchor',\n 'overflowBlock',\n 'overflowClipMargin',\n 'overflowClipMarginBlock',\n 'overflowClipMarginBlockEnd',\n 'overflowClipMarginBlockStart',\n 'overflowClipMarginBottom',\n 'overflowClipMarginInline',\n 'overflowClipMarginInlineEnd',\n 'overflowClipMarginInlineStart',\n 'overflowClipMarginLeft',\n 'overflowClipMarginRight',\n 'overflowClipMarginTop',\n 'overflowInline',\n 'overflowWrap',\n 'overflowX',\n 'overflowY',\n 'overlay',\n 'overrideColors',\n 'overscrollBehavior',\n 'overscrollBehaviorBlock',\n 'overscrollBehaviorInline',\n 'overscrollBehaviorX',\n 'overscrollBehaviorY',\n 'pad',\n 'padding',\n 'paddingBlock',\n 'paddingBlockEnd',\n 'paddingBlockStart',\n 'paddingBottom',\n 'paddingInline',\n 'paddingInlineEnd',\n 'paddingInlineStart',\n 'paddingLeft',\n 'paddingRight',\n 'paddingTop',\n 'page',\n 'pageBreakAfter',\n 'pageBreakBefore',\n 'pageBreakInside',\n 'pageOrientation',\n 'paintOrder',\n 'pause',\n 'pauseAfter',\n 'pauseBefore',\n 'penAction',\n 'perspective',\n 'perspectiveOrigin',\n 'perspectiveOriginX',\n 'perspectiveOriginY',\n 'pitch',\n 'pitchRange',\n 'placeContent',\n 'placeItems',\n 'placeSelf',\n 'playDuring',\n 'pointerEvents',\n 'position',\n 'positionAnchor',\n 'positionArea',\n 'positionTry',\n 'positionTryFallbacks',\n 'positionTryOptions',\n 'positionTryOrder',\n 'positionVisibility',\n 'prefix',\n 'printColorAdjust',\n 'propertyName',\n 'quotes',\n 'r',\n 'range',\n 'readingFlow',\n 'readingOrder',\n 'regionFragment',\n 'resize',\n 'rest',\n 'restAfter',\n 'restBefore',\n 'richness',\n 'right',\n 'rotate',\n 'rowGap',\n 'rowRule',\n 'rowRuleBreak',\n 'rowRuleColor',\n 'rowRuleOutset',\n 'rowRuleStyle',\n 'rowRuleWidth',\n 'rubyAlign',\n 'rubyMerge',\n 'rubyOverhang',\n 'rubyPosition',\n 'rule',\n 'ruleBreak',\n 'ruleColor',\n 'ruleOutset',\n 'rulePaintOrder',\n 'ruleStyle',\n 'ruleWidth',\n 'running',\n 'rx',\n 'ry',\n 'scale',\n 'scrollBehavior',\n 'scrollInitialTarget',\n 'scrollMargin',\n 'scrollMarginBlock',\n 'scrollMarginBlockEnd',\n 'scrollMarginBlockStart',\n 'scrollMarginBottom',\n 'scrollMarginInline',\n 'scrollMarginInlineEnd',\n 'scrollMarginInlineStart',\n 'scrollMarginLeft',\n 'scrollMarginRight',\n 'scrollMarginTop',\n 'scrollMarkerGroup',\n 'scrollPadding',\n 'scrollPaddingBlock',\n 'scrollPaddingBlockEnd',\n 'scrollPaddingBlockStart',\n 'scrollPaddingBottom',\n 'scrollPaddingInline',\n 'scrollPaddingInlineEnd',\n 'scrollPaddingInlineStart',\n 'scrollPaddingLeft',\n 'scrollPaddingRight',\n 'scrollPaddingTop',\n 'scrollSnapAlign',\n 'scrollSnapCoordinate',\n 'scrollSnapDestination',\n 'scrollSnapMargin',\n 'scrollSnapMarginBottom',\n 'scrollSnapMarginLeft',\n 'scrollSnapMarginRight',\n 'scrollSnapMarginTop',\n 'scrollSnapPointsX',\n 'scrollSnapPointsY',\n 'scrollSnapStop',\n 'scrollSnapType',\n 'scrollSnapTypeX',\n 'scrollSnapTypeY',\n 'scrollStartTarget',\n 'scrollTargetGroup',\n 'scrollTimeline',\n 'scrollTimelineAxis',\n 'scrollTimelineName',\n 'scrollbar3dLightColor',\n 'scrollbar3dlightColor',\n 'scrollbarArrowColor',\n 'scrollbarBaseColor',\n 'scrollbarColor',\n 'scrollbarDarkShadowColor',\n 'scrollbarDarkshadowColor',\n 'scrollbarFaceColor',\n 'scrollbarGutter',\n 'scrollbarHighlightColor',\n 'scrollbarShadowColor',\n 'scrollbarTrackColor',\n 'scrollbarWidth',\n 'shapeImageThreshold',\n 'shapeInside',\n 'shapeMargin',\n 'shapeOutside',\n 'shapeRendering',\n 'size',\n 'sizeAdjust',\n 'sliderOrientation',\n 'snapHeight',\n 'solidColor',\n 'solidOpacity',\n 'spatialNavigationAction',\n 'spatialNavigationContain',\n 'spatialNavigationFunction',\n 'speak',\n 'speakAs',\n 'speakHeader',\n 'speakNumeral',\n 'speakPunctuation',\n 'speechRate',\n 'src',\n 'stopColor',\n 'stopOpacity',\n 'stress',\n 'stringSet',\n 'stroke',\n 'strokeAlign',\n 'strokeAlignment',\n 'strokeBreak',\n 'strokeColor',\n 'strokeDashCorner',\n 'strokeDashJustify',\n 'strokeDashadjust',\n 'strokeDasharray',\n 'strokeDashcorner',\n 'strokeDashoffset',\n 'strokeImage',\n 'strokeLinecap',\n 'strokeLinejoin',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeOrigin',\n 'strokePosition',\n 'strokeRepeat',\n 'strokeSize',\n 'strokeWidth',\n 'suffix',\n 'supportedColorSchemes',\n 'symbols',\n 'syntax',\n 'system',\n 'tabSize',\n 'tableLayout',\n 'textAlign',\n 'textAlignAll',\n 'textAlignLast',\n 'textAnchor',\n 'textAutospace',\n 'textBox',\n 'textBoxEdge',\n 'textBoxTrim',\n 'textCombineUpright',\n 'textDecoration',\n 'textDecorationBlink',\n 'textDecorationColor',\n 'textDecorationLine',\n 'textDecorationLineThrough',\n 'textDecorationNone',\n 'textDecorationOverline',\n 'textDecorationSkip',\n 'textDecorationSkipBox',\n 'textDecorationSkipInk',\n 'textDecorationSkipInset',\n 'textDecorationSkipSelf',\n 'textDecorationSkipSpaces',\n 'textDecorationStyle',\n 'textDecorationThickness',\n 'textDecorationTrim',\n 'textDecorationUnderline',\n 'textEmphasis',\n 'textEmphasisColor',\n 'textEmphasisPosition',\n 'textEmphasisSkip',\n 'textEmphasisStyle',\n 'textGroupAlign',\n 'textIndent',\n 'textJustify',\n 'textJustifyTrim',\n 'textKashida',\n 'textKashidaSpace',\n 'textLineThrough',\n 'textLineThroughColor',\n 'textLineThroughMode',\n 'textLineThroughStyle',\n 'textLineThroughWidth',\n 'textOrientation',\n 'textOverflow',\n 'textOverline',\n 'textOverlineColor',\n 'textOverlineMode',\n 'textOverlineStyle',\n 'textOverlineWidth',\n 'textRendering',\n 'textShadow',\n 'textSizeAdjust',\n 'textSpacing',\n 'textSpacingTrim',\n 'textTransform',\n 'textUnderline',\n 'textUnderlineColor',\n 'textUnderlineMode',\n 'textUnderlineOffset',\n 'textUnderlinePosition',\n 'textUnderlineStyle',\n 'textUnderlineWidth',\n 'textWrap',\n 'textWrapMode',\n 'textWrapStyle',\n 'timelineScope',\n 'top',\n 'touchAction',\n 'touchActionDelay',\n 'transform',\n 'transformBox',\n 'transformOrigin',\n 'transformOriginX',\n 'transformOriginY',\n 'transformOriginZ',\n 'transformStyle',\n 'transition',\n 'transitionBehavior',\n 'transitionDelay',\n 'transitionDuration',\n 'transitionProperty',\n 'transitionTimingFunction',\n 'translate',\n 'types',\n 'ucAltSkin',\n 'ucSkin',\n 'unicodeBidi',\n 'unicodeRange',\n 'userSelect',\n 'userZoom',\n 'vectorEffect',\n 'verticalAlign',\n 'viewTimeline',\n 'viewTimelineAxis',\n 'viewTimelineInset',\n 'viewTimelineName',\n 'viewTransitionClass',\n 'viewTransitionGroup',\n 'viewTransitionName',\n 'viewportFill',\n 'viewportFillOpacity',\n 'viewportFit',\n 'visibility',\n 'voiceBalance',\n 'voiceDuration',\n 'voiceFamily',\n 'voicePitch',\n 'voiceRange',\n 'voiceRate',\n 'voiceStress',\n 'voiceVolume',\n 'volume',\n 'whiteSpace',\n 'whiteSpaceCollapse',\n 'whiteSpaceTrim',\n 'widows',\n 'width',\n 'willChange',\n 'wordBreak',\n 'wordSpaceTransform',\n 'wordSpacing',\n 'wordWrap',\n 'wrapAfter',\n 'wrapBefore',\n 'wrapFlow',\n 'wrapInside',\n 'wrapThrough',\n 'writingMode',\n 'x',\n 'y',\n 'zIndex',\n 'zoom',\n];\n\n/**\n * Properties that ship in browsers or sit in a spec, but have not landed in\n * `known-css-properties` yet. Hand-maintained, and pruned as the dataset catches\n * up — the guard test fails on any entry the dataset already covers.\n */\nexport const EMERGING_CSS_PROPERTIES = [\n // CSS Borders and Box Decorations 4 — squircles and notched corners, shipped in\n // Chromium 139.\n 'cornerShape',\n 'cornerTopShape',\n 'cornerRightShape',\n 'cornerBottomShape',\n 'cornerLeftShape',\n 'cornerBlockStartShape',\n 'cornerBlockEndShape',\n 'cornerInlineStartShape',\n 'cornerInlineEndShape',\n 'cornerTopLeftShape',\n 'cornerTopRightShape',\n 'cornerBottomRightShape',\n 'cornerBottomLeftShape',\n 'cornerStartStartShape',\n 'cornerStartEndShape',\n 'cornerEndStartShape',\n 'cornerEndEndShape',\n];\n\n/**\n * Every CSS property `known-property` accepts as a camelCase style key.\n */\nexport const KNOWN_CSS_PROPERTIES = new Set([\n ...DATASET_CSS_PROPERTIES,\n ...EMERGING_CSS_PROPERTIES,\n]);\n\n/**\n * Built-in custom units recognized by the tasty parser.\n */\nexport const BUILT_IN_UNITS = new Set(['x', 'r', 'cr', 'bw', 'ow', 'lh', 'sf']);\n\n/**\n * Standard CSS units (always valid).\n */\nexport const CSS_UNITS = new Set([\n 'px',\n 'em',\n 'rem',\n '%',\n 'vw',\n 'vh',\n 'vmin',\n 'vmax',\n 'ch',\n 'ex',\n 'cm',\n 'mm',\n 'in',\n 'pt',\n 'pc',\n 'fr',\n 'deg',\n 'rad',\n 'turn',\n 'grad',\n 's',\n 'ms',\n 'dpi',\n 'dpcm',\n 'dppx',\n 'svw',\n 'svh',\n 'lvw',\n 'lvh',\n 'dvw',\n 'dvh',\n 'cqw',\n 'cqh',\n 'cqi',\n 'cqb',\n 'cqmin',\n 'cqmax',\n 'cap',\n 'ic',\n 'rlh',\n 'vi',\n 'vb',\n]);\n\n/**\n * Properties that accept `true` as a value (means \"use default\").\n *\n * `fill` is documented as accepting `true` but is deliberately absent: as of\n * tasty 3.8 the handler passes the boolean straight through, so `fill: true`\n * emits `background-color: true` — a declaration the browser drops, leaving no\n * background at all. Reporting it is the useful answer while that is what runs.\n * `constants.round-trip.test.ts` pins both halves of that: no property in this\n * set may swallow `true`, and `fill` still does — so whichever way tasty\n * resolves it (docs or implementation), a test says so rather than the plugin\n * quietly disagreeing with the runtime.\n */\nexport const BOOLEAN_TRUE_PROPERTIES = new Set([\n 'border',\n 'radius',\n 'padding',\n 'margin',\n 'gap',\n 'color',\n 'shadow',\n 'outline',\n 'inset',\n 'width',\n 'height',\n // `fade: true` falls back to `calc(2 * var(--gap))` — a token-following\n // default no explicit value can express, so it is genuinely useful.\n 'fade',\n 'hide',\n 'preset',\n 'font',\n 'scrollbar',\n 'scrollMargin',\n 'scrollPadding',\n // Every enhanced logical *category* carries the same design-system default as\n // its physical counterpart: `1x` for spacing and scroll edges, `0` for inset,\n // `1bw` for border, a reset for the sizes.\n //\n // The min/max size constraints are deliberately excluded, mirroring the\n // absence of `minWidth`/`maxWidth` here. The runtime does accept `true` on\n // them — they route through the same dimension handler as the axis — but what\n // it does with it is reset the whole axis: `minBlockSize: true` emits\n // `block-size: auto; min-block-size: initial; max-block-size: initial`, not a\n // default for the one constraint named. `blockSize: true` is how you ask for\n // that, so the constraint spellings stay an error, exactly as `minWidth: true`\n // already is.\n ...LOGICAL_SIZE_STYLES,\n ...LOGICAL_SPACING_STYLES,\n ...LOGICAL_BORDER_STYLES,\n]);\n\n/**\n * Directional modifiers and which properties accept them.\n */\nexport const DIRECTIONAL_MODIFIERS: Record<string, Set<string>> = {\n border: new Set(['top', 'right', 'bottom', 'left']),\n radius: new Set([\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n ]),\n padding: new Set(['top', 'right', 'bottom', 'left']),\n margin: new Set(['top', 'right', 'bottom', 'left']),\n fade: new Set(['top', 'right', 'bottom', 'left']),\n scrollMargin: new Set(['top', 'right', 'bottom', 'left']),\n scrollPadding: new Set(['top', 'right', 'bottom', 'left']),\n inset: new Set(['top', 'right', 'bottom', 'left', 'dock']),\n // Enhanced logical styles take `start`/`end` and nothing else. Listing them\n // here is what makes a physical modifier on a logical axis — `blockPadding:\n // '1x top'`, which the handler silently drops — a report rather than silence.\n ...Object.fromEntries(\n [...LOGICAL_SPACING_STYLES, ...LOGICAL_BORDER_STYLES].map((style) => [\n style,\n new Set<string>(LOGICAL_EDGES),\n ]),\n ),\n};\n\n/**\n * Properties where a group naming directions carries exactly **one** value.\n *\n * Tasty v3 dropped the positional multi-value form for these: values and\n * modifiers are bucketed separately per comma group, so `'2x 4x top right'` and\n * `'2x top 4x right'` were the same input and the pairing depended on modifier\n * order. Per-side values now come from comma groups.\n *\n * Deliberately excludes `border` and `radius`. A `border` group legitimately\n * carries width + style + color, and a `radius` group can carry two values with\n * `leaf`/`backleaf`; neither takes the positional-per-side form, so neither is\n * subject to the rule.\n */\nexport const SINGLE_VALUE_DIRECTIONAL_PROPERTIES = new Set<string>([\n 'padding',\n 'margin',\n 'inset',\n 'scrollMargin',\n 'scrollPadding',\n 'fade',\n // The logical axis handlers bucket values and modifiers the same way, and warn\n // on a second value in a group that names an edge. `blockBorder`/`inlineBorder`\n // are excluded for the same reason `border` is: width + style + colour.\n ...LOGICAL_SPACING_STYLES,\n]);\n\n/**\n * Position names a 4-value box maps onto, in source order.\n */\nexport const BOX_SIDES = ['top', 'right', 'bottom', 'left'] as const;\nexport const BOX_CORNERS = [\n 'top-left',\n 'top-right',\n 'bottom-right',\n 'bottom-left',\n] as const;\n\nexport interface DirectionalBoxConfig {\n /**\n * The token that marks an unset position — dropping it from the shorthand\n * leaves the emitted CSS unchanged. This is the CSS initial value of the\n * longhand, which is not always `0`: `inset` initialises to `auto`, so a `0`\n * there is a real offset the shorthand must not drop.\n */\n identity: string;\n /** Modifier names for each of the four positions, in source order. */\n positions: readonly string[];\n /**\n * Whether `eslint --fix` may apply the rewrite. False when the rewrite is\n * still an improvement but changes what the browser renders, so a human has\n * to opt in.\n */\n fixable: boolean;\n /**\n * Set when the 4-value form is not interpreted per-position at all, so the\n * current output is wrong rather than merely verbose. Surfaced in the report.\n */\n note?: string;\n}\n\n/**\n * Properties whose 4-value box syntax has a directional-shorthand equivalent.\n *\n * `radius` uses corner names rather than sides: a directional modifier\n * addresses the corner *pair* along an edge, so only `top-left` & co. can\n * express a single corner (requires `@tenphi/tasty` >= 2.10).\n */\nexport const DIRECTIONAL_BOX: Record<string, DirectionalBoxConfig> = {\n margin: { identity: '0', positions: BOX_SIDES, fixable: true },\n padding: { identity: '0', positions: BOX_SIDES, fixable: true },\n inset: { identity: 'auto', positions: BOX_SIDES, fixable: true },\n radius: { identity: '0', positions: BOX_CORNERS, fixable: true },\n fade: { identity: '0', positions: BOX_SIDES, fixable: true },\n // Four `border` tokens parse as a single border value (width/style/color),\n // not as a box: `border: '0 0 1bw 0'` emits `border: 0 solid …` — no border\n // on any side. The directional form is what the author meant, but applying\n // it changes rendering, so it stays a manual fix.\n border: {\n identity: '0',\n positions: BOX_SIDES,\n fixable: false,\n note: 'four border tokens parse as one border value, so the current style sets no border at all',\n },\n};\n\n/**\n * Properties that support a `dock` modifier — pin one edge and span its full\n * length. Lets a 4-value box with a single `identity` side collapse, e.g.\n * `inset: 'auto 0 0 0'` -> `inset: '0 bottom dock'`.\n * Requires `@tenphi/tasty` >= 2.10.\n */\nexport const DOCK_PROPERTIES = new Set(['inset']);\n\n/**\n * Valid radius shape keywords.\n */\nexport const RADIUS_SHAPES = new Set(['round', 'ellipse', 'leaf', 'backleaf']);\n\n/**\n * Known semantic transition names.\n */\nexport const SEMANTIC_TRANSITIONS = new Set([\n 'fade',\n 'fill',\n 'border',\n 'radius',\n 'shadow',\n 'preset',\n 'gap',\n 'theme',\n 'color',\n 'outline',\n 'dimension',\n 'flow',\n 'inset',\n 'text',\n 'opacity',\n 'translate',\n 'rotate',\n 'scale',\n 'filter',\n 'image',\n 'background',\n 'width',\n 'height',\n 'zIndex',\n]);\n\n/**\n * Mapping of native CSS properties to tasty shorthand alternatives.\n */\nexport const SHORTHAND_MAPPING: Record<\n string,\n { property: string; hint: string; safeFix?: boolean }\n> = {\n backgroundColor: { property: 'fill', hint: \"fill: '...'\", safeFix: true },\n borderColor: { property: 'border', hint: \"border: '...'\" },\n borderWidth: { property: 'border', hint: \"border: '...'\" },\n borderStyle: { property: 'border', hint: \"border: '...'\" },\n borderTop: { property: 'border', hint: \"border: '... top'\" },\n borderRight: { property: 'border', hint: \"border: '... right'\" },\n borderBottom: { property: 'border', hint: \"border: '... bottom'\" },\n borderLeft: { property: 'border', hint: \"border: '... left'\" },\n borderRadius: { property: 'radius', hint: \"radius: '...'\", safeFix: true },\n maxWidth: { property: 'width', hint: \"width: 'max ...'\" },\n minWidth: { property: 'width', hint: \"width: 'min ...'\" },\n maxHeight: { property: 'height', hint: \"height: 'max ...'\" },\n minHeight: { property: 'height', hint: \"height: 'min ...'\" },\n flexDirection: { property: 'flow', hint: \"flow: '...'\" },\n flexWrap: { property: 'flow', hint: \"flow: '...'\" },\n flexFlow: { property: 'flow', hint: \"flow: '...'\" },\n gridAutoFlow: { property: 'flow', hint: \"flow: '...'\" },\n outlineOffset: { property: 'outline', hint: \"outline: '... / offset'\" },\n paddingTop: { property: 'padding', hint: \"padding: '... top'\" },\n paddingRight: { property: 'padding', hint: \"padding: '... right'\" },\n paddingBottom: { property: 'padding', hint: \"padding: '... bottom'\" },\n paddingLeft: { property: 'padding', hint: \"padding: '... left'\" },\n marginTop: { property: 'margin', hint: \"margin: '... top'\" },\n marginRight: { property: 'margin', hint: \"margin: '... right'\" },\n marginBottom: { property: 'margin', hint: \"margin: '... bottom'\" },\n marginLeft: { property: 'margin', hint: \"margin: '... left'\" },\n fontSize: { property: 'preset', hint: \"preset: '...'\" },\n fontWeight: {\n property: 'preset',\n hint: \"preset: 'name / strong' (with strong modifier)\",\n },\n lineHeight: {\n property: 'preset',\n hint: \"preset: 'name / tight' (with tight modifier)\",\n },\n boxShadow: { property: 'shadow', hint: \"shadow: '...'\" },\n backgroundImage: { property: 'image', hint: \"image: '...'\" },\n fontFamily: { property: 'font', hint: \"font: '...'\" },\n fontStyle: {\n property: 'preset',\n hint: \"preset: 'name / italic' (with italic modifier)\",\n },\n letterSpacing: { property: 'preset', hint: \"preset: '...'\" },\n textTransform: { property: 'preset', hint: \"preset: '...'\" },\n flexGap: { property: 'gap', hint: \"gap: '...'\" },\n scrollbarWidth: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n scrollbarColor: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n scrollbarGutter: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n gridTemplateColumns: {\n property: 'gridColumns',\n hint: \"gridColumns: '...'\",\n },\n gridTemplateRows: { property: 'gridRows', hint: \"gridRows: '...'\" },\n gridTemplateAreas: { property: 'gridAreas', hint: \"gridAreas: '...'\" },\n top: { property: 'inset', hint: \"inset: '... top'\" },\n right: { property: 'inset', hint: \"inset: '... right'\" },\n bottom: { property: 'inset', hint: \"inset: '... bottom'\" },\n left: { property: 'inset', hint: \"inset: '... left'\" },\n outlineColor: { property: 'outline', hint: \"outline: '...'\" },\n outlineWidth: { property: 'outline', hint: \"outline: '...'\" },\n outlineStyle: { property: 'outline', hint: \"outline: '...'\" },\n lineClamp: {\n property: 'textOverflow',\n hint: \"textOverflow: 'ellipsis / {lines}'\",\n },\n\n // --- Native logical CSS -> the enhanced Tasty logical style. ---\n //\n // These mirror the physical rows above one for one. Tasty v3.8 moved the\n // enhanced behaviour onto `<axis><Category>` names and stopped reading the\n // native spellings in its handlers, so `paddingBlock` is now a plain CSS\n // declaration with no category default: the rename is the same advice as\n // `paddingTop` -> `padding: '... top'`, not a new opinion.\n //\n // Only the axis shorthands carry `safeFix`. `paddingBlock: '1x 2x'` and\n // `blockPadding: '1x 2x'` both emit `padding-block: 1x 2x`, so the key rename\n // is the whole edit — `constants.round-trip.test.ts` asserts that against the\n // installed runtime rather than trusting this comment. An edge longhand needs\n // a `start`/`end` modifier added to the value, and a `border-*` rename also\n // gains the style/colour defaults `border-block` never had (`border-block:\n // 1bw` renders nothing at all), so those stay report-only.\n //\n // These renames are why the `@tenphi/tasty` peer floor is 3.8.0. The plugin\n // never imports tasty, so its constants are fixed at publish time and cannot\n // adapt to the consumer's version: on 3.7 and below `blockPadding` is not a\n // style tasty knows, and an unhandled camelCase key renders straight through\n // to `block-padding`, which the browser drops. The fix would silently delete\n // the padding, so the floor is the only thing standing between that and a\n // consumer — recognising the logical names is harmless on an older tasty\n // (a missed warning), but rewriting *to* them is not.\n paddingBlock: {\n property: 'blockPadding',\n hint: \"blockPadding: '...'\",\n safeFix: true,\n },\n paddingBlockStart: {\n property: 'blockPadding',\n hint: \"blockPadding: '... start'\",\n },\n paddingBlockEnd: {\n property: 'blockPadding',\n hint: \"blockPadding: '... end'\",\n },\n paddingInline: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '...'\",\n safeFix: true,\n },\n paddingInlineStart: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '... start'\",\n },\n paddingInlineEnd: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '... end'\",\n },\n marginBlock: {\n property: 'blockMargin',\n hint: \"blockMargin: '...'\",\n safeFix: true,\n },\n marginBlockStart: {\n property: 'blockMargin',\n hint: \"blockMargin: '... start'\",\n },\n marginBlockEnd: { property: 'blockMargin', hint: \"blockMargin: '... end'\" },\n marginInline: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '...'\",\n safeFix: true,\n },\n marginInlineStart: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '... start'\",\n },\n marginInlineEnd: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '... end'\",\n },\n insetBlock: {\n property: 'blockInset',\n hint: \"blockInset: '...'\",\n safeFix: true,\n },\n insetBlockStart: { property: 'blockInset', hint: \"blockInset: '... start'\" },\n insetBlockEnd: { property: 'blockInset', hint: \"blockInset: '... end'\" },\n insetInline: {\n property: 'inlineInset',\n hint: \"inlineInset: '...'\",\n safeFix: true,\n },\n insetInlineStart: {\n property: 'inlineInset',\n hint: \"inlineInset: '... start'\",\n },\n insetInlineEnd: { property: 'inlineInset', hint: \"inlineInset: '... end'\" },\n scrollMarginBlock: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '...'\",\n safeFix: true,\n },\n scrollMarginBlockStart: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '... start'\",\n },\n scrollMarginBlockEnd: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '... end'\",\n },\n scrollMarginInline: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '...'\",\n safeFix: true,\n },\n scrollMarginInlineStart: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '... start'\",\n },\n scrollMarginInlineEnd: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '... end'\",\n },\n scrollPaddingBlock: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '...'\",\n safeFix: true,\n },\n scrollPaddingBlockStart: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '... start'\",\n },\n scrollPaddingBlockEnd: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '... end'\",\n },\n scrollPaddingInline: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '...'\",\n safeFix: true,\n },\n scrollPaddingInlineStart: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '... start'\",\n },\n scrollPaddingInlineEnd: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '... end'\",\n },\n borderBlock: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockColor: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockWidth: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockStyle: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockStart: {\n property: 'blockBorder',\n hint: \"blockBorder: '... start'\",\n },\n borderBlockEnd: { property: 'blockBorder', hint: \"blockBorder: '... end'\" },\n borderInline: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineColor: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineWidth: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineStyle: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineStart: {\n property: 'inlineBorder',\n hint: \"inlineBorder: '... start'\",\n },\n borderInlineEnd: {\n property: 'inlineBorder',\n hint: \"inlineBorder: '... end'\",\n },\n minBlockSize: { property: 'blockSize', hint: \"blockSize: 'min ...'\" },\n maxBlockSize: { property: 'blockSize', hint: \"blockSize: 'max ...'\" },\n minInlineSize: { property: 'inlineSize', hint: \"inlineSize: 'min ...'\" },\n maxInlineSize: { property: 'inlineSize', hint: \"inlineSize: 'max ...'\" },\n};\n\n/**\n * CSS shorthands whose *longhands* are the preferred Tasty form — the reverse\n * of `SHORTHAND_MAPPING`.\n *\n * `flex` is lossy in a way the longhands are not: it resets the two components\n * you did not write to values that are not their CSS initial. `flex: '0'`\n * expands to `flex-grow: 0; flex-shrink: 1; flex-basis: 0%`, so it cannot\n * express \"do not shrink\" (`flexShrink: 0`) at all, and it silently changes\n * `flex-basis`. Writing the longhands keeps each component independent, which\n * also lets them carry separate state maps.\n */\nexport const LONGHAND_MAPPING: Record<\n string,\n { longhands: string[]; hint: string }\n> = {\n flex: {\n longhands: ['flexGrow', 'flexShrink', 'flexBasis'],\n hint: 'flexGrow / flexShrink / flexBasis',\n },\n};\n\n/**\n * CSS transition property names mapped to Tasty semantic transition names.\n */\nexport const TRANSITION_SEMANTIC_MAPPING: Record<string, string> = {\n backgroundColor: 'fill',\n 'background-color': 'fill',\n backgroundImage: 'image',\n 'background-image': 'image',\n borderRadius: 'radius',\n 'border-radius': 'radius',\n borderColor: 'border',\n 'border-color': 'border',\n borderWidth: 'border',\n 'border-width': 'border',\n borderStyle: 'border',\n 'border-style': 'border',\n boxShadow: 'shadow',\n 'box-shadow': 'shadow',\n outlineColor: 'outline',\n 'outline-color': 'outline',\n outlineWidth: 'outline',\n 'outline-width': 'outline',\n outlineOffset: 'outline',\n 'outline-offset': 'outline',\n gridTemplateColumns: 'gridColumns',\n 'grid-template-columns': 'gridColumns',\n gridTemplateRows: 'gridRows',\n 'grid-template-rows': 'gridRows',\n flexDirection: 'flow',\n 'flex-direction': 'flow',\n gridAutoFlow: 'flow',\n 'grid-auto-flow': 'flow',\n fontSize: 'preset',\n 'font-size': 'preset',\n lineHeight: 'preset',\n 'line-height': 'preset',\n letterSpacing: 'preset',\n 'letter-spacing': 'preset',\n};\n\n/**\n * Known preset modifiers.\n */\nexport const PRESET_MODIFIERS = new Set([\n 'strong',\n 'bold',\n 'italic',\n 'icon',\n 'tight',\n]);\n\n/**\n * Default import sources for tasty.\n */\nexport const DEFAULT_IMPORT_SOURCES = ['@tenphi/tasty', '@tenphi/tasty/static'];\n\n/**\n * Built-in state prefixes that are always valid (not aliases).\n */\nexport const BUILT_IN_STATE_PREFIXES = new Set([\n '@media',\n '@root',\n '@parent',\n '@own',\n '@supports',\n '@starting',\n '@keyframes',\n '@property',\n '@font-face',\n '@counter-style',\n '@function',\n]);\n\n/**\n * Known CSS pseudo-classes.\n */\nexport const KNOWN_PSEUDO_CLASSES = new Set([\n ':hover',\n ':focus',\n ':focus-visible',\n ':focus-within',\n ':active',\n ':visited',\n ':link',\n ':checked',\n ':disabled',\n ':enabled',\n ':empty',\n ':first-child',\n ':last-child',\n ':first-of-type',\n ':last-of-type',\n ':only-child',\n ':only-of-type',\n ':root',\n ':target',\n ':valid',\n ':invalid',\n ':required',\n ':optional',\n ':read-only',\n ':read-write',\n ':placeholder-shown',\n ':autofill',\n ':default',\n ':indeterminate',\n ':in-range',\n ':out-of-range',\n ':any-link',\n ':local-link',\n ':is',\n ':not',\n ':where',\n ':has',\n ':nth-child',\n ':nth-last-child',\n ':nth-of-type',\n ':nth-last-of-type',\n '::before',\n '::after',\n '::placeholder',\n '::selection',\n '::first-line',\n '::first-letter',\n '::marker',\n '::backdrop',\n]);\n\n/**\n * CSS named colors (level 1–4) that should be replaced by `#color` tokens.\n * `transparent` and `currentcolor` are excluded — they have their own token\n * suggestions (`#clear`, `#current`) handled by `prefer-custom-property-syntax`.\n */\nexport const NAMED_CSS_COLORS = new Set<string>([\n 'aliceblue',\n 'antiquewhite',\n 'aqua',\n 'aquamarine',\n 'azure',\n 'beige',\n 'bisque',\n 'black',\n 'blanchedalmond',\n 'blue',\n 'blueviolet',\n 'brown',\n 'burlywood',\n 'cadetblue',\n 'chartreuse',\n 'chocolate',\n 'coral',\n 'cornflowerblue',\n 'cornsilk',\n 'crimson',\n 'cyan',\n 'darkblue',\n 'darkcyan',\n 'darkgoldenrod',\n 'darkgray',\n 'darkgreen',\n 'darkgrey',\n 'darkkhaki',\n 'darkmagenta',\n 'darkolivegreen',\n 'darkorange',\n 'darkorchid',\n 'darkred',\n 'darksalmon',\n 'darkseagreen',\n 'darkslateblue',\n 'darkslategray',\n 'darkslategrey',\n 'darkturquoise',\n 'darkviolet',\n 'deeppink',\n 'deepskyblue',\n 'dimgray',\n 'dimgrey',\n 'dodgerblue',\n 'firebrick',\n 'floralwhite',\n 'forestgreen',\n 'fuchsia',\n 'gainsboro',\n 'ghostwhite',\n 'gold',\n 'goldenrod',\n 'gray',\n 'green',\n 'greenyellow',\n 'grey',\n 'honeydew',\n 'hotpink',\n 'indianred',\n 'indigo',\n 'ivory',\n 'khaki',\n 'lavender',\n 'lavenderblush',\n 'lawngreen',\n 'lemonchiffon',\n 'lightblue',\n 'lightcoral',\n 'lightcyan',\n 'lightgoldenrodyellow',\n 'lightgray',\n 'lightgreen',\n 'lightgrey',\n 'lightpink',\n 'lightsalmon',\n 'lightseagreen',\n 'lightskyblue',\n 'lightslategray',\n 'lightslategrey',\n 'lightsteelblue',\n 'lightyellow',\n 'lime',\n 'limegreen',\n 'linen',\n 'magenta',\n 'maroon',\n 'mediumaquamarine',\n 'mediumblue',\n 'mediumorchid',\n 'mediumpurple',\n 'mediumseagreen',\n 'mediumslateblue',\n 'mediumspringgreen',\n 'mediumturquoise',\n 'mediumvioletred',\n 'midnightblue',\n 'mintcream',\n 'mistyrose',\n 'moccasin',\n 'navajowhite',\n 'navy',\n 'oldlace',\n 'olive',\n 'olivedrab',\n 'orange',\n 'orangered',\n 'orchid',\n 'palegoldenrod',\n 'palegreen',\n 'paleturquoise',\n 'palevioletred',\n 'papayawhip',\n 'peachpuff',\n 'peru',\n 'pink',\n 'plum',\n 'powderblue',\n 'purple',\n 'rebeccapurple',\n 'red',\n 'rosybrown',\n 'royalblue',\n 'saddlebrown',\n 'salmon',\n 'sandybrown',\n 'seagreen',\n 'seashell',\n 'sienna',\n 'silver',\n 'skyblue',\n 'slateblue',\n 'slategray',\n 'slategrey',\n 'snow',\n 'springgreen',\n 'steelblue',\n 'tan',\n 'teal',\n 'thistle',\n 'tomato',\n 'turquoise',\n 'violet',\n 'wheat',\n 'white',\n 'whitesmoke',\n 'yellow',\n 'yellowgreen',\n]);\n\n/**\n * Properties that accept color values. Used to scope named-color checks so\n * string properties like `content` or `cursor` don't false-positive.\n */\nexport const COLOR_BEARING_PROPERTIES = new Set<string>([\n // Tasty enhanced properties\n 'fill',\n 'color',\n 'caretColor',\n 'accentColor',\n 'shadow',\n 'border',\n 'blockBorder',\n 'inlineBorder',\n 'outline',\n 'fade',\n // CSS background\n 'background',\n 'backgroundColor',\n // CSS border colors\n 'borderColor',\n 'borderTopColor',\n 'borderRightColor',\n 'borderBottomColor',\n 'borderLeftColor',\n // CSS logical border colors — the `block`/`inline` counterparts of the four\n // above, which a writing-mode-aware codebase reaches for just as often.\n 'borderBlockColor',\n 'borderBlockStartColor',\n 'borderBlockEndColor',\n 'borderInlineColor',\n 'borderInlineStartColor',\n 'borderInlineEndColor',\n // CSS shadows\n 'boxShadow',\n 'textShadow',\n // CSS text decoration\n 'textDecorationColor',\n // CSS outline / misc\n 'outlineColor',\n 'scrollbarColor',\n 'columnRuleColor',\n // SVG\n 'stroke',\n 'stopColor',\n 'floodColor',\n 'lightingColor',\n]);\n\n/**\n * Properties where tasty does NOT expand a `$name` custom-property reference.\n *\n * **Empty since tasty 3.0.2**, which taught the pass-through style handlers to\n * substitute `$name` refs too (tasty#264). Before that, `fontFamily:\n * '$font-sans'` emitted a literal `font-family: $font-sans` — an invalid\n * declaration the browser drops — so `prefer-custom-property-syntax` had to\n * suppress the rewrite for two dozen properties. It no longer does, which means\n * those rewrites are now offered where they were previously (and, since 3.0.2,\n * needlessly) held back.\n *\n * The set is kept rather than deleted: it is the seam the round-trip guard\n * writes to, so a future handler that regains pass-through behaviour surfaces\n * as a failing test instead of a silently broken autofix.\n *\n * An empty list here would let `--fix` delete a declaration on 3.0.0/3.0.1,\n * which the `@tenphi/tasty` peer floor rules out — that floor is 3.8.0, set by\n * the logical renames in {@link SHORTHAND_MAPPING} rather than by this list.\n *\n * Note that {@link PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION} is *not* empty:\n * `#token` expansion is still property-scoped (`fontSize: '#accent'` is\n * meaningless and passes through). The two sets are genuinely different.\n *\n * Derived from the runtime, not by hand — `constants.round-trip.test.ts` rebuilds\n * both sets against the installed `@tenphi/tasty` and fails if either drifts.\n */\nexport const PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION = new Set<string>([]);\n\n/**\n * Properties where tasty does NOT expand a `#name` colour token.\n *\n * Mostly dimension and keyword properties: `fontSize: '#accent'` is meaningless,\n * so the handler leaves the value alone and the declaration is dropped. Suggesting\n * `#x` for a `var(--x-color)` inside one of these would break it.\n *\n * See {@link PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION} for why this is a\n * separate list rather than the same one.\n */\nexport const PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION = new Set([\n 'align',\n 'alignContent',\n 'alignItems',\n 'backgroundAttachment',\n 'backgroundClip',\n 'backgroundOrigin',\n 'backgroundRepeat',\n 'display',\n 'font',\n 'fontFamily',\n 'fontSize',\n 'fontWeight',\n 'justify',\n 'justifyContent',\n 'justifyItems',\n 'letterSpacing',\n 'lineHeight',\n 'outlineOffset',\n 'overflow',\n 'place',\n 'placeContent',\n 'placeItems',\n 'textTransform',\n 'whiteSpace',\n]);\n"],"mappings":";;AAuBA,MAAa,gBAAgB,CAAC,SAAS,MAAM;;AAG7C,MAAa,sBAAsB,CAAC,aAAa,aAAa;AAC9D,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,wBAAwB,CAAC,eAAe,eAAe;;AAGpE,MAAa,iBAAiB;CAC5B,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACJ;;;;AAKD,MAAa,yBAAyB,IAAI,IAAI;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAIA;CACA;CACA,GAAG;CACJ,CAAC;;;;;;AAOF,MAAa,qBAAqB,IAAI,IAAI;CACxC;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;AAQF,MAAa,6BAAqD;CAChE,eAAe;CACf,aAAa;CACb,iBAAiB;CAClB;;;;;;;;;;;;;;;;;;;;AAqBD,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,0BAA0B;CAGrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,MAAa,uBAAuB,IAAI,IAAI,CAC1C,GAAG,wBACH,GAAG,wBACJ,CAAC;;;;AAKF,MAAa,iBAAiB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;;;;AAK/E,MAAa,YAAY,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;AAcF,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CAaA,GAAG;CACH,GAAG;CACH,GAAG;CACJ,CAAC;;;;AAKF,MAAa,wBAAqD;CAChE,QAAQ,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CACnD,QAAQ,IAAI,IAAI;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,SAAS,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CACpD,QAAQ,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CACnD,MAAM,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CAC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,WAAW;EAAC;EAAY;EAAc;EAAY;CAClD,MAAM;CACP,EACF;;;;AAKD,MAAa,8BAAsD;CACjE,iBAAiB;CACjB,oBAAoB;CACpB,iBAAiB;CACjB,oBAAoB;CACpB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,gBAAgB;CAChB,WAAW;CACX,cAAc;CACd,cAAc;CACd,iBAAiB;CACjB,cAAc;CACd,iBAAiB;CACjB,eAAe;CACf,kBAAkB;CAClB,qBAAqB;CACrB,yBAAyB;CACzB,kBAAkB;CAClB,sBAAsB;CACtB,eAAe;CACf,kBAAkB;CAClB,cAAc;CACd,kBAAkB;CAClB,UAAU;CACV,aAAa;CACb,YAAY;CACZ,eAAe;CACf,eAAe;CACf,kBAAkB;CACnB;;;;AAKD,MAAa,mBAAmB,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,yBAAyB,CAAC,iBAAiB,uBAAuB;;;;AAK/E,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,uBAAuB,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,MAAa,mBAAmB,IAAI,IAAY;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAa,2BAA2B,IAAI,IAAY;CAEtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BF,MAAa,+DAA+C,IAAI,IAAY,EAAE,CAAC;;;;;;;;;;;AAY/E,MAAa,2CAA2C,IAAI,IAAI;CAC9D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC"}
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Enhanced logical style families, mirroring `@tenphi/tasty`'s `logical-list.ts`.\n//\n// Tasty v3.8 gave each logical axis/category pair its own handler, named\n// `<axis><Category>` — `blockPadding`, `inlineBorder` — which emits native\n// logical CSS (`padding-block`, `border-inline`) rather than converting the\n// value to a physical edge. The browser resolves `start`/`end` from the\n// element's `writingMode` and `direction`.\n//\n// The native CSS spellings (`paddingBlock`, `insetInlineStart`, …) stay valid\n// keys, but they are now *ordinary* CSS properties: tasty stopped reading\n// `paddingBlock`/`paddingInline`/`insetBlock`/`insetInline` in its physical\n// handlers, so they no longer get category defaults or directional behaviour.\n// That is why they are absent from `KNOWN_TASTY_PROPERTIES` (they are still\n// recognised, via `KNOWN_CSS_PROPERTIES`) and why `SHORTHAND_MAPPING` points\n// them at the enhanced style instead.\n//\n// The groups are split by *vocabulary*, not for tidiness: each one is spread\n// into a different set below, and the splits are what keep `true` off the size\n// constraints and the single-value rule off the borders.\n// ---------------------------------------------------------------------------\n\n/** Logical edge modifiers, the `start`/`end` counterpart of {@link BOX_SIDES}. */\nexport const LOGICAL_EDGES = ['start', 'end'] as const;\n\n/** Enhanced logical sizes. `min`/`max` variants are read by the same handler. */\nexport const LOGICAL_SIZE_STYLES = ['blockSize', 'inlineSize'] as const;\nexport const LOGICAL_SIZE_CONSTRAINT_STYLES = [\n 'minBlockSize',\n 'maxBlockSize',\n 'minInlineSize',\n 'maxInlineSize',\n] as const;\n\n/**\n * Enhanced logical spacing and inset axes — the categories that take a value\n * per logical edge, so they share the `start`/`end` modifier vocabulary and the\n * one-value-per-directional-group rule.\n */\nexport const LOGICAL_SPACING_STYLES = [\n 'blockPadding',\n 'inlinePadding',\n 'blockMargin',\n 'inlineMargin',\n 'blockInset',\n 'inlineInset',\n 'blockScrollMargin',\n 'inlineScrollMargin',\n 'blockScrollPadding',\n 'inlineScrollPadding',\n] as const;\n\n/**\n * Enhanced logical borders. Kept apart from {@link LOGICAL_SPACING_STYLES}\n * because a border group legitimately carries width + style + colour, exactly\n * as physical `border` does, so it is not subject to the single-value rule.\n */\nexport const LOGICAL_BORDER_STYLES = ['blockBorder', 'inlineBorder'] as const;\n\n/** Every enhanced logical style, in the order tasty's own lists declare them. */\nexport const LOGICAL_STYLES = [\n ...LOGICAL_SIZE_STYLES,\n ...LOGICAL_SIZE_CONSTRAINT_STYLES,\n ...LOGICAL_SPACING_STYLES,\n ...LOGICAL_BORDER_STYLES,\n] as const;\n\n/**\n * Built-in tasty style properties that are always valid as style keys.\n */\nexport const KNOWN_TASTY_PROPERTIES = new Set([\n 'display',\n 'font',\n 'preset',\n 'hide',\n 'whiteSpace',\n 'opacity',\n 'transition',\n 'gridArea',\n 'order',\n 'gridColumn',\n 'gridRow',\n 'placeSelf',\n 'alignSelf',\n 'justifySelf',\n 'zIndex',\n 'margin',\n 'inset',\n 'position',\n 'padding',\n 'overflow',\n 'scrollbar',\n 'textAlign',\n 'border',\n 'radius',\n 'shadow',\n 'outline',\n 'color',\n 'fill',\n 'fade',\n 'image',\n 'textTransform',\n 'fontWeight',\n 'fontStyle',\n 'width',\n 'height',\n 'flexBasis',\n 'flexGrow',\n 'flexShrink',\n 'flex',\n 'flow',\n 'place',\n 'placeItems',\n 'placeContent',\n 'alignItems',\n 'alignContent',\n 'justifyItems',\n 'justifyContent',\n 'align',\n 'justify',\n 'gap',\n 'columnGap',\n 'rowGap',\n 'gridColumns',\n 'gridRows',\n 'gridTemplate',\n 'gridAreas',\n 'recipe',\n 'textOverflow',\n 'textDecoration',\n 'animation',\n 'cursor',\n 'pointerEvents',\n 'userSelect',\n 'transform',\n 'transformOrigin',\n 'filter',\n 'backdropFilter',\n 'mixBlendMode',\n 'objectFit',\n 'objectPosition',\n 'resize',\n 'appearance',\n 'listStyle',\n 'listStyleType',\n 'content',\n 'boxSizing',\n 'verticalAlign',\n 'wordBreak',\n 'overflowWrap',\n 'hyphens',\n 'tabSize',\n 'direction',\n 'unicodeBidi',\n 'writingMode',\n 'lineClamp',\n 'aspectRatio',\n 'contain',\n 'container',\n 'containerType',\n 'containerName',\n 'interpolateSize',\n 'willChange',\n 'isolation',\n 'touchAction',\n 'scrollBehavior',\n 'scrollSnapType',\n 'scrollSnapAlign',\n 'caretColor',\n 'accentColor',\n 'colorScheme',\n // Physical scroll shorthands: tasty v3.8 gave `scrollPadding` its own\n // directional handler alongside the `scrollMargin` one, so both take the\n // padding vocabulary (modifiers, `true`, one value per directional group).\n 'scrollMargin',\n 'scrollPadding',\n ...LOGICAL_STYLES,\n]);\n\n/**\n * Special top-level keys that are valid but not regular style properties.\n *\n * These match the real CSS at-rule names Tasty emits, so they are kebab-case.\n */\nexport const SPECIAL_STYLE_KEYS = new Set([\n '@keyframes',\n '@property',\n '@font-face',\n '@counter-style',\n '@function',\n]);\n\n/**\n * At-rule style keys renamed in Tasty v3, mapped old spelling -> new.\n *\n * The v2 spellings were camelCase; v3 uses the CSS at-rule names Tasty already\n * emitted. Reported (and auto-fixed) so upgrading is mechanical.\n */\nexport const RENAMED_SPECIAL_STYLE_KEYS: Record<string, string> = {\n '@properties': '@property',\n '@fontFace': '@font-face',\n '@counterStyle': '@counter-style',\n};\n\n/**\n * CSS property names, camelCase, derived from `known-css-properties` — the\n * MDN/W3C-sourced dataset stylelint uses for `property-no-unknown`.\n *\n * Generated. Run `pnpm generate:css-properties` after bumping the dataset rather\n * than editing this list; `constants.css-properties.test.ts` fails on drift.\n *\n * Tasty renders any camelCase style key straight through to its kebab-case CSS\n * declaration (`maskSize` -> `mask-size`), so anything absent here is a false\n * positive from `known-property` on valid CSS — which is why the list is derived\n * from data instead of maintained by hand.\n *\n * The dataset also carries at-rule descriptors (`src`, `syntax`, `symbols`, `size`),\n * which are kept, as stylelint keeps them. They are only reachable as top-level\n * style keys, where the cost is a declaration browsers drop rather than a report —\n * and separating them would mean hand-maintaining the exclusion list this change\n * exists to delete. Inside an at-rule block nothing validates them: `known-property`\n * exempts every `@`-prefixed key and does not descend.\n */\nconst DATASET_CSS_PROPERTIES = [\n 'accelerator',\n 'accentColor',\n 'additiveSymbols',\n 'alignContent',\n 'alignItems',\n 'alignSelf',\n 'alignmentBaseline',\n 'all',\n 'alt',\n 'anchorName',\n 'anchorScope',\n 'animation',\n 'animationComposition',\n 'animationDelay',\n 'animationDirection',\n 'animationDuration',\n 'animationFillMode',\n 'animationIterationCount',\n 'animationName',\n 'animationPlayState',\n 'animationRange',\n 'animationRangeEnd',\n 'animationRangeStart',\n 'animationTimeline',\n 'animationTimingFunction',\n 'animationTrigger',\n 'animationTriggerBehavior',\n 'animationTriggerExitRange',\n 'animationTriggerExitRangeEnd',\n 'animationTriggerExitRangeStart',\n 'animationTriggerRange',\n 'animationTriggerRangeEnd',\n 'animationTriggerRangeStart',\n 'animationTriggerTimeline',\n 'appRegion',\n 'appearance',\n 'ascentOverride',\n 'aspectRatio',\n 'audioLevel',\n 'azimuth',\n 'backdropFilter',\n 'backfaceVisibility',\n 'background',\n 'backgroundAttachment',\n 'backgroundBlendMode',\n 'backgroundClip',\n 'backgroundColor',\n 'backgroundImage',\n 'backgroundOrigin',\n 'backgroundPosition',\n 'backgroundPositionX',\n 'backgroundPositionY',\n 'backgroundRepeat',\n 'backgroundRepeatX',\n 'backgroundRepeatY',\n 'backgroundSize',\n 'basePalette',\n 'baselineShift',\n 'baselineSource',\n 'behavior',\n 'blockEllipsis',\n 'blockSize',\n 'blockStep',\n 'blockStepAlign',\n 'blockStepInsert',\n 'blockStepRound',\n 'blockStepSize',\n 'bookmarkLabel',\n 'bookmarkLevel',\n 'bookmarkState',\n 'border',\n 'borderBlock',\n 'borderBlockColor',\n 'borderBlockEnd',\n 'borderBlockEndColor',\n 'borderBlockEndStyle',\n 'borderBlockEndWidth',\n 'borderBlockStart',\n 'borderBlockStartColor',\n 'borderBlockStartStyle',\n 'borderBlockStartWidth',\n 'borderBlockStyle',\n 'borderBlockWidth',\n 'borderBottom',\n 'borderBottomColor',\n 'borderBottomLeftRadius',\n 'borderBottomRightRadius',\n 'borderBottomStyle',\n 'borderBottomWidth',\n 'borderBoundary',\n 'borderCollapse',\n 'borderColor',\n 'borderEndEndRadius',\n 'borderEndStartRadius',\n 'borderImage',\n 'borderImageOutset',\n 'borderImageRepeat',\n 'borderImageSlice',\n 'borderImageSource',\n 'borderImageWidth',\n 'borderInline',\n 'borderInlineColor',\n 'borderInlineEnd',\n 'borderInlineEndColor',\n 'borderInlineEndStyle',\n 'borderInlineEndWidth',\n 'borderInlineStart',\n 'borderInlineStartColor',\n 'borderInlineStartStyle',\n 'borderInlineStartWidth',\n 'borderInlineStyle',\n 'borderInlineWidth',\n 'borderLeft',\n 'borderLeftColor',\n 'borderLeftStyle',\n 'borderLeftWidth',\n 'borderRadius',\n 'borderRight',\n 'borderRightColor',\n 'borderRightStyle',\n 'borderRightWidth',\n 'borderSpacing',\n 'borderStartEndRadius',\n 'borderStartStartRadius',\n 'borderStyle',\n 'borderTop',\n 'borderTopColor',\n 'borderTopLeftRadius',\n 'borderTopRightRadius',\n 'borderTopStyle',\n 'borderTopWidth',\n 'borderWidth',\n 'bottom',\n 'boxDecorationBreak',\n 'boxShadow',\n 'boxSizing',\n 'boxSnap',\n 'breakAfter',\n 'breakBefore',\n 'breakInside',\n 'bufferedRendering',\n 'captionSide',\n 'caret',\n 'caretAnimation',\n 'caretColor',\n 'caretShape',\n 'chains',\n 'clear',\n 'clip',\n 'clipPath',\n 'clipRule',\n 'color',\n 'colorAdjust',\n 'colorInterpolation',\n 'colorInterpolationFilters',\n 'colorProfile',\n 'colorRendering',\n 'colorScheme',\n 'columnCount',\n 'columnFill',\n 'columnGap',\n 'columnHeight',\n 'columnProgression',\n 'columnRule',\n 'columnRuleBreak',\n 'columnRuleColor',\n 'columnRuleOutset',\n 'columnRuleStyle',\n 'columnRuleWidth',\n 'columnSpan',\n 'columnWidth',\n 'columnWrap',\n 'columns',\n 'contain',\n 'containIntrinsicBlockSize',\n 'containIntrinsicHeight',\n 'containIntrinsicInlineSize',\n 'containIntrinsicSize',\n 'containIntrinsicWidth',\n 'container',\n 'containerName',\n 'containerType',\n 'content',\n 'contentVisibility',\n 'continue',\n 'counterIncrement',\n 'counterReset',\n 'counterSet',\n 'cue',\n 'cueAfter',\n 'cueBefore',\n 'cursor',\n 'cx',\n 'cy',\n 'd',\n 'descentOverride',\n 'direction',\n 'display',\n 'displayAlign',\n 'dominantBaseline',\n 'dynamicRangeLimit',\n 'elevation',\n 'emptyCells',\n 'enableBackground',\n 'epubCaptionSide',\n 'epubHyphens',\n 'epubTextCombine',\n 'epubTextEmphasis',\n 'epubTextEmphasisColor',\n 'epubTextEmphasisStyle',\n 'epubTextOrientation',\n 'epubTextTransform',\n 'epubWordBreak',\n 'epubWritingMode',\n 'fallback',\n 'fieldSizing',\n 'fill',\n 'fillBreak',\n 'fillColor',\n 'fillImage',\n 'fillOpacity',\n 'fillOrigin',\n 'fillPosition',\n 'fillRepeat',\n 'fillRule',\n 'fillSize',\n 'filter',\n 'flex',\n 'flexBasis',\n 'flexDirection',\n 'flexFlow',\n 'flexGrow',\n 'flexShrink',\n 'flexWrap',\n 'float',\n 'floatDefer',\n 'floatOffset',\n 'floatReference',\n 'floodColor',\n 'floodOpacity',\n 'flow',\n 'flowFrom',\n 'flowInto',\n 'font',\n 'fontDisplay',\n 'fontFamily',\n 'fontFeatureSettings',\n 'fontKerning',\n 'fontLanguageOverride',\n 'fontOpticalSizing',\n 'fontPalette',\n 'fontSize',\n 'fontSizeAdjust',\n 'fontStretch',\n 'fontStyle',\n 'fontSynthesis',\n 'fontSynthesisPosition',\n 'fontSynthesisSmallCaps',\n 'fontSynthesisStyle',\n 'fontSynthesisWeight',\n 'fontVariant',\n 'fontVariantAlternates',\n 'fontVariantCaps',\n 'fontVariantEastAsian',\n 'fontVariantEmoji',\n 'fontVariantLigatures',\n 'fontVariantNumeric',\n 'fontVariantPosition',\n 'fontVariationSettings',\n 'fontWeight',\n 'fontWidth',\n 'footnoteDisplay',\n 'footnotePolicy',\n 'forcedColorAdjust',\n 'gap',\n 'glyphOrientationHorizontal',\n 'glyphOrientationVertical',\n 'grid',\n 'gridArea',\n 'gridAutoColumns',\n 'gridAutoFlow',\n 'gridAutoRows',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnGap',\n 'gridColumnStart',\n 'gridGap',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowGap',\n 'gridRowStart',\n 'gridTemplate',\n 'gridTemplateAreas',\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'hangingPunctuation',\n 'height',\n 'hyphenateCharacter',\n 'hyphenateLimitChars',\n 'hyphenateLimitLast',\n 'hyphenateLimitLines',\n 'hyphenateLimitZone',\n 'hyphens',\n 'imageOrientation',\n 'imageRendering',\n 'imageResolution',\n 'imeMode',\n 'inherits',\n 'initialLetter',\n 'initialLetterAlign',\n 'initialLetterWrap',\n 'initialValue',\n 'inlineSize',\n 'inlineSizing',\n 'inputFormat',\n 'inputSecurity',\n 'inset',\n 'insetArea',\n 'insetBlock',\n 'insetBlockEnd',\n 'insetBlockStart',\n 'insetInline',\n 'insetInlineEnd',\n 'insetInlineStart',\n 'interactivity',\n 'interpolateSize',\n 'isolation',\n 'itemCross',\n 'itemDirection',\n 'itemFlow',\n 'itemPack',\n 'itemSlack',\n 'itemTrack',\n 'itemWrap',\n 'justifyContent',\n 'justifyItems',\n 'justifySelf',\n 'kerning',\n 'layoutFlow',\n 'layoutGrid',\n 'layoutGridChar',\n 'layoutGridLine',\n 'layoutGridMode',\n 'layoutGridType',\n 'left',\n 'letterSpacing',\n 'lightingColor',\n 'lineBreak',\n 'lineClamp',\n 'lineFitEdge',\n 'lineGapOverride',\n 'lineGrid',\n 'lineHeight',\n 'lineHeightStep',\n 'lineIncrement',\n 'linePadding',\n 'lineSnap',\n 'listStyle',\n 'listStyleImage',\n 'listStylePosition',\n 'listStyleType',\n 'margin',\n 'marginBlock',\n 'marginBlockEnd',\n 'marginBlockStart',\n 'marginBottom',\n 'marginBreak',\n 'marginInline',\n 'marginInlineEnd',\n 'marginInlineStart',\n 'marginLeft',\n 'marginRight',\n 'marginTop',\n 'marginTrim',\n 'marker',\n 'markerEnd',\n 'markerKnockoutLeft',\n 'markerKnockoutRight',\n 'markerMid',\n 'markerOffset',\n 'markerPattern',\n 'markerSegment',\n 'markerSide',\n 'markerStart',\n 'marks',\n 'mask',\n 'maskBorder',\n 'maskBorderMode',\n 'maskBorderOutset',\n 'maskBorderRepeat',\n 'maskBorderSlice',\n 'maskBorderSource',\n 'maskBorderWidth',\n 'maskClip',\n 'maskComposite',\n 'maskImage',\n 'maskMode',\n 'maskOrigin',\n 'maskPosition',\n 'maskPositionX',\n 'maskPositionY',\n 'maskRepeat',\n 'maskSize',\n 'maskSourceType',\n 'maskType',\n 'mathDepth',\n 'mathShift',\n 'mathStyle',\n 'maxBlockSize',\n 'maxHeight',\n 'maxInlineSize',\n 'maxLines',\n 'maxWidth',\n 'maxZoom',\n 'minBlockSize',\n 'minHeight',\n 'minInlineSize',\n 'minIntrinsicSizing',\n 'minWidth',\n 'minZoom',\n 'mixBlendMode',\n 'motion',\n 'motionOffset',\n 'motionPath',\n 'motionRotation',\n 'navDown',\n 'navIndex',\n 'navLeft',\n 'navRight',\n 'navUp',\n 'navigation',\n 'negative',\n 'objectFit',\n 'objectPosition',\n 'objectViewBox',\n 'offset',\n 'offsetAnchor',\n 'offsetBlockEnd',\n 'offsetBlockStart',\n 'offsetDistance',\n 'offsetInlineEnd',\n 'offsetInlineStart',\n 'offsetPath',\n 'offsetPosition',\n 'offsetRotate',\n 'offsetRotation',\n 'opacity',\n 'order',\n 'orientation',\n 'orphans',\n 'outline',\n 'outlineColor',\n 'outlineOffset',\n 'outlineStyle',\n 'outlineWidth',\n 'overflow',\n 'overflowAnchor',\n 'overflowBlock',\n 'overflowClipMargin',\n 'overflowClipMarginBlock',\n 'overflowClipMarginBlockEnd',\n 'overflowClipMarginBlockStart',\n 'overflowClipMarginBottom',\n 'overflowClipMarginInline',\n 'overflowClipMarginInlineEnd',\n 'overflowClipMarginInlineStart',\n 'overflowClipMarginLeft',\n 'overflowClipMarginRight',\n 'overflowClipMarginTop',\n 'overflowInline',\n 'overflowWrap',\n 'overflowX',\n 'overflowY',\n 'overlay',\n 'overrideColors',\n 'overscrollBehavior',\n 'overscrollBehaviorBlock',\n 'overscrollBehaviorInline',\n 'overscrollBehaviorX',\n 'overscrollBehaviorY',\n 'pad',\n 'padding',\n 'paddingBlock',\n 'paddingBlockEnd',\n 'paddingBlockStart',\n 'paddingBottom',\n 'paddingInline',\n 'paddingInlineEnd',\n 'paddingInlineStart',\n 'paddingLeft',\n 'paddingRight',\n 'paddingTop',\n 'page',\n 'pageBreakAfter',\n 'pageBreakBefore',\n 'pageBreakInside',\n 'pageOrientation',\n 'paintOrder',\n 'pause',\n 'pauseAfter',\n 'pauseBefore',\n 'penAction',\n 'perspective',\n 'perspectiveOrigin',\n 'perspectiveOriginX',\n 'perspectiveOriginY',\n 'pitch',\n 'pitchRange',\n 'placeContent',\n 'placeItems',\n 'placeSelf',\n 'playDuring',\n 'pointerEvents',\n 'position',\n 'positionAnchor',\n 'positionArea',\n 'positionTry',\n 'positionTryFallbacks',\n 'positionTryOptions',\n 'positionTryOrder',\n 'positionVisibility',\n 'prefix',\n 'printColorAdjust',\n 'propertyName',\n 'quotes',\n 'r',\n 'range',\n 'readingFlow',\n 'readingOrder',\n 'regionFragment',\n 'resize',\n 'rest',\n 'restAfter',\n 'restBefore',\n 'richness',\n 'right',\n 'rotate',\n 'rowGap',\n 'rowRule',\n 'rowRuleBreak',\n 'rowRuleColor',\n 'rowRuleOutset',\n 'rowRuleStyle',\n 'rowRuleWidth',\n 'rubyAlign',\n 'rubyMerge',\n 'rubyOverhang',\n 'rubyPosition',\n 'rule',\n 'ruleBreak',\n 'ruleColor',\n 'ruleOutset',\n 'rulePaintOrder',\n 'ruleStyle',\n 'ruleWidth',\n 'running',\n 'rx',\n 'ry',\n 'scale',\n 'scrollBehavior',\n 'scrollInitialTarget',\n 'scrollMargin',\n 'scrollMarginBlock',\n 'scrollMarginBlockEnd',\n 'scrollMarginBlockStart',\n 'scrollMarginBottom',\n 'scrollMarginInline',\n 'scrollMarginInlineEnd',\n 'scrollMarginInlineStart',\n 'scrollMarginLeft',\n 'scrollMarginRight',\n 'scrollMarginTop',\n 'scrollMarkerGroup',\n 'scrollPadding',\n 'scrollPaddingBlock',\n 'scrollPaddingBlockEnd',\n 'scrollPaddingBlockStart',\n 'scrollPaddingBottom',\n 'scrollPaddingInline',\n 'scrollPaddingInlineEnd',\n 'scrollPaddingInlineStart',\n 'scrollPaddingLeft',\n 'scrollPaddingRight',\n 'scrollPaddingTop',\n 'scrollSnapAlign',\n 'scrollSnapCoordinate',\n 'scrollSnapDestination',\n 'scrollSnapMargin',\n 'scrollSnapMarginBottom',\n 'scrollSnapMarginLeft',\n 'scrollSnapMarginRight',\n 'scrollSnapMarginTop',\n 'scrollSnapPointsX',\n 'scrollSnapPointsY',\n 'scrollSnapStop',\n 'scrollSnapType',\n 'scrollSnapTypeX',\n 'scrollSnapTypeY',\n 'scrollStartTarget',\n 'scrollTargetGroup',\n 'scrollTimeline',\n 'scrollTimelineAxis',\n 'scrollTimelineName',\n 'scrollbar3dLightColor',\n 'scrollbar3dlightColor',\n 'scrollbarArrowColor',\n 'scrollbarBaseColor',\n 'scrollbarColor',\n 'scrollbarDarkShadowColor',\n 'scrollbarDarkshadowColor',\n 'scrollbarFaceColor',\n 'scrollbarGutter',\n 'scrollbarHighlightColor',\n 'scrollbarShadowColor',\n 'scrollbarTrackColor',\n 'scrollbarWidth',\n 'shapeImageThreshold',\n 'shapeInside',\n 'shapeMargin',\n 'shapeOutside',\n 'shapeRendering',\n 'size',\n 'sizeAdjust',\n 'sliderOrientation',\n 'snapHeight',\n 'solidColor',\n 'solidOpacity',\n 'spatialNavigationAction',\n 'spatialNavigationContain',\n 'spatialNavigationFunction',\n 'speak',\n 'speakAs',\n 'speakHeader',\n 'speakNumeral',\n 'speakPunctuation',\n 'speechRate',\n 'src',\n 'stopColor',\n 'stopOpacity',\n 'stress',\n 'stringSet',\n 'stroke',\n 'strokeAlign',\n 'strokeAlignment',\n 'strokeBreak',\n 'strokeColor',\n 'strokeDashCorner',\n 'strokeDashJustify',\n 'strokeDashadjust',\n 'strokeDasharray',\n 'strokeDashcorner',\n 'strokeDashoffset',\n 'strokeImage',\n 'strokeLinecap',\n 'strokeLinejoin',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeOrigin',\n 'strokePosition',\n 'strokeRepeat',\n 'strokeSize',\n 'strokeWidth',\n 'suffix',\n 'supportedColorSchemes',\n 'symbols',\n 'syntax',\n 'system',\n 'tabSize',\n 'tableLayout',\n 'textAlign',\n 'textAlignAll',\n 'textAlignLast',\n 'textAnchor',\n 'textAutospace',\n 'textBox',\n 'textBoxEdge',\n 'textBoxTrim',\n 'textCombineUpright',\n 'textDecoration',\n 'textDecorationBlink',\n 'textDecorationColor',\n 'textDecorationLine',\n 'textDecorationLineThrough',\n 'textDecorationNone',\n 'textDecorationOverline',\n 'textDecorationSkip',\n 'textDecorationSkipBox',\n 'textDecorationSkipInk',\n 'textDecorationSkipInset',\n 'textDecorationSkipSelf',\n 'textDecorationSkipSpaces',\n 'textDecorationStyle',\n 'textDecorationThickness',\n 'textDecorationTrim',\n 'textDecorationUnderline',\n 'textEmphasis',\n 'textEmphasisColor',\n 'textEmphasisPosition',\n 'textEmphasisSkip',\n 'textEmphasisStyle',\n 'textGroupAlign',\n 'textIndent',\n 'textJustify',\n 'textJustifyTrim',\n 'textKashida',\n 'textKashidaSpace',\n 'textLineThrough',\n 'textLineThroughColor',\n 'textLineThroughMode',\n 'textLineThroughStyle',\n 'textLineThroughWidth',\n 'textOrientation',\n 'textOverflow',\n 'textOverline',\n 'textOverlineColor',\n 'textOverlineMode',\n 'textOverlineStyle',\n 'textOverlineWidth',\n 'textRendering',\n 'textShadow',\n 'textSizeAdjust',\n 'textSpacing',\n 'textSpacingTrim',\n 'textTransform',\n 'textUnderline',\n 'textUnderlineColor',\n 'textUnderlineMode',\n 'textUnderlineOffset',\n 'textUnderlinePosition',\n 'textUnderlineStyle',\n 'textUnderlineWidth',\n 'textWrap',\n 'textWrapMode',\n 'textWrapStyle',\n 'timelineScope',\n 'top',\n 'touchAction',\n 'touchActionDelay',\n 'transform',\n 'transformBox',\n 'transformOrigin',\n 'transformOriginX',\n 'transformOriginY',\n 'transformOriginZ',\n 'transformStyle',\n 'transition',\n 'transitionBehavior',\n 'transitionDelay',\n 'transitionDuration',\n 'transitionProperty',\n 'transitionTimingFunction',\n 'translate',\n 'types',\n 'ucAltSkin',\n 'ucSkin',\n 'unicodeBidi',\n 'unicodeRange',\n 'userSelect',\n 'userZoom',\n 'vectorEffect',\n 'verticalAlign',\n 'viewTimeline',\n 'viewTimelineAxis',\n 'viewTimelineInset',\n 'viewTimelineName',\n 'viewTransitionClass',\n 'viewTransitionGroup',\n 'viewTransitionName',\n 'viewportFill',\n 'viewportFillOpacity',\n 'viewportFit',\n 'visibility',\n 'voiceBalance',\n 'voiceDuration',\n 'voiceFamily',\n 'voicePitch',\n 'voiceRange',\n 'voiceRate',\n 'voiceStress',\n 'voiceVolume',\n 'volume',\n 'whiteSpace',\n 'whiteSpaceCollapse',\n 'whiteSpaceTrim',\n 'widows',\n 'width',\n 'willChange',\n 'wordBreak',\n 'wordSpaceTransform',\n 'wordSpacing',\n 'wordWrap',\n 'wrapAfter',\n 'wrapBefore',\n 'wrapFlow',\n 'wrapInside',\n 'wrapThrough',\n 'writingMode',\n 'x',\n 'y',\n 'zIndex',\n 'zoom',\n];\n\n/**\n * Properties that ship in browsers or sit in a spec, but have not landed in\n * `known-css-properties` yet. Hand-maintained, and pruned as the dataset catches\n * up — the guard test fails on any entry the dataset already covers.\n */\nexport const EMERGING_CSS_PROPERTIES = [\n // CSS Borders and Box Decorations 4 — squircles and notched corners, shipped in\n // Chromium 139.\n 'cornerShape',\n 'cornerTopShape',\n 'cornerRightShape',\n 'cornerBottomShape',\n 'cornerLeftShape',\n 'cornerBlockStartShape',\n 'cornerBlockEndShape',\n 'cornerInlineStartShape',\n 'cornerInlineEndShape',\n 'cornerTopLeftShape',\n 'cornerTopRightShape',\n 'cornerBottomRightShape',\n 'cornerBottomLeftShape',\n 'cornerStartStartShape',\n 'cornerStartEndShape',\n 'cornerEndStartShape',\n 'cornerEndEndShape',\n];\n\n/**\n * Every CSS property `known-property` accepts as a camelCase style key.\n */\nexport const KNOWN_CSS_PROPERTIES = new Set([\n ...DATASET_CSS_PROPERTIES,\n ...EMERGING_CSS_PROPERTIES,\n]);\n\n/**\n * Built-in custom units recognized by the tasty parser.\n */\nexport const BUILT_IN_UNITS = new Set(['x', 'r', 'cr', 'bw', 'ow', 'lh', 'sf']);\n\n/**\n * Custom properties Tasty registers as default `@property` rules, so they are\n * always defined whatever the config's `tokens` list says. The scale tokens\n * match their built-in units; style handlers read the rest as bare `var()`s\n * (`radius: 'leaf'`, `preset: 'h1 / strong'`, the font fallbacks, timings).\n */\nexport const BUILT_IN_CUSTOM_PROPERTIES = new Set([\n '$gap',\n '$radius',\n '$border-width',\n '$outline-width',\n '$transition',\n '$sharp-radius',\n '$bold-font-weight',\n '$font-sans-fallback',\n '$font-mono-fallback',\n]);\n\n/**\n * Standard CSS units (always valid).\n */\nexport const CSS_UNITS = new Set([\n 'px',\n 'em',\n 'rem',\n '%',\n 'vw',\n 'vh',\n 'vmin',\n 'vmax',\n 'ch',\n 'ex',\n 'cm',\n 'mm',\n 'in',\n 'pt',\n 'pc',\n 'fr',\n 'deg',\n 'rad',\n 'turn',\n 'grad',\n 's',\n 'ms',\n 'dpi',\n 'dpcm',\n 'dppx',\n 'svw',\n 'svh',\n 'lvw',\n 'lvh',\n 'dvw',\n 'dvh',\n 'cqw',\n 'cqh',\n 'cqi',\n 'cqb',\n 'cqmin',\n 'cqmax',\n 'cap',\n 'ic',\n 'rlh',\n 'vi',\n 'vb',\n]);\n\n/**\n * Properties that accept `true` as a value (means \"use default\").\n *\n * `fill` is documented as accepting `true` but is deliberately absent: as of\n * tasty 3.8 the handler passes the boolean straight through, so `fill: true`\n * emits `background-color: true` — a declaration the browser drops, leaving no\n * background at all. Reporting it is the useful answer while that is what runs.\n * `constants.round-trip.test.ts` pins both halves of that: no property in this\n * set may swallow `true`, and `fill` still does — so whichever way tasty\n * resolves it (docs or implementation), a test says so rather than the plugin\n * quietly disagreeing with the runtime.\n */\nexport const BOOLEAN_TRUE_PROPERTIES = new Set([\n 'border',\n 'radius',\n 'padding',\n 'margin',\n 'gap',\n 'color',\n 'shadow',\n 'outline',\n 'inset',\n 'width',\n 'height',\n // `fade: true` falls back to `2x`, which follows `configure({ units })`.\n 'fade',\n 'hide',\n 'preset',\n 'font',\n 'scrollbar',\n 'scrollMargin',\n 'scrollPadding',\n // Every enhanced logical *category* carries the same design-system default as\n // its physical counterpart: `1x` for spacing and scroll edges, `0` for inset,\n // `1bw` for border, a reset for the sizes.\n //\n // The min/max size constraints are deliberately excluded, mirroring the\n // absence of `minWidth`/`maxWidth` here. The runtime does accept `true` on\n // them — they route through the same dimension handler as the axis — but what\n // it does with it is reset the whole axis: `minBlockSize: true` emits\n // `block-size: auto; min-block-size: initial; max-block-size: initial`, not a\n // default for the one constraint named. `blockSize: true` is how you ask for\n // that, so the constraint spellings stay an error, exactly as `minWidth: true`\n // already is.\n ...LOGICAL_SIZE_STYLES,\n ...LOGICAL_SPACING_STYLES,\n ...LOGICAL_BORDER_STYLES,\n]);\n\n/**\n * Directional modifiers and which properties accept them.\n */\nexport const DIRECTIONAL_MODIFIERS: Record<string, Set<string>> = {\n border: new Set(['top', 'right', 'bottom', 'left']),\n radius: new Set([\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n ]),\n padding: new Set(['top', 'right', 'bottom', 'left']),\n margin: new Set(['top', 'right', 'bottom', 'left']),\n fade: new Set(['top', 'right', 'bottom', 'left']),\n scrollMargin: new Set(['top', 'right', 'bottom', 'left']),\n scrollPadding: new Set(['top', 'right', 'bottom', 'left']),\n inset: new Set(['top', 'right', 'bottom', 'left', 'dock']),\n // Enhanced logical styles take `start`/`end` and nothing else. Listing them\n // here is what makes a physical modifier on a logical axis — `blockPadding:\n // '1x top'`, which the handler silently drops — a report rather than silence.\n ...Object.fromEntries(\n [...LOGICAL_SPACING_STYLES, ...LOGICAL_BORDER_STYLES].map((style) => [\n style,\n new Set<string>(LOGICAL_EDGES),\n ]),\n ),\n};\n\n/**\n * Properties where a group naming directions carries exactly **one** value.\n *\n * Tasty v3 dropped the positional multi-value form for these: values and\n * modifiers are bucketed separately per comma group, so `'2x 4x top right'` and\n * `'2x top 4x right'` were the same input and the pairing depended on modifier\n * order. Per-side values now come from comma groups.\n *\n * Deliberately excludes `border` and `radius`. A `border` group legitimately\n * carries width + style + color, and a `radius` group can carry two values with\n * `leaf`/`backleaf`; neither takes the positional-per-side form, so neither is\n * subject to the rule.\n */\nexport const SINGLE_VALUE_DIRECTIONAL_PROPERTIES = new Set<string>([\n 'padding',\n 'margin',\n 'inset',\n 'scrollMargin',\n 'scrollPadding',\n 'fade',\n // The logical axis handlers bucket values and modifiers the same way, and warn\n // on a second value in a group that names an edge. `blockBorder`/`inlineBorder`\n // are excluded for the same reason `border` is: width + style + colour.\n ...LOGICAL_SPACING_STYLES,\n]);\n\n/**\n * Position names a 4-value box maps onto, in source order.\n */\nexport const BOX_SIDES = ['top', 'right', 'bottom', 'left'] as const;\nexport const BOX_CORNERS = [\n 'top-left',\n 'top-right',\n 'bottom-right',\n 'bottom-left',\n] as const;\n\nexport interface DirectionalBoxConfig {\n /**\n * The token that marks an unset position — dropping it from the shorthand\n * leaves the emitted CSS unchanged. This is the CSS initial value of the\n * longhand, which is not always `0`: `inset` initialises to `auto`, so a `0`\n * there is a real offset the shorthand must not drop.\n */\n identity: string;\n /** Modifier names for each of the four positions, in source order. */\n positions: readonly string[];\n /**\n * Whether `eslint --fix` may apply the rewrite. False when the rewrite is\n * still an improvement but changes what the browser renders, so a human has\n * to opt in.\n */\n fixable: boolean;\n /**\n * Set when the 4-value form is not interpreted per-position at all, so the\n * current output is wrong rather than merely verbose. Surfaced in the report.\n */\n note?: string;\n}\n\n/**\n * Properties whose 4-value box syntax has a directional-shorthand equivalent.\n *\n * `radius` uses corner names rather than sides: a directional modifier\n * addresses the corner *pair* along an edge, so only `top-left` & co. can\n * express a single corner (requires `@tenphi/tasty` >= 2.10).\n */\nexport const DIRECTIONAL_BOX: Record<string, DirectionalBoxConfig> = {\n margin: { identity: '0', positions: BOX_SIDES, fixable: true },\n padding: { identity: '0', positions: BOX_SIDES, fixable: true },\n inset: { identity: 'auto', positions: BOX_SIDES, fixable: true },\n radius: { identity: '0', positions: BOX_CORNERS, fixable: true },\n fade: { identity: '0', positions: BOX_SIDES, fixable: true },\n // Four `border` tokens parse as a single border value (width/style/color),\n // not as a box: `border: '0 0 1bw 0'` emits `border: 0 solid …` — no border\n // on any side. The directional form is what the author meant, but applying\n // it changes rendering, so it stays a manual fix.\n border: {\n identity: '0',\n positions: BOX_SIDES,\n fixable: false,\n note: 'four border tokens parse as one border value, so the current style sets no border at all',\n },\n};\n\n/**\n * Properties that support a `dock` modifier — pin one edge and span its full\n * length. Lets a 4-value box with a single `identity` side collapse, e.g.\n * `inset: 'auto 0 0 0'` -> `inset: '0 bottom dock'`.\n * Requires `@tenphi/tasty` >= 2.10.\n */\nexport const DOCK_PROPERTIES = new Set(['inset']);\n\n/**\n * Valid radius shape keywords.\n */\nexport const RADIUS_SHAPES = new Set(['round', 'ellipse', 'leaf', 'backleaf']);\n\n/**\n * Known semantic transition names — the keys of Tasty's transition map, plus\n * `zIndex`, which Tasty compiles to `z-index`. Any other name is emitted\n * verbatim as a CSS property.\n */\nexport const SEMANTIC_TRANSITIONS = new Set([\n 'fade',\n 'fill',\n 'border',\n 'radius',\n 'shadow',\n 'preset',\n 'gap',\n 'theme',\n 'color',\n 'outline',\n 'inset',\n 'text',\n 'opacity',\n 'translate',\n 'rotate',\n 'scale',\n 'filter',\n 'image',\n 'background',\n 'width',\n 'height',\n 'zIndex',\n]);\n\n/**\n * Mapping of native CSS properties to tasty shorthand alternatives.\n */\nexport const SHORTHAND_MAPPING: Record<\n string,\n { property: string; hint: string; safeFix?: boolean }\n> = {\n backgroundColor: { property: 'fill', hint: \"fill: '...'\", safeFix: true },\n borderColor: { property: 'border', hint: \"border: '...'\" },\n borderWidth: { property: 'border', hint: \"border: '...'\" },\n borderStyle: { property: 'border', hint: \"border: '...'\" },\n borderTop: { property: 'border', hint: \"border: '... top'\" },\n borderRight: { property: 'border', hint: \"border: '... right'\" },\n borderBottom: { property: 'border', hint: \"border: '... bottom'\" },\n borderLeft: { property: 'border', hint: \"border: '... left'\" },\n borderRadius: { property: 'radius', hint: \"radius: '...'\", safeFix: true },\n maxWidth: { property: 'width', hint: \"width: 'max ...'\" },\n minWidth: { property: 'width', hint: \"width: 'min ...'\" },\n maxHeight: { property: 'height', hint: \"height: 'max ...'\" },\n minHeight: { property: 'height', hint: \"height: 'min ...'\" },\n flexDirection: { property: 'flow', hint: \"flow: '...'\" },\n flexWrap: { property: 'flow', hint: \"flow: '...'\" },\n flexFlow: { property: 'flow', hint: \"flow: '...'\" },\n gridAutoFlow: { property: 'flow', hint: \"flow: '...'\" },\n outlineOffset: { property: 'outline', hint: \"outline: '... / offset'\" },\n paddingTop: { property: 'padding', hint: \"padding: '... top'\" },\n paddingRight: { property: 'padding', hint: \"padding: '... right'\" },\n paddingBottom: { property: 'padding', hint: \"padding: '... bottom'\" },\n paddingLeft: { property: 'padding', hint: \"padding: '... left'\" },\n marginTop: { property: 'margin', hint: \"margin: '... top'\" },\n marginRight: { property: 'margin', hint: \"margin: '... right'\" },\n marginBottom: { property: 'margin', hint: \"margin: '... bottom'\" },\n marginLeft: { property: 'margin', hint: \"margin: '... left'\" },\n fontSize: { property: 'preset', hint: \"preset: '...'\" },\n fontWeight: {\n property: 'preset',\n hint: \"preset: 'name / strong' (with strong modifier)\",\n },\n lineHeight: {\n property: 'preset',\n hint: \"preset: 'name / tight' (with tight modifier)\",\n },\n boxShadow: { property: 'shadow', hint: \"shadow: '...'\" },\n backgroundImage: { property: 'image', hint: \"image: '...'\" },\n fontFamily: { property: 'font', hint: \"font: '...'\" },\n fontStyle: {\n property: 'preset',\n hint: \"preset: 'name / italic' (with italic modifier)\",\n },\n letterSpacing: { property: 'preset', hint: \"preset: '...'\" },\n textTransform: { property: 'preset', hint: \"preset: '...'\" },\n flexGap: { property: 'gap', hint: \"gap: '...'\" },\n scrollbarWidth: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n scrollbarColor: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n scrollbarGutter: { property: 'scrollbar', hint: \"scrollbar: '...'\" },\n gridTemplateColumns: {\n property: 'gridColumns',\n hint: \"gridColumns: '...'\",\n },\n gridTemplateRows: { property: 'gridRows', hint: \"gridRows: '...'\" },\n gridTemplateAreas: { property: 'gridAreas', hint: \"gridAreas: '...'\" },\n top: { property: 'inset', hint: \"inset: '... top'\" },\n right: { property: 'inset', hint: \"inset: '... right'\" },\n bottom: { property: 'inset', hint: \"inset: '... bottom'\" },\n left: { property: 'inset', hint: \"inset: '... left'\" },\n outlineColor: { property: 'outline', hint: \"outline: '...'\" },\n outlineWidth: { property: 'outline', hint: \"outline: '...'\" },\n outlineStyle: { property: 'outline', hint: \"outline: '...'\" },\n lineClamp: {\n property: 'textOverflow',\n hint: \"textOverflow: 'ellipsis / {lines}'\",\n },\n\n // --- Native logical CSS -> the enhanced Tasty logical style. ---\n //\n // These mirror the physical rows above one for one. Tasty v3.8 moved the\n // enhanced behaviour onto `<axis><Category>` names and stopped reading the\n // native spellings in its handlers, so `paddingBlock` is now a plain CSS\n // declaration with no category default: the rename is the same advice as\n // `paddingTop` -> `padding: '... top'`, not a new opinion.\n //\n // Only the axis shorthands carry `safeFix`. `paddingBlock: '1x 2x'` and\n // `blockPadding: '1x 2x'` both emit `padding-block: 1x 2x`, so the key rename\n // is the whole edit — `constants.round-trip.test.ts` asserts that against the\n // installed runtime rather than trusting this comment. An edge longhand needs\n // a `start`/`end` modifier added to the value, and a `border-*` rename also\n // gains the style/colour defaults `border-block` never had (`border-block:\n // 1bw` renders nothing at all), so those stay report-only.\n //\n // These renames are why the `@tenphi/tasty` peer floor is 3.8.0. The plugin\n // never imports tasty, so its constants are fixed at publish time and cannot\n // adapt to the consumer's version: on 3.7 and below `blockPadding` is not a\n // style tasty knows, and an unhandled camelCase key renders straight through\n // to `block-padding`, which the browser drops. The fix would silently delete\n // the padding, so the floor is the only thing standing between that and a\n // consumer — recognising the logical names is harmless on an older tasty\n // (a missed warning), but rewriting *to* them is not.\n paddingBlock: {\n property: 'blockPadding',\n hint: \"blockPadding: '...'\",\n safeFix: true,\n },\n paddingBlockStart: {\n property: 'blockPadding',\n hint: \"blockPadding: '... start'\",\n },\n paddingBlockEnd: {\n property: 'blockPadding',\n hint: \"blockPadding: '... end'\",\n },\n paddingInline: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '...'\",\n safeFix: true,\n },\n paddingInlineStart: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '... start'\",\n },\n paddingInlineEnd: {\n property: 'inlinePadding',\n hint: \"inlinePadding: '... end'\",\n },\n marginBlock: {\n property: 'blockMargin',\n hint: \"blockMargin: '...'\",\n safeFix: true,\n },\n marginBlockStart: {\n property: 'blockMargin',\n hint: \"blockMargin: '... start'\",\n },\n marginBlockEnd: { property: 'blockMargin', hint: \"blockMargin: '... end'\" },\n marginInline: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '...'\",\n safeFix: true,\n },\n marginInlineStart: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '... start'\",\n },\n marginInlineEnd: {\n property: 'inlineMargin',\n hint: \"inlineMargin: '... end'\",\n },\n insetBlock: {\n property: 'blockInset',\n hint: \"blockInset: '...'\",\n safeFix: true,\n },\n insetBlockStart: { property: 'blockInset', hint: \"blockInset: '... start'\" },\n insetBlockEnd: { property: 'blockInset', hint: \"blockInset: '... end'\" },\n insetInline: {\n property: 'inlineInset',\n hint: \"inlineInset: '...'\",\n safeFix: true,\n },\n insetInlineStart: {\n property: 'inlineInset',\n hint: \"inlineInset: '... start'\",\n },\n insetInlineEnd: { property: 'inlineInset', hint: \"inlineInset: '... end'\" },\n scrollMarginBlock: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '...'\",\n safeFix: true,\n },\n scrollMarginBlockStart: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '... start'\",\n },\n scrollMarginBlockEnd: {\n property: 'blockScrollMargin',\n hint: \"blockScrollMargin: '... end'\",\n },\n scrollMarginInline: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '...'\",\n safeFix: true,\n },\n scrollMarginInlineStart: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '... start'\",\n },\n scrollMarginInlineEnd: {\n property: 'inlineScrollMargin',\n hint: \"inlineScrollMargin: '... end'\",\n },\n scrollPaddingBlock: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '...'\",\n safeFix: true,\n },\n scrollPaddingBlockStart: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '... start'\",\n },\n scrollPaddingBlockEnd: {\n property: 'blockScrollPadding',\n hint: \"blockScrollPadding: '... end'\",\n },\n scrollPaddingInline: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '...'\",\n safeFix: true,\n },\n scrollPaddingInlineStart: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '... start'\",\n },\n scrollPaddingInlineEnd: {\n property: 'inlineScrollPadding',\n hint: \"inlineScrollPadding: '... end'\",\n },\n borderBlock: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockColor: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockWidth: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockStyle: { property: 'blockBorder', hint: \"blockBorder: '...'\" },\n borderBlockStart: {\n property: 'blockBorder',\n hint: \"blockBorder: '... start'\",\n },\n borderBlockEnd: { property: 'blockBorder', hint: \"blockBorder: '... end'\" },\n borderInline: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineColor: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineWidth: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineStyle: { property: 'inlineBorder', hint: \"inlineBorder: '...'\" },\n borderInlineStart: {\n property: 'inlineBorder',\n hint: \"inlineBorder: '... start'\",\n },\n borderInlineEnd: {\n property: 'inlineBorder',\n hint: \"inlineBorder: '... end'\",\n },\n minBlockSize: { property: 'blockSize', hint: \"blockSize: 'min ...'\" },\n maxBlockSize: { property: 'blockSize', hint: \"blockSize: 'max ...'\" },\n minInlineSize: { property: 'inlineSize', hint: \"inlineSize: 'min ...'\" },\n maxInlineSize: { property: 'inlineSize', hint: \"inlineSize: 'max ...'\" },\n};\n\n/**\n * CSS shorthands whose *longhands* are the preferred Tasty form — the reverse\n * of `SHORTHAND_MAPPING`.\n *\n * `flex` is lossy in a way the longhands are not: it resets the two components\n * you did not write to values that are not their CSS initial. `flex: '0'`\n * expands to `flex-grow: 0; flex-shrink: 1; flex-basis: 0%`, so it cannot\n * express \"do not shrink\" (`flexShrink: 0`) at all, and it silently changes\n * `flex-basis`. Writing the longhands keeps each component independent, which\n * also lets them carry separate state maps.\n */\nexport const LONGHAND_MAPPING: Record<\n string,\n { longhands: string[]; hint: string }\n> = {\n flex: {\n longhands: ['flexGrow', 'flexShrink', 'flexBasis'],\n hint: 'flexGrow / flexShrink / flexBasis',\n },\n};\n\n/**\n * CSS transition property names mapped to Tasty semantic transition names.\n */\nexport const TRANSITION_SEMANTIC_MAPPING: Record<string, string> = {\n backgroundColor: 'fill',\n 'background-color': 'fill',\n backgroundImage: 'image',\n 'background-image': 'image',\n borderRadius: 'radius',\n 'border-radius': 'radius',\n borderColor: 'border',\n 'border-color': 'border',\n borderWidth: 'border',\n 'border-width': 'border',\n borderStyle: 'border',\n 'border-style': 'border',\n boxShadow: 'shadow',\n 'box-shadow': 'shadow',\n outlineColor: 'outline',\n 'outline-color': 'outline',\n outlineWidth: 'outline',\n 'outline-width': 'outline',\n outlineOffset: 'outline',\n 'outline-offset': 'outline',\n fontSize: 'preset',\n 'font-size': 'preset',\n lineHeight: 'preset',\n 'line-height': 'preset',\n letterSpacing: 'preset',\n 'letter-spacing': 'preset',\n};\n\n/**\n * Known preset modifiers, mirroring Tasty's preset handler. `normal` sets\n * `line-height: normal`.\n */\nexport const PRESET_MODIFIERS = new Set([\n 'strong',\n 'bold',\n 'italic',\n 'icon',\n 'tight',\n 'normal',\n]);\n\n/**\n * Default import sources for tasty.\n */\nexport const DEFAULT_IMPORT_SOURCES = ['@tenphi/tasty', '@tenphi/tasty/static'];\n\n/**\n * Built-in state prefixes that are always valid (not aliases).\n */\nexport const BUILT_IN_STATE_PREFIXES = new Set([\n '@media',\n '@root',\n '@parent',\n '@own',\n '@supports',\n '@starting',\n '@keyframes',\n '@property',\n '@font-face',\n '@counter-style',\n '@function',\n]);\n\n/**\n * Known CSS pseudo-classes.\n */\nexport const KNOWN_PSEUDO_CLASSES = new Set([\n ':hover',\n ':focus',\n ':focus-visible',\n ':focus-within',\n ':active',\n ':visited',\n ':link',\n ':checked',\n ':disabled',\n ':enabled',\n ':empty',\n ':first-child',\n ':last-child',\n ':first-of-type',\n ':last-of-type',\n ':only-child',\n ':only-of-type',\n ':root',\n ':target',\n ':valid',\n ':invalid',\n ':required',\n ':optional',\n ':read-only',\n ':read-write',\n ':placeholder-shown',\n ':autofill',\n ':default',\n ':indeterminate',\n ':in-range',\n ':out-of-range',\n ':any-link',\n ':local-link',\n ':is',\n ':not',\n ':where',\n ':has',\n ':nth-child',\n ':nth-last-child',\n ':nth-of-type',\n ':nth-last-of-type',\n '::before',\n '::after',\n '::placeholder',\n '::selection',\n '::first-line',\n '::first-letter',\n '::marker',\n '::backdrop',\n]);\n\n/**\n * Color tokens Tasty provides without any project configuration, so they are\n * always defined whatever the config's `tokens` list says.\n *\n * `#current` compiles to the `currentcolor` keyword, `#clear` is a default\n * replace token for `transparent`, and the rest are registered as default\n * `@property` rules (`#white`, `#black`, and `#border`/`#outline`, which fall\n * back to `currentcolor`). `prefer-custom-property-syntax` rewrites\n * `transparent` / `currentColor` to `#clear` / `#current`, so both must stay here.\n */\nexport const BUILT_IN_COLOR_TOKENS = new Set([\n '#current',\n '#clear',\n '#white',\n '#black',\n '#border',\n '#outline',\n]);\n\n/**\n * CSS named colors (level 1–4) that should be replaced by `#color` tokens.\n * `transparent` and `currentcolor` are excluded — they have their own token\n * suggestions (`#clear`, `#current`) handled by `prefer-custom-property-syntax`.\n */\nexport const NAMED_CSS_COLORS = new Set<string>([\n 'aliceblue',\n 'antiquewhite',\n 'aqua',\n 'aquamarine',\n 'azure',\n 'beige',\n 'bisque',\n 'black',\n 'blanchedalmond',\n 'blue',\n 'blueviolet',\n 'brown',\n 'burlywood',\n 'cadetblue',\n 'chartreuse',\n 'chocolate',\n 'coral',\n 'cornflowerblue',\n 'cornsilk',\n 'crimson',\n 'cyan',\n 'darkblue',\n 'darkcyan',\n 'darkgoldenrod',\n 'darkgray',\n 'darkgreen',\n 'darkgrey',\n 'darkkhaki',\n 'darkmagenta',\n 'darkolivegreen',\n 'darkorange',\n 'darkorchid',\n 'darkred',\n 'darksalmon',\n 'darkseagreen',\n 'darkslateblue',\n 'darkslategray',\n 'darkslategrey',\n 'darkturquoise',\n 'darkviolet',\n 'deeppink',\n 'deepskyblue',\n 'dimgray',\n 'dimgrey',\n 'dodgerblue',\n 'firebrick',\n 'floralwhite',\n 'forestgreen',\n 'fuchsia',\n 'gainsboro',\n 'ghostwhite',\n 'gold',\n 'goldenrod',\n 'gray',\n 'green',\n 'greenyellow',\n 'grey',\n 'honeydew',\n 'hotpink',\n 'indianred',\n 'indigo',\n 'ivory',\n 'khaki',\n 'lavender',\n 'lavenderblush',\n 'lawngreen',\n 'lemonchiffon',\n 'lightblue',\n 'lightcoral',\n 'lightcyan',\n 'lightgoldenrodyellow',\n 'lightgray',\n 'lightgreen',\n 'lightgrey',\n 'lightpink',\n 'lightsalmon',\n 'lightseagreen',\n 'lightskyblue',\n 'lightslategray',\n 'lightslategrey',\n 'lightsteelblue',\n 'lightyellow',\n 'lime',\n 'limegreen',\n 'linen',\n 'magenta',\n 'maroon',\n 'mediumaquamarine',\n 'mediumblue',\n 'mediumorchid',\n 'mediumpurple',\n 'mediumseagreen',\n 'mediumslateblue',\n 'mediumspringgreen',\n 'mediumturquoise',\n 'mediumvioletred',\n 'midnightblue',\n 'mintcream',\n 'mistyrose',\n 'moccasin',\n 'navajowhite',\n 'navy',\n 'oldlace',\n 'olive',\n 'olivedrab',\n 'orange',\n 'orangered',\n 'orchid',\n 'palegoldenrod',\n 'palegreen',\n 'paleturquoise',\n 'palevioletred',\n 'papayawhip',\n 'peachpuff',\n 'peru',\n 'pink',\n 'plum',\n 'powderblue',\n 'purple',\n 'rebeccapurple',\n 'red',\n 'rosybrown',\n 'royalblue',\n 'saddlebrown',\n 'salmon',\n 'sandybrown',\n 'seagreen',\n 'seashell',\n 'sienna',\n 'silver',\n 'skyblue',\n 'slateblue',\n 'slategray',\n 'slategrey',\n 'snow',\n 'springgreen',\n 'steelblue',\n 'tan',\n 'teal',\n 'thistle',\n 'tomato',\n 'turquoise',\n 'violet',\n 'wheat',\n 'white',\n 'whitesmoke',\n 'yellow',\n 'yellowgreen',\n]);\n\n/**\n * Properties that accept color values. Used to scope named-color checks so\n * string properties like `content` or `cursor` don't false-positive.\n */\nexport const COLOR_BEARING_PROPERTIES = new Set<string>([\n // Tasty enhanced properties\n 'fill',\n 'color',\n 'caretColor',\n 'accentColor',\n 'shadow',\n 'border',\n 'blockBorder',\n 'inlineBorder',\n 'outline',\n 'fade',\n // CSS background\n 'background',\n 'backgroundColor',\n // CSS border colors\n 'borderColor',\n 'borderTopColor',\n 'borderRightColor',\n 'borderBottomColor',\n 'borderLeftColor',\n // CSS logical border colors — the `block`/`inline` counterparts of the four\n // above, which a writing-mode-aware codebase reaches for just as often.\n 'borderBlockColor',\n 'borderBlockStartColor',\n 'borderBlockEndColor',\n 'borderInlineColor',\n 'borderInlineStartColor',\n 'borderInlineEndColor',\n // CSS shadows\n 'boxShadow',\n 'textShadow',\n // CSS text decoration\n 'textDecorationColor',\n // CSS outline / misc\n 'outlineColor',\n 'scrollbarColor',\n 'columnRuleColor',\n // SVG\n 'stroke',\n 'stopColor',\n 'floodColor',\n 'lightingColor',\n]);\n\n/**\n * Properties where tasty does NOT expand a `$name` custom-property reference.\n *\n * **Empty since tasty 3.0.2**, which taught the pass-through style handlers to\n * substitute `$name` refs too (tasty#264). Before that, `fontFamily:\n * '$font-sans'` emitted a literal `font-family: $font-sans` — an invalid\n * declaration the browser drops — so `prefer-custom-property-syntax` had to\n * suppress the rewrite for two dozen properties. It no longer does, which means\n * those rewrites are now offered where they were previously (and, since 3.0.2,\n * needlessly) held back.\n *\n * The set is kept rather than deleted: it is the seam the round-trip guard\n * writes to, so a future handler that regains pass-through behaviour surfaces\n * as a failing test instead of a silently broken autofix.\n *\n * An empty list here would let `--fix` delete a declaration on 3.0.0/3.0.1,\n * which the `@tenphi/tasty` peer floor rules out — that floor is 3.8.0, set by\n * the logical renames in {@link SHORTHAND_MAPPING} rather than by this list.\n *\n * Note that {@link PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION} is *not* empty:\n * `#token` expansion is still property-scoped (`fontSize: '#accent'` is\n * meaningless and passes through). The two sets are genuinely different.\n *\n * Derived from the runtime, not by hand — `constants.round-trip.test.ts` rebuilds\n * both sets against the installed `@tenphi/tasty` and fails if either drifts.\n */\nexport const PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION = new Set<string>([]);\n\n/**\n * Properties where tasty does NOT expand a `#name` colour token.\n *\n * Mostly dimension and keyword properties: `fontSize: '#accent'` is meaningless,\n * so the handler leaves the value alone and the declaration is dropped. Suggesting\n * `#x` for a `var(--x-color)` inside one of these would break it.\n *\n * See {@link PROPERTIES_WITHOUT_CUSTOM_PROPERTY_EXPANSION} for why this is a\n * separate list rather than the same one.\n */\nexport const PROPERTIES_WITHOUT_COLOR_TOKEN_EXPANSION = new Set([\n 'align',\n 'alignContent',\n 'alignItems',\n 'backgroundAttachment',\n 'backgroundClip',\n 'backgroundOrigin',\n 'backgroundRepeat',\n 'display',\n 'font',\n 'fontFamily',\n 'fontSize',\n 'fontWeight',\n 'justify',\n 'justifyContent',\n 'justifyItems',\n 'letterSpacing',\n 'lineHeight',\n 'outlineOffset',\n 'overflow',\n 'place',\n 'placeContent',\n 'placeItems',\n 'textTransform',\n 'whiteSpace',\n]);\n"],"mappings":";;AAuBA,MAAa,gBAAgB,CAAC,SAAS,MAAM;;AAG7C,MAAa,sBAAsB,CAAC,aAAa,aAAa;AAC9D,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,yBAAyB;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,wBAAwB,CAAC,eAAe,eAAe;;AAGpE,MAAa,iBAAiB;CAC5B,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACJ;;;;AAKD,MAAa,yBAAyB,IAAI,IAAI;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAIA;CACA;CACA,GAAG;CACJ,CAAC;;;;;;AAOF,MAAa,qBAAqB,IAAI,IAAI;CACxC;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;AAQF,MAAa,6BAAqD;CAChE,eAAe;CACf,aAAa;CACb,iBAAiB;CAClB;;;;;;;;;;;;;;;;;;;;AAqBD,MAAM,yBAAyB;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;AAOD,MAAa,0BAA0B;CAGrC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,MAAa,uBAAuB,IAAI,IAAI,CAC1C,GAAG,wBACH,GAAG,wBACJ,CAAC;;;;AAKF,MAAa,iBAAiB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAM;CAAM;CAAM;CAAM;CAAK,CAAC;;;;;;;AAQ/E,MAAa,6BAA6B,IAAI,IAAI;CAChD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,YAAY,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;AAcF,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAaA,GAAG;CACH,GAAG;CACH,GAAG;CACJ,CAAC;;;;AAKF,MAAa,wBAAqD;CAChE,QAAQ,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CACnD,QAAQ,IAAI,IAAI;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,SAAS,IAAI,IAAI;EAAC;EAAO;EAAS;EAAU;EAAO,CAAC;CACpD,QAAQ,IAAI,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xE,eAAe;EAAE,UAAU;EAAc,MAAM;EAAyB;CACzE;;;;;;;;;;;;AAaD,MAAa,mBAGT,EACF,MAAM;CACJ,WAAW;EAAC;EAAY;EAAc;EAAY;CAClD,MAAM;CACP,EACF;;;;AAKD,MAAa,8BAAsD;CACjE,iBAAiB;CACjB,oBAAoB;CACpB,iBAAiB;CACjB,oBAAoB;CACpB,cAAc;CACd,iBAAiB;CACjB,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,gBAAgB;CAChB,aAAa;CACb,gBAAgB;CAChB,WAAW;CACX,cAAc;CACd,cAAc;CACd,iBAAiB;CACjB,cAAc;CACd,iBAAiB;CACjB,eAAe;CACf,kBAAkB;CAClB,UAAU;CACV,aAAa;CACb,YAAY;CACZ,eAAe;CACf,eAAe;CACf,kBAAkB;CACnB;;;;;AAMD,MAAa,mBAAmB,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,yBAAyB,CAAC,iBAAiB,uBAAuB;;;;AAK/E,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,uBAAuB,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;AAYF,MAAa,wBAAwB,IAAI,IAAI;CAC3C;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;AAOF,MAAa,mBAAmB,IAAI,IAAY;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAa,2BAA2B,IAAI,IAAY;CAEtD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CAEA;CAEA;CACA;CACA;CAEA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BF,MAAa,+DAA+C,IAAI,IAAY,EAAE,CAAC;;;;;;;;;;;AAY/E,MAAa,2CAA2C,IAAI,IAAI;CAC9D;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC"}
@@ -577,34 +577,9 @@ function classifyColorToken(token, offset, errors) {
577
577
  raw
578
578
  };
579
579
  }
580
- const opacity = Number(opacitySuffix);
581
- if (isNaN(opacity)) {
580
+ if (!/^\d+$/.test(opacitySuffix)) {
582
581
  errors.push({
583
- message: `Invalid opacity value '${opacitySuffix}'.`,
584
- offset,
585
- length: token.length,
586
- raw
587
- });
588
- return {
589
- type: "unknown",
590
- raw
591
- };
592
- }
593
- if (opacity < 0) {
594
- errors.push({
595
- message: "Opacity cannot be negative.",
596
- offset,
597
- length: token.length,
598
- raw
599
- });
600
- return {
601
- type: "unknown",
602
- raw
603
- };
604
- }
605
- if (opacity > 100) {
606
- errors.push({
607
- message: `Opacity '${opacitySuffix}' exceeds 100.`,
582
+ message: /^-\d+$/.test(opacitySuffix) ? "Opacity cannot be negative." : `Invalid opacity value '${opacitySuffix}'.`,
608
583
  offset,
609
584
  length: token.length,
610
585
  raw
@@ -1 +1 @@
1
- {"version":3,"file":"value-parser.js","names":[],"sources":["../../src/parsers/value-parser.ts"],"sourcesContent":["import { scanTokens, checkBracketBalance } from './utils.js';\nimport { BUILT_IN_UNITS, CSS_UNITS } from '../constants.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ValueToken =\n | { type: 'color-token'; name: string; opacity?: string; raw: string }\n | { type: 'color-ref'; name: string; raw: string }\n | { type: 'custom-prop'; name: string; raw: string }\n | { type: 'custom-prop-ref'; name: string; raw: string }\n | { type: 'custom-unit'; value: number; unit: string; raw: string }\n | { type: 'css-unit'; value: number; unit: string; raw: string }\n | { type: 'number'; value: number; raw: string }\n | { type: 'keyword'; value: string }\n | { type: 'css-function'; name: string; args: string; raw: string }\n /**\n * A Tasty CSS `@function` call: `$$name(args)` (emits native `--name(args)`).\n * Distinct from `custom-prop-ref`, which is the bare `$$name` form that\n * `transition` uses to name a custom property.\n */\n | { type: 'tasty-function'; name: string; args: string; raw: string }\n | { type: 'string'; value: string; raw: string }\n | { type: 'important'; raw: string }\n | { type: 'group-expr'; inner: string; raw: string }\n | { type: 'unknown'; raw: string };\n\nexport interface ValueError {\n message: string;\n offset: number;\n length: number;\n raw?: string;\n}\n\nexport interface ValuePart {\n tokens: ValueToken[];\n}\n\nexport interface ValueGroup {\n parts: ValuePart[];\n}\n\nexport interface ValueParseResult {\n groups: ValueGroup[];\n errors: ValueError[];\n}\n\nexport interface ValueParserOptions {\n knownUnits?: Set<string> | string[];\n knownFuncs?: Set<string> | string[];\n skipUnitValidation?: boolean;\n skipFuncValidation?: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst RE_UNIT_NUM = /^[+-]?(?:\\d*\\.\\d+|\\d+)([a-z][a-z0-9]*)$/;\nconst RE_NUMBER = /^[+-]?(?:\\d*\\.\\d+|\\d+)$/;\nconst RE_CSS_UNIT_NUM = /^[+-]?(?:\\d*\\.\\d+|\\d+)([a-z%]+)$/;\n\nconst COLOR_FUNCS = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'device-cmyk',\n 'gray',\n 'color-mix',\n 'color-contrast',\n]);\n\nconst CSS_FUNCS = new Set([\n 'calc',\n 'calc-size',\n 'min',\n 'max',\n 'clamp',\n 'abs',\n 'sign',\n 'round',\n 'mod',\n 'rem',\n 'sin',\n 'cos',\n 'tan',\n 'asin',\n 'acos',\n 'atan',\n 'atan2',\n 'pow',\n 'sqrt',\n 'hypot',\n 'log',\n 'exp',\n 'var',\n 'env',\n 'attr',\n 'counter',\n 'counters',\n 'image-set',\n 'cross-fade',\n 'image',\n 'paint',\n 'element',\n 'linear-gradient',\n 'radial-gradient',\n 'conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'repeating-conic-gradient',\n 'url',\n 'fit-content',\n 'minmax',\n 'repeat',\n 'cubic-bezier',\n 'steps',\n 'linear',\n 'rotate',\n 'scale',\n 'translate',\n 'skew',\n 'matrix',\n 'perspective',\n 'rotate3d',\n 'rotateX',\n 'rotateY',\n 'rotateZ',\n 'scale3d',\n 'scaleX',\n 'scaleY',\n 'scaleZ',\n 'translate3d',\n 'translateX',\n 'translateY',\n 'translateZ',\n 'skewX',\n 'skewY',\n 'matrix3d',\n 'blur',\n 'brightness',\n 'contrast',\n 'drop-shadow',\n 'grayscale',\n 'hue-rotate',\n 'invert',\n 'opacity',\n 'saturate',\n 'sepia',\n 'polygon',\n 'circle',\n 'ellipse',\n 'inset',\n 'path',\n 'xywh',\n 'rect',\n 'ray',\n 'light-dark',\n 'anchor',\n 'anchor-size',\n 'scroll',\n 'view',\n 'symbols',\n]);\n\nconst VALUE_KEYWORDS = new Set([\n 'auto',\n 'none',\n 'normal',\n 'inherit',\n 'initial',\n 'unset',\n 'revert',\n 'revert-layer',\n 'max-content',\n 'min-content',\n 'fit-content',\n 'stretch',\n 'transparent',\n 'currentcolor',\n 'currentColor',\n // display\n 'block',\n 'inline',\n 'inline-block',\n 'flex',\n 'inline-flex',\n 'grid',\n 'inline-grid',\n 'contents',\n 'table',\n 'table-row',\n 'table-cell',\n 'list-item',\n 'flow-root',\n // position\n 'static',\n 'relative',\n 'absolute',\n 'fixed',\n 'sticky',\n // overflow\n 'visible',\n 'hidden',\n 'scroll',\n 'clip',\n 'overlay',\n // flex/align\n 'center',\n 'start',\n 'end',\n 'flex-start',\n 'flex-end',\n 'space-between',\n 'space-around',\n 'space-evenly',\n 'baseline',\n // flow\n 'row',\n 'column',\n 'row-reverse',\n 'column-reverse',\n 'wrap',\n 'nowrap',\n 'dense',\n // border\n 'solid',\n 'dashed',\n 'dotted',\n 'double',\n 'groove',\n 'ridge',\n 'outset',\n // directional\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n // radius shapes\n 'round',\n 'ellipse',\n 'leaf',\n 'backleaf',\n // dimension modifiers\n 'min',\n 'max',\n // text\n 'bold',\n 'bolder',\n 'lighter',\n 'italic',\n 'oblique',\n 'uppercase',\n 'lowercase',\n 'capitalize',\n 'line-through',\n 'underline',\n 'overline',\n 'wavy',\n // cursor\n 'pointer',\n 'default',\n 'text',\n 'move',\n 'grab',\n 'grabbing',\n 'not-allowed',\n 'crosshair',\n 'wait',\n 'help',\n 'col-resize',\n 'row-resize',\n 'n-resize',\n 's-resize',\n 'e-resize',\n 'w-resize',\n 'ne-resize',\n 'nw-resize',\n 'se-resize',\n 'sw-resize',\n 'ew-resize',\n 'ns-resize',\n 'zoom-in',\n 'zoom-out',\n // misc\n 'cover',\n 'contain',\n 'fill',\n 'no-repeat',\n 'repeat-x',\n 'repeat-y',\n 'border-box',\n 'padding-box',\n 'content-box',\n 'break-word',\n 'break-all',\n 'keep-all',\n 'anywhere',\n 'pre',\n 'pre-wrap',\n 'pre-line',\n 'balance',\n 'smooth',\n 'horizontal',\n 'vertical',\n 'both',\n 'mandatory',\n 'proximity',\n // white-space\n 'collapse',\n 'preserve',\n 'preserve-breaks',\n 'break-spaces',\n // text-wrap\n 'pretty',\n 'stable',\n // will-change\n 'transform',\n 'opacity',\n // animation\n 'infinite',\n 'alternate',\n 'alternate-reverse',\n 'reverse',\n 'forwards',\n 'backwards',\n 'running',\n 'paused',\n 'ease',\n 'ease-in',\n 'ease-out',\n 'ease-in-out',\n 'step-start',\n 'step-end',\n // scrollbar\n 'thin',\n 'always',\n 'both-edges',\n // width/height\n 'fixed',\n // shadow\n 'inset',\n // textOverflow\n 'clip',\n // other\n 'ltr',\n 'rtl',\n 'embed',\n 'isolate',\n 'isolate-override',\n 'plaintext',\n 'horizontal-tb',\n 'vertical-rl',\n 'vertical-lr',\n 'sideways-rl',\n 'sideways-lr',\n 'monospace',\n 'serif',\n 'sans-serif',\n 'cursive',\n 'fantasy',\n 'system-ui',\n 'ui-serif',\n 'ui-sans-serif',\n 'ui-monospace',\n 'ui-rounded',\n 'to',\n // misc\n 'strong',\n 'tight',\n 'icon',\n]);\n\n// ============================================================================\n// Classifier\n// ============================================================================\n\nfunction toSet(input?: Set<string> | string[]): Set<string> {\n if (!input) return new Set();\n return input instanceof Set ? input : new Set(input);\n}\n\nfunction classifyToken(\n raw: string,\n offset: number,\n errors: ValueError[],\n opts: ValueParserOptions,\n): ValueToken {\n const token = raw.trim();\n if (!token) return { type: 'keyword', value: '' };\n\n // Quoted strings\n if (\n (token.startsWith('\"') && token.endsWith('\"')) ||\n (token.startsWith(\"'\") && token.endsWith(\"'\"))\n ) {\n return { type: 'string', value: token.slice(1, -1), raw: token };\n }\n\n // !important\n if (token === '!important') {\n return { type: 'important', raw: token };\n }\n\n // Double prefix: $$name(...) is a Tasty `@function` call; bare $$name is the\n // custom-property reference `transition` uses. The call form must be checked\n // first, or the bare-name regex rejects `$$negative(10px)` outright.\n if (token.startsWith('$$') || token.startsWith('##')) {\n const call = classifyTastyFunctionCall(token, offset, errors);\n if (call) return call;\n }\n\n // Double prefix: $$name (custom property reference for transitions)\n if (token.startsWith('$$')) {\n const name = token.slice(2);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'custom-prop-ref', name, raw: token };\n }\n errors.push({\n message: `Invalid custom property reference '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Double prefix: ##name (color reference for transitions)\n if (token.startsWith('##')) {\n const name = token.slice(2);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'color-ref', name, raw: token };\n }\n errors.push({\n message: `Invalid color reference '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Custom property: $name\n if (token.startsWith('$')) {\n const name = token.slice(1);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'custom-prop', name, raw: token };\n }\n errors.push({\n message: `Invalid custom property syntax '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Color token: #name, #name.N, #name.$prop, or bare # (error)\n if (token.startsWith('#')) {\n if (token.length === 1) {\n errors.push({\n message: 'Empty color token name.',\n offset,\n length: 1,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n return classifyColorToken(token, offset, errors);\n }\n\n // Parenthesized expression: (expr) — fallback, auto-calc, etc.\n if (token.startsWith('(') && token.endsWith(')')) {\n return { type: 'group-expr', inner: token.slice(1, -1), raw: token };\n }\n\n // Function call: name(...)\n const openIdx = token.indexOf('(');\n if (openIdx > 0 && token.endsWith(')')) {\n const fname = token.slice(0, openIdx);\n const args = token.slice(openIdx + 1, -1);\n\n if (opts.skipFuncValidation) {\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n const knownFuncs = toSet(opts.knownFuncs);\n\n if (\n COLOR_FUNCS.has(fname) ||\n CSS_FUNCS.has(fname) ||\n knownFuncs.has(fname)\n ) {\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n // Unknown function — still return as css-function but flag it\n errors.push({\n message: `Unknown function '${fname}()'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n // Unit number: 2x, 1.5r, 10px, 50%, 1fr\n const unitMatch = token.match(RE_UNIT_NUM);\n if (unitMatch) {\n const unit = unitMatch[1];\n const numVal = parseFloat(token.slice(0, -unit.length));\n\n if (opts.skipUnitValidation) {\n return { type: 'custom-unit', value: numVal, unit, raw: token };\n }\n\n const knownUnits = toSet(opts.knownUnits);\n\n if (BUILT_IN_UNITS.has(unit) || knownUnits.has(unit)) {\n return { type: 'custom-unit', value: numVal, unit, raw: token };\n }\n if (CSS_UNITS.has(unit)) {\n return { type: 'css-unit', value: numVal, unit, raw: token };\n }\n\n errors.push({\n message: `Unknown unit '${unit}' in '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // CSS unit with % (RE_UNIT_NUM doesn't match % since it expects alpha)\n const cssUnitMatch = token.match(RE_CSS_UNIT_NUM);\n if (cssUnitMatch) {\n const unit = cssUnitMatch[1];\n const numVal = parseFloat(token.slice(0, -unit.length));\n if (CSS_UNITS.has(unit)) {\n return { type: 'css-unit', value: numVal, unit, raw: token };\n }\n }\n\n // Plain number\n if (RE_NUMBER.test(token)) {\n return { type: 'number', value: parseFloat(token), raw: token };\n }\n\n // Tasty merge directive (@inherit)\n if (token === '@inherit') {\n return { type: 'keyword', value: token };\n }\n\n // Known keyword\n if (VALUE_KEYWORDS.has(token) || VALUE_KEYWORDS.has(token.toLowerCase())) {\n return { type: 'keyword', value: token };\n }\n\n // CSS custom property function var(--name)\n if (token.startsWith('var(') && token.endsWith(')')) {\n return {\n type: 'css-function',\n name: 'var',\n args: token.slice(4, -1),\n raw: token,\n };\n }\n\n // Unknown token\n return { type: 'unknown', raw: token };\n}\n\n/**\n * Classify a `$$name(...)` / `##name(...)` Tasty `@function` call.\n *\n * Returns `null` when the token is not a call, so the caller falls through to the\n * bare `$$name` / `##name` reference handling that `transition` relies on.\n */\nfunction classifyTastyFunctionCall(\n token: string,\n offset: number,\n errors: ValueError[],\n): ValueToken | null {\n const openIdx = token.indexOf('(');\n\n if (openIdx <= 2 || !token.endsWith(')')) return null;\n\n const name = token.slice(2, openIdx);\n const args = token.slice(openIdx + 1, -1);\n\n if (!/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n errors.push({\n message: `Invalid function name in '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n return { type: 'tasty-function', name, args, raw: token };\n}\n\nfunction classifyColorToken(\n token: string,\n offset: number,\n errors: ValueError[],\n): ValueToken {\n const raw = token;\n\n // Strip leading #\n const name = token.slice(1);\n\n if (name.length === 0) {\n errors.push({\n message: 'Empty color token name.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n // Check for opacity suffix\n const dotIndex = name.indexOf('.');\n if (dotIndex !== -1) {\n const tokenName = name.slice(0, dotIndex);\n const opacitySuffix = name.slice(dotIndex + 1);\n\n if (tokenName.length === 0) {\n errors.push({\n message: 'Empty color token name before opacity.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n // Dynamic opacity from CSS custom property\n if (opacitySuffix.startsWith('$')) {\n return {\n type: 'color-token',\n name: tokenName,\n opacity: opacitySuffix,\n raw,\n };\n }\n\n if (opacitySuffix.length === 0) {\n errors.push({\n message: 'Trailing dot with no opacity value.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n const opacity = Number(opacitySuffix);\n if (isNaN(opacity)) {\n errors.push({\n message: `Invalid opacity value '${opacitySuffix}'.`,\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n if (opacity < 0) {\n errors.push({\n message: 'Opacity cannot be negative.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n if (opacity > 100) {\n errors.push({\n message: `Opacity '${opacitySuffix}' exceeds 100.`,\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n return {\n type: 'color-token',\n name: tokenName,\n opacity: opacitySuffix,\n raw,\n };\n }\n\n return { type: 'color-token', name, raw };\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Parse a style value string into typed tokens organized by\n * comma-separated groups and slash-separated parts.\n */\nexport function parseValue(\n src: string,\n opts: ValueParserOptions = {},\n): ValueParseResult {\n const errors: ValueError[] = [];\n\n // Check bracket balance first\n const bracketError = checkBracketBalance(src);\n if (bracketError) {\n errors.push({\n message: bracketError.message,\n offset: bracketError.position,\n length: 1,\n });\n return { groups: [], errors };\n }\n\n const scanned = scanTokens(src);\n\n const groups: ValueGroup[] = [];\n let currentParts: ValuePart[] = [];\n let currentTokens: ValueToken[] = [];\n\n const endPart = () => {\n if (currentTokens.length > 0) {\n currentParts.push({ tokens: currentTokens });\n currentTokens = [];\n }\n };\n\n const endGroup = () => {\n endPart();\n if (currentParts.length > 0) {\n groups.push({ parts: currentParts });\n } else {\n groups.push({ parts: [{ tokens: [] }] });\n }\n currentParts = [];\n };\n\n for (const scanned_token of scanned) {\n if (scanned_token.value) {\n const classified = classifyToken(\n scanned_token.value,\n scanned_token.offset,\n errors,\n opts,\n );\n currentTokens.push(classified);\n }\n if (scanned_token.isSlash) endPart();\n if (scanned_token.isComma) endGroup();\n }\n\n // Push final group\n if (\n currentTokens.length > 0 ||\n currentParts.length > 0 ||\n groups.length === 0\n ) {\n endGroup();\n }\n\n return { groups, errors };\n}\n\n/**\n * Extract all tokens of a specific type from a parse result.\n */\nexport function extractTokensByType<T extends ValueToken['type']>(\n result: ValueParseResult,\n type: T,\n): Extract<ValueToken, { type: T }>[] {\n const tokens: Extract<ValueToken, { type: T }>[] = [];\n for (const group of result.groups) {\n for (const part of group.parts) {\n for (const token of part.tokens) {\n if (token.type === type) {\n tokens.push(token as Extract<ValueToken, { type: T }>);\n }\n }\n }\n }\n return tokens;\n}\n\n/**\n * Get a flat list of all tokens from a parse result.\n */\nexport function flattenTokens(result: ValueParseResult): ValueToken[] {\n const tokens: ValueToken[] = [];\n for (const group of result.groups) {\n for (const part of group.parts) {\n tokens.push(...part.tokens);\n }\n }\n return tokens;\n}\n"],"mappings":";;;;AA2DA,MAAM,cAAc;AACpB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AAExB,MAAM,cAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,YAAY,IAAI,IAAI;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;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"}
1
+ {"version":3,"file":"value-parser.js","names":[],"sources":["../../src/parsers/value-parser.ts"],"sourcesContent":["import { scanTokens, checkBracketBalance } from './utils.js';\nimport { BUILT_IN_UNITS, CSS_UNITS } from '../constants.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ValueToken =\n | { type: 'color-token'; name: string; opacity?: string; raw: string }\n | { type: 'color-ref'; name: string; raw: string }\n | { type: 'custom-prop'; name: string; raw: string }\n | { type: 'custom-prop-ref'; name: string; raw: string }\n | { type: 'custom-unit'; value: number; unit: string; raw: string }\n | { type: 'css-unit'; value: number; unit: string; raw: string }\n | { type: 'number'; value: number; raw: string }\n | { type: 'keyword'; value: string }\n | { type: 'css-function'; name: string; args: string; raw: string }\n /**\n * A Tasty CSS `@function` call: `$$name(args)` (emits native `--name(args)`).\n * Distinct from `custom-prop-ref`, which is the bare `$$name` form that\n * `transition` uses to name a custom property.\n */\n | { type: 'tasty-function'; name: string; args: string; raw: string }\n | { type: 'string'; value: string; raw: string }\n | { type: 'important'; raw: string }\n | { type: 'group-expr'; inner: string; raw: string }\n | { type: 'unknown'; raw: string };\n\nexport interface ValueError {\n message: string;\n offset: number;\n length: number;\n raw?: string;\n}\n\nexport interface ValuePart {\n tokens: ValueToken[];\n}\n\nexport interface ValueGroup {\n parts: ValuePart[];\n}\n\nexport interface ValueParseResult {\n groups: ValueGroup[];\n errors: ValueError[];\n}\n\nexport interface ValueParserOptions {\n knownUnits?: Set<string> | string[];\n knownFuncs?: Set<string> | string[];\n skipUnitValidation?: boolean;\n skipFuncValidation?: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst RE_UNIT_NUM = /^[+-]?(?:\\d*\\.\\d+|\\d+)([a-z][a-z0-9]*)$/;\nconst RE_NUMBER = /^[+-]?(?:\\d*\\.\\d+|\\d+)$/;\nconst RE_CSS_UNIT_NUM = /^[+-]?(?:\\d*\\.\\d+|\\d+)([a-z%]+)$/;\n\nconst COLOR_FUNCS = new Set([\n 'rgb',\n 'rgba',\n 'hsl',\n 'hsla',\n 'hwb',\n 'lab',\n 'lch',\n 'oklab',\n 'oklch',\n 'color',\n 'device-cmyk',\n 'gray',\n 'color-mix',\n 'color-contrast',\n]);\n\nconst CSS_FUNCS = new Set([\n 'calc',\n 'calc-size',\n 'min',\n 'max',\n 'clamp',\n 'abs',\n 'sign',\n 'round',\n 'mod',\n 'rem',\n 'sin',\n 'cos',\n 'tan',\n 'asin',\n 'acos',\n 'atan',\n 'atan2',\n 'pow',\n 'sqrt',\n 'hypot',\n 'log',\n 'exp',\n 'var',\n 'env',\n 'attr',\n 'counter',\n 'counters',\n 'image-set',\n 'cross-fade',\n 'image',\n 'paint',\n 'element',\n 'linear-gradient',\n 'radial-gradient',\n 'conic-gradient',\n 'repeating-linear-gradient',\n 'repeating-radial-gradient',\n 'repeating-conic-gradient',\n 'url',\n 'fit-content',\n 'minmax',\n 'repeat',\n 'cubic-bezier',\n 'steps',\n 'linear',\n 'rotate',\n 'scale',\n 'translate',\n 'skew',\n 'matrix',\n 'perspective',\n 'rotate3d',\n 'rotateX',\n 'rotateY',\n 'rotateZ',\n 'scale3d',\n 'scaleX',\n 'scaleY',\n 'scaleZ',\n 'translate3d',\n 'translateX',\n 'translateY',\n 'translateZ',\n 'skewX',\n 'skewY',\n 'matrix3d',\n 'blur',\n 'brightness',\n 'contrast',\n 'drop-shadow',\n 'grayscale',\n 'hue-rotate',\n 'invert',\n 'opacity',\n 'saturate',\n 'sepia',\n 'polygon',\n 'circle',\n 'ellipse',\n 'inset',\n 'path',\n 'xywh',\n 'rect',\n 'ray',\n 'light-dark',\n 'anchor',\n 'anchor-size',\n 'scroll',\n 'view',\n 'symbols',\n]);\n\nconst VALUE_KEYWORDS = new Set([\n 'auto',\n 'none',\n 'normal',\n 'inherit',\n 'initial',\n 'unset',\n 'revert',\n 'revert-layer',\n 'max-content',\n 'min-content',\n 'fit-content',\n 'stretch',\n 'transparent',\n 'currentcolor',\n 'currentColor',\n // display\n 'block',\n 'inline',\n 'inline-block',\n 'flex',\n 'inline-flex',\n 'grid',\n 'inline-grid',\n 'contents',\n 'table',\n 'table-row',\n 'table-cell',\n 'list-item',\n 'flow-root',\n // position\n 'static',\n 'relative',\n 'absolute',\n 'fixed',\n 'sticky',\n // overflow\n 'visible',\n 'hidden',\n 'scroll',\n 'clip',\n 'overlay',\n // flex/align\n 'center',\n 'start',\n 'end',\n 'flex-start',\n 'flex-end',\n 'space-between',\n 'space-around',\n 'space-evenly',\n 'baseline',\n // flow\n 'row',\n 'column',\n 'row-reverse',\n 'column-reverse',\n 'wrap',\n 'nowrap',\n 'dense',\n // border\n 'solid',\n 'dashed',\n 'dotted',\n 'double',\n 'groove',\n 'ridge',\n 'outset',\n // directional\n 'top',\n 'right',\n 'bottom',\n 'left',\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n // radius shapes\n 'round',\n 'ellipse',\n 'leaf',\n 'backleaf',\n // dimension modifiers\n 'min',\n 'max',\n // text\n 'bold',\n 'bolder',\n 'lighter',\n 'italic',\n 'oblique',\n 'uppercase',\n 'lowercase',\n 'capitalize',\n 'line-through',\n 'underline',\n 'overline',\n 'wavy',\n // cursor\n 'pointer',\n 'default',\n 'text',\n 'move',\n 'grab',\n 'grabbing',\n 'not-allowed',\n 'crosshair',\n 'wait',\n 'help',\n 'col-resize',\n 'row-resize',\n 'n-resize',\n 's-resize',\n 'e-resize',\n 'w-resize',\n 'ne-resize',\n 'nw-resize',\n 'se-resize',\n 'sw-resize',\n 'ew-resize',\n 'ns-resize',\n 'zoom-in',\n 'zoom-out',\n // misc\n 'cover',\n 'contain',\n 'fill',\n 'no-repeat',\n 'repeat-x',\n 'repeat-y',\n 'border-box',\n 'padding-box',\n 'content-box',\n 'break-word',\n 'break-all',\n 'keep-all',\n 'anywhere',\n 'pre',\n 'pre-wrap',\n 'pre-line',\n 'balance',\n 'smooth',\n 'horizontal',\n 'vertical',\n 'both',\n 'mandatory',\n 'proximity',\n // white-space\n 'collapse',\n 'preserve',\n 'preserve-breaks',\n 'break-spaces',\n // text-wrap\n 'pretty',\n 'stable',\n // will-change\n 'transform',\n 'opacity',\n // animation\n 'infinite',\n 'alternate',\n 'alternate-reverse',\n 'reverse',\n 'forwards',\n 'backwards',\n 'running',\n 'paused',\n 'ease',\n 'ease-in',\n 'ease-out',\n 'ease-in-out',\n 'step-start',\n 'step-end',\n // scrollbar\n 'thin',\n 'always',\n 'both-edges',\n // width/height\n 'fixed',\n // shadow\n 'inset',\n // textOverflow\n 'clip',\n // other\n 'ltr',\n 'rtl',\n 'embed',\n 'isolate',\n 'isolate-override',\n 'plaintext',\n 'horizontal-tb',\n 'vertical-rl',\n 'vertical-lr',\n 'sideways-rl',\n 'sideways-lr',\n 'monospace',\n 'serif',\n 'sans-serif',\n 'cursive',\n 'fantasy',\n 'system-ui',\n 'ui-serif',\n 'ui-sans-serif',\n 'ui-monospace',\n 'ui-rounded',\n 'to',\n // misc\n 'strong',\n 'tight',\n 'icon',\n]);\n\n// ============================================================================\n// Classifier\n// ============================================================================\n\nfunction toSet(input?: Set<string> | string[]): Set<string> {\n if (!input) return new Set();\n return input instanceof Set ? input : new Set(input);\n}\n\nfunction classifyToken(\n raw: string,\n offset: number,\n errors: ValueError[],\n opts: ValueParserOptions,\n): ValueToken {\n const token = raw.trim();\n if (!token) return { type: 'keyword', value: '' };\n\n // Quoted strings\n if (\n (token.startsWith('\"') && token.endsWith('\"')) ||\n (token.startsWith(\"'\") && token.endsWith(\"'\"))\n ) {\n return { type: 'string', value: token.slice(1, -1), raw: token };\n }\n\n // !important\n if (token === '!important') {\n return { type: 'important', raw: token };\n }\n\n // Double prefix: $$name(...) is a Tasty `@function` call; bare $$name is the\n // custom-property reference `transition` uses. The call form must be checked\n // first, or the bare-name regex rejects `$$negative(10px)` outright.\n if (token.startsWith('$$') || token.startsWith('##')) {\n const call = classifyTastyFunctionCall(token, offset, errors);\n if (call) return call;\n }\n\n // Double prefix: $$name (custom property reference for transitions)\n if (token.startsWith('$$')) {\n const name = token.slice(2);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'custom-prop-ref', name, raw: token };\n }\n errors.push({\n message: `Invalid custom property reference '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Double prefix: ##name (color reference for transitions)\n if (token.startsWith('##')) {\n const name = token.slice(2);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'color-ref', name, raw: token };\n }\n errors.push({\n message: `Invalid color reference '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Custom property: $name\n if (token.startsWith('$')) {\n const name = token.slice(1);\n if (/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n return { type: 'custom-prop', name, raw: token };\n }\n errors.push({\n message: `Invalid custom property syntax '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // Color token: #name, #name.N, #name.$prop, or bare # (error)\n if (token.startsWith('#')) {\n if (token.length === 1) {\n errors.push({\n message: 'Empty color token name.',\n offset,\n length: 1,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n return classifyColorToken(token, offset, errors);\n }\n\n // Parenthesized expression: (expr) — fallback, auto-calc, etc.\n if (token.startsWith('(') && token.endsWith(')')) {\n return { type: 'group-expr', inner: token.slice(1, -1), raw: token };\n }\n\n // Function call: name(...)\n const openIdx = token.indexOf('(');\n if (openIdx > 0 && token.endsWith(')')) {\n const fname = token.slice(0, openIdx);\n const args = token.slice(openIdx + 1, -1);\n\n if (opts.skipFuncValidation) {\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n const knownFuncs = toSet(opts.knownFuncs);\n\n if (\n COLOR_FUNCS.has(fname) ||\n CSS_FUNCS.has(fname) ||\n knownFuncs.has(fname)\n ) {\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n // Unknown function — still return as css-function but flag it\n errors.push({\n message: `Unknown function '${fname}()'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'css-function', name: fname, args, raw: token };\n }\n\n // Unit number: 2x, 1.5r, 10px, 50%, 1fr\n const unitMatch = token.match(RE_UNIT_NUM);\n if (unitMatch) {\n const unit = unitMatch[1];\n const numVal = parseFloat(token.slice(0, -unit.length));\n\n if (opts.skipUnitValidation) {\n return { type: 'custom-unit', value: numVal, unit, raw: token };\n }\n\n const knownUnits = toSet(opts.knownUnits);\n\n if (BUILT_IN_UNITS.has(unit) || knownUnits.has(unit)) {\n return { type: 'custom-unit', value: numVal, unit, raw: token };\n }\n if (CSS_UNITS.has(unit)) {\n return { type: 'css-unit', value: numVal, unit, raw: token };\n }\n\n errors.push({\n message: `Unknown unit '${unit}' in '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n // CSS unit with % (RE_UNIT_NUM doesn't match % since it expects alpha)\n const cssUnitMatch = token.match(RE_CSS_UNIT_NUM);\n if (cssUnitMatch) {\n const unit = cssUnitMatch[1];\n const numVal = parseFloat(token.slice(0, -unit.length));\n if (CSS_UNITS.has(unit)) {\n return { type: 'css-unit', value: numVal, unit, raw: token };\n }\n }\n\n // Plain number\n if (RE_NUMBER.test(token)) {\n return { type: 'number', value: parseFloat(token), raw: token };\n }\n\n // Tasty merge directive (@inherit)\n if (token === '@inherit') {\n return { type: 'keyword', value: token };\n }\n\n // Known keyword\n if (VALUE_KEYWORDS.has(token) || VALUE_KEYWORDS.has(token.toLowerCase())) {\n return { type: 'keyword', value: token };\n }\n\n // CSS custom property function var(--name)\n if (token.startsWith('var(') && token.endsWith(')')) {\n return {\n type: 'css-function',\n name: 'var',\n args: token.slice(4, -1),\n raw: token,\n };\n }\n\n // Unknown token\n return { type: 'unknown', raw: token };\n}\n\n/**\n * Classify a `$$name(...)` / `##name(...)` Tasty `@function` call.\n *\n * Returns `null` when the token is not a call, so the caller falls through to the\n * bare `$$name` / `##name` reference handling that `transition` relies on.\n */\nfunction classifyTastyFunctionCall(\n token: string,\n offset: number,\n errors: ValueError[],\n): ValueToken | null {\n const openIdx = token.indexOf('(');\n\n if (openIdx <= 2 || !token.endsWith(')')) return null;\n\n const name = token.slice(2, openIdx);\n const args = token.slice(openIdx + 1, -1);\n\n if (!/^[a-z_][a-z0-9-_]*$/i.test(name)) {\n errors.push({\n message: `Invalid function name in '${token}'.`,\n offset,\n length: token.length,\n raw: token,\n });\n return { type: 'unknown', raw: token };\n }\n\n return { type: 'tasty-function', name, args, raw: token };\n}\n\nfunction classifyColorToken(\n token: string,\n offset: number,\n errors: ValueError[],\n): ValueToken {\n const raw = token;\n\n // Strip leading #\n const name = token.slice(1);\n\n if (name.length === 0) {\n errors.push({\n message: 'Empty color token name.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n // Check for opacity suffix\n const dotIndex = name.indexOf('.');\n if (dotIndex !== -1) {\n const tokenName = name.slice(0, dotIndex);\n const opacitySuffix = name.slice(dotIndex + 1);\n\n if (tokenName.length === 0) {\n errors.push({\n message: 'Empty color token name before opacity.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n // Dynamic opacity from CSS custom property\n if (opacitySuffix.startsWith('$')) {\n return {\n type: 'color-token',\n name: tokenName,\n opacity: opacitySuffix,\n raw,\n };\n }\n\n if (opacitySuffix.length === 0) {\n errors.push({\n message: 'Trailing dot with no opacity value.',\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n // Tasty reads the digits as a decimal fraction (`.5` and `.50` are 50%,\n // `.150` is 15%), so any digit string is in range.\n if (!/^\\d+$/.test(opacitySuffix)) {\n errors.push({\n message: /^-\\d+$/.test(opacitySuffix)\n ? 'Opacity cannot be negative.'\n : `Invalid opacity value '${opacitySuffix}'.`,\n offset,\n length: token.length,\n raw,\n });\n return { type: 'unknown', raw };\n }\n\n return {\n type: 'color-token',\n name: tokenName,\n opacity: opacitySuffix,\n raw,\n };\n }\n\n return { type: 'color-token', name, raw };\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Parse a style value string into typed tokens organized by\n * comma-separated groups and slash-separated parts.\n */\nexport function parseValue(\n src: string,\n opts: ValueParserOptions = {},\n): ValueParseResult {\n const errors: ValueError[] = [];\n\n // Check bracket balance first\n const bracketError = checkBracketBalance(src);\n if (bracketError) {\n errors.push({\n message: bracketError.message,\n offset: bracketError.position,\n length: 1,\n });\n return { groups: [], errors };\n }\n\n const scanned = scanTokens(src);\n\n const groups: ValueGroup[] = [];\n let currentParts: ValuePart[] = [];\n let currentTokens: ValueToken[] = [];\n\n const endPart = () => {\n if (currentTokens.length > 0) {\n currentParts.push({ tokens: currentTokens });\n currentTokens = [];\n }\n };\n\n const endGroup = () => {\n endPart();\n if (currentParts.length > 0) {\n groups.push({ parts: currentParts });\n } else {\n groups.push({ parts: [{ tokens: [] }] });\n }\n currentParts = [];\n };\n\n for (const scanned_token of scanned) {\n if (scanned_token.value) {\n const classified = classifyToken(\n scanned_token.value,\n scanned_token.offset,\n errors,\n opts,\n );\n currentTokens.push(classified);\n }\n if (scanned_token.isSlash) endPart();\n if (scanned_token.isComma) endGroup();\n }\n\n // Push final group\n if (\n currentTokens.length > 0 ||\n currentParts.length > 0 ||\n groups.length === 0\n ) {\n endGroup();\n }\n\n return { groups, errors };\n}\n\n/**\n * Extract all tokens of a specific type from a parse result.\n */\nexport function extractTokensByType<T extends ValueToken['type']>(\n result: ValueParseResult,\n type: T,\n): Extract<ValueToken, { type: T }>[] {\n const tokens: Extract<ValueToken, { type: T }>[] = [];\n for (const group of result.groups) {\n for (const part of group.parts) {\n for (const token of part.tokens) {\n if (token.type === type) {\n tokens.push(token as Extract<ValueToken, { type: T }>);\n }\n }\n }\n }\n return tokens;\n}\n\n/**\n * Get a flat list of all tokens from a parse result.\n */\nexport function flattenTokens(result: ValueParseResult): ValueToken[] {\n const tokens: ValueToken[] = [];\n for (const group of result.groups) {\n for (const part of group.parts) {\n tokens.push(...part.tokens);\n }\n }\n return tokens;\n}\n"],"mappings":";;;;AA2DA,MAAM,cAAc;AACpB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AAExB,MAAM,cAAc,IAAI,IAAI;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,YAAY,IAAI,IAAI;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;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"}
@@ -1,4 +1,5 @@
1
1
  import { createRule } from "../create-rule.js";
2
+ import { BUILT_IN_COLOR_TOKENS } from "../constants.js";
2
3
  import { getKeyName, isRawHexColor, validateColorTokenSyntax } from "../utils.js";
3
4
  import { TastyContext } from "../context.js";
4
5
  import { stringStyleValueListeners } from "../style-values.js";
@@ -48,7 +49,7 @@ var valid_color_token_default = createRule({
48
49
  }
49
50
  if (ctx.config.tokens === false) continue;
50
51
  const baseName = token.startsWith("##") ? "#" + token.slice(2).split(".")[0] : "#" + token.slice(1).split(".")[0];
51
- if (baseName === "#current") continue;
52
+ if (BUILT_IN_COLOR_TOKENS.has(baseName)) continue;
52
53
  pendingChecks.push({
53
54
  token,
54
55
  baseName,
@@ -1 +1 @@
1
- {"version":3,"file":"valid-color-token.js","names":[],"sources":["../../src/rules/valid-color-token.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport {\n getKeyName,\n validateColorTokenSyntax,\n isRawHexColor,\n} from '../utils.js';\nimport { scanValueWords } from '../value-words.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownToken';\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-color-token',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate color token syntax and existence',\n },\n messages: {\n invalidSyntax: \"Invalid color token '{{token}}': {{reason}}.\",\n unknownToken:\n \"Unknown color token '{{token}}'. Declare it as a '#name' key, or add it to 'tokens' in your tasty config.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileColorTokens = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalTokens(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('#') && !key.startsWith('##')) {\n fileColorTokens.add(key);\n }\n }\n }\n\n function checkColorTokensInValue(value: string, node: TSESTree.Node): void {\n for (const word of scanValueWords(value)) {\n const token = word.value;\n if (!token.startsWith('#')) continue;\n if (\n token.startsWith('##') &&\n word.isFunction &&\n value[word.offset + token.length] === '('\n )\n continue;\n\n if (isRawHexColor(token)) continue;\n\n const syntaxError = validateColorTokenSyntax(token);\n if (syntaxError) {\n context.report({\n node,\n messageId: 'invalidSyntax',\n data: { token, reason: syntaxError },\n });\n continue;\n }\n\n if (ctx.config.tokens === false) continue;\n\n const baseName = token.startsWith('##')\n ? '#' + token.slice(2).split('.')[0]\n : '#' + token.slice(1).split('.')[0];\n\n if (baseName === '#current') continue;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n return stringStyleValueListeners(ctx, checkColorTokensInValue, {\n onStyleObject: collectLocalTokens,\n onProgramExit() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileColorTokens.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownToken',\n data: { token },\n });\n }\n },\n });\n },\n});\n"],"mappings":";;;;;;;AAmBA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,cACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,kCAAkB,IAAI,KAAa;EACzC,MAAM,gBAAyC,EAAE;EAEjD,SAAS,mBAAmB,MAAuC;AACjE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,iBAAgB,IAAI,IAAI;;;EAK9B,SAAS,wBAAwB,OAAe,MAA2B;AACzE,QAAK,MAAM,QAAQ,eAAe,MAAM,EAAE;IACxC,MAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAM,WAAW,IAAI,CAAE;AAC5B,QACE,MAAM,WAAW,KAAK,IACtB,KAAK,cACL,MAAM,KAAK,SAAS,MAAM,YAAY,IAEtC;AAEF,QAAI,cAAc,MAAM,CAAE;IAE1B,MAAM,cAAc,yBAAyB,MAAM;AACnD,QAAI,aAAa;AACf,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE;OAAO,QAAQ;OAAa;MACrC,CAAC;AACF;;AAGF,QAAI,IAAI,OAAO,WAAW,MAAO;IAEjC,MAAM,WAAW,MAAM,WAAW,KAAK,GACnC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,KAChC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC;AAEpC,QAAI,aAAa,WAAY;AAE7B,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;AAIjD,SAAO,0BAA0B,KAAK,yBAAyB;GAC7D,eAAe;GACf,gBAAgB;AACd,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,gBAAgB,IAAI,SAAS,CAAE;AACnC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"valid-color-token.js","names":[],"sources":["../../src/rules/valid-color-token.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport { BUILT_IN_COLOR_TOKENS } from '../constants.js';\nimport {\n getKeyName,\n validateColorTokenSyntax,\n isRawHexColor,\n} from '../utils.js';\nimport { scanValueWords } from '../value-words.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownToken';\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-color-token',\n meta: {\n type: 'problem',\n docs: {\n description: 'Validate color token syntax and existence',\n },\n messages: {\n invalidSyntax: \"Invalid color token '{{token}}': {{reason}}.\",\n unknownToken:\n \"Unknown color token '{{token}}'. Declare it as a '#name' key, or add it to 'tokens' in your tasty config.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileColorTokens = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalTokens(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('#') && !key.startsWith('##')) {\n fileColorTokens.add(key);\n }\n }\n }\n\n function checkColorTokensInValue(value: string, node: TSESTree.Node): void {\n for (const word of scanValueWords(value)) {\n const token = word.value;\n if (!token.startsWith('#')) continue;\n if (\n token.startsWith('##') &&\n word.isFunction &&\n value[word.offset + token.length] === '('\n )\n continue;\n\n if (isRawHexColor(token)) continue;\n\n const syntaxError = validateColorTokenSyntax(token);\n if (syntaxError) {\n context.report({\n node,\n messageId: 'invalidSyntax',\n data: { token, reason: syntaxError },\n });\n continue;\n }\n\n if (ctx.config.tokens === false) continue;\n\n const baseName = token.startsWith('##')\n ? '#' + token.slice(2).split('.')[0]\n : '#' + token.slice(1).split('.')[0];\n\n if (BUILT_IN_COLOR_TOKENS.has(baseName)) continue;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n return stringStyleValueListeners(ctx, checkColorTokensInValue, {\n onStyleObject: collectLocalTokens,\n onProgramExit() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileColorTokens.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownToken',\n data: { token },\n });\n }\n },\n });\n },\n});\n"],"mappings":";;;;;;;;AAoBA,gCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eAAe;GACf,cACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,kCAAkB,IAAI,KAAa;EACzC,MAAM,gBAAyC,EAAE;EAEjD,SAAS,mBAAmB,MAAuC;AACjE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,iBAAgB,IAAI,IAAI;;;EAK9B,SAAS,wBAAwB,OAAe,MAA2B;AACzE,QAAK,MAAM,QAAQ,eAAe,MAAM,EAAE;IACxC,MAAM,QAAQ,KAAK;AACnB,QAAI,CAAC,MAAM,WAAW,IAAI,CAAE;AAC5B,QACE,MAAM,WAAW,KAAK,IACtB,KAAK,cACL,MAAM,KAAK,SAAS,MAAM,YAAY,IAEtC;AAEF,QAAI,cAAc,MAAM,CAAE;IAE1B,MAAM,cAAc,yBAAyB,MAAM;AACnD,QAAI,aAAa;AACf,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE;OAAO,QAAQ;OAAa;MACrC,CAAC;AACF;;AAGF,QAAI,IAAI,OAAO,WAAW,MAAO;IAEjC,MAAM,WAAW,MAAM,WAAW,KAAK,GACnC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC,KAChC,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC;AAEpC,QAAI,sBAAsB,IAAI,SAAS,CAAE;AAEzC,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;AAIjD,SAAO,0BAA0B,KAAK,yBAAyB;GAC7D,eAAe;GACf,gBAAgB;AACd,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,gBAAgB,IAAI,SAAS,CAAE;AACnC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP,CAAC;;CAEL,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { createRule } from "../create-rule.js";
2
+ import { BUILT_IN_CUSTOM_PROPERTIES } from "../constants.js";
2
3
  import { getKeyName } from "../utils.js";
3
4
  import { TastyContext } from "../context.js";
4
5
  import { stringStyleValueListeners } from "../style-values.js";
@@ -35,6 +36,7 @@ var valid_custom_property_default = createRule({
35
36
  if (!token.startsWith("$")) continue;
36
37
  if (token.startsWith("$$") && word.isFunction && value[word.offset + word.value.length] === "(") continue;
37
38
  const baseName = token.startsWith("$$") ? "$" + token.slice(2) : token;
39
+ if (BUILT_IN_CUSTOM_PROPERTIES.has(baseName)) continue;
38
40
  pendingChecks.push({
39
41
  token,
40
42
  baseName,
@@ -1 +1 @@
1
- {"version":3,"file":"valid-custom-property.js","names":[],"sources":["../../src/rules/valid-custom-property.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport { getKeyName } from '../utils.js';\nimport { scanValueWords } from '../value-words.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownProperty';\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-property',\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Validate $name custom property references',\n },\n messages: {\n invalidSyntax:\n \"Invalid custom property '{{token}}'. Use '$name' with letters, digits, or hyphens (e.g. $accent-color).\",\n unknownProperty:\n \"Unknown custom property '{{token}}'. Declare it as a '$name' key in this styles object, or add it to your tasty config.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileCustomProperties = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('$') && !key.startsWith('$$')) {\n fileCustomProperties.add(key);\n }\n }\n }\n\n function checkValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.tokens === false) return;\n\n for (const word of scanValueWords(value)) {\n // A dynamic color opacity is another custom-property reference.\n const token =\n word.value.startsWith('#') && word.value.includes('.$')\n ? word.value.slice(word.value.indexOf('.$') + 1)\n : word.value;\n if (!token.startsWith('$')) continue;\n if (\n token.startsWith('$$') &&\n word.isFunction &&\n value[word.offset + word.value.length] === '('\n )\n continue;\n const baseName = token.startsWith('$$') ? '$' + token.slice(2) : token;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n return stringStyleValueListeners(ctx, checkValue, {\n onStyleObject: collectLocalProperties,\n onProgramExit() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileCustomProperties.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownProperty',\n data: { token },\n });\n }\n },\n });\n },\n});\n"],"mappings":";;;;;;;AAeA,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eACE;GACF,iBACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,uCAAuB,IAAI,KAAa;EAC9C,MAAM,gBAAyC,EAAE;EAEjD,SAAS,uBAAuB,MAAuC;AACrE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,sBAAqB,IAAI,IAAI;;;EAKnC,SAAS,WAAW,OAAe,MAA2B;AAC5D,OAAI,IAAI,OAAO,WAAW,MAAO;AAEjC,QAAK,MAAM,QAAQ,eAAe,MAAM,EAAE;IAExC,MAAM,QACJ,KAAK,MAAM,WAAW,IAAI,IAAI,KAAK,MAAM,SAAS,KAAK,GACnD,KAAK,MAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,EAAE,GAC9C,KAAK;AACX,QAAI,CAAC,MAAM,WAAW,IAAI,CAAE;AAC5B,QACE,MAAM,WAAW,KAAK,IACtB,KAAK,cACL,MAAM,KAAK,SAAS,KAAK,MAAM,YAAY,IAE3C;IACF,MAAM,WAAW,MAAM,WAAW,KAAK,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG;AAEjE,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;AAIjD,SAAO,0BAA0B,KAAK,YAAY;GAChD,eAAe;GACf,gBAAgB;AACd,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,qBAAqB,IAAI,SAAS,CAAE;AACxC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP,CAAC;;CAEL,CAAC"}
1
+ {"version":3,"file":"valid-custom-property.js","names":[],"sources":["../../src/rules/valid-custom-property.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport { BUILT_IN_CUSTOM_PROPERTIES } from '../constants.js';\nimport { getKeyName } from '../utils.js';\nimport { scanValueWords } from '../value-words.js';\n\ntype MessageIds = 'invalidSyntax' | 'unknownProperty';\n\ninterface PendingExistenceCheck {\n token: string;\n baseName: string;\n node: TSESTree.Node;\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-custom-property',\n meta: {\n type: 'suggestion',\n docs: {\n description: 'Validate $name custom property references',\n },\n messages: {\n invalidSyntax:\n \"Invalid custom property '{{token}}'. Use '$name' with letters, digits, or hyphens (e.g. $accent-color).\",\n unknownProperty:\n \"Unknown custom property '{{token}}'. Declare it as a '$name' key in this styles object, or add it to your tasty config.\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n const fileCustomProperties = new Set<string>();\n const pendingChecks: PendingExistenceCheck[] = [];\n\n function collectLocalProperties(node: TSESTree.ObjectExpression): void {\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n const key = getKeyName(prop.key);\n if (key && key.startsWith('$') && !key.startsWith('$$')) {\n fileCustomProperties.add(key);\n }\n }\n }\n\n function checkValue(value: string, node: TSESTree.Node): void {\n if (ctx.config.tokens === false) return;\n\n for (const word of scanValueWords(value)) {\n // A dynamic color opacity is another custom-property reference.\n const token =\n word.value.startsWith('#') && word.value.includes('.$')\n ? word.value.slice(word.value.indexOf('.$') + 1)\n : word.value;\n if (!token.startsWith('$')) continue;\n if (\n token.startsWith('$$') &&\n word.isFunction &&\n value[word.offset + word.value.length] === '('\n )\n continue;\n const baseName = token.startsWith('$$') ? '$' + token.slice(2) : token;\n\n if (BUILT_IN_CUSTOM_PROPERTIES.has(baseName)) continue;\n\n pendingChecks.push({ token, baseName, node });\n }\n }\n\n return stringStyleValueListeners(ctx, checkValue, {\n onStyleObject: collectLocalProperties,\n onProgramExit() {\n if (\n !Array.isArray(ctx.config.tokens) ||\n ctx.config.tokens.length === 0\n ) {\n return;\n }\n\n for (const { token, baseName, node } of pendingChecks) {\n if (fileCustomProperties.has(baseName)) continue;\n if (ctx.config.tokens.includes(baseName)) continue;\n\n context.report({\n node,\n messageId: 'unknownProperty',\n data: { token },\n });\n }\n },\n });\n },\n});\n"],"mappings":";;;;;;;;AAgBA,oCAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,MAAM,EACJ,aAAa,6CACd;EACD,UAAU;GACR,eACE;GACF,iBACE;GACH;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EACrC,MAAM,uCAAuB,IAAI,KAAa;EAC9C,MAAM,gBAAyC,EAAE;EAEjD,SAAS,uBAAuB,MAAuC;AACrE,QAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,QAAI,KAAK,SAAS,cAAc,KAAK,SAAU;IAC/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,QAAI,OAAO,IAAI,WAAW,IAAI,IAAI,CAAC,IAAI,WAAW,KAAK,CACrD,sBAAqB,IAAI,IAAI;;;EAKnC,SAAS,WAAW,OAAe,MAA2B;AAC5D,OAAI,IAAI,OAAO,WAAW,MAAO;AAEjC,QAAK,MAAM,QAAQ,eAAe,MAAM,EAAE;IAExC,MAAM,QACJ,KAAK,MAAM,WAAW,IAAI,IAAI,KAAK,MAAM,SAAS,KAAK,GACnD,KAAK,MAAM,MAAM,KAAK,MAAM,QAAQ,KAAK,GAAG,EAAE,GAC9C,KAAK;AACX,QAAI,CAAC,MAAM,WAAW,IAAI,CAAE;AAC5B,QACE,MAAM,WAAW,KAAK,IACtB,KAAK,cACL,MAAM,KAAK,SAAS,KAAK,MAAM,YAAY,IAE3C;IACF,MAAM,WAAW,MAAM,WAAW,KAAK,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG;AAEjE,QAAI,2BAA2B,IAAI,SAAS,CAAE;AAE9C,kBAAc,KAAK;KAAE;KAAO;KAAU;KAAM,CAAC;;;AAIjD,SAAO,0BAA0B,KAAK,YAAY;GAChD,eAAe;GACf,gBAAgB;AACd,QACE,CAAC,MAAM,QAAQ,IAAI,OAAO,OAAO,IACjC,IAAI,OAAO,OAAO,WAAW,EAE7B;AAGF,SAAK,MAAM,EAAE,OAAO,UAAU,UAAU,eAAe;AACrD,SAAI,qBAAqB,IAAI,SAAS,CAAE;AACxC,SAAI,IAAI,OAAO,OAAO,SAAS,SAAS,CAAE;AAE1C,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM,EAAE,OAAO;MAChB,CAAC;;;GAGP,CAAC;;CAEL,CAAC"}
@@ -14,6 +14,17 @@ import { replaceInStringValue } from "../fix-utils.js";
14
14
  function kebabToCamel(name) {
15
15
  return name.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
16
16
  }
17
+ /** `gridTemplateColumns` -> `grid-template-columns`. */
18
+ function camelToKebab(name) {
19
+ return name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
20
+ }
21
+ /**
22
+ * Tasty style names that `KNOWN_CSS_PROPERTIES` also lists, but that name no
23
+ * property a browser implements. The `flow` style sets `flex-direction` /
24
+ * `grid-auto-flow`, yet Tasty has no `flow` transition: it emits the name
25
+ * verbatim (`transition: flow …`), which animates nothing.
26
+ */
27
+ const NON_TRANSITION_STYLE_NAMES = new Set(["flow"]);
17
28
  var valid_transition_default = createRule({
18
29
  name: "valid-transition",
19
30
  meta: {
@@ -23,7 +34,9 @@ var valid_transition_default = createRule({
23
34
  messages: {
24
35
  unknownTransition: "Unknown transition name '{{name}}'. Use a semantic name ({{known}}) or a CSS property name.",
25
36
  preferSemanticTransition: "Transition '{{native}}' has a Tasty semantic equivalent — use '{{semantic}}' instead.",
26
- useSemantic: "Use '{{semantic}}' instead of '{{native}}'"
37
+ useSemantic: "Use '{{semantic}}' instead of '{{native}}'",
38
+ camelCaseTransition: "Transition '{{name}}' compiles to '{{lowered}}', which is not a CSS property. Use the CSS name '{{kebab}}'.",
39
+ useKebabCase: "Use '{{kebab}}' instead of '{{name}}'"
27
40
  },
28
41
  schema: []
29
42
  },
@@ -48,11 +61,10 @@ var valid_transition_default = createRule({
48
61
  offset += group.length + 1;
49
62
  continue;
50
63
  }
64
+ const start = offset + group.indexOf(name);
65
+ const end = start + name.length;
51
66
  const semantic = TRANSITION_SEMANTIC_MAPPING[name];
52
67
  if (semantic) {
53
- const nameStartInGroup = group.indexOf(name);
54
- const start = offset + nameStartInGroup;
55
- const end = start + name.length;
56
68
  context.report({
57
69
  node,
58
70
  messageId: "preferSemanticTransition",
@@ -78,7 +90,33 @@ var valid_transition_default = createRule({
78
90
  offset += group.length + 1;
79
91
  continue;
80
92
  }
81
- if (!SEMANTIC_TRANSITIONS.has(name) && !KNOWN_CSS_PROPERTIES.has(name) && !KNOWN_CSS_PROPERTIES.has(kebabToCamel(name)) && !name.startsWith("--") && name !== "all" && name !== "none") context.report({
93
+ if (/[A-Z]/.test(name) && !SEMANTIC_TRANSITIONS.has(name) && KNOWN_CSS_PROPERTIES.has(name)) {
94
+ const kebab = camelToKebab(name);
95
+ const data = {
96
+ name,
97
+ lowered: name.toLowerCase(),
98
+ kebab
99
+ };
100
+ context.report({
101
+ node,
102
+ messageId: "camelCaseTransition",
103
+ data,
104
+ suggest: [{
105
+ messageId: "useKebabCase",
106
+ data,
107
+ fix(fixer) {
108
+ return replaceInStringValue(fixer, node, [{
109
+ start,
110
+ end,
111
+ replacement: kebab
112
+ }], context.sourceCode);
113
+ }
114
+ }]
115
+ });
116
+ offset += group.length + 1;
117
+ continue;
118
+ }
119
+ if (NON_TRANSITION_STYLE_NAMES.has(name) || !SEMANTIC_TRANSITIONS.has(name) && !KNOWN_CSS_PROPERTIES.has(name) && !KNOWN_CSS_PROPERTIES.has(kebabToCamel(name)) && !name.startsWith("--") && name !== "all" && name !== "none") context.report({
82
120
  node,
83
121
  messageId: "unknownTransition",
84
122
  data: {
@@ -1 +1 @@
1
- {"version":3,"file":"valid-transition.js","names":[],"sources":["../../src/rules/valid-transition.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport {\n SEMANTIC_TRANSITIONS,\n KNOWN_CSS_PROPERTIES,\n TRANSITION_SEMANTIC_MAPPING,\n} from '../constants.js';\nimport { replaceInStringValue } from '../fix-utils.js';\n\ntype MessageIds =\n | 'unknownTransition'\n | 'preferSemanticTransition'\n | 'useSemantic';\n\n/**\n * `KNOWN_CSS_PROPERTIES` is keyed by camelCase style keys, but a `transition`\n * value names *CSS* properties, which are kebab-case (`text-decoration-color`).\n * Tasty emits an unmapped name verbatim into the CSS `transition`, so the\n * kebab form is the correct spelling — normalize before the lookup.\n */\nfunction kebabToCamel(name: string): string {\n return name.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());\n}\n\nexport default createRule<[], MessageIds>({\n name: 'valid-transition',\n meta: {\n type: 'suggestion',\n hasSuggestions: true,\n docs: {\n description:\n 'Validate transition property values use valid semantic transition names',\n },\n messages: {\n unknownTransition:\n \"Unknown transition name '{{name}}'. Use a semantic name ({{known}}) or a CSS property name.\",\n preferSemanticTransition:\n \"Transition '{{native}}' has a Tasty semantic equivalent — use '{{semantic}}' instead.\",\n useSemantic: \"Use '{{semantic}}' instead of '{{native}}'\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkTransitionValue(value: string, node: TSESTree.Node): void {\n const groups = value.split(',');\n let offset = 0;\n\n for (const group of groups) {\n const parts = group.trim().split(/\\s+/);\n if (parts.length === 0 || parts[0] === '') {\n offset += group.length + 1;\n continue;\n }\n\n const name = parts[0];\n\n // $$ prefix is always valid (custom property reference: $$name -> --name)\n if (name.startsWith('$$')) {\n offset += group.length + 1;\n continue;\n }\n\n // ## prefix is always valid (color property reference: ##name -> --name-color)\n if (name.startsWith('##')) {\n offset += group.length + 1;\n continue;\n }\n\n const semantic = TRANSITION_SEMANTIC_MAPPING[name];\n if (semantic) {\n const nameStartInGroup = group.indexOf(name);\n const start = offset + nameStartInGroup;\n const end = start + name.length;\n context.report({\n node,\n messageId: 'preferSemanticTransition',\n data: { native: name, semantic },\n suggest: [\n {\n messageId: 'useSemantic',\n data: { native: name, semantic },\n fix(fixer) {\n return replaceInStringValue(\n fixer,\n node,\n [{ start, end, replacement: semantic }],\n context.sourceCode,\n );\n },\n },\n ],\n });\n offset += group.length + 1;\n continue;\n }\n\n if (\n !SEMANTIC_TRANSITIONS.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(kebabToCamel(name)) &&\n !name.startsWith('--') &&\n name !== 'all' &&\n name !== 'none'\n ) {\n context.report({\n node,\n messageId: 'unknownTransition',\n data: {\n name,\n known: [...SEMANTIC_TRANSITIONS].join(', '),\n },\n });\n }\n\n offset += group.length + 1;\n }\n }\n\n return stringStyleValueListeners(\n ctx,\n (value, node, property) => {\n if (property === 'transition') checkTransitionValue(value, node);\n },\n { requireTastyJSX: true },\n );\n },\n});\n"],"mappings":";;;;;;;;;;;;;AAsBA,SAAS,aAAa,MAAsB;AAC1C,QAAO,KAAK,QAAQ,cAAc,GAAG,SAAiB,KAAK,aAAa,CAAC;;AAG3E,+BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,gBAAgB;EAChB,MAAM,EACJ,aACE,2EACH;EACD,UAAU;GACR,mBACE;GACF,0BACE;GACF,aAAa;GACd;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,qBAAqB,OAAe,MAA2B;GACtE,MAAM,SAAS,MAAM,MAAM,IAAI;GAC/B,IAAI,SAAS;AAEb,QAAK,MAAM,SAAS,QAAQ;IAC1B,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;AACvC,QAAI,MAAM,WAAW,KAAK,MAAM,OAAO,IAAI;AACzC,eAAU,MAAM,SAAS;AACzB;;IAGF,MAAM,OAAO,MAAM;AAGnB,QAAI,KAAK,WAAW,KAAK,EAAE;AACzB,eAAU,MAAM,SAAS;AACzB;;AAIF,QAAI,KAAK,WAAW,KAAK,EAAE;AACzB,eAAU,MAAM,SAAS;AACzB;;IAGF,MAAM,WAAW,4BAA4B;AAC7C,QAAI,UAAU;KACZ,MAAM,mBAAmB,MAAM,QAAQ,KAAK;KAC5C,MAAM,QAAQ,SAAS;KACvB,MAAM,MAAM,QAAQ,KAAK;AACzB,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE,QAAQ;OAAM;OAAU;MAChC,SAAS,CACP;OACE,WAAW;OACX,MAAM;QAAE,QAAQ;QAAM;QAAU;OAChC,IAAI,OAAO;AACT,eAAO,qBACL,OACA,MACA,CAAC;SAAE;SAAO;SAAK,aAAa;SAAU,CAAC,EACvC,QAAQ,WACT;;OAEJ,CACF;MACF,CAAC;AACF,eAAU,MAAM,SAAS;AACzB;;AAGF,QACE,CAAC,qBAAqB,IAAI,KAAK,IAC/B,CAAC,qBAAqB,IAAI,KAAK,IAC/B,CAAC,qBAAqB,IAAI,aAAa,KAAK,CAAC,IAC7C,CAAC,KAAK,WAAW,KAAK,IACtB,SAAS,SACT,SAAS,OAET,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MACJ;MACA,OAAO,CAAC,GAAG,qBAAqB,CAAC,KAAK,KAAK;MAC5C;KACF,CAAC;AAGJ,cAAU,MAAM,SAAS;;;AAI7B,SAAO,0BACL,MACC,OAAO,MAAM,aAAa;AACzB,OAAI,aAAa,aAAc,sBAAqB,OAAO,KAAK;KAElE,EAAE,iBAAiB,MAAM,CAC1B;;CAEJ,CAAC"}
1
+ {"version":3,"file":"valid-transition.js","names":[],"sources":["../../src/rules/valid-transition.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport { createRule } from '../create-rule.js';\nimport { TastyContext } from '../context.js';\nimport { stringStyleValueListeners } from '../style-values.js';\nimport {\n SEMANTIC_TRANSITIONS,\n KNOWN_CSS_PROPERTIES,\n TRANSITION_SEMANTIC_MAPPING,\n} from '../constants.js';\nimport { replaceInStringValue } from '../fix-utils.js';\n\ntype MessageIds =\n | 'unknownTransition'\n | 'preferSemanticTransition'\n | 'useSemantic'\n | 'camelCaseTransition'\n | 'useKebabCase';\n\n/**\n * `KNOWN_CSS_PROPERTIES` is keyed by camelCase style keys, but a `transition`\n * value names *CSS* properties, which are kebab-case (`text-decoration-color`).\n * Tasty emits an unmapped name verbatim into the CSS `transition`, so the\n * kebab form is the correct spelling — normalize before the lookup.\n */\nfunction kebabToCamel(name: string): string {\n return name.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase());\n}\n\n/** `gridTemplateColumns` -> `grid-template-columns`. */\nfunction camelToKebab(name: string): string {\n return name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n}\n\n/**\n * Tasty style names that `KNOWN_CSS_PROPERTIES` also lists, but that name no\n * property a browser implements. The `flow` style sets `flex-direction` /\n * `grid-auto-flow`, yet Tasty has no `flow` transition: it emits the name\n * verbatim (`transition: flow …`), which animates nothing.\n */\nconst NON_TRANSITION_STYLE_NAMES = new Set(['flow']);\n\nexport default createRule<[], MessageIds>({\n name: 'valid-transition',\n meta: {\n type: 'suggestion',\n hasSuggestions: true,\n docs: {\n description:\n 'Validate transition property values use valid semantic transition names',\n },\n messages: {\n unknownTransition:\n \"Unknown transition name '{{name}}'. Use a semantic name ({{known}}) or a CSS property name.\",\n preferSemanticTransition:\n \"Transition '{{native}}' has a Tasty semantic equivalent — use '{{semantic}}' instead.\",\n useSemantic: \"Use '{{semantic}}' instead of '{{native}}'\",\n camelCaseTransition:\n \"Transition '{{name}}' compiles to '{{lowered}}', which is not a CSS property. Use the CSS name '{{kebab}}'.\",\n useKebabCase: \"Use '{{kebab}}' instead of '{{name}}'\",\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n const ctx = new TastyContext(context);\n\n function checkTransitionValue(value: string, node: TSESTree.Node): void {\n const groups = value.split(',');\n let offset = 0;\n\n for (const group of groups) {\n const parts = group.trim().split(/\\s+/);\n if (parts.length === 0 || parts[0] === '') {\n offset += group.length + 1;\n continue;\n }\n\n const name = parts[0];\n\n // $$ prefix is always valid (custom property reference: $$name -> --name)\n if (name.startsWith('$$')) {\n offset += group.length + 1;\n continue;\n }\n\n // ## prefix is always valid (color property reference: ##name -> --name-color)\n if (name.startsWith('##')) {\n offset += group.length + 1;\n continue;\n }\n\n const start = offset + group.indexOf(name);\n const end = start + name.length;\n\n const semantic = TRANSITION_SEMANTIC_MAPPING[name];\n if (semantic) {\n context.report({\n node,\n messageId: 'preferSemanticTransition',\n data: { native: name, semantic },\n suggest: [\n {\n messageId: 'useSemantic',\n data: { native: name, semantic },\n fix(fixer) {\n return replaceInStringValue(\n fixer,\n node,\n [{ start, end, replacement: semantic }],\n context.sourceCode,\n );\n },\n },\n ],\n });\n offset += group.length + 1;\n continue;\n }\n\n // Tasty lowercases the name and emits it verbatim, so a camelCase CSS\n // property comes out as `gridtemplatecolumns`, which is no property.\n // Only the semantic names (`zIndex` -> `z-index`) are translated.\n if (\n /[A-Z]/.test(name) &&\n !SEMANTIC_TRANSITIONS.has(name) &&\n KNOWN_CSS_PROPERTIES.has(name)\n ) {\n const kebab = camelToKebab(name);\n const data = { name, lowered: name.toLowerCase(), kebab };\n context.report({\n node,\n messageId: 'camelCaseTransition',\n data,\n suggest: [\n {\n messageId: 'useKebabCase',\n data,\n fix(fixer) {\n return replaceInStringValue(\n fixer,\n node,\n [{ start, end, replacement: kebab }],\n context.sourceCode,\n );\n },\n },\n ],\n });\n offset += group.length + 1;\n continue;\n }\n\n if (\n NON_TRANSITION_STYLE_NAMES.has(name) ||\n (!SEMANTIC_TRANSITIONS.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(name) &&\n !KNOWN_CSS_PROPERTIES.has(kebabToCamel(name)) &&\n !name.startsWith('--') &&\n name !== 'all' &&\n name !== 'none')\n ) {\n context.report({\n node,\n messageId: 'unknownTransition',\n data: {\n name,\n known: [...SEMANTIC_TRANSITIONS].join(', '),\n },\n });\n }\n\n offset += group.length + 1;\n }\n }\n\n return stringStyleValueListeners(\n ctx,\n (value, node, property) => {\n if (property === 'transition') checkTransitionValue(value, node);\n },\n { requireTastyJSX: true },\n );\n },\n});\n"],"mappings":";;;;;;;;;;;;;AAwBA,SAAS,aAAa,MAAsB;AAC1C,QAAO,KAAK,QAAQ,cAAc,GAAG,SAAiB,KAAK,aAAa,CAAC;;;AAI3E,SAAS,aAAa,MAAsB;AAC1C,QAAO,KAAK,QAAQ,WAAW,SAAS,IAAI,KAAK,aAAa,GAAG;;;;;;;;AASnE,MAAM,6BAA6B,IAAI,IAAI,CAAC,OAAO,CAAC;AAEpD,+BAAe,WAA2B;CACxC,MAAM;CACN,MAAM;EACJ,MAAM;EACN,gBAAgB;EAChB,MAAM,EACJ,aACE,2EACH;EACD,UAAU;GACR,mBACE;GACF,0BACE;GACF,aAAa;GACb,qBACE;GACF,cAAc;GACf;EACD,QAAQ,EAAE;EACX;CACD,gBAAgB,EAAE;CAClB,OAAO,SAAS;EACd,MAAM,MAAM,IAAI,aAAa,QAAQ;EAErC,SAAS,qBAAqB,OAAe,MAA2B;GACtE,MAAM,SAAS,MAAM,MAAM,IAAI;GAC/B,IAAI,SAAS;AAEb,QAAK,MAAM,SAAS,QAAQ;IAC1B,MAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,MAAM;AACvC,QAAI,MAAM,WAAW,KAAK,MAAM,OAAO,IAAI;AACzC,eAAU,MAAM,SAAS;AACzB;;IAGF,MAAM,OAAO,MAAM;AAGnB,QAAI,KAAK,WAAW,KAAK,EAAE;AACzB,eAAU,MAAM,SAAS;AACzB;;AAIF,QAAI,KAAK,WAAW,KAAK,EAAE;AACzB,eAAU,MAAM,SAAS;AACzB;;IAGF,MAAM,QAAQ,SAAS,MAAM,QAAQ,KAAK;IAC1C,MAAM,MAAM,QAAQ,KAAK;IAEzB,MAAM,WAAW,4BAA4B;AAC7C,QAAI,UAAU;AACZ,aAAQ,OAAO;MACb;MACA,WAAW;MACX,MAAM;OAAE,QAAQ;OAAM;OAAU;MAChC,SAAS,CACP;OACE,WAAW;OACX,MAAM;QAAE,QAAQ;QAAM;QAAU;OAChC,IAAI,OAAO;AACT,eAAO,qBACL,OACA,MACA,CAAC;SAAE;SAAO;SAAK,aAAa;SAAU,CAAC,EACvC,QAAQ,WACT;;OAEJ,CACF;MACF,CAAC;AACF,eAAU,MAAM,SAAS;AACzB;;AAMF,QACE,QAAQ,KAAK,KAAK,IAClB,CAAC,qBAAqB,IAAI,KAAK,IAC/B,qBAAqB,IAAI,KAAK,EAC9B;KACA,MAAM,QAAQ,aAAa,KAAK;KAChC,MAAM,OAAO;MAAE;MAAM,SAAS,KAAK,aAAa;MAAE;MAAO;AACzD,aAAQ,OAAO;MACb;MACA,WAAW;MACX;MACA,SAAS,CACP;OACE,WAAW;OACX;OACA,IAAI,OAAO;AACT,eAAO,qBACL,OACA,MACA,CAAC;SAAE;SAAO;SAAK,aAAa;SAAO,CAAC,EACpC,QAAQ,WACT;;OAEJ,CACF;MACF,CAAC;AACF,eAAU,MAAM,SAAS;AACzB;;AAGF,QACE,2BAA2B,IAAI,KAAK,IACnC,CAAC,qBAAqB,IAAI,KAAK,IAC9B,CAAC,qBAAqB,IAAI,KAAK,IAC/B,CAAC,qBAAqB,IAAI,aAAa,KAAK,CAAC,IAC7C,CAAC,KAAK,WAAW,KAAK,IACtB,SAAS,SACT,SAAS,OAEX,SAAQ,OAAO;KACb;KACA,WAAW;KACX,MAAM;MACJ;MACA,OAAO,CAAC,GAAG,qBAAqB,CAAC,KAAK,KAAK;MAC5C;KACF,CAAC;AAGJ,cAAU,MAAM,SAAS;;;AAI7B,SAAO,0BACL,MACC,OAAO,MAAM,aAAa;AACzB,OAAI,aAAa,aAAc,sBAAqB,OAAO,KAAK;KAElE,EAAE,iBAAiB,MAAM,CAC1B;;CAEJ,CAAC"}
package/dist/utils.js CHANGED
@@ -53,10 +53,7 @@ function validateColorTokenSyntax(token) {
53
53
  if (tokenName.length === 0) return "Empty color token name before opacity";
54
54
  if (opacitySuffix.startsWith("$")) return null;
55
55
  if (opacitySuffix.length === 0) return "Trailing dot with no opacity value";
56
- const opacity = Number(opacitySuffix);
57
- if (isNaN(opacity)) return `Invalid opacity value '${opacitySuffix}'`;
58
- if (opacity < 0) return "Opacity cannot be negative";
59
- if (opacity > 100) return `Opacity '${opacitySuffix}' exceeds 100`;
56
+ if (!/^\d+$/.test(opacitySuffix)) return /^-\d+$/.test(opacitySuffix) ? "Opacity cannot be negative" : `Invalid opacity value '${opacitySuffix}'`;
60
57
  }
61
58
  return null;
62
59
  }
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport {\n BUILT_IN_UNITS,\n CSS_UNITS,\n BUILT_IN_STATE_PREFIXES,\n} from './constants.js';\nimport type { ResolvedConfig } from './types.js';\n\n/** Strip TypeScript wrappers that do not change an expression's runtime value. */\nexport function unwrapExpression(node: TSESTree.Node): TSESTree.Node {\n let current = node;\n\n while (\n current.type === 'TSAsExpression' ||\n current.type === 'TSSatisfiesExpression' ||\n current.type === 'TSNonNullExpression' ||\n current.type === 'TSTypeAssertion' ||\n current.type === 'TSInstantiationExpression'\n ) {\n current = current.expression;\n }\n\n return current;\n}\n\n/**\n * Gets the string value of a property key node.\n */\nexport function getKeyName(key: TSESTree.Node): string | null {\n if (key.type === 'Identifier') return key.name;\n if (key.type === 'Literal' && typeof key.value === 'string') return key.value;\n if (key.type === 'Literal' && typeof key.value === 'number')\n return String(key.value);\n return null;\n}\n\n/**\n * Gets the string value of a node if it is a string literal.\n */\nexport function getStringValue(node: TSESTree.Node): string | null {\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return node.quasis[0].value.cooked ?? null;\n }\n return null;\n}\n\n/**\n * Checks if a value node is a static literal.\n */\nexport function isStaticValue(node: TSESTree.Node): boolean {\n node = unwrapExpression(node);\n if (node.type === 'Literal') return true;\n if (\n node.type === 'UnaryExpression' &&\n node.operator === '-' &&\n node.argument.type === 'Literal'\n ) {\n return true;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return true;\n }\n if (node.type === 'ArrayExpression') {\n return node.elements.every((el) => el !== null && isStaticValue(el));\n }\n if (node.type === 'ObjectExpression') {\n return node.properties.every(\n (prop) =>\n prop.type === 'Property' && !prop.computed && isStaticValue(prop.value),\n );\n }\n return false;\n}\n\n/**\n * Validates color token syntax.\n * Returns null if valid, or an error message if invalid.\n */\nexport function validateColorTokenSyntax(token: string): string | null {\n // Strip leading # or ##\n let name = token;\n if (name.startsWith('##')) {\n name = name.slice(2);\n } else if (name.startsWith('#')) {\n name = name.slice(1);\n } else {\n return 'Color token must start with #';\n }\n\n if (name.length === 0) return 'Empty color token name';\n\n // Check for opacity suffix\n const dotIndex = name.indexOf('.');\n if (dotIndex !== -1) {\n const tokenName = name.slice(0, dotIndex);\n const opacitySuffix = name.slice(dotIndex + 1);\n\n if (tokenName.length === 0) return 'Empty color token name before opacity';\n\n if (opacitySuffix.startsWith('$')) {\n // Dynamic opacity from CSS custom property — always valid\n return null;\n }\n\n if (opacitySuffix.length === 0) return 'Trailing dot with no opacity value';\n\n const opacity = Number(opacitySuffix);\n if (isNaN(opacity)) return `Invalid opacity value '${opacitySuffix}'`;\n if (opacity < 0) return 'Opacity cannot be negative';\n if (opacity > 100) return `Opacity '${opacitySuffix}' exceeds 100`;\n }\n\n return null;\n}\n\n/**\n * Checks if a string looks like a raw hex color (not a token).\n * Hex colors: #fff, #ffff, #ffffff, #ffffffff (3, 4, 6, or 8 hex chars).\n */\nexport function isRawHexColor(value: string): boolean {\n if (!value.startsWith('#')) return false;\n const hex = value.slice(1).split('.')[0];\n if (![3, 4, 6, 8].includes(hex.length)) return false;\n return /^[0-9a-fA-F]+$/.test(hex);\n}\n\n/**\n * Extracts custom unit from a value token like \"2x\", \"1.5r\", \"3cols\".\n * Returns the unit name, or null if not a custom-unit value.\n */\nexport function extractCustomUnit(token: string): string | null {\n const match = token.match(/^-?[\\d.]+([a-zA-Z]+)$/);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Checks if a unit is valid (built-in, CSS, or in config).\n */\nexport function isValidUnit(unit: string, config: ResolvedConfig): boolean {\n if (config.units === false) return true;\n if (BUILT_IN_UNITS.has(unit)) return true;\n if (CSS_UNITS.has(unit)) return true;\n if (Array.isArray(config.units) && config.units.includes(unit)) return true;\n return false;\n}\n\n/**\n * Checks if a state alias key (starting with @) is known.\n */\nexport function isKnownStateAlias(\n key: string,\n config: ResolvedConfig,\n): boolean {\n // Built-in prefixes\n for (const prefix of BUILT_IN_STATE_PREFIXES) {\n if (key === prefix || key.startsWith(prefix + '(')) return true;\n }\n // Container query shorthand\n if (key.startsWith('@(')) return true;\n // Config aliases\n return config.states.includes(key);\n}\n\n/**\n * Checks if a CSS selector string is basically valid.\n */\nexport function isValidSelector(selector: string): string | null {\n if (selector.length === 0) return 'Selector cannot be empty';\n\n // Check balanced brackets\n let depth = 0;\n for (const char of selector) {\n if (char === '(' || char === '[') depth++;\n if (char === ')' || char === ']') depth--;\n if (depth < 0) return 'Unbalanced brackets in selector';\n }\n if (depth !== 0) return 'Unbalanced brackets in selector';\n\n return null;\n}\n\n/**\n * Finds a property by key name in an object expression.\n */\nexport function findProperty(\n obj: TSESTree.ObjectExpression,\n name: string,\n): TSESTree.Property | undefined {\n for (const prop of obj.properties) {\n if (prop.type === 'Property' && !prop.computed) {\n const keyName = getKeyName(prop.key);\n if (keyName === name) return prop;\n }\n }\n return undefined;\n}\n\n/**\n * Collects local predefined state alias names from a styles ObjectExpression.\n * Local states are top-level `@name` keys with string literal values,\n * mirroring the runtime's `extractLocalPredefinedStates()`.\n */\nexport function collectLocalStateAliases(\n node: TSESTree.ObjectExpression,\n): string[] {\n const aliases: string[] = [];\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null) continue;\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(key)) continue;\n\n // Skip built-in prefixes — they are not local state definitions\n if (BUILT_IN_STATE_PREFIXES.has(key)) continue;\n\n // Must have a string value to be a state definition\n if (getStringValue(prop.value) !== null) {\n aliases.push(key);\n }\n }\n\n return aliases;\n}\n"],"mappings":";;;;AASA,SAAgB,iBAAiB,MAAoC;CACnE,IAAI,UAAU;AAEd,QACE,QAAQ,SAAS,oBACjB,QAAQ,SAAS,2BACjB,QAAQ,SAAS,yBACjB,QAAQ,SAAS,qBACjB,QAAQ,SAAS,4BAEjB,WAAU,QAAQ;AAGpB,QAAO;;;;;AAMT,SAAgB,WAAW,KAAmC;AAC5D,KAAI,IAAI,SAAS,aAAc,QAAO,IAAI;AAC1C,KAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,SAAU,QAAO,IAAI;AACxE,KAAI,IAAI,SAAS,aAAa,OAAO,IAAI,UAAU,SACjD,QAAO,OAAO,IAAI,MAAM;AAC1B,QAAO;;;;;AAMT,SAAgB,eAAe,MAAoC;AACjE,KAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,SACnD,QAAO,KAAK;AAEd,KAAI,KAAK,SAAS,qBAAqB,KAAK,YAAY,WAAW,EACjE,QAAO,KAAK,OAAO,GAAG,MAAM,UAAU;AAExC,QAAO;;;;;AAMT,SAAgB,cAAc,MAA8B;AAC1D,QAAO,iBAAiB,KAAK;AAC7B,KAAI,KAAK,SAAS,UAAW,QAAO;AACpC,KACE,KAAK,SAAS,qBACd,KAAK,aAAa,OAClB,KAAK,SAAS,SAAS,UAEvB,QAAO;AAET,KAAI,KAAK,SAAS,qBAAqB,KAAK,YAAY,WAAW,EACjE,QAAO;AAET,KAAI,KAAK,SAAS,kBAChB,QAAO,KAAK,SAAS,OAAO,OAAO,OAAO,QAAQ,cAAc,GAAG,CAAC;AAEtE,KAAI,KAAK,SAAS,mBAChB,QAAO,KAAK,WAAW,OACpB,SACC,KAAK,SAAS,cAAc,CAAC,KAAK,YAAY,cAAc,KAAK,MAAM,CAC1E;AAEH,QAAO;;;;;;AAOT,SAAgB,yBAAyB,OAA8B;CAErE,IAAI,OAAO;AACX,KAAI,KAAK,WAAW,KAAK,CACvB,QAAO,KAAK,MAAM,EAAE;UACX,KAAK,WAAW,IAAI,CAC7B,QAAO,KAAK,MAAM,EAAE;KAEpB,QAAO;AAGT,KAAI,KAAK,WAAW,EAAG,QAAO;CAG9B,MAAM,WAAW,KAAK,QAAQ,IAAI;AAClC,KAAI,aAAa,IAAI;EACnB,MAAM,YAAY,KAAK,MAAM,GAAG,SAAS;EACzC,MAAM,gBAAgB,KAAK,MAAM,WAAW,EAAE;AAE9C,MAAI,UAAU,WAAW,EAAG,QAAO;AAEnC,MAAI,cAAc,WAAW,IAAI,CAE/B,QAAO;AAGT,MAAI,cAAc,WAAW,EAAG,QAAO;EAEvC,MAAM,UAAU,OAAO,cAAc;AACrC,MAAI,MAAM,QAAQ,CAAE,QAAO,0BAA0B,cAAc;AACnE,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,IAAK,QAAO,YAAY,cAAc;;AAGtD,QAAO;;;;;;AAOT,SAAgB,cAAc,OAAwB;AACpD,KAAI,CAAC,MAAM,WAAW,IAAI,CAAE,QAAO;CACnC,MAAM,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM,IAAI,CAAC;AACtC,KAAI,CAAC;EAAC;EAAG;EAAG;EAAG;EAAE,CAAC,SAAS,IAAI,OAAO,CAAE,QAAO;AAC/C,QAAO,iBAAiB,KAAK,IAAI;;;;;AAgBnC,SAAgB,YAAY,MAAc,QAAiC;AACzE,KAAI,OAAO,UAAU,MAAO,QAAO;AACnC,KAAI,eAAe,IAAI,KAAK,CAAE,QAAO;AACrC,KAAI,UAAU,IAAI,KAAK,CAAE,QAAO;AAChC,KAAI,MAAM,QAAQ,OAAO,MAAM,IAAI,OAAO,MAAM,SAAS,KAAK,CAAE,QAAO;AACvE,QAAO;;;;;AAMT,SAAgB,kBACd,KACA,QACS;AAET,MAAK,MAAM,UAAU,wBACnB,KAAI,QAAQ,UAAU,IAAI,WAAW,SAAS,IAAI,CAAE,QAAO;AAG7D,KAAI,IAAI,WAAW,KAAK,CAAE,QAAO;AAEjC,QAAO,OAAO,OAAO,SAAS,IAAI;;;;;AAMpC,SAAgB,gBAAgB,UAAiC;AAC/D,KAAI,SAAS,WAAW,EAAG,QAAO;CAGlC,IAAI,QAAQ;AACZ,MAAK,MAAM,QAAQ,UAAU;AAC3B,MAAI,SAAS,OAAO,SAAS,IAAK;AAClC,MAAI,SAAS,OAAO,SAAS,IAAK;AAClC,MAAI,QAAQ,EAAG,QAAO;;AAExB,KAAI,UAAU,EAAG,QAAO;AAExB,QAAO;;;;;;;AAwBT,SAAgB,yBACd,MACU;CACV,MAAM,UAAoB,EAAE;AAE5B,MAAK,MAAM,QAAQ,KAAK,YAAY;AAClC,MAAI,KAAK,SAAS,cAAc,KAAK,SAAU;EAE/C,MAAM,MAAM,WAAW,KAAK,IAAI;AAChC,MAAI,QAAQ,KAAM;AAClB,MAAI,CAAC,2BAA2B,KAAK,IAAI,CAAE;AAG3C,MAAI,wBAAwB,IAAI,IAAI,CAAE;AAGtC,MAAI,eAAe,KAAK,MAAM,KAAK,KACjC,SAAQ,KAAK,IAAI;;AAIrB,QAAO"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport {\n BUILT_IN_UNITS,\n CSS_UNITS,\n BUILT_IN_STATE_PREFIXES,\n} from './constants.js';\nimport type { ResolvedConfig } from './types.js';\n\n/** Strip TypeScript wrappers that do not change an expression's runtime value. */\nexport function unwrapExpression(node: TSESTree.Node): TSESTree.Node {\n let current = node;\n\n while (\n current.type === 'TSAsExpression' ||\n current.type === 'TSSatisfiesExpression' ||\n current.type === 'TSNonNullExpression' ||\n current.type === 'TSTypeAssertion' ||\n current.type === 'TSInstantiationExpression'\n ) {\n current = current.expression;\n }\n\n return current;\n}\n\n/**\n * Gets the string value of a property key node.\n */\nexport function getKeyName(key: TSESTree.Node): string | null {\n if (key.type === 'Identifier') return key.name;\n if (key.type === 'Literal' && typeof key.value === 'string') return key.value;\n if (key.type === 'Literal' && typeof key.value === 'number')\n return String(key.value);\n return null;\n}\n\n/**\n * Gets the string value of a node if it is a string literal.\n */\nexport function getStringValue(node: TSESTree.Node): string | null {\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return node.quasis[0].value.cooked ?? null;\n }\n return null;\n}\n\n/**\n * Checks if a value node is a static literal.\n */\nexport function isStaticValue(node: TSESTree.Node): boolean {\n node = unwrapExpression(node);\n if (node.type === 'Literal') return true;\n if (\n node.type === 'UnaryExpression' &&\n node.operator === '-' &&\n node.argument.type === 'Literal'\n ) {\n return true;\n }\n if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {\n return true;\n }\n if (node.type === 'ArrayExpression') {\n return node.elements.every((el) => el !== null && isStaticValue(el));\n }\n if (node.type === 'ObjectExpression') {\n return node.properties.every(\n (prop) =>\n prop.type === 'Property' && !prop.computed && isStaticValue(prop.value),\n );\n }\n return false;\n}\n\n/**\n * Validates color token syntax.\n * Returns null if valid, or an error message if invalid.\n */\nexport function validateColorTokenSyntax(token: string): string | null {\n // Strip leading # or ##\n let name = token;\n if (name.startsWith('##')) {\n name = name.slice(2);\n } else if (name.startsWith('#')) {\n name = name.slice(1);\n } else {\n return 'Color token must start with #';\n }\n\n if (name.length === 0) return 'Empty color token name';\n\n // Check for opacity suffix\n const dotIndex = name.indexOf('.');\n if (dotIndex !== -1) {\n const tokenName = name.slice(0, dotIndex);\n const opacitySuffix = name.slice(dotIndex + 1);\n\n if (tokenName.length === 0) return 'Empty color token name before opacity';\n\n if (opacitySuffix.startsWith('$')) {\n // Dynamic opacity from CSS custom property — always valid\n return null;\n }\n\n if (opacitySuffix.length === 0) return 'Trailing dot with no opacity value';\n\n // Tasty reads the digits as a decimal fraction (`.5` and `.50` are 50%,\n // `.150` is 15%), so any digit string is in range.\n if (!/^\\d+$/.test(opacitySuffix)) {\n return /^-\\d+$/.test(opacitySuffix)\n ? 'Opacity cannot be negative'\n : `Invalid opacity value '${opacitySuffix}'`;\n }\n }\n\n return null;\n}\n\n/**\n * Checks if a string looks like a raw hex color (not a token).\n * Hex colors: #fff, #ffff, #ffffff, #ffffffff (3, 4, 6, or 8 hex chars).\n */\nexport function isRawHexColor(value: string): boolean {\n if (!value.startsWith('#')) return false;\n const hex = value.slice(1).split('.')[0];\n if (![3, 4, 6, 8].includes(hex.length)) return false;\n return /^[0-9a-fA-F]+$/.test(hex);\n}\n\n/**\n * Extracts custom unit from a value token like \"2x\", \"1.5r\", \"3cols\".\n * Returns the unit name, or null if not a custom-unit value.\n */\nexport function extractCustomUnit(token: string): string | null {\n const match = token.match(/^-?[\\d.]+([a-zA-Z]+)$/);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Checks if a unit is valid (built-in, CSS, or in config).\n */\nexport function isValidUnit(unit: string, config: ResolvedConfig): boolean {\n if (config.units === false) return true;\n if (BUILT_IN_UNITS.has(unit)) return true;\n if (CSS_UNITS.has(unit)) return true;\n if (Array.isArray(config.units) && config.units.includes(unit)) return true;\n return false;\n}\n\n/**\n * Checks if a state alias key (starting with @) is known.\n */\nexport function isKnownStateAlias(\n key: string,\n config: ResolvedConfig,\n): boolean {\n // Built-in prefixes\n for (const prefix of BUILT_IN_STATE_PREFIXES) {\n if (key === prefix || key.startsWith(prefix + '(')) return true;\n }\n // Container query shorthand\n if (key.startsWith('@(')) return true;\n // Config aliases\n return config.states.includes(key);\n}\n\n/**\n * Checks if a CSS selector string is basically valid.\n */\nexport function isValidSelector(selector: string): string | null {\n if (selector.length === 0) return 'Selector cannot be empty';\n\n // Check balanced brackets\n let depth = 0;\n for (const char of selector) {\n if (char === '(' || char === '[') depth++;\n if (char === ')' || char === ']') depth--;\n if (depth < 0) return 'Unbalanced brackets in selector';\n }\n if (depth !== 0) return 'Unbalanced brackets in selector';\n\n return null;\n}\n\n/**\n * Finds a property by key name in an object expression.\n */\nexport function findProperty(\n obj: TSESTree.ObjectExpression,\n name: string,\n): TSESTree.Property | undefined {\n for (const prop of obj.properties) {\n if (prop.type === 'Property' && !prop.computed) {\n const keyName = getKeyName(prop.key);\n if (keyName === name) return prop;\n }\n }\n return undefined;\n}\n\n/**\n * Collects local predefined state alias names from a styles ObjectExpression.\n * Local states are top-level `@name` keys with string literal values,\n * mirroring the runtime's `extractLocalPredefinedStates()`.\n */\nexport function collectLocalStateAliases(\n node: TSESTree.ObjectExpression,\n): string[] {\n const aliases: string[] = [];\n\n for (const prop of node.properties) {\n if (prop.type !== 'Property' || prop.computed) continue;\n\n const key = getKeyName(prop.key);\n if (key === null) continue;\n if (!/^@[A-Za-z][A-Za-z0-9-]*$/.test(key)) continue;\n\n // Skip built-in prefixes — they are not local state definitions\n if (BUILT_IN_STATE_PREFIXES.has(key)) continue;\n\n // Must have a string value to be a state definition\n if (getStringValue(prop.value) !== null) {\n aliases.push(key);\n }\n }\n\n return aliases;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/eslint-plugin-tasty",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "ESLint plugin for validating tasty() and tastyStatic() style objects",
5
5
  "type": "module",
6
6
  "exports": {