Skip to content

Commit

Permalink
progress on border plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mperrotti committed Apr 25, 2024
1 parent 0bfd369 commit db5abeb
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 32 deletions.
91 changes: 80 additions & 11 deletions __tests__/borders.js
Expand Up @@ -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.",

Check failure on line 80 in __tests__/borders.js

View workflow job for this annotation

GitHub Actions / lint

Replace `"CSS·>·Does·not·accept·a·border·radius·variable·for·border·width."` with `'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.",

Check failure on line 108 in __tests__/borders.js

View workflow job for this annotation

GitHub Actions / lint

Replace `"CSS·>·Does·not·accept·a·border·width·variable·for·border·radius."` with `'CSS·>·Does·not·accept·a·border·width·variable·for·border·radius.'`
},
],
})
53 changes: 32 additions & 21 deletions plugins/borders.js
Expand Up @@ -4,27 +4,24 @@ 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({
name: `--${variable}`,
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,
Expand All @@ -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) {
Expand All @@ -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) => {
Expand All @@ -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 =>

Check failure on line 79 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎·········`
new RegExp(`${variable['name'].replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`).test(nodeValue),

Check failure on line 80 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Insert `··`
)

Check failure on line 81 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Insert `··`

// 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)) {

Check failure on line 90 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Replace `['*',·'+',·'-',·'/',·'0',·'none',·'inherit',·'initial',·'revert',·'revert-layer',·'unset',·'solid',·'dashed'].includes(node.value)` with `⏎··········[⏎············'*',⏎············'+',⏎············'-',⏎············'/',⏎············'0',⏎············'none',⏎············'inherit',⏎············'initial',⏎············'revert',⏎············'revert-layer',⏎············'unset',⏎············'solid',⏎············'dashed',⏎··········].includes(node.value)⏎········`
return
}

Expand All @@ -101,14 +104,22 @@ const ruleFunction = (primary, secondaryOptions, context) => {

// If the variable is found in the value, skip it.
if (

Check failure on line 106 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎··········prop.includes('width')·||·prop·===·'border'⏎········` with `prop.includes('width')·||·prop·===·'border'`
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 (

Check failure on line 114 in plugins/borders.js

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎··········prop.includes('radius')⏎········` with `prop.includes('radius')`
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) {
Expand Down

0 comments on commit db5abeb

Please sign in to comment.