From a31e0d392cf73c623ae8a8cf957796ede4386e00 Mon Sep 17 00:00:00 2001 From: Katie Langerman <18661030+langermank@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:56:41 -0800 Subject: [PATCH] New plugin: `new-color-vars-have-fallback` (#376) * babys first solo stylelint plugin * lint * Create beige-cobras-reflect.md * try adding severity * fix tests --- .changeset/beige-cobras-reflect.md | 5 +++ __tests__/new-color-vars-have-fallback.js | 24 +++++++++++ index.js | 2 + plugins/new-color-vars-have-fallback.js | 52 +++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 .changeset/beige-cobras-reflect.md create mode 100644 __tests__/new-color-vars-have-fallback.js create mode 100644 plugins/new-color-vars-have-fallback.js diff --git a/.changeset/beige-cobras-reflect.md b/.changeset/beige-cobras-reflect.md new file mode 100644 index 00000000..6890345e --- /dev/null +++ b/.changeset/beige-cobras-reflect.md @@ -0,0 +1,5 @@ +--- +"@primer/stylelint-config": minor +--- + +Adds new plugin: `new-color-vars-have-fallback` to check that if new Primitive v8 colors are used, they have a fallback value. diff --git a/__tests__/new-color-vars-have-fallback.js b/__tests__/new-color-vars-have-fallback.js new file mode 100644 index 00000000..35757f9a --- /dev/null +++ b/__tests__/new-color-vars-have-fallback.js @@ -0,0 +1,24 @@ +const {ruleName} = require('../plugins/new-color-vars-have-fallback') + +// eslint-disable-next-line no-undef +testRule({ + plugins: ['./plugins/new-color-vars-have-fallback'], + customSyntax: 'postcss-scss', + ruleName, + config: [true], + accept: [ + { + code: '.x { color: var(--fgColor-default, var(--color-fg-default)); }', + description: 'Variable has fallback', + }, + ], + reject: [ + { + code: '.x { color: var(--fgColor-default); }', + message: + 'Expected a fallback value for CSS variable --fgColor-default. New color variables fallbacks, check primer.style/primitives to find the correct value (primer/new-color-vars-have-fallback)', + line: 1, + column: 6, + }, + ], +}) diff --git a/index.js b/index.js index d6c3d80b..c97d73e9 100644 --- a/index.js +++ b/index.js @@ -22,6 +22,7 @@ module.exports = { './plugins/spacing', './plugins/typography', './plugins/utilities', + './plugins/new-color-vars-have-fallback', ], rules: { 'alpha-value-notation': 'number', @@ -75,6 +76,7 @@ module.exports = { 'primer/spacing': true, 'primer/typography': true, 'primer/utilities': null, + 'primer/new-color-vars-have-fallback': [true, {severity: 'error'}], 'scss/at-extend-no-missing-placeholder': true, 'scss/at-rule-no-unknown': true, 'scss/declaration-nested-properties-no-divided-groups': true, diff --git a/plugins/new-color-vars-have-fallback.js b/plugins/new-color-vars-have-fallback.js new file mode 100644 index 00000000..9acdea4f --- /dev/null +++ b/plugins/new-color-vars-have-fallback.js @@ -0,0 +1,52 @@ +const stylelint = require('stylelint') + +const ruleName = 'primer/new-color-vars-have-fallback' +const messages = stylelint.utils.ruleMessages(ruleName, { + expectedFallback: variable => + `Expected a fallback value for CSS variable ${variable}. New color variables fallbacks, check primer.style/primitives to find the correct value`, +}) + +const fs = require('fs') +const path = require('path') + +const jsonFilePath = 'node_modules/@primer/primitives/tokens-next-private/docs/functional/themes/light.json' +let jsonContent + +try { + const fileContent = fs.readFileSync(path.resolve(jsonFilePath), 'utf8') + jsonContent = JSON.parse(fileContent) + // console.log('JSON content:', jsonContent) // Log to see the content +} catch (error) { + // eslint-disable-next-line no-console + console.error('Error reading JSON file:', error) +} + +module.exports = stylelint.createPlugin(ruleName, enabled => { + if (!enabled) { + return noop + } + + return (root, result) => { + root.walkDecls(decl => { + for (const key of Object.keys(jsonContent)) { + if (decl.value.includes(`var(--${key})`)) { + // Check if the declaration uses a CSS variable from the JSON + const match = decl.value.match(/var\(--\w+,(.*)\)/) + if (!match || match[1].trim() === '') { + stylelint.utils.report({ + ruleName, + result, + node: decl, + message: messages.expectedFallback(`--${key}`), + }) + } + } + } + }) + } +}) + +function noop() {} + +module.exports.ruleName = ruleName +module.exports.messages = messages