| "use strict"; |
| |
| Object.defineProperty(exports, "__esModule", { |
| value: true |
| }); |
| exports.calculationOperatorSpaceChecker = calculationOperatorSpaceChecker; |
| exports["default"] = _default; |
| exports.ruleName = exports.messages = void 0; |
| |
| var _postcssMediaQueryParser = _interopRequireDefault(require("postcss-media-query-parser")); |
| |
| var _stylelint = require("stylelint"); |
| |
| var _utils = require("../../utils"); |
| |
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } |
| |
| var ruleName = (0, _utils.namespace)("operator-no-unspaced"); |
| exports.ruleName = ruleName; |
| |
| var messages = _stylelint.utils.ruleMessages(ruleName, { |
| expectedAfter: function expectedAfter(operator) { |
| return "Expected single space after \"".concat(operator, "\""); |
| }, |
| expectedBefore: function expectedBefore(operator) { |
| return "Expected single space before \"".concat(operator, "\""); |
| } |
| }); |
| /** |
| * The actual check for are there (un)necessary whitespaces |
| */ |
| |
| |
| exports.messages = messages; |
| |
| function checkSpaces(_ref) { |
| var string = _ref.string, |
| globalIndex = _ref.globalIndex, |
| startIndex = _ref.startIndex, |
| endIndex = _ref.endIndex, |
| node = _ref.node, |
| result = _ref.result; |
| var symbol = string.substring(startIndex, endIndex + 1); |
| var beforeOk = string[startIndex - 1] === " " && !(0, _utils.isWhitespace)(string[startIndex - 2]) || newlineBefore(string, startIndex - 1); |
| |
| if (!beforeOk) { |
| _stylelint.utils.report({ |
| ruleName: ruleName, |
| result: result, |
| node: node, |
| message: messages.expectedBefore(symbol), |
| index: startIndex + globalIndex |
| }); |
| } |
| |
| var afterOk = string[endIndex + 1] === " " && !(0, _utils.isWhitespace)(string[endIndex + 2]) || string[endIndex + 1] === "\n" || string.substr(endIndex + 1, 2) === "\r\n"; |
| |
| if (!afterOk) { |
| _stylelint.utils.report({ |
| ruleName: ruleName, |
| result: result, |
| node: node, |
| message: messages.expectedAfter(symbol), |
| index: endIndex + globalIndex |
| }); |
| } |
| } |
| |
| function newlineBefore(str, startIndex) { |
| var index = startIndex; |
| |
| while (index && (0, _utils.isWhitespace)(str[index])) { |
| if (str[index] === "\n") return true; |
| index--; |
| } |
| |
| return false; |
| } |
| |
| function _default(expectation) { |
| return function (root, result) { |
| var validOptions = _stylelint.utils.validateOptions(result, ruleName, { |
| actual: expectation |
| }); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| (0, _utils.eachRoot)(root, checkRoot); |
| |
| function checkRoot(root) { |
| var rootString = root.source.input.css; |
| |
| if (rootString.trim() === "") { |
| return; |
| } |
| |
| calculationOperatorSpaceChecker({ |
| root: root, |
| result: result, |
| checker: checkSpaces |
| }); |
| } |
| }; |
| } |
| /** |
| * The core rule logic function. This one can be imported by some other rules |
| * that work with Sass operators |
| * |
| * @param {Object} args -- Named arguments object |
| * @param {PostCSS Root} args.root |
| * @param {PostCSS Result} args.result |
| * @param {function} args.checker -- the function that is run against all the |
| * operators found in the input. Takes these arguments: |
| * {Object} cbArgs -- Named arguments object |
| * {string} cbArgs.string -- the input string (suspected operation) |
| * {number} cbArgs.globalIndex -- the string's index in a global input |
| * {number} cbArgs.startIndex -- the start index of a symbol to inspect |
| * {number} cbArgs.endIndex -- the end index of a symbol to inspect |
| * (two indexes needed to allow for `==`, `!=`, etc.) |
| * {PostCSS Node} cbArgs.node -- for stylelint.utils.report |
| * {PostCSS Result} cbArgs.result -- for stylelint.utils.report |
| */ |
| |
| |
| function calculationOperatorSpaceChecker(_ref2) { |
| var root = _ref2.root, |
| result = _ref2.result, |
| checker = _ref2.checker; |
| |
| /** |
| * Takes a string, finds all occurrences of Sass interpolation in it, then |
| * finds all operators inside that interpolation |
| * |
| * @return {array} An array of objects { string, operators } - effectively, |
| * a list of operators for each Sass interpolation occurrence |
| */ |
| function findInterpolation(string, startIndex) { |
| var interpolationRegex = /#{(.*?)}/g; |
| var results = []; // Searching for interpolation |
| |
| var match = interpolationRegex.exec(string); |
| startIndex = !isNaN(startIndex) ? Number(startIndex) : 0; |
| |
| while (match !== null) { |
| results.push({ |
| source: match[0], |
| operators: (0, _utils.findOperators)({ |
| string: match[0], |
| globalIndex: match.index + startIndex |
| }) |
| }); |
| match = interpolationRegex.exec(string); |
| } |
| |
| return results; |
| } |
| |
| var dataURIRegex = /^url\(\s*['"]?data:.+;base64,.+['"]?\s*\)$/; |
| root.walk(function (item) { |
| if (item.prop === "unicode-range") { |
| return; |
| } |
| |
| var results = []; // Check a value (`10px` in `width: 10px;`) |
| |
| if (item.value !== undefined) { |
| if (dataURIRegex.test(item.value)) { |
| return results; |
| } |
| |
| results.push({ |
| source: item.value, |
| operators: (0, _utils.findOperators)({ |
| string: item.value, |
| globalIndex: (0, _utils.declarationValueIndex)(item), |
| // For Sass variable values some special rules apply |
| isAfterColon: item.prop[0] === "$" |
| }) |
| }); |
| } // Property name |
| |
| |
| if (item.prop !== undefined) { |
| results = results.concat(findInterpolation(item.prop)); |
| } // Selector |
| |
| |
| if (item.selector !== undefined) { |
| results = results.concat(findInterpolation(item.selector)); |
| } |
| |
| if (item.type === "atrule") { |
| // @forward, @use and @at-root |
| if (item.name === "forward" || item.name === "use" || item.name === "at-root") { |
| return; |
| } // Media queries |
| |
| |
| if (item.name === "media" || item.name === "import") { |
| (0, _postcssMediaQueryParser["default"])(item.params).walk(function (node) { |
| var type = node.type; |
| |
| if (["keyword", "media-type", "media-feature"].includes(type)) { |
| results = results.concat(findInterpolation(node.value, (0, _utils.atRuleParamIndex)(item) + node.sourceIndex)); |
| } else if (type === "value") { |
| results.push({ |
| source: node.value, |
| operators: (0, _utils.findOperators)({ |
| string: node.value, |
| globalIndex: (0, _utils.atRuleParamIndex)(item) + node.sourceIndex, |
| isAfterColon: true |
| }) |
| }); |
| } else if (type === "url") { |
| var isQuoted = node.value[0] === '"' || node.value[0] === "'"; |
| var containsWhitespace = node.value.search(/\s/) > -1; |
| |
| if (isQuoted || containsWhitespace) { |
| // The argument to the url function is only parsed as SassScript if it is a quoted |
| // string, or a _valid_ unquoted URL [1]. |
| // |
| // [1] https://sass-lang.com/documentation/syntax/special-functions#url |
| results.push({ |
| source: node.value, |
| operators: (0, _utils.findOperators)({ |
| string: node.value, |
| globalIndex: (0, _utils.atRuleParamIndex)(item) + node.sourceIndex, |
| isAfterColon: true |
| }) |
| }); |
| } |
| } |
| }); |
| } else { |
| // Function and mixin definitions and other rules |
| results.push({ |
| source: item.params, |
| operators: (0, _utils.findOperators)({ |
| string: item.params, |
| globalIndex: (0, _utils.atRuleParamIndex)(item), |
| isAfterColon: true |
| }) |
| }); |
| } |
| } // All the strings have been parsed, now run whitespace checking |
| |
| |
| results.forEach(function (el) { |
| // Only if there are operators within a string |
| if (el.operators && el.operators.length > 0) { |
| el.operators.forEach(function (operator) { |
| checker({ |
| string: el.source, |
| globalIndex: operator.globalIndex, |
| startIndex: operator.startIndex, |
| endIndex: operator.endIndex, |
| node: item, |
| result: result |
| }); |
| }); |
| } |
| }); |
| }); // Checking interpolation inside comments |
| // We have to give up on PostCSS here because it skips some inline comments |
| |
| (0, _utils.findCommentsInRaws)(root.source.input.css).forEach(function (comment) { |
| var startIndex = comment.source.start + comment.raws.startToken.length + comment.raws.left.length; |
| |
| if (comment.type !== "css") { |
| return; |
| } |
| |
| findInterpolation(comment.text).forEach(function (el) { |
| // Only if there are operators within a string |
| if (el.operators && el.operators.length > 0) { |
| el.operators.forEach(function (operator) { |
| checker({ |
| string: el.source, |
| globalIndex: operator.globalIndex + startIndex, |
| startIndex: operator.startIndex, |
| endIndex: operator.endIndex, |
| node: root, |
| result: result |
| }); |
| }); |
| } |
| }); |
| }); |
| } |