UNPKG

@antebudimir/eslint-plugin-vanilla-extract

Version:

Comprehensive ESLint plugin for vanilla-extract with CSS property ordering, style validation, and best practices enforcement. Supports alphabetical, concentric and custom CSS ordering, auto-fixing, and zero-runtime safety.

573 lines (572 loc) 17.2 kB
import tsParser from '@typescript-eslint/parser'; import { run } from 'eslint-vitest-rule-tester'; import preferLogicalPropertiesRule from '../rule-definition.js'; run({ name: 'vanilla-extract/prefer-logical-properties', rule: preferLogicalPropertiesRule, languageOptions: { parser: tsParser, parserOptions: { ecmaVersion: 2022, sourceType: 'module', }, }, valid: [ { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginInlineStart: '1rem', marginInlineEnd: '1rem', marginBlockStart: '2rem', marginBlockEnd: '2rem', }); `, name: 'allows logical properties in camelCase', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ 'margin-inline-start': '1rem', 'margin-inline-end': '1rem', 'margin-block-start': '2rem', 'margin-block-end': '2rem', }); `, name: 'allows logical properties in kebab-case', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ insetInlineStart: 0, insetInlineEnd: 0, insetBlockStart: 0, insetBlockEnd: 0, }); `, name: 'allows logical inset properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderInlineStartWidth: '1px', borderInlineEndColor: 'red', borderBlockStartStyle: 'solid', }); `, name: 'allows logical border properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ inlineSize: '100%', blockSize: '50vh', minInlineSize: '200px', maxBlockSize: '800px', }); `, name: 'allows logical size properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ textAlign: 'start', float: 'inline-start', clear: 'inline-end', }); `, name: 'allows logical values for directional properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginLeft: '1rem', paddingTop: '2rem', top: 0, left: 0, }); `, options: [{ allow: ['marginLeft', 'paddingTop', 'top', 'left'] }], name: 'respects allowlist for camelCase properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ 'margin-left': '1rem', 'padding-top': '2rem', }); `, options: [{ allow: ['margin-left', 'padding-top'] }], name: 'respects allowlist for kebab-case properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginLeft: '1rem', }); `, options: [{ allow: ['margin-left'] }], name: 'allowlist works with mixed case (kebab in config, camel in code)', }, { code: ` import { recipe } from '@vanilla-extract/css'; const myRecipe = recipe({ base: { marginInlineStart: '1rem', paddingBlockEnd: '2rem', }, variants: { size: { sm: { insetInlineStart: 0 }, lg: { borderInlineEndWidth: '2px' }, }, }, }); `, name: 'allows logical properties in recipe base and variants', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ '@media': { '(min-width: 768px)': { marginInlineStart: '2rem', }, }, selectors: { '&:hover': { paddingBlockStart: '1rem', }, }, }); `, name: 'allows logical properties in nested @media and selectors', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ color: 'red', display: 'flex', fontSize: '16px', }); `, name: 'ignores non-directional properties', }, ], invalid: [ { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginTop: '1rem', marginBottom: '2rem', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginBlockStart: '1rem', marginBlockEnd: '2rem', }); `, errors: [ { messageId: 'preferLogicalProperty', data: { physical: 'marginTop', logical: 'marginBlockStart' }, }, { messageId: 'preferLogicalProperty', data: { physical: 'marginBottom', logical: 'marginBlockEnd' }, }, ], name: 'reports and fixes margin properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ 'padding-left': '1rem', 'padding-right': '2rem', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ 'padding-inline-start': '1rem', 'padding-inline-end': '2rem', }); `, errors: [ { messageId: 'preferLogicalProperty', data: { physical: 'padding-left', logical: 'padding-inline-start' }, }, { messageId: 'preferLogicalProperty', data: { physical: 'padding-right', logical: 'padding-inline-end' }, }, ], name: 'reports and fixes kebab-case padding properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ top: 0, left: 0, right: 0, bottom: 0, }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ insetBlockStart: 0, insetInlineStart: 0, insetInlineEnd: 0, insetBlockEnd: 0, }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes positioning properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderLeftWidth: '1px', borderRightColor: 'red', borderTopStyle: 'solid', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderInlineStartWidth: '1px', borderInlineEndColor: 'red', borderBlockStartStyle: 'solid', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes border sub-properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderLeft: '1px solid red', borderRight: '2px dashed blue', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderInlineStart: '1px solid red', borderInlineEnd: '2px dashed blue', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes border shorthand properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderTopLeftRadius: '4px', borderBottomRightRadius: '8px', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ borderStartStartRadius: '4px', borderEndEndRadius: '8px', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes border radius properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ width: '100px', height: '200px', minWidth: '50px', maxHeight: '400px', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ inlineSize: '100px', blockSize: '200px', minInlineSize: '50px', maxBlockSize: '400px', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes size properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ overflowX: 'auto', overflowY: 'hidden', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ overflowInline: 'auto', overflowBlock: 'hidden', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes overflow properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ textAlign: 'left', float: 'right', clear: 'left', resize: 'horizontal', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ textAlign: 'start', float: 'inline-end', clear: 'inline-start', resize: 'inline', }); `, errors: [ { messageId: 'preferLogicalValue', data: { property: 'textAlign', physical: 'left', logical: 'start' }, }, { messageId: 'preferLogicalValue', data: { property: 'float', physical: 'right', logical: 'inline-end' }, }, { messageId: 'preferLogicalValue', data: { property: 'clear', physical: 'left', logical: 'inline-start' }, }, { messageId: 'preferLogicalValue', data: { property: 'resize', physical: 'horizontal', logical: 'inline' }, }, ], name: 'reports and fixes directional values', }, { code: ` import { recipe } from '@vanilla-extract/css'; const myRecipe = recipe({ base: { marginLeft: '1rem', paddingTop: '2rem', }, variants: { size: { sm: { left: 0 }, lg: { borderRightWidth: '2px' }, }, }, }); `, output: ` import { recipe } from '@vanilla-extract/css'; const myRecipe = recipe({ base: { marginInlineStart: '1rem', paddingBlockStart: '2rem', }, variants: { size: { sm: { insetInlineStart: 0 }, lg: { borderInlineEndWidth: '2px' }, }, }, }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes physical properties in recipe base and variants', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ '@media': { '(min-width: 768px)': { marginLeft: '2rem', paddingTop: '1rem', }, }, selectors: { '&:hover': { right: 0, bottom: 0, }, }, }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ '@media': { '(min-width: 768px)': { marginInlineStart: '2rem', paddingBlockStart: '1rem', }, }, selectors: { '&:hover': { insetInlineEnd: 0, insetBlockEnd: 0, }, }, }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes physical properties in nested @media and selectors', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ scrollMarginLeft: '10px', scrollPaddingTop: '20px', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ scrollMarginInlineStart: '10px', scrollPaddingBlockStart: '20px', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes scroll margin and padding properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ overscrollBehaviorX: 'contain', overscrollBehaviorY: 'auto', }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ overscrollBehaviorInline: 'contain', overscrollBehaviorBlock: 'auto', }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalProperty' }, ], name: 'reports and fixes overscroll behavior properties', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginLeft: \`1rem\`, textAlign: \`left\`, }); `, output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginInlineStart: \`1rem\`, textAlign: \`start\`, }); `, errors: [ { messageId: 'preferLogicalProperty' }, { messageId: 'preferLogicalValue' }, ], name: 'handles template literals', }, { code: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginLeft: '1rem', paddingTop: '2rem', }); `, options: [{ allow: ['paddingTop'] }], output: ` import { style } from '@vanilla-extract/css'; const myStyle = style({ marginInlineStart: '1rem', paddingTop: '2rem', }); `, errors: [ { messageId: 'preferLogicalProperty', data: { physical: 'marginLeft', logical: 'marginInlineStart' }, }, ], name: 'only reports non-allowlisted properties', }, ], });