plugins/lib/variable-rules.js (86 lines of code) (raw):

const stylelint = require('stylelint') const {requirePrimerFile} = require('./primer') const declarationValidator = require('./decl-validator') const CSS_IMPORTANT = '!important' const CSS_DIRECTIONS = ['top', 'right', 'bottom', 'left'] const CSS_CORNERS = ['top-right', 'bottom-right', 'bottom-left', 'top-left'] module.exports = { createVariableRule, CSS_DIRECTIONS, CSS_CORNERS, CSS_IMPORTANT } function createVariableRule(ruleName, rules, url) { let variables = {} try { variables = requirePrimerFile('dist/variables.json') } catch (error) { // eslint-disable-next-line no-console console.warn(`Unable to get variables.json from @primer/css. Replacements will need to be specified manually.`) } const plugin = stylelint.createPlugin(ruleName, (enabled, options = {}, context) => { if (enabled === false) { return noop } let actualRules = rules let overrides = options.rules if (typeof rules === 'function') { actualRules = rules({variables, options, ruleName}) } else { actualRules = Object.assign({}, rules) } if (typeof overrides === 'function') { delete options.rules overrides = overrides({rules: actualRules, options, ruleName, variables}) } if (overrides) { Object.assign(actualRules, overrides) } const validate = declarationValidator(actualRules, {variables}) // The stylelint docs suggest respecting a "disableFix" rule option that // overrides the "global" context.fix (--fix) linting option. const {verbose = false, disableFix} = options const fixEnabled = context && context.fix && !disableFix const seen = new WeakMap() return (root, result) => { root.walkRules(rule => { rule.walkDecls(decl => { if (seen.has(decl)) { return } else { seen.set(decl, true) } const validated = validate(decl) const {valid, fixable, replacement, errors} = validated if (valid) { // eslint-disable-next-line no-console if (verbose) console.warn(`valid: "${decl.toString()}" in: "${rule.selector}"`) return } else if (fixEnabled && fixable) { // eslint-disable-next-line no-console if (verbose) console.warn(` fixed: ${replacement}`) decl.value = replacement } else { // eslint-disable-next-line no-console if (verbose) console.warn(` ${errors.length} error(s)`) for (const error of errors) { const message = stylelint.utils .ruleMessages(ruleName, { rejected: m => { if (url) { return `${m}. See ${url}.` } return `${m}.` } }) .rejected(error) stylelint.utils.report({ message, node: decl, result, ruleName }) } } }) }) } }) Object.assign(plugin, {rules}) return plugin } function noop() {}