"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.messages = exports.ruleName = undefined; exports.default = function (expectation) { return function (root, result) { var validOptions = _stylelint.utils.validateOptions(result, ruleName, { actual: expectation }); if (!validOptions) { return; } calculationOperatorSpaceChecker({ root: root, result: result, checker: checkSpaces }); }; }; exports.calculationOperatorSpaceChecker = calculationOperatorSpaceChecker; var _stylelint = require("stylelint"); var _utils = require("../../utils"); var _postcssMediaQueryParser = require("postcss-media-query-parser"); var _postcssMediaQueryParser2 = _interopRequireDefault(_postcssMediaQueryParser); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var ruleName = exports.ruleName = (0, _utils.namespace)("operator-no-unspaced"); var messages = exports.messages = _stylelint.utils.ruleMessages(ruleName, { expectedAfter: function expectedAfter(operator) { return "Expected single space after \"" + operator + "\""; }, expectedBefore: function expectedBefore(operator) { return "Expected single space before \"" + operator + "\""; } }); /** * The actual check for are there (un)necessary whitespaces */ 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; } /** * 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 sybmol to inspect * {number} cbArgs.endIndex -- the end index of a sybmol 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 occurencies of Sass interpolaion in it, then * finds all operators inside that interpolation * * @return {array} An array of ojbects { string, operators } - effectively, * a list of operators for each Sass interpolation occurence */ 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; } root.walk(function (item) { var results = []; // Check a value (`10px` in `width: 10px;`) if (item.value !== undefined) { 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") { // Media queries if (item.name === "media" || item.name === "import") { (0, _postcssMediaQueryParser2.default)(item.params).walk(function (node) { var type = node.type; if (["keyword", "media-type", "media-feature"].indexOf(type) !== -1) { results = results.concat(findInterpolation(node.value, (0, _utils.atRuleParamIndex)(item) + node.sourceIndex)); } else if (["value", "url"].indexOf(type) !== -1) { 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 }); }); } }); }); }