UNPKG

stylelint-scss

Version:

A collection of SCSS specific rules for stylelint

130 lines (102 loc) 3.1 kB
import { includes } from "lodash" import { utils } from "stylelint" import { namespace } from "../../utils" import valueParser from "postcss-value-parser" export const ruleName = namespace("dollar-variable-no-missing-interpolation") export const messages = utils.ruleMessages(ruleName, { rejected: (n, v) => `Expected variable ${v} to be interpolated when using it with ${n}`, }) // https://developer.mozilla.org/en/docs/Web/CSS/custom-ident#Lists_of_excluded_values const customIdentProps = [ "animation", "animation-name", "counter-reset", "counter-increment", "list-style-type", "will-change", ] // https://developer.mozilla.org/en/docs/Web/CSS/At-rule const customIdentAtRules = [ "counter-style", "keyframes", "supports", ] function isAtRule(type) { return type === "atrule" } function isCustomIdentAtRule(node) { return isAtRule(node.type) && includes(customIdentAtRules, node.name) } function isCustomIdentProp(node) { return includes(customIdentProps, node.prop) } function isAtSupports(node) { return isAtRule(node.type) && node.name === "supports" } function isSassVar(value) { return value[0] === "$" } function isStringVal(value) { return (/^("|').*("|')$/).test(value) } function toRegex(arr) { return new RegExp(`(${arr.join("|")})`) } export default function (actual) { return function (root, result) { const validOptions = utils.validateOptions(result, ruleName, { actual }) if (!validOptions) { return } const stringVars = [] const vars = [] function findVars(node) { node.walkDecls(decl => { const { prop, value } = decl if (!isSassVar(prop) || includes(vars, prop)) { return } if (isStringVal(value)) { stringVars.push(prop) } vars.push(prop) }) } findVars(root) root.walkRules(findVars) if (!vars.length) { return } function shouldReport(node, value) { if (isAtSupports(node) || isCustomIdentProp(node)) { return includes(stringVars, value) } if (isCustomIdentAtRule(node)) { return includes(vars, value) } return false } function report(node, value) { const { name, prop, type } = node const nodeName = isAtRule(type) ? "@" + name : prop utils.report({ ruleName, result, node, message: messages.rejected(nodeName, value), }) } function exitEarly(node) { return node.type !== "word" || !node.value } function walkValues(node, value) { valueParser(value).walk(valNode => { const { value } = valNode if (exitEarly(valNode) || !shouldReport(node, value)) { return } report(node, value) }) } root.walkDecls(toRegex(customIdentProps), decl => { walkValues(decl, decl.value) }) root.walkAtRules(toRegex(customIdentAtRules), atRule => { walkValues(atRule, atRule.params) }) } }