| "use strict"; |
| |
| const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule"); |
| const parseSelector = require("../../utils/parseSelector"); |
| const report = require("../../utils/report"); |
| const ruleMessages = require("../../utils/ruleMessages"); |
| const validateOptions = require("../../utils/validateOptions"); |
| |
| const ruleName = "selector-pseudo-class-parentheses-space-inside"; |
| |
| const messages = ruleMessages(ruleName, { |
| expectedOpening: 'Expected single space after "("', |
| rejectedOpening: 'Unexpected whitespace after "("', |
| expectedClosing: 'Expected single space before ")"', |
| rejectedClosing: 'Unexpected whitespace before ")"' |
| }); |
| |
| const rule = function(expectation, options, context) { |
| return (root, result) => { |
| const validOptions = validateOptions(result, ruleName, { |
| actual: expectation, |
| possible: ["always", "never"] |
| }); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| root.walkRules(rule => { |
| if (!isStandardSyntaxRule(rule)) { |
| return; |
| } |
| |
| if (rule.selector.indexOf("(") === -1) { |
| return; |
| } |
| |
| let hasFixed = false; |
| const selector = rule.raws.selector |
| ? rule.raws.selector.raw |
| : rule.selector; |
| const fixedSelector = parseSelector( |
| selector, |
| result, |
| rule, |
| selectorTree => { |
| selectorTree.walkPseudos(pseudoNode => { |
| if (!pseudoNode.length) { |
| return; |
| } |
| |
| const beforeString = |
| (pseudoNode.rawSpaceBefore || "") + |
| stringifyProperty(pseudoNode, "value"); |
| const paramString = pseudoNode.map(String).join(","); |
| const nextCharIsSpace = paramString.startsWith(" "); |
| const openIndex = pseudoNode.sourceIndex + beforeString.length + 1; |
| |
| if (nextCharIsSpace && expectation === "never") { |
| if (context.fix) { |
| hasFixed = true; |
| setFirstNodeSpaceBefore(pseudoNode, ""); |
| } else { |
| complain(messages.rejectedOpening, openIndex); |
| } |
| } |
| |
| if (!nextCharIsSpace && expectation === "always") { |
| if (context.fix) { |
| hasFixed = true; |
| setFirstNodeSpaceBefore(pseudoNode, " "); |
| } else { |
| complain(messages.expectedOpening, openIndex); |
| } |
| } |
| |
| const prevCharIsSpace = paramString.endsWith(" "); |
| const closeIndex = openIndex + paramString.length - 1; |
| |
| if (prevCharIsSpace && expectation === "never") { |
| if (context.fix) { |
| hasFixed = true; |
| setLastNodeSpaceAfter(pseudoNode, ""); |
| } else { |
| complain(messages.rejectedClosing, closeIndex); |
| } |
| } |
| |
| if (!prevCharIsSpace && expectation === "always") { |
| if (context.fix) { |
| hasFixed = true; |
| setLastNodeSpaceAfter(pseudoNode, " "); |
| } else { |
| complain(messages.expectedClosing, closeIndex); |
| } |
| } |
| }); |
| } |
| ); |
| |
| if (hasFixed) { |
| if (!rule.raws.selector) { |
| rule.selector = fixedSelector; |
| } else { |
| rule.raws.selector.raw = fixedSelector; |
| } |
| } |
| |
| function complain(message, index) { |
| report({ |
| message, |
| index, |
| result, |
| ruleName, |
| node: rule |
| }); |
| } |
| }); |
| }; |
| }; |
| |
| function setFirstNodeSpaceBefore(node, value) { |
| const target = node.first; |
| |
| if (target.type === "selector") { |
| setFirstNodeSpaceBefore(target, value); |
| } else { |
| target.spaces.before = value; |
| } |
| } |
| |
| function setLastNodeSpaceAfter(node, value) { |
| const target = node.last; |
| |
| if (target.type === "selector") { |
| setLastNodeSpaceAfter(target, value); |
| } else { |
| target.spaces.after = value; |
| } |
| } |
| |
| function stringifyProperty(node, propName) { |
| return (node.raws && node.raws[propName]) || node[propName]; |
| } |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |