All files / plugins/lib variable-rules.js

96.3% Statements 52/54
96.55% Branches 28/29
100% Functions 7/7
96.08% Lines 49/51

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 98 99 100 101 102 103 104 1058x 8x 8x   8x 8x 8x   8x               17x 17x 17x           17x 38x 2x     36x 36x 36x 9x   27x   36x 1x 1x   36x 2x   36x       36x 36x 36x   36x 34x 63x 65x 2x   63x     63x 63x 63x   33x 33x 30x   17x 17x     13x 13x 16x     16x 16x             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, 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: message => {
                    Eif (url) {
                      return `${message}. See ${url}.`
                    }
                    return `${message}.`
                  }
                })
                .rejected(error)
 
              stylelint.utils.report({
                message,
                node: decl,
                result,
                ruleName
              })
            }
          }
        })
      })
    }
  })
 
  Object.assign(plugin, {rules})
 
  return plugin
}
 
function noop() {}