From db5abeb7b6744505c1abf96bac1e62226b87e61f Mon Sep 17 00:00:00 2001 From: Mike Perrotti Date: Thu, 25 Apr 2024 17:11:53 -0400 Subject: [PATCH] progress on border plugin --- __tests__/borders.js | 91 ++++++++++++++++++++++++++++++++++++++------ plugins/borders.js | 53 ++++++++++++++++---------- 2 files changed, 112 insertions(+), 32 deletions(-) diff --git a/__tests__/borders.js b/__tests__/borders.js index d59aee9d..9123c95c 100644 --- a/__tests__/borders.js +++ b/__tests__/borders.js @@ -14,29 +14,98 @@ testRule({ fix: true, cache: false, accept: [ + // Border widths { code: '.x { border: var(--borderWidth-thin) solid var(--borderColor-default); }', description: 'CSS > Accepts border shorthand with variables', }, + { + code: '.x { border-width: var(--borderWidth-thin); }', + description: 'CSS > Accepts border shorthand with variables', + }, + { + code: '.x { border-left-width: var(--borderWidth-thin); }', + description: 'CSS > Accepts directional border longhand with variables', + }, + { + code: '.x { border-inline-start-width: var(--borderWidth-thin); }', + description: 'CSS > Accepts logical properties directional border longhand with variables', + }, + { + code: '.x { border: 0; }', + description: 'CSS > Allows zero values', + }, + { + code: '.x { border: inherit; border: initial; border: revert; border: revert-layer; border: unset; }', + description: 'CSS > Allows global values', + }, + // Border radii + { + code: '.x { border-radius: var(--borderRadius-medium); }', + description: 'CSS > Accepts border-radius with variables', + }, + { + code: '.x { border-radius: var(--borderRadius-large) var(--borderRadius-small); }', + description: 'CSS > Accepts border-radius shorthand with variables', + }, + // Figure out how to allow `calc()` values ], reject: [ + // Border widths + { + code: '.x { border: 20px; }', + unfixable: true, + message: messages.rejected('20px'), + line: 1, + column: 14, + endColumn: 18, + description: 'CSS > Errors on value not in border width list', + }, + { + code: '.x { border: 1px; }', + fixed: '.x { border: var(--borderWidth-thin); }', + message: messages.rejected('1px', {name: '--borderWidth-thin'}), + line: 1, + column: 14, + endColumn: 17, + description: "CSS > Replaces '1px' with 'var(--borderWidth-thin)'.", + }, { - code: '.x { padding-bottom: 1px; }', + code: '.x { border-width: var(--borderRadius-small); }', unfixable: true, - message: messages.rejected('1px'), + message: 'Border radius variables can not be used for border widths', // TODO: handle this in the plugin + line: 1, + column: 24, + endColumn: 44, + description: "CSS > Does not accept a border radius variable for border width.", + }, + // Border radii + { + code: '.x { border-radius: 3px; }', + fixed: '.x { border-radius: var(--borderRadius-small); }', + message: messages.rejected('3px', {name: '--borderRadius-small'}), + line: 1, + column: 21, + endColumn: 24, + description: "CSS > Replaces '3px' with 'var(--borderRadius-small)'.", + }, + { + code: '.x { border-radius: 0.1875rem; }', + fixed: '.x { border-radius: var(--borderRadius-small); }', + message: messages.rejected('0.1875rem', {name: '--borderRadius-small'}), line: 1, - column: 22, - endColumn: 25, - description: 'CSS > Errors on value not in spacer list', + column: 21, + endColumn: 30, + description: "CSS > Replaces '0.1875rem' with 'var(--borderRadius-small)'.", }, { - code: '.x { padding-bottom: 0.25rem; }', - fixed: '.x { padding-bottom: var(--base-size-4); }', - message: messages.rejected('0.25rem', {name: '--base-size-4'}), + code: '.x { border-radius: var(--borderWidth-thin); }', + fixable: false, + message: 'Border width variables can not be used for border radii', // TODO: handle this in the plugin line: 1, - column: 22, - endColumn: 29, - description: "CSS > Replaces '0.25rem' with 'var(--base-size-4)'.", + column: 25, + endColumn: 43, + description: "CSS > Does not accept a border width variable for border radius.", }, ], }) diff --git a/plugins/borders.js b/plugins/borders.js index f3961144..999b1b6a 100644 --- a/plugins/borders.js +++ b/plugins/borders.js @@ -4,13 +4,8 @@ import valueParser from 'postcss-value-parser' import borderSizes from '@primer/primitives/dist/styleLint/functional/size/border.json' with {type: 'json'} const sizes = [] +const radii = [] for (const variable of Object.keys(borderSizes)) { - // const values = [size['value']] - // for (const value of Object.values(size['original'])) { - // values.push(value) - // values.push(`${parseInt(value) + 1}px`) - // values.push(`${parseInt(value) - 1}px`) - // } if (variable.includes('borderWidth')) { const value = borderSizes[variable]['value'].replace(/max|\(|\)/g, '').split(',')[0] sizes.push({ @@ -18,13 +13,15 @@ for (const variable of Object.keys(borderSizes)) { values: [value], }) } - // return { - // name: `--${size['name']}`, - // values, - // } -} -console.log(sizes) + if (variable.includes('borderRadius')) { + const value = borderSizes[variable]['value'] + radii.push({ + name: `--${variable}`, + values: value, + }) + } +} const { createPlugin, @@ -42,7 +39,7 @@ const walkGroups = (root, validate) => { return root } -export const ruleName = 'primer/spacing' +export const ruleName = 'primer/borders' export const messages = ruleMessages(ruleName, { rejected: (value, replacement) => { if (!replacement) { @@ -58,7 +55,7 @@ const meta = { } // Props that we want to check -const propList = ['padding', 'margin', 'top', 'right', 'bottom', 'left'] +const propList = ['border', 'border-width', 'border-radius'] /** @type {import('stylelint').Rule} */ const ruleFunction = (primary, secondaryOptions, context) => { @@ -67,24 +64,30 @@ const ruleFunction = (primary, secondaryOptions, context) => { actual: primary, possible: [true], }) + const validValues = [...sizes, ...radii] if (!validOptions) return root.walkDecls(declNode => { const {prop, value} = declNode - if (!propList.some(spacingProp => prop.startsWith(spacingProp))) return + if (!propList.some(borderProp => prop.startsWith(borderProp))) return const problems = [] const parsedValue = walkGroups(valueParser(value), node => { + const checkForVariable = (vars, nodeValue) => vars.some(variable => + new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue), + ) + // Only check word types. https://github.com/TrySound/postcss-value-parser#word if (node.type !== 'word') { return } + // TODO: figure out a better way to forbid border styles other than 'solid' and 'dashed' // Exact values to ignore. - if (['*', '+', '-', '/', '0', 'auto', 'inherit', 'initial'].includes(node.value)) { + if (['*', '+', '-', '/', '0', 'none', 'inherit', 'initial', 'revert', 'revert-layer', 'unset', 'solid', 'dashed'].includes(node.value)) { return } @@ -101,14 +104,22 @@ const ruleFunction = (primary, secondaryOptions, context) => { // If the variable is found in the value, skip it. if ( - sizes.some(variable => - new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(node.value), - ) + prop.includes('width') || prop === 'border' ) { - return + if (checkForVariable(sizes, node.value)) { + return + } + } + + if ( + prop.includes('radius') + ) { + if (checkForVariable(radii, node.value)) { + return + } } - const replacement = sizes.find(variable => variable.values.includes(node.value.replace('-', ''))) + const replacement = validValues.find(variable => variable.values.includes(node.value.replace('-', ''))) const fixable = replacement && valueUnit && !valueUnit.number.includes('-') if (fixable && context.fix) {