@tenphi/eslint-plugin-tasty 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/dist/_virtual/_rolldown/runtime.mjs +7 -0
  4. package/dist/config.mjs +118 -0
  5. package/dist/config.mjs.map +1 -0
  6. package/dist/configs.d.mts +8 -0
  7. package/dist/configs.mjs +36 -0
  8. package/dist/configs.mjs.map +1 -0
  9. package/dist/constants.mjs +700 -0
  10. package/dist/constants.mjs.map +1 -0
  11. package/dist/context.mjs +180 -0
  12. package/dist/context.mjs.map +1 -0
  13. package/dist/create-rule.mjs +8 -0
  14. package/dist/create-rule.mjs.map +1 -0
  15. package/dist/index.d.mts +8 -0
  16. package/dist/index.mjs +83 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/parser.mjs +46 -0
  19. package/dist/parser.mjs.map +1 -0
  20. package/dist/property-expectations.mjs +168 -0
  21. package/dist/property-expectations.mjs.map +1 -0
  22. package/dist/rules/consistent-token-usage.mjs +88 -0
  23. package/dist/rules/consistent-token-usage.mjs.map +1 -0
  24. package/dist/rules/known-property.mjs +55 -0
  25. package/dist/rules/known-property.mjs.map +1 -0
  26. package/dist/rules/no-duplicate-state.mjs +51 -0
  27. package/dist/rules/no-duplicate-state.mjs.map +1 -0
  28. package/dist/rules/no-important.mjs +44 -0
  29. package/dist/rules/no-important.mjs.map +1 -0
  30. package/dist/rules/no-nested-selector.mjs +40 -0
  31. package/dist/rules/no-nested-selector.mjs.map +1 -0
  32. package/dist/rules/no-nested-state-map.mjs +44 -0
  33. package/dist/rules/no-nested-state-map.mjs.map +1 -0
  34. package/dist/rules/no-raw-color-values.mjs +84 -0
  35. package/dist/rules/no-raw-color-values.mjs.map +1 -0
  36. package/dist/rules/no-runtime-styles-mutation.mjs +52 -0
  37. package/dist/rules/no-runtime-styles-mutation.mjs.map +1 -0
  38. package/dist/rules/no-styles-prop.mjs +25 -0
  39. package/dist/rules/no-styles-prop.mjs.map +1 -0
  40. package/dist/rules/no-unknown-state-alias.mjs +49 -0
  41. package/dist/rules/no-unknown-state-alias.mjs.map +1 -0
  42. package/dist/rules/prefer-shorthand-property.mjs +45 -0
  43. package/dist/rules/prefer-shorthand-property.mjs.map +1 -0
  44. package/dist/rules/require-default-state.mjs +47 -0
  45. package/dist/rules/require-default-state.mjs.map +1 -0
  46. package/dist/rules/static-no-dynamic-values.mjs +54 -0
  47. package/dist/rules/static-no-dynamic-values.mjs.map +1 -0
  48. package/dist/rules/static-valid-selector.mjs +51 -0
  49. package/dist/rules/static-valid-selector.mjs.map +1 -0
  50. package/dist/rules/valid-boolean-property.mjs +45 -0
  51. package/dist/rules/valid-boolean-property.mjs.map +1 -0
  52. package/dist/rules/valid-color-token.mjs +84 -0
  53. package/dist/rules/valid-color-token.mjs.map +1 -0
  54. package/dist/rules/valid-custom-property.mjs +69 -0
  55. package/dist/rules/valid-custom-property.mjs.map +1 -0
  56. package/dist/rules/valid-custom-unit.mjs +62 -0
  57. package/dist/rules/valid-custom-unit.mjs.map +1 -0
  58. package/dist/rules/valid-directional-modifier.mjs +71 -0
  59. package/dist/rules/valid-directional-modifier.mjs.map +1 -0
  60. package/dist/rules/valid-preset.mjs +64 -0
  61. package/dist/rules/valid-preset.mjs.map +1 -0
  62. package/dist/rules/valid-radius-shape.mjs +77 -0
  63. package/dist/rules/valid-radius-shape.mjs.map +1 -0
  64. package/dist/rules/valid-recipe.mjs +51 -0
  65. package/dist/rules/valid-recipe.mjs.map +1 -0
  66. package/dist/rules/valid-state-key.mjs +71 -0
  67. package/dist/rules/valid-state-key.mjs.map +1 -0
  68. package/dist/rules/valid-styles-structure.mjs +79 -0
  69. package/dist/rules/valid-styles-structure.mjs.map +1 -0
  70. package/dist/rules/valid-sub-element.mjs +46 -0
  71. package/dist/rules/valid-sub-element.mjs.map +1 -0
  72. package/dist/rules/valid-transition.mjs +62 -0
  73. package/dist/rules/valid-transition.mjs.map +1 -0
  74. package/dist/rules/valid-value.mjs +123 -0
  75. package/dist/rules/valid-value.mjs.map +1 -0
  76. package/dist/types.d.mts +25 -0
  77. package/dist/utils.mjs +152 -0
  78. package/dist/utils.mjs.map +1 -0
  79. package/package.json +90 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.mjs","names":[],"sources":["../src/constants.ts"],"sourcesContent":["/**\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 'paddingInline',\n 'paddingBlock',\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 '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 'containerType',\n 'containerName',\n 'willChange',\n 'isolation',\n 'touchAction',\n 'scrollBehavior',\n 'scrollSnapType',\n 'scrollSnapAlign',\n 'caretColor',\n 'accentColor',\n 'colorScheme',\n]);\n\n/**\n * Special top-level keys that are valid but not regular style properties.\n */\nexport const SPECIAL_STYLE_KEYS = new Set(['@keyframes', '@properties']);\n\n/**\n * CSS property names (common subset for validation).\n * When a key is camelCase and matches a known CSS property, it's valid.\n */\nexport const KNOWN_CSS_PROPERTIES = new Set([\n 'all',\n 'animation',\n 'animationDelay',\n 'animationDirection',\n 'animationDuration',\n 'animationFillMode',\n 'animationIterationCount',\n 'animationName',\n 'animationPlayState',\n 'animationTimingFunction',\n 'appearance',\n 'aspectRatio',\n 'backdropFilter',\n 'backfaceVisibility',\n 'background',\n 'backgroundAttachment',\n 'backgroundBlendMode',\n 'backgroundClip',\n 'backgroundColor',\n 'backgroundImage',\n 'backgroundOrigin',\n 'backgroundPosition',\n 'backgroundRepeat',\n 'backgroundSize',\n 'blockSize',\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 'borderCollapse',\n 'borderColor',\n 'borderImage',\n 'borderInline',\n 'borderInlineColor',\n 'borderInlineEnd',\n 'borderInlineStart',\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 'borderStyle',\n 'borderTop',\n 'borderTopColor',\n 'borderTopLeftRadius',\n 'borderTopRightRadius',\n 'borderTopStyle',\n 'borderTopWidth',\n 'borderWidth',\n 'bottom',\n 'boxDecorationBreak',\n 'boxShadow',\n 'boxSizing',\n 'breakAfter',\n 'breakBefore',\n 'breakInside',\n 'captionSide',\n 'caretColor',\n 'clear',\n 'clip',\n 'clipPath',\n 'color',\n 'colorScheme',\n 'columnCount',\n 'columnFill',\n 'columnGap',\n 'columnRule',\n 'columnRuleColor',\n 'columnRuleStyle',\n 'columnRuleWidth',\n 'columnSpan',\n 'columnWidth',\n 'columns',\n 'contain',\n 'containerName',\n 'containerType',\n 'content',\n 'contentVisibility',\n 'counterIncrement',\n 'counterReset',\n 'counterSet',\n 'cursor',\n 'direction',\n 'display',\n 'emptyCells',\n 'filter',\n 'flex',\n 'flexBasis',\n 'flexDirection',\n 'flexFlow',\n 'flexGrow',\n 'flexShrink',\n 'flexWrap',\n 'float',\n 'font',\n 'fontFamily',\n 'fontFeatureSettings',\n 'fontKerning',\n 'fontOpticalSizing',\n 'fontSize',\n 'fontSizeAdjust',\n 'fontStretch',\n 'fontStyle',\n 'fontSynthesis',\n 'fontVariant',\n 'fontVariantAlternates',\n 'fontVariantCaps',\n 'fontVariantEastAsian',\n 'fontVariantLigatures',\n 'fontVariantNumeric',\n 'fontVariantPosition',\n 'fontWeight',\n 'gap',\n 'grid',\n 'gridArea',\n 'gridAutoColumns',\n 'gridAutoFlow',\n 'gridAutoRows',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnStart',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowStart',\n 'gridTemplate',\n 'gridTemplateAreas',\n 'gridTemplateColumns',\n 'gridTemplateRows',\n 'height',\n 'hyphens',\n 'imageRendering',\n 'inlineSize',\n 'inset',\n 'insetBlock',\n 'insetBlockEnd',\n 'insetBlockStart',\n 'insetInline',\n 'insetInlineEnd',\n 'insetInlineStart',\n 'isolation',\n 'justifyContent',\n 'justifyItems',\n 'justifySelf',\n 'left',\n 'letterSpacing',\n 'lineBreak',\n 'lineHeight',\n 'listStyle',\n 'listStyleImage',\n 'listStylePosition',\n 'listStyleType',\n 'margin',\n 'marginBlock',\n 'marginBlockEnd',\n 'marginBlockStart',\n 'marginBottom',\n 'marginInline',\n 'marginInlineEnd',\n 'marginInlineStart',\n 'marginLeft',\n 'marginRight',\n 'marginTop',\n 'maskImage',\n 'maxBlockSize',\n 'maxHeight',\n 'maxInlineSize',\n 'maxWidth',\n 'minBlockSize',\n 'minHeight',\n 'minInlineSize',\n 'minWidth',\n 'mixBlendMode',\n 'objectFit',\n 'objectPosition',\n 'opacity',\n 'order',\n 'orphans',\n 'outline',\n 'outlineColor',\n 'outlineOffset',\n 'outlineStyle',\n 'outlineWidth',\n 'overflow',\n 'overflowAnchor',\n 'overflowWrap',\n 'overflowX',\n 'overflowY',\n 'overscrollBehavior',\n 'padding',\n 'paddingBlock',\n 'paddingBlockEnd',\n 'paddingBlockStart',\n 'paddingBottom',\n 'paddingInline',\n 'paddingInlineEnd',\n 'paddingInlineStart',\n 'paddingLeft',\n 'paddingRight',\n 'paddingTop',\n 'perspective',\n 'perspectiveOrigin',\n 'placeContent',\n 'placeItems',\n 'placeSelf',\n 'pointerEvents',\n 'position',\n 'quotes',\n 'resize',\n 'right',\n 'rotate',\n 'rowGap',\n 'scale',\n 'scrollBehavior',\n 'scrollMargin',\n 'scrollPadding',\n 'scrollSnapAlign',\n 'scrollSnapStop',\n 'scrollSnapType',\n 'scrollbarColor',\n 'scrollbarGutter',\n 'scrollbarWidth',\n 'shapeOutside',\n 'tabSize',\n 'tableLayout',\n 'textAlign',\n 'textAlignLast',\n 'textCombineUpright',\n 'textDecoration',\n 'textDecorationColor',\n 'textDecorationLine',\n 'textDecorationSkipInk',\n 'textDecorationStyle',\n 'textDecorationThickness',\n 'textEmphasis',\n 'textIndent',\n 'textOrientation',\n 'textOverflow',\n 'textRendering',\n 'textShadow',\n 'textTransform',\n 'textUnderlineOffset',\n 'textUnderlinePosition',\n 'textWrap',\n 'top',\n 'touchAction',\n 'transform',\n 'transformOrigin',\n 'transformStyle',\n 'transition',\n 'transitionDelay',\n 'transitionDuration',\n 'transitionProperty',\n 'transitionTimingFunction',\n 'translate',\n 'unicodeBidi',\n 'userSelect',\n 'verticalAlign',\n 'visibility',\n 'whiteSpace',\n 'widows',\n 'width',\n 'willChange',\n 'wordBreak',\n 'wordSpacing',\n 'writingMode',\n 'zIndex',\n]);\n\n/**\n * Built-in custom units recognized by the tasty parser.\n */\nexport const BUILT_IN_UNITS = new Set([\n 'x',\n 'r',\n 'cr',\n 'bw',\n 'ow',\n 'fs',\n 'lh',\n 'sf',\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 */\nexport const BOOLEAN_TRUE_PROPERTIES = new Set([\n 'border',\n 'radius',\n 'padding',\n 'gap',\n 'fill',\n 'color',\n 'outline',\n 'width',\n 'height',\n 'hide',\n 'preset',\n 'font',\n 'scrollbar',\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};\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]);\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 }\n> = {\n backgroundColor: { property: 'fill', hint: \"fill: '...'\" },\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: '...'\" },\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: '... strong' (with strong modifier)\",\n },\n lineHeight: {\n property: 'preset',\n hint: \"preset: '... tight' (with tight modifier)\",\n },\n boxShadow: { property: 'shadow', hint: \"shadow: '...'\" },\n};\n\n/**\n * Known preset modifiers.\n */\nexport const PRESET_MODIFIERS = new Set(['strong', 'italic', 'tight']);\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 '@own',\n '@supports',\n '@starting',\n '@keyframes',\n '@properties',\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"],"mappings":";;;;AAGA,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;CACD,CAAC;;;;AAKF,MAAa,qBAAqB,IAAI,IAAI,CAAC,cAAc,cAAc,CAAC;;;;;AAMxE,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;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;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;;;;AAKF,MAAa,iBAAiB,IAAI,IAAI;CACpC;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;;;;AAKF,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,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;CAClD;;;;AAKD,MAAa,gBAAgB,IAAI,IAAI;CAAC;CAAS;CAAW;CAAQ;CAAW,CAAC;;;;AAK9E,MAAa,uBAAuB,IAAI,IAAI;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;AAKF,MAAa,oBAGT;CACF,iBAAiB;EAAE,UAAU;EAAQ,MAAM;EAAe;CAC1D,aAAa;EAAE,UAAU;EAAU,MAAM;EAAiB;CAC1D,aAAa;EAAE,UAAU;EAAU,MAAM;EAAiB;CAC1D,aAAa;EAAE,UAAU;EAAU,MAAM;EAAiB;CAC1D,WAAW;EAAE,UAAU;EAAU,MAAM;EAAqB;CAC5D,aAAa;EAAE,UAAU;EAAU,MAAM;EAAuB;CAChE,cAAc;EAAE,UAAU;EAAU,MAAM;EAAwB;CAClE,YAAY;EAAE,UAAU;EAAU,MAAM;EAAsB;CAC9D,cAAc;EAAE,UAAU;EAAU,MAAM;EAAiB;CAC3D,UAAU;EAAE,UAAU;EAAS,MAAM;EAAoB;CACzD,UAAU;EAAE,UAAU;EAAS,MAAM;EAAoB;CACzD,WAAW;EAAE,UAAU;EAAU,MAAM;EAAqB;CAC5D,WAAW;EAAE,UAAU;EAAU,MAAM;EAAqB;CAC5D,eAAe;EAAE,UAAU;EAAQ,MAAM;EAAe;CACxD,UAAU;EAAE,UAAU;EAAQ,MAAM;EAAe;CACnD,UAAU;EAAE,UAAU;EAAQ,MAAM;EAAe;CACnD,cAAc;EAAE,UAAU;EAAQ,MAAM;EAAe;CACvD,eAAe;EAAE,UAAU;EAAW,MAAM;EAA2B;CACvE,YAAY;EAAE,UAAU;EAAW,MAAM;EAAsB;CAC/D,cAAc;EAAE,UAAU;EAAW,MAAM;EAAwB;CACnE,eAAe;EAAE,UAAU;EAAW,MAAM;EAAyB;CACrE,aAAa;EAAE,UAAU;EAAW,MAAM;EAAuB;CACjE,WAAW;EAAE,UAAU;EAAU,MAAM;EAAqB;CAC5D,aAAa;EAAE,UAAU;EAAU,MAAM;EAAuB;CAChE,cAAc;EAAE,UAAU;EAAU,MAAM;EAAwB;CAClE,YAAY;EAAE,UAAU;EAAU,MAAM;EAAsB;CAC9D,UAAU;EAAE,UAAU;EAAU,MAAM;EAAiB;CACvD,YAAY;EACV,UAAU;EACV,MAAM;EACP;CACD,YAAY;EACV,UAAU;EACV,MAAM;EACP;CACD,WAAW;EAAE,UAAU;EAAU,MAAM;EAAiB;CACzD;;;;AAKD,MAAa,mBAAmB,IAAI,IAAI;CAAC;CAAU;CAAU;CAAQ,CAAC;;;;AAKtE,MAAa,yBAAyB,CAAC,iBAAiB,uBAAuB;;;;AAK/E,MAAa,0BAA0B,IAAI,IAAI;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC"}
