3 const _ = require("lodash");
4 const atRuleParamIndex = require("../../utils/atRuleParamIndex");
5 const declarationValueIndex = require("../../utils/declarationValueIndex");
6 const getUnitFromValueNode = require("../../utils/getUnitFromValueNode");
7 const mediaParser = require("postcss-media-query-parser").default;
8 const optionsMatches = require("../../utils/optionsMatches");
9 const report = require("../../utils/report");
10 const ruleMessages = require("../../utils/ruleMessages");
11 const validateObjectWithStringArrayProps = require("../../utils/validateObjectWithStringArrayProps");
12 const validateOptions = require("../../utils/validateOptions");
13 const valueParser = require("postcss-value-parser");
15 const ruleName = "unit-blacklist";
17 const messages = ruleMessages(ruleName, {
18 rejected: unit => `Unexpected unit "${unit}"`
21 // a function to retrieve only the media feature name
22 // could be externalized in an utils function if needed in other code
23 const getMediaFeatureName = mediaFeatureNode => {
24 const value = mediaFeatureNode.value.toLowerCase();
26 return /((-?\w*)*)/i.exec(value)[1];
29 const rule = function(blacklistInput, options) {
30 const blacklist = [].concat(blacklistInput);
31 return (root, result) => {
32 const validOptions = validateOptions(
37 possible: [_.isString]
43 ignoreProperties: validateObjectWithStringArrayProps,
44 ignoreMediaFeatureNames: validateObjectWithStringArrayProps
52 function check(node, nodeIndex, valueNode, input, option) {
53 const unit = getUnitFromValueNode(valueNode);
55 // There is not unit or it is not configured as a violation
56 if (!unit || (unit && blacklist.indexOf(unit.toLowerCase()) === -1)) {
60 // The unit has an ignore option for the specific input
61 if (optionsMatches(option, unit.toLowerCase(), input)) {
66 index: nodeIndex + valueNode.sourceIndex,
67 message: messages.rejected(unit),
74 function checkMedia(node, value, getIndex) {
75 mediaParser(node.params).walk(/^media-feature$/i, mediaFeatureNode => {
76 const mediaName = getMediaFeatureName(mediaFeatureNode),
77 parentValue = mediaFeatureNode.parent.value;
79 valueParser(value).walk(function(valueNode) {
80 // Ignore all non-word valueNode and
81 // the values not included in the parentValue string
83 valueNode.type !== "word" ||
84 !parentValue.includes(valueNode.value)
93 options ? options.ignoreMediaFeatureNames : {}
100 function checkDecl(node, value, getIndex) {
101 // make sure multiplication operations (*) are divided - not handled
102 // by postcss-value-parser
103 value = value.replace(/\*/g, ",");
105 valueParser(value).walk(function(valueNode) {
106 // Ignore wrong units within `url` function
108 valueNode.type === "function" &&
109 valueNode.value.toLowerCase() === "url"
118 options ? options.ignoreProperties : {}
123 root.walkAtRules(/^media$/i, atRule =>
124 checkMedia(atRule, atRule.params, atRuleParamIndex)
126 root.walkDecls(decl => checkDecl(decl, decl.value, declarationValueIndex));
130 rule.primaryOptionArray = true;
132 rule.ruleName = ruleName;
133 rule.messages = messages;
134 module.exports = rule;