All files / plugins/lib variable-rules.js

98.08% Statements 51/52
100% Branches 27/27
100% Functions 7/7
97.96% Lines 48/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 988x 8x 8x   8x 8x 8x   8x               17x 17x 17x           17x 34x 2x     32x 32x 32x 9x   23x   32x 1x 1x   32x 2x   32x   32x 16x         32x 32x 32x   32x 30x 47x 49x 2x   47x     47x 47x 47x   17x 17x 30x   17x 17x     13x 13x 16x                         17x   17x        
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) {
  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})
 
    const messages = stylelint.utils.ruleMessages(ruleName, {
      rejected: message => `${message}.`
    })
 
    // 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) {
              stylelint.utils.report({
                message: messages.rejected(error),
                node: decl,
                result,
                ruleName
              })
            }
          }
        })
      })
    }
  })
 
  Object.assign(plugin, {rules})
 
  return plugin
}
 
function noop() {}