"use strict"; const atRuleParamIndex = require("../../utils/atRuleParamIndex"); const declarationValueIndex = require("../../utils/declarationValueIndex"); const report = require("../../utils/report"); const ruleMessages = require("../../utils/ruleMessages"); const validateOptions = require("../../utils/validateOptions"); const valueParser = require("postcss-value-parser"); const ruleName = "number-leading-zero"; const messages = ruleMessages(ruleName, { expected: "Expected a leading zero", rejected: "Unexpected leading zero" }); const rule = function(expectation, secondary, context) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, possible: ["always", "never"] }); if (!validOptions) { return; } root.walkAtRules(atRule => { if (atRule.name.toLowerCase() === "import") { return; } check(atRule, atRule.params, atRuleParamIndex); }); root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)); function check(node, value, getIndex) { const neverFixPositions = []; const alwaysFixPositions = []; // Get out quickly if there are no periods if (value.indexOf(".") === -1) { return; } valueParser(value).walk(valueNode => { // Ignore `url` function if ( valueNode.type === "function" && valueNode.value.toLowerCase() === "url" ) { return false; } // Ignore strings, comments, etc if (valueNode.type !== "word") { return; } // Check leading zero if (expectation === "always") { const match = /(?:\D|^)(\.\d+)/.exec(valueNode.value); if (match === null) { return; } // The regexp above consists of 2 capturing groups (or capturing parentheses). // We need the index of the second group. This makes sanse when we have "-.5" as an input // for regex. And we need the index of ".5". const capturingGroupIndex = match[0].length - match[1].length; const index = valueNode.sourceIndex + match.index + capturingGroupIndex; if (context.fix) { alwaysFixPositions.unshift({ index }); return; } else { complain(messages.expected, node, getIndex(node) + index); } } if (expectation === "never") { const match = /(?:\D|^)(0+)(\.\d+)/.exec(valueNode.value); if (match === null) { return; } // The regexp above consists of 3 capturing groups (or capturing parentheses). // We need the index of the second group. This makes sanse when we have "-00.5" // as an input for regex. And we need the index of "00". const capturingGroupIndex = match[0].length - (match[1].length + match[2].length); const index = valueNode.sourceIndex + match.index + capturingGroupIndex; if (context.fix) { neverFixPositions.unshift({ startIndex: index, // match[1].length is the length of our matched zero(s) endIndex: index + match[1].length }); return; } else { complain(messages.rejected, node, getIndex(node) + index); } } }); if (alwaysFixPositions.length) { alwaysFixPositions.forEach(function(fixPosition) { const index = fixPosition.index; if (node.type === "atrule") { node.params = addLeadingZero(node.params, index); } else { node.value = addLeadingZero(node.value, index); } }); } if (neverFixPositions.length) { neverFixPositions.forEach(function(fixPosition) { const startIndex = fixPosition.startIndex; const endIndex = fixPosition.endIndex; if (node.type === "atrule") { node.params = removeLeadingZeros(node.params, startIndex, endIndex); } else { node.value = removeLeadingZeros(node.value, startIndex, endIndex); } }); } } function complain(message, node, index) { report({ result, ruleName, message, node, index }); } }; }; function addLeadingZero(input, index) { return input.slice(0, index) + "0" + input.slice(index); } function removeLeadingZeros(input, startIndex, endIndex) { return input.slice(0, startIndex) + input.slice(endIndex); } rule.ruleName = ruleName; rule.messages = messages; module.exports = rule;