diff --git a/packages/linters/src/stylelint/plugins/__tests__/use-tokens.spec.js b/packages/linters/src/stylelint/plugins/__tests__/use-tokens.spec.js index 152301f..1258097 100644 --- a/packages/linters/src/stylelint/plugins/__tests__/use-tokens.spec.js +++ b/packages/linters/src/stylelint/plugins/__tests__/use-tokens.spec.js @@ -4,6 +4,8 @@ const { messages } = require('../use-tokens') const validCss = '.class { color: var(--color-primary); }' const invalidCss = '.class { color: #fff; }' +const invalidCssTwoProps = + '.class { color: #fff; }; .class-border { border-radius: 4px }' const config = { plugins: ['@juntossomosmais/linters/stylelint/plugins/use-tokens.js'], @@ -53,6 +55,30 @@ describe('use-tokens rule', () => { ) }) + it('should fix invalid CSS', async () => { + const result = await stylelint.lint({ + code: invalidCss, + config, + fix: true, + }) + + expect(result.results[0].warnings).toHaveLength(0) + expect(result.output).toBe('.class { color: var(--color-neutral-white); }') + }) + + it('should fix invalid CSS when have two props', async () => { + const result = await stylelint.lint({ + code: invalidCssTwoProps, + config, + fix: true, + }) + + expect(result.results[0].warnings).toHaveLength(0) + expect(result.output).toBe( + '.class { color: var(--color-neutral-white); }; .class-border { border-radius: var(--spacing-xxsmall) }' + ) + }) + it('should not report custom properties', async () => { const result = await stylelint.lint({ code: '.class { --color-primary: #000; }', diff --git a/packages/linters/src/stylelint/plugins/use-tokens.js b/packages/linters/src/stylelint/plugins/use-tokens.js index 39f2cfc..13e28c3 100644 --- a/packages/linters/src/stylelint/plugins/use-tokens.js +++ b/packages/linters/src/stylelint/plugins/use-tokens.js @@ -7,52 +7,69 @@ const messages = stylelint.utils.ruleMessages(ruleName, { `Avoid using ${tokenValue} directly. Use var(--${tokenName}) instead. Read more: https://github.com/juntossomosmais/frontend-guideline`, }) +/** + * @type {import('stylelint').RuleMeta} + */ const meta = { docs: { description: 'Disallow the use of static values in favor of design tokens.', category: 'Best Practices', recommended: true, }, - fixable: null, + fixable: true, schema: [], } -module.exports = stylelint.createPlugin(ruleName, (primaryOption) => { - return function (root, result) { - const validOptions = stylelint.utils.validateOptions(result, ruleName, { - actual: primaryOption, - }) - - if (!validOptions) return - - root.walkDecls((decl) => { - if (decl.prop.startsWith('--')) return - - Object.entries(tokens).forEach(([tokenName, tokenValue]) => { - const specificTokens = ['zindex', 'border-radius'] - const isSpecificTokens = specificTokens.some((output) => - tokenName.includes(output) - ) - - const regexPattern = new RegExp( - `(^|\\s)(${tokens[tokenName]}|#${ - tokens[tokens[tokenName]] - })(\\s|;|$)`, - 'g' - ) - - if (!isSpecificTokens && regexPattern.test(decl.value)) { - stylelint.utils.report({ - message: messages.useToken({ tokenName, tokenValue }), - node: decl, - result, - ruleName, - }) - } +module.exports = stylelint.createPlugin( + ruleName, + (primaryOption, _, context) => { + return function (root, result) { + const validOptions = stylelint.utils.validateOptions(result, ruleName, { + actual: primaryOption, }) - }) + + if (!validOptions) return + + root.walkDecls((decl) => { + if (decl.prop.startsWith('--')) return + + Object.entries(tokens).forEach(([tokenName, tokenValue]) => { + const specificTokens = ['zindex', 'border-radius'] + const isSpecificTokens = specificTokens.some((output) => + tokenName.includes(output) + ) + + const regexPattern = new RegExp( + `(^|\\s)(${tokens[tokenName]}|#${ + tokens[tokens[tokenName]] + })(\\s|;|$)`, + 'g' + ) + + if (!isSpecificTokens && regexPattern.test(decl.value)) { + const fix = () => { + decl.value = `var(--${tokenName})` + } + + //TODO: in stylelint@16 should remove context.fix and pass `fix` callback to report function. https://stylelint.io/developer-guide/rules#context + if (context.fix) { + fix() + + return + } + + stylelint.utils.report({ + message: messages.useToken({ tokenName, tokenValue }), + node: decl, + result, + ruleName, + }) + } + }) + }) + } } -}) +) module.exports.ruleName = ruleName module.exports.messages = messages