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 38x 2x     36x 36x 36x 9x   27x   36x 1x 1x   36x 2x   36x   36x 16x         36x 36x 36x   36x 34x 63x 65x 2x   63x     63x 63x 63x   33x 33x 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() {}