@@ -0,0 +1,180 @@
1
+ import { DEFAULT_IMPORT_SOURCES } from "./constants.mjs";
2
+ import { loadConfig } from "./config.mjs";
3
+
4
+ //#region src/context.ts
5
+ const TASTY_FUNCTION_NAMES = new Set([
6
+ "tasty",
7
+ "tastyStatic",
8
+ "useStyles",
9
+ "useGlobalStyles"
10
+ ]);
11
+ /**
12
+ * Context tracker for a single file's lint pass.
13
+ * Tracks which imports come from tasty and provides
14
+ * helpers to determine if a node is in a tasty style context.
15
+ */
16
+ var TastyContext = class {
17
+ config;
18
+ imports = /* @__PURE__ */ new Map();
19
+ importSources;
20
+ constructor(context, config) {
21
+ this.context = context;
22
+ this.config = config ?? loadConfig(context.filename);
23
+ this.importSources = new Set([...DEFAULT_IMPORT_SOURCES, ...this.config.importSources]);
24
+ }
25
+ trackImport(node) {
26
+ const source = node.source.value;
27
+ if (!this.importSources.has(source)) return;
28
+ for (const specifier of node.specifiers) if (specifier.type === "ImportSpecifier") {
29
+ const importedName = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
30
+ if (TASTY_FUNCTION_NAMES.has(importedName)) this.imports.set(specifier.local.name, {
31
+ localName: specifier.local.name,
32
+ importedName,
33
+ source
34
+ });
35
+ }
36
+ }
37
+ getImport(localName) {
38
+ return this.imports.get(localName);
39
+ }
40
+ isTastyCall(node) {
41
+ if (node.callee.type !== "Identifier") return void 0;
42
+ return this.imports.get(node.callee.name);
43
+ }
44
+ /**
45
+ * Determines whether an object expression is a tasty style object
46
+ * by walking up the AST to find a recognized call expression.
47
+ */
48
+ isStyleObject(node) {
49
+ return this.getStyleContext(node) !== null;
50
+ }
51
+ getStyleContext(node) {
52
+ let current = node;
53
+ while (current) {
54
+ if (current.type === "CallExpression") {
55
+ const imp = this.isTastyCall(current);
56
+ if (!imp) return null;
57
+ const name = imp.importedName;
58
+ const isStaticCall = name === "tastyStatic";
59
+ if (name === "tasty") return this.getTastyCallContext(current, node);
60
+ if (name === "tastyStatic") return this.getTastyStaticCallContext(current, node);
61
+ if (name === "useStyles") {
62
+ if (current.arguments[0] === node) return {
63
+ type: "useStyles",
64
+ isStaticCall: false,
65
+ isSelectorMode: false,
66
+ isExtending: false
67
+ };
68
+ }
69
+ if (name === "useGlobalStyles") {
70
+ if (current.arguments[1] === node) return {
71
+ type: "useGlobalStyles",
72
+ isStaticCall,
73
+ isSelectorMode: true,
74
+ isExtending: false
75
+ };
76
+ }
77
+ return null;
78
+ }
79
+ current = current.parent;
80
+ }
81
+ return null;
82
+ }
83
+ getTastyCallContext(call, targetNode) {
84
+ const args = call.arguments;
85
+ const optionsArg = args.length >= 2 && args[0].type !== "ObjectExpression" ? args[1] : args[0];
86
+ if (optionsArg?.type === "ObjectExpression" && this.isInsideStylesProperty(optionsArg, targetNode)) return {
87
+ type: "tasty",
88
+ isStaticCall: false,
89
+ isSelectorMode: false,
90
+ isExtending: args.length >= 2 && args[0].type !== "ObjectExpression"
91
+ };
92
+ if (optionsArg?.type === "ObjectExpression" && this.isInsideVariantsProperty(optionsArg, targetNode)) return {
93
+ type: "tasty",
94
+ isStaticCall: false,
95
+ isSelectorMode: false,
96
+ isExtending: false
97
+ };
98
+ return null;
99
+ }
100
+ getTastyStaticCallContext(call, targetNode) {
101
+ const args = call.arguments;
102
+ if (args.length === 1 && args[0] === targetNode) return {
103
+ type: "tastyStatic",
104
+ isStaticCall: true,
105
+ isSelectorMode: false,
106
+ isExtending: false
107
+ };
108
+ if (args.length === 2 && args[1] === targetNode) {
109
+ const isSelectorMode = args[0].type === "Literal";
110
+ return {
111
+ type: "tastyStatic",
112
+ isStaticCall: true,
113
+ isSelectorMode,
114
+ isExtending: !isSelectorMode
115
+ };
116
+ }
117
+ return null;
118
+ }
119
+ isInsideStylesProperty(optionsObj, targetNode) {
120
+ for (const prop of optionsObj.properties) if (prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "styles" && prop.value === targetNode) return true;
121
+ return false;
122
+ }
123
+ isInsideVariantsProperty(optionsObj, targetNode) {
124
+ for (const prop of optionsObj.properties) if (prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "variants" && prop.value.type === "ObjectExpression") {
125
+ for (const variantProp of prop.value.properties) if (variantProp.type === "Property" && variantProp.value === targetNode) return true;
126
+ }
127
+ return false;
128
+ }
129
+ /**
130
+ * Checks if a property value node is a state mapping object
131
+ * (i.e., an object where keys are state expressions and values are style values).
132
+ */
133
+ isStateMap(node, parentProperty) {
134
+ const key = parentProperty.key;
135
+ if (key.type !== "Identifier") return false;
136
+ if (/^[A-Z]/.test(key.name)) return false;
137
+ if (key.name === "@keyframes" || key.name === "@properties") return false;
138
+ return node.properties.some((prop) => {
139
+ if (prop.type !== "Property") return false;
140
+ if (prop.key.type === "Literal" && prop.key.value === "") return true;
141
+ if (prop.key.type === "Identifier") return true;
142
+ if (prop.key.type === "Literal" && typeof prop.key.value === "string") return true;
143
+ return false;
144
+ });
145
+ }
146
+ /**
147
+ * Checks if a key represents a sub-element (starts with uppercase).
148
+ */
149
+ isSubElementKey(key) {
150
+ return /^[A-Z]/.test(key);
151
+ }
152
+ /**
153
+ * Checks if a key represents a nested selector (starts with &).
154
+ */
155
+ isNestedSelectorKey(key) {
156
+ return key.startsWith("&");
157
+ }
158
+ /**
159
+ * Checks if a key is a custom CSS property definition ($name or $$name).
160
+ */
161
+ isCustomPropertyKey(key) {
162
+ return key.startsWith("$");
163
+ }
164
+ /**
165
+ * Checks if a key is a color token definition (#name or ##name).
166
+ */
167
+ isColorTokenKey(key) {
168
+ return key.startsWith("#");
169
+ }
170
+ /**
171
+ * Checks if a key is a special @ property (@keyframes, @properties).
172
+ */
173
+ isSpecialKey(key) {
174
+ return key.startsWith("@");
175
+ }
176
+ };
177
+
178
+ //#endregion
179
+ export { TastyContext };
180
+ //# sourceMappingURL=context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.mjs","names":[],"sources":["../src/context.ts"],"sourcesContent":["import type { TSESTree } from '@typescript-eslint/utils';\nimport type { RuleContext } from '@typescript-eslint/utils/ts-eslint';\nimport type { ResolvedConfig } from './types.js';\nimport { loadConfig } from './config.js';\nimport { DEFAULT_IMPORT_SOURCES } from './constants.js';\n\nexport interface TastyImport {\n localName: string;\n importedName: string;\n source: string;\n}\n\nconst TASTY_FUNCTION_NAMES = new Set([\n 'tasty',\n 'tastyStatic',\n 'useStyles',\n 'useGlobalStyles',\n]);\n\n/**\n * Context tracker for a single file's lint pass.\n * Tracks which imports come from tasty and provides\n * helpers to determine if a node is in a tasty style context.\n */\nexport class TastyContext {\n readonly config: ResolvedConfig;\n private imports = new Map<string, TastyImport>();\n private importSources: Set<string>;\n\n constructor(\n private context: RuleContext<string, unknown[]>,\n config?: ResolvedConfig,\n ) {\n this.config = config ?? loadConfig(context.filename);\n this.importSources = new Set([\n ...DEFAULT_IMPORT_SOURCES,\n ...this.config.importSources,\n ]);\n }\n\n trackImport(node: TSESTree.ImportDeclaration): void {\n const source = node.source.value;\n if (!this.importSources.has(source)) return;\n\n for (const specifier of node.specifiers) {\n if (specifier.type === 'ImportSpecifier') {\n const importedName =\n specifier.imported.type === 'Identifier'\n ? specifier.imported.name\n : specifier.imported.value;\n if (TASTY_FUNCTION_NAMES.has(importedName)) {\n this.imports.set(specifier.local.name, {\n localName: specifier.local.name,\n importedName,\n source,\n });\n }\n }\n }\n }\n\n getImport(localName: string): TastyImport | undefined {\n return this.imports.get(localName);\n }\n\n isTastyCall(node: TSESTree.CallExpression): TastyImport | undefined {\n if (node.callee.type !== 'Identifier') return undefined;\n return this.imports.get(node.callee.name);\n }\n\n /**\n * Determines whether an object expression is a tasty style object\n * by walking up the AST to find a recognized call expression.\n */\n isStyleObject(node: TSESTree.ObjectExpression): boolean {\n return this.getStyleContext(node) !== null;\n }\n\n getStyleContext(node: TSESTree.Node): {\n type: 'tasty' | 'tastyStatic' | 'useStyles' | 'useGlobalStyles';\n isStaticCall: boolean;\n isSelectorMode: boolean;\n isExtending: boolean;\n } | null {\n let current: TSESTree.Node | undefined = node;\n\n while (current) {\n if (current.type === 'CallExpression') {\n const imp = this.isTastyCall(current);\n if (!imp) return null;\n\n const name = imp.importedName;\n const isStaticCall = name === 'tastyStatic';\n\n if (name === 'tasty') {\n return this.getTastyCallContext(current, node);\n }\n\n if (name === 'tastyStatic') {\n return this.getTastyStaticCallContext(current, node);\n }\n\n if (name === 'useStyles') {\n if (current.arguments[0] === node) {\n return {\n type: 'useStyles',\n isStaticCall: false,\n isSelectorMode: false,\n isExtending: false,\n };\n }\n }\n\n if (name === 'useGlobalStyles') {\n if (current.arguments[1] === node) {\n return {\n type: 'useGlobalStyles',\n isStaticCall,\n isSelectorMode: true,\n isExtending: false,\n };\n }\n }\n\n return null;\n }\n\n current = current.parent;\n }\n\n return null;\n }\n\n private getTastyCallContext(\n call: TSESTree.CallExpression,\n targetNode: TSESTree.Node,\n ) {\n const args = call.arguments;\n\n // tasty({ styles: { ... } }) or tasty(Component, { styles: { ... } })\n const optionsArg =\n args.length >= 2 && args[0].type !== 'ObjectExpression'\n ? args[1]\n : args[0];\n\n if (\n optionsArg?.type === 'ObjectExpression' &&\n this.isInsideStylesProperty(optionsArg, targetNode)\n ) {\n return {\n type: 'tasty' as const,\n isStaticCall: false,\n isSelectorMode: false,\n isExtending: args.length >= 2 && args[0].type !== 'ObjectExpression',\n };\n }\n\n // Check if inside variants\n if (\n optionsArg?.type === 'ObjectExpression' &&\n this.isInsideVariantsProperty(optionsArg, targetNode)\n ) {\n return {\n type: 'tasty' as const,\n isStaticCall: false,\n isSelectorMode: false,\n isExtending: false,\n };\n }\n\n return null;\n }\n\n private getTastyStaticCallContext(\n call: TSESTree.CallExpression,\n targetNode: TSESTree.Node,\n ) {\n const args = call.arguments;\n\n // tastyStatic({ ... })\n if (args.length === 1 && args[0] === targetNode) {\n return {\n type: 'tastyStatic' as const,\n isStaticCall: true,\n isSelectorMode: false,\n isExtending: false,\n };\n }\n\n // tastyStatic(base, { ... }) or tastyStatic('selector', { ... })\n if (args.length === 2 && args[1] === targetNode) {\n const isSelectorMode = args[0].type === 'Literal';\n return {\n type: 'tastyStatic' as const,\n isStaticCall: true,\n isSelectorMode,\n isExtending: !isSelectorMode,\n };\n }\n\n return null;\n }\n\n private isInsideStylesProperty(\n optionsObj: TSESTree.ObjectExpression,\n targetNode: TSESTree.Node,\n ): boolean {\n for (const prop of optionsObj.properties) {\n if (\n prop.type === 'Property' &&\n prop.key.type === 'Identifier' &&\n prop.key.name === 'styles' &&\n prop.value === targetNode\n ) {\n return true;\n }\n }\n return false;\n }\n\n private isInsideVariantsProperty(\n optionsObj: TSESTree.ObjectExpression,\n targetNode: TSESTree.Node,\n ): boolean {\n for (const prop of optionsObj.properties) {\n if (\n prop.type === 'Property' &&\n prop.key.type === 'Identifier' &&\n prop.key.name === 'variants' &&\n prop.value.type === 'ObjectExpression'\n ) {\n for (const variantProp of prop.value.properties) {\n if (\n variantProp.type === 'Property' &&\n variantProp.value === targetNode\n ) {\n return true;\n }\n }\n }\n }\n return false;\n }\n\n /**\n * Checks if a property value node is a state mapping object\n * (i.e., an object where keys are state expressions and values are style values).\n */\n isStateMap(\n node: TSESTree.ObjectExpression,\n parentProperty: TSESTree.Property,\n ): boolean {\n const key = parentProperty.key;\n if (key.type !== 'Identifier') return false;\n\n // If the key starts with uppercase, it's a sub-element, not a state map\n if (/^[A-Z]/.test(key.name)) return false;\n\n // Special keys are not state maps\n if (key.name === '@keyframes' || key.name === '@properties') return false;\n\n // If the object has keys that look like state expressions, it's a state map\n return node.properties.some((prop) => {\n if (prop.type !== 'Property') return false;\n if (prop.key.type === 'Literal' && prop.key.value === '') return true;\n if (prop.key.type === 'Identifier') return true;\n if (prop.key.type === 'Literal' && typeof prop.key.value === 'string') {\n return true;\n }\n return false;\n });\n }\n\n /**\n * Checks if a key represents a sub-element (starts with uppercase).\n */\n isSubElementKey(key: string): boolean {\n return /^[A-Z]/.test(key);\n }\n\n /**\n * Checks if a key represents a nested selector (starts with &).\n */\n isNestedSelectorKey(key: string): boolean {\n return key.startsWith('&');\n }\n\n /**\n * Checks if a key is a custom CSS property definition ($name or $$name).\n */\n isCustomPropertyKey(key: string): boolean {\n return key.startsWith('$');\n }\n\n /**\n * Checks if a key is a color token definition (#name or ##name).\n */\n isColorTokenKey(key: string): boolean {\n return key.startsWith('#');\n }\n\n /**\n * Checks if a key is a special @ property (@keyframes, @properties).\n */\n isSpecialKey(key: string): boolean {\n return key.startsWith('@');\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { ESLintUtils } from "@typescript-eslint/utils";
2
+
3
+ //#region src/create-rule.ts
4
+ const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/tenphi/eslint-plugin-tasty/blob/main/docs/rules/${name}.md`);
5
+
6
+ //#endregion
7
+ export { createRule };
8
+ //# sourceMappingURL=create-rule.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-rule.mjs","names":[],"sources":["../src/create-rule.ts"],"sourcesContent":["import { ESLintUtils } from '@typescript-eslint/utils';\n\nexport const createRule = ESLintUtils.RuleCreator(\n (name) =>\n `https://github.com/tenphi/eslint-plugin-tasty/blob/main/docs/rules/${name}.md`,\n);\n"],"mappings":";;;AAEA,MAAa,aAAa,YAAY,aACnC,SACC,sEAAsE,KAAK,KAC9E"}
@@ -0,0 +1,8 @@
1
+ import { recommended, strict } from "./configs.mjs";
2
+ import { ResolvedConfig, TastyValidationConfig } from "./types.mjs";
3
+
4
+ //#region src/index.d.ts
5
+ declare const plugin: any;
6
+ //#endregion
7
+ export { type ResolvedConfig, type TastyValidationConfig, plugin as default, recommended, strict };
8
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,83 @@
1
+ import known_property_default from "./rules/known-property.mjs";
2
+ import valid_value_default from "./rules/valid-value.mjs";
3
+ import valid_color_token_default from "./rules/valid-color-token.mjs";
4
+ import valid_custom_unit_default from "./rules/valid-custom-unit.mjs";
5
+ import valid_state_key_default from "./rules/valid-state-key.mjs";
6
+ import valid_styles_structure_default from "./rules/valid-styles-structure.mjs";
7
+ import no_nested_selector_default from "./rules/no-nested-selector.mjs";
8
+ import valid_custom_property_default from "./rules/valid-custom-property.mjs";
9
+ import valid_preset_default from "./rules/valid-preset.mjs";
10
+ import valid_recipe_default from "./rules/valid-recipe.mjs";
11
+ import valid_boolean_property_default from "./rules/valid-boolean-property.mjs";
12
+ import valid_directional_modifier_default from "./rules/valid-directional-modifier.mjs";
13
+ import valid_radius_shape_default from "./rules/valid-radius-shape.mjs";
14
+ import no_important_default from "./rules/no-important.mjs";
15
+ import valid_sub_element_default from "./rules/valid-sub-element.mjs";
16
+ import no_nested_state_map_default from "./rules/no-nested-state-map.mjs";
17
+ import static_no_dynamic_values_default from "./rules/static-no-dynamic-values.mjs";
18
+ import static_valid_selector_default from "./rules/static-valid-selector.mjs";
19
+ import prefer_shorthand_property_default from "./rules/prefer-shorthand-property.mjs";
20
+ import valid_transition_default from "./rules/valid-transition.mjs";
21
+ import require_default_state_default from "./rules/require-default-state.mjs";
22
+ import no_duplicate_state_default from "./rules/no-duplicate-state.mjs";
23
+ import no_unknown_state_alias_default from "./rules/no-unknown-state-alias.mjs";
24
+ import no_raw_color_values_default from "./rules/no-raw-color-values.mjs";
25
+ import no_styles_prop_default from "./rules/no-styles-prop.mjs";
26
+ import consistent_token_usage_default from "./rules/consistent-token-usage.mjs";
27
+ import no_runtime_styles_mutation_default from "./rules/no-runtime-styles-mutation.mjs";
28
+ import { recommended, strict } from "./configs.mjs";
29
+
30
+ //#region src/index.ts
31
+ const plugin = {
32
+ meta: {
33
+ name: "@tenphi/eslint-plugin-tasty",
34
+ version: "0.1.0"
35
+ },
36
+ rules: {
37
+ "known-property": known_property_default,
38
+ "valid-value": valid_value_default,
39
+ "valid-color-token": valid_color_token_default,
40
+ "valid-custom-unit": valid_custom_unit_default,
41
+ "valid-state-key": valid_state_key_default,
42
+ "valid-styles-structure": valid_styles_structure_default,
43
+ "no-nested-selector": no_nested_selector_default,
44
+ "valid-custom-property": valid_custom_property_default,
45
+ "valid-preset": valid_preset_default,
46
+ "valid-recipe": valid_recipe_default,
47
+ "valid-boolean-property": valid_boolean_property_default,
48
+ "valid-directional-modifier": valid_directional_modifier_default,
49
+ "valid-radius-shape": valid_radius_shape_default,
50
+ "no-important": no_important_default,
51
+ "valid-sub-element": valid_sub_element_default,
52
+ "no-nested-state-map": no_nested_state_map_default,
53
+ "static-no-dynamic-values": static_no_dynamic_values_default,
54
+ "static-valid-selector": static_valid_selector_default,
55
+ "prefer-shorthand-property": prefer_shorthand_property_default,
56
+ "valid-transition": valid_transition_default,
57
+ "require-default-state": require_default_state_default,
58
+ "no-duplicate-state": no_duplicate_state_default,
59
+ "no-unknown-state-alias": no_unknown_state_alias_default,
60
+ "no-raw-color-values": no_raw_color_values_default,
61
+ "no-styles-prop": no_styles_prop_default,
62
+ "consistent-token-usage": consistent_token_usage_default,
63
+ "no-runtime-styles-mutation": no_runtime_styles_mutation_default
64
+ },
65
+ configs: {
66
+ recommended: {
67
+ plugins: { get tasty() {
68
+ return plugin;
69
+ } },
70
+ rules: recommended
71
+ },
72
+ strict: {
73
+ plugins: { get tasty() {
74
+ return plugin;
75
+ } },
76
+ rules: strict
77
+ }
78
+ }
79
+ };
80
+
81
+ //#endregion
82
+ export { plugin as default, recommended, strict };
83
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["rules.knownProperty","rules.validValue","rules.validColorToken","rules.validCustomUnit","rules.validStateKey","rules.validStylesStructure","rules.noNestedSelector","rules.validCustomProperty","rules.validPreset","rules.validRecipe","rules.validBooleanProperty","rules.validDirectionalModifier","rules.validRadiusShape","rules.noImportant","rules.validSubElement","rules.noNestedStateMap","rules.staticNoDynamicValues","rules.staticValidSelector","rules.preferShorthandProperty","rules.validTransition","rules.requireDefaultState","rules.noDuplicateState","rules.noUnknownStateAlias","rules.noRawColorValues","rules.noStylesProp","rules.consistentTokenUsage","rules.noRuntimeStylesMutation"],"sources":["../src/index.ts"],"sourcesContent":["import type { TSESLint } from '@typescript-eslint/utils';\nimport * as rules from './rules/index.js';\nimport { recommended, strict } from './configs.js';\n\nconst ruleMap: Record<string, TSESLint.RuleModule<string, unknown[]>> = {\n 'known-property': rules.knownProperty,\n 'valid-value': rules.validValue,\n 'valid-color-token': rules.validColorToken,\n 'valid-custom-unit': rules.validCustomUnit,\n 'valid-state-key': rules.validStateKey,\n 'valid-styles-structure': rules.validStylesStructure,\n 'no-nested-selector': rules.noNestedSelector,\n 'valid-custom-property': rules.validCustomProperty,\n 'valid-preset': rules.validPreset,\n 'valid-recipe': rules.validRecipe,\n 'valid-boolean-property': rules.validBooleanProperty,\n 'valid-directional-modifier': rules.validDirectionalModifier,\n 'valid-radius-shape': rules.validRadiusShape,\n 'no-important': rules.noImportant,\n 'valid-sub-element': rules.validSubElement,\n 'no-nested-state-map': rules.noNestedStateMap,\n 'static-no-dynamic-values': rules.staticNoDynamicValues,\n 'static-valid-selector': rules.staticValidSelector,\n 'prefer-shorthand-property': rules.preferShorthandProperty,\n 'valid-transition': rules.validTransition,\n 'require-default-state': rules.requireDefaultState,\n 'no-duplicate-state': rules.noDuplicateState,\n 'no-unknown-state-alias': rules.noUnknownStateAlias,\n 'no-raw-color-values': rules.noRawColorValues,\n 'no-styles-prop': rules.noStylesProp,\n 'consistent-token-usage': rules.consistentTokenUsage,\n 'no-runtime-styles-mutation': rules.noRuntimeStylesMutation,\n};\n\nconst plugin = {\n meta: {\n name: '@tenphi/eslint-plugin-tasty',\n version: '0.1.0',\n },\n rules: ruleMap,\n configs: {\n recommended: {\n plugins: {\n get tasty() {\n return plugin;\n },\n },\n rules: recommended,\n },\n strict: {\n plugins: {\n get tasty() {\n return plugin;\n },\n },\n rules: strict,\n },\n },\n} satisfies TSESLint.FlatConfig.Plugin & {\n configs: Record<string, TSESLint.FlatConfig.Config>;\n};\n\nexport default plugin;\n\nexport { recommended, strict } from './configs.js';\nexport type { TastyValidationConfig, ResolvedConfig } from './types.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAM,SAAS;CACb,MAAM;EACJ,MAAM;EACN,SAAS;EACV;CACD,OAnCsE;EACtE,kBAAkBA;EAClB,eAAeC;EACf,qBAAqBC;EACrB,qBAAqBC;EACrB,mBAAmBC;EACnB,0BAA0BC;EAC1B,sBAAsBC;EACtB,yBAAyBC;EACzB,gBAAgBC;EAChB,gBAAgBC;EAChB,0BAA0BC;EAC1B,8BAA8BC;EAC9B,sBAAsBC;EACtB,gBAAgBC;EAChB,qBAAqBC;EACrB,uBAAuBC;EACvB,4BAA4BC;EAC5B,yBAAyBC;EACzB,6BAA6BC;EAC7B,oBAAoBC;EACpB,yBAAyBC;EACzB,sBAAsBC;EACtB,0BAA0BC;EAC1B,uBAAuBC;EACvB,kBAAkBC;EAClB,0BAA0BC;EAC1B,8BAA8BC;EAC/B;CAQC,SAAS;EACP,aAAa;GACX,SAAS,EACP,IAAI,QAAQ;AACV,WAAO;MAEV;GACD,OAAO;GACR;EACD,QAAQ;GACN,SAAS,EACP,IAAI,QAAQ;AACV,WAAO;MAEV;GACD,OAAO;GACR;EACF;CACF"}
@@ -0,0 +1,46 @@
1
+ import { BUILT_IN_UNITS } from "./constants.mjs";
2
+ import { StyleParser } from "@tenphi/tasty/parser";
3
+
4
+ //#region src/parser.ts
5
+ const BUILT_IN_UNIT_STUBS = {
6
+ x: "var(--gap)",
7
+ r: "var(--radius)",
8
+ cr: "var(--card-radius)",
9
+ bw: "var(--border-width)",
10
+ ow: "var(--outline-width)",
11
+ fs: "var(--font-size)",
12
+ lh: "var(--line-height)",
13
+ sf: "var(--scale-factor)"
14
+ };
15
+ let cachedParser = null;
16
+ function configKey(config) {
17
+ return `${config.units === false ? "false" : JSON.stringify(config.units)}|${config.funcs === false ? "false" : JSON.stringify(config.funcs)}`;
18
+ }
19
+ /**
20
+ * Build a StyleParser from the ESLint plugin config.
21
+ * Unit handlers are stubs (they produce placeholder CSS) because we only
22
+ * care about bucket classification, not actual CSS output.
23
+ */
24
+ function getParser(config) {
25
+ const key = configKey(config);
26
+ if (cachedParser && cachedParser.configKey === key) return cachedParser.parser;
27
+ const units = { ...BUILT_IN_UNIT_STUBS };
28
+ if (Array.isArray(config.units)) {
29
+ for (const u of config.units) if (!units[u]) units[u] = `var(--${u})`;
30
+ } else if (config.units !== false) for (const u of BUILT_IN_UNITS) units[u] = BUILT_IN_UNIT_STUBS[u] ?? `var(--${u})`;
31
+ const funcs = {};
32
+ if (Array.isArray(config.funcs)) for (const f of config.funcs) funcs[f] = (groups) => groups.map((g) => g.output).join(", ");
33
+ const parser = new StyleParser({
34
+ units: config.units === false ? void 0 : units,
35
+ funcs: Object.keys(funcs).length > 0 ? funcs : void 0
36
+ });
37
+ cachedParser = {
38
+ parser,
39
+ configKey: key
40
+ };
41
+ return parser;
42
+ }
43
+
44
+ //#endregion
45
+ export { getParser };
46
+ //# sourceMappingURL=parser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.mjs","names":[],"sources":["../src/parser.ts"],"sourcesContent":["import { StyleParser } from '@tenphi/tasty/parser';\nimport type { ParserOptions } from '@tenphi/tasty/parser';\nimport type { ResolvedConfig } from './types.js';\nimport { BUILT_IN_UNITS } from './constants.js';\n\nconst BUILT_IN_UNIT_STUBS: Record<string, string> = {\n x: 'var(--gap)',\n r: 'var(--radius)',\n cr: 'var(--card-radius)',\n bw: 'var(--border-width)',\n ow: 'var(--outline-width)',\n fs: 'var(--font-size)',\n lh: 'var(--line-height)',\n sf: 'var(--scale-factor)',\n};\n\nlet cachedParser: { parser: StyleParser; configKey: string } | null = null;\n\nfunction configKey(config: ResolvedConfig): string {\n const units = config.units === false ? 'false' : JSON.stringify(config.units);\n const funcs = config.funcs === false ? 'false' : JSON.stringify(config.funcs);\n return `${units}|${funcs}`;\n}\n\n/**\n * Build a StyleParser from the ESLint plugin config.\n * Unit handlers are stubs (they produce placeholder CSS) because we only\n * care about bucket classification, not actual CSS output.\n */\nexport function getParser(config: ResolvedConfig): StyleParser {\n const key = configKey(config);\n if (cachedParser && cachedParser.configKey === key) {\n return cachedParser.parser;\n }\n\n const units: Record<string, string> = { ...BUILT_IN_UNIT_STUBS };\n\n if (Array.isArray(config.units)) {\n for (const u of config.units) {\n if (!units[u]) {\n units[u] = `var(--${u})`;\n }\n }\n } else if (config.units !== false) {\n for (const u of BUILT_IN_UNITS) {\n units[u] = BUILT_IN_UNIT_STUBS[u] ?? `var(--${u})`;\n }\n }\n\n const funcs: ParserOptions['funcs'] = {};\n if (Array.isArray(config.funcs)) {\n for (const f of config.funcs) {\n funcs[f] = (groups) => groups.map((g) => g.output).join(', ');\n }\n }\n\n const opts: ParserOptions = {\n units: config.units === false ? undefined : units,\n funcs: Object.keys(funcs).length > 0 ? funcs : undefined,\n };\n\n const parser = new StyleParser(opts);\n\n cachedParser = { parser, configKey: key };\n return parser;\n}\n"],"mappings":";;;;AAKA,MAAM,sBAA8C;CAClD,GAAG;CACH,GAAG;CACH,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACL;AAED,IAAI,eAAkE;AAEtE,SAAS,UAAU,QAAgC;AAGjD,QAAO,GAFO,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,MAAM,CAE7D,GADF,OAAO,UAAU,QAAQ,UAAU,KAAK,UAAU,OAAO,MAAM;;;;;;;AAS/E,SAAgB,UAAU,QAAqC;CAC7D,MAAM,MAAM,UAAU,OAAO;AAC7B,KAAI,gBAAgB,aAAa,cAAc,IAC7C,QAAO,aAAa;CAGtB,MAAM,QAAgC,EAAE,GAAG,qBAAqB;AAEhE,KAAI,MAAM,QAAQ,OAAO,MAAM,EAC7B;OAAK,MAAM,KAAK,OAAO,MACrB,KAAI,CAAC,MAAM,GACT,OAAM,KAAK,SAAS,EAAE;YAGjB,OAAO,UAAU,MAC1B,MAAK,MAAM,KAAK,eACd,OAAM,KAAK,oBAAoB,MAAM,SAAS,EAAE;CAIpD,MAAM,QAAgC,EAAE;AACxC,KAAI,MAAM,QAAQ,OAAO,MAAM,CAC7B,MAAK,MAAM,KAAK,OAAO,MACrB,OAAM,MAAM,WAAW,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,KAAK;CASjE,MAAM,SAAS,IAAI,YALS;EAC1B,OAAO,OAAO,UAAU,QAAQ,SAAY;EAC5C,OAAO,OAAO,KAAK,MAAM,CAAC,SAAS,IAAI,QAAQ;EAChD,CAEmC;AAEpC,gBAAe;EAAE;EAAQ,WAAW;EAAK;AACzC,QAAO"}
@@ -0,0 +1,168 @@
1
+ //#region src/property-expectations.ts
2
+ const DIRECTIONAL_MODS = [
3
+ "top",
4
+ "right",
5
+ "bottom",
6
+ "left"
7
+ ];
8
+ const RADIUS_DIRECTIONAL_MODS = [
9
+ ...DIRECTIONAL_MODS,
10
+ "top-left",
11
+ "top-right",
12
+ "bottom-left",
13
+ "bottom-right"
14
+ ];
15
+ const BORDER_STYLE_MODS = [
16
+ "solid",
17
+ "dashed",
18
+ "dotted",
19
+ "double",
20
+ "groove",
21
+ "ridge",
22
+ "inset",
23
+ "outset",
24
+ "none",
25
+ "hidden"
26
+ ];
27
+ const DIMENSION_MODS = ["min", "max"];
28
+ const FLOW_MODS = [
29
+ "row",
30
+ "column",
31
+ "wrap",
32
+ "nowrap",
33
+ "dense",
34
+ "row-reverse",
35
+ "column-reverse"
36
+ ];
37
+ const OVERFLOW_MODS = [
38
+ "visible",
39
+ "hidden",
40
+ "scroll",
41
+ "clip",
42
+ "auto",
43
+ "overlay"
44
+ ];
45
+ const POSITION_MODS = [
46
+ "static",
47
+ "relative",
48
+ "absolute",
49
+ "fixed",
50
+ "sticky"
51
+ ];
52
+ const COLOR_ONLY = {
53
+ acceptsColor: true,
54
+ acceptsMods: false
55
+ };
56
+ const VALUE_ONLY = {
57
+ acceptsColor: false,
58
+ acceptsMods: false
59
+ };
60
+ const PASSTHROUGH = {
61
+ acceptsColor: true,
62
+ acceptsMods: true
63
+ };
64
+ const PROPERTY_EXPECTATIONS = {
65
+ fill: COLOR_ONLY,
66
+ color: COLOR_ONLY,
67
+ caretColor: COLOR_ONLY,
68
+ accentColor: COLOR_ONLY,
69
+ shadow: COLOR_ONLY,
70
+ border: {
71
+ acceptsColor: true,
72
+ acceptsMods: [...DIRECTIONAL_MODS, ...BORDER_STYLE_MODS]
73
+ },
74
+ outline: {
75
+ acceptsColor: true,
76
+ acceptsMods: BORDER_STYLE_MODS
77
+ },
78
+ radius: {
79
+ acceptsColor: false,
80
+ acceptsMods: [
81
+ ...RADIUS_DIRECTIONAL_MODS,
82
+ "round",
83
+ "ellipse",
84
+ "leaf",
85
+ "backleaf"
86
+ ]
87
+ },
88
+ padding: {
89
+ acceptsColor: false,
90
+ acceptsMods: DIRECTIONAL_MODS
91
+ },
92
+ paddingInline: VALUE_ONLY,
93
+ paddingBlock: VALUE_ONLY,
94
+ margin: {
95
+ acceptsColor: false,
96
+ acceptsMods: DIRECTIONAL_MODS
97
+ },
98
+ fade: {
99
+ acceptsColor: false,
100
+ acceptsMods: DIRECTIONAL_MODS
101
+ },
102
+ inset: {
103
+ acceptsColor: false,
104
+ acceptsMods: DIRECTIONAL_MODS
105
+ },
106
+ width: {
107
+ acceptsColor: false,
108
+ acceptsMods: DIMENSION_MODS
109
+ },
110
+ height: {
111
+ acceptsColor: false,
112
+ acceptsMods: DIMENSION_MODS
113
+ },
114
+ gap: VALUE_ONLY,
115
+ columnGap: VALUE_ONLY,
116
+ rowGap: VALUE_ONLY,
117
+ flexBasis: VALUE_ONLY,
118
+ flexGrow: VALUE_ONLY,
119
+ flexShrink: VALUE_ONLY,
120
+ flex: VALUE_ONLY,
121
+ order: VALUE_ONLY,
122
+ zIndex: VALUE_ONLY,
123
+ opacity: VALUE_ONLY,
124
+ aspectRatio: VALUE_ONLY,
125
+ lineClamp: VALUE_ONLY,
126
+ tabSize: VALUE_ONLY,
127
+ flow: {
128
+ acceptsColor: false,
129
+ acceptsMods: FLOW_MODS
130
+ },
131
+ display: {
132
+ acceptsColor: false,
133
+ acceptsMods: [
134
+ "block",
135
+ "inline",
136
+ "inline-block",
137
+ "flex",
138
+ "inline-flex",
139
+ "grid",
140
+ "inline-grid",
141
+ "none",
142
+ "contents",
143
+ "table",
144
+ "table-row",
145
+ "table-cell",
146
+ "list-item"
147
+ ]
148
+ },
149
+ overflow: {
150
+ acceptsColor: false,
151
+ acceptsMods: OVERFLOW_MODS
152
+ },
153
+ position: {
154
+ acceptsColor: false,
155
+ acceptsMods: POSITION_MODS
156
+ }
157
+ };
158
+ /**
159
+ * Get expectations for a property. Properties not in the map
160
+ * are treated as passthrough (accept everything).
161
+ */
162
+ function getExpectation(property) {
163
+ return PROPERTY_EXPECTATIONS[property] ?? PASSTHROUGH;
164
+ }
165
+
166
+ //#endregion
167
+ export { getExpectation };
168
+ //# sourceMappingURL=property-expectations.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-expectations.mjs","names":[],"sources":["../src/property-expectations.ts"],"sourcesContent":["/**\n * Per-property expectations for parser bucket validation.\n *\n * After parsing a value through StyleParser.process(), each group contains\n * `colors`, `values`, and `mods` arrays. This map defines what is expected\n * for each tasty property so we can flag unexpected tokens.\n *\n * - `acceptsColor`: whether Color bucket tokens are valid\n * - `acceptsMods`: whether Mod bucket tokens are valid, and if so which ones\n * - `false` = no mods accepted (any mod is an error)\n * - `true` = any mod accepted (pass-through)\n * - `string[]` = only these specific mods are accepted\n *\n * Properties NOT listed here default to PASSTHROUGH (accept everything).\n * Only add properties that have actual restrictions.\n */\n\nexport interface PropertyExpectation {\n acceptsColor: boolean;\n acceptsMods: boolean | string[];\n}\n\nconst DIRECTIONAL_MODS = ['top', 'right', 'bottom', 'left'];\nconst RADIUS_DIRECTIONAL_MODS = [\n ...DIRECTIONAL_MODS,\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n];\nconst BORDER_STYLE_MODS = [\n 'solid',\n 'dashed',\n 'dotted',\n 'double',\n 'groove',\n 'ridge',\n 'inset',\n 'outset',\n 'none',\n 'hidden',\n];\nconst DIMENSION_MODS = ['min', 'max'];\nconst FLOW_MODS = [\n 'row',\n 'column',\n 'wrap',\n 'nowrap',\n 'dense',\n 'row-reverse',\n 'column-reverse',\n];\nconst OVERFLOW_MODS = [\n 'visible',\n 'hidden',\n 'scroll',\n 'clip',\n 'auto',\n 'overlay',\n];\nconst POSITION_MODS = [\n 'static',\n 'relative',\n 'absolute',\n 'fixed',\n 'sticky',\n];\n\nconst COLOR_ONLY: PropertyExpectation = {\n acceptsColor: true,\n acceptsMods: false,\n};\n\nconst VALUE_ONLY: PropertyExpectation = {\n acceptsColor: false,\n acceptsMods: false,\n};\n\nconst PASSTHROUGH: PropertyExpectation = {\n acceptsColor: true,\n acceptsMods: true,\n};\n\nexport const PROPERTY_EXPECTATIONS: Record<string, PropertyExpectation> = {\n fill: COLOR_ONLY,\n color: COLOR_ONLY,\n caretColor: COLOR_ONLY,\n accentColor: COLOR_ONLY,\n shadow: COLOR_ONLY,\n\n border: {\n acceptsColor: true,\n acceptsMods: [...DIRECTIONAL_MODS, ...BORDER_STYLE_MODS],\n },\n outline: {\n acceptsColor: true,\n acceptsMods: BORDER_STYLE_MODS,\n },\n\n radius: {\n acceptsColor: false,\n acceptsMods: [\n ...RADIUS_DIRECTIONAL_MODS,\n 'round',\n 'ellipse',\n 'leaf',\n 'backleaf',\n ],\n },\n\n padding: { acceptsColor: false, acceptsMods: DIRECTIONAL_MODS },\n paddingInline: VALUE_ONLY,\n paddingBlock: VALUE_ONLY,\n margin: { acceptsColor: false, acceptsMods: DIRECTIONAL_MODS },\n fade: { acceptsColor: false, acceptsMods: DIRECTIONAL_MODS },\n inset: { acceptsColor: false, acceptsMods: DIRECTIONAL_MODS },\n\n width: { acceptsColor: false, acceptsMods: DIMENSION_MODS },\n height: { acceptsColor: false, acceptsMods: DIMENSION_MODS },\n\n gap: VALUE_ONLY,\n columnGap: VALUE_ONLY,\n rowGap: VALUE_ONLY,\n flexBasis: VALUE_ONLY,\n flexGrow: VALUE_ONLY,\n flexShrink: VALUE_ONLY,\n flex: VALUE_ONLY,\n order: VALUE_ONLY,\n zIndex: VALUE_ONLY,\n opacity: VALUE_ONLY,\n aspectRatio: VALUE_ONLY,\n lineClamp: VALUE_ONLY,\n tabSize: VALUE_ONLY,\n\n flow: { acceptsColor: false, acceptsMods: FLOW_MODS },\n display: {\n acceptsColor: false,\n acceptsMods: [\n 'block',\n 'inline',\n 'inline-block',\n 'flex',\n 'inline-flex',\n 'grid',\n 'inline-grid',\n 'none',\n 'contents',\n 'table',\n 'table-row',\n 'table-cell',\n 'list-item',\n ],\n },\n overflow: { acceptsColor: false, acceptsMods: OVERFLOW_MODS },\n position: { acceptsColor: false, acceptsMods: POSITION_MODS },\n};\n\n/**\n * Get expectations for a property. Properties not in the map\n * are treated as passthrough (accept everything).\n */\nexport function getExpectation(property: string): PropertyExpectation {\n return PROPERTY_EXPECTATIONS[property] ?? PASSTHROUGH;\n}\n"],"mappings":";AAsBA,MAAM,mBAAmB;CAAC;CAAO;CAAS;CAAU;CAAO;AAC3D,MAAM,0BAA0B;CAC9B,GAAG;CACH;CACA;CACA;CACA;CACD;AACD,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,iBAAiB,CAAC,OAAO,MAAM;AACrC,MAAM,YAAY;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,gBAAgB;CACpB;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,aAAkC;CACtC,cAAc;CACd,aAAa;CACd;AAED,MAAM,aAAkC;CACtC,cAAc;CACd,aAAa;CACd;AAED,MAAM,cAAmC;CACvC,cAAc;CACd,aAAa;CACd;AAED,MAAa,wBAA6D;CACxE,MAAM;CACN,OAAO;CACP,YAAY;CACZ,aAAa;CACb,QAAQ;CAER,QAAQ;EACN,cAAc;EACd,aAAa,CAAC,GAAG,kBAAkB,GAAG,kBAAkB;EACzD;CACD,SAAS;EACP,cAAc;EACd,aAAa;EACd;CAED,QAAQ;EACN,cAAc;EACd,aAAa;GACX,GAAG;GACH;GACA;GACA;GACA;GACD;EACF;CAED,SAAS;EAAE,cAAc;EAAO,aAAa;EAAkB;CAC/D,eAAe;CACf,cAAc;CACd,QAAQ;EAAE,cAAc;EAAO,aAAa;EAAkB;CAC9D,MAAM;EAAE,cAAc;EAAO,aAAa;EAAkB;CAC5D,OAAO;EAAE,cAAc;EAAO,aAAa;EAAkB;CAE7D,OAAO;EAAE,cAAc;EAAO,aAAa;EAAgB;CAC3D,QAAQ;EAAE,cAAc;EAAO,aAAa;EAAgB;CAE5D,KAAK;CACL,WAAW;CACX,QAAQ;CACR,WAAW;CACX,UAAU;CACV,YAAY;CACZ,MAAM;CACN,OAAO;CACP,QAAQ;CACR,SAAS;CACT,aAAa;CACb,WAAW;CACX,SAAS;CAET,MAAM;EAAE,cAAc;EAAO,aAAa;EAAW;CACrD,SAAS;EACP,cAAc;EACd,aAAa;GACX;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EACF;CACD,UAAU;EAAE,cAAc;EAAO,aAAa;EAAe;CAC7D,UAAU;EAAE,cAAc;EAAO,aAAa;EAAe;CAC9D;;;;;AAMD,SAAgB,eAAe,UAAuC;AACpE,QAAO,sBAAsB,aAAa"}