"use strict"; const _ = require("lodash"); const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule"); const parseSelector = require("../../utils/parseSelector"); const report = require("../../utils/report"); const ruleMessages = require("../../utils/ruleMessages"); const styleSearch = require("style-search"); const validateOptions = require("../../utils/validateOptions"); const ruleName = "selector-attribute-brackets-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; } const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector; let hasFixed; const fixedSelector = parseSelector( selector, result, rule, selectorTree => { selectorTree.walkAttributes(attributeNode => { const attributeSelectorString = attributeNode.toString(); styleSearch( { source: attributeSelectorString, target: "[" }, match => { const nextCharIsSpace = attributeSelectorString[match.startIndex + 1] === " "; const index = attributeNode.sourceIndex + match.startIndex + 1; if (nextCharIsSpace && expectation === "never") { if (context.fix) { hasFixed = true; fixBefore(attributeNode); return; } complain(messages.rejectedOpening, index); } if (!nextCharIsSpace && expectation === "always") { if (context.fix) { hasFixed = true; fixBefore(attributeNode); return; } complain(messages.expectedOpening, index); } } ); styleSearch( { source: attributeSelectorString, target: "]" }, match => { const prevCharIsSpace = attributeSelectorString[match.startIndex - 1] === " "; const index = attributeNode.sourceIndex + match.startIndex - 1; if (prevCharIsSpace && expectation === "never") { if (context.fix) { hasFixed = true; fixAfter(attributeNode); return; } complain(messages.rejectedClosing, index); } if (!prevCharIsSpace && expectation === "always") { if (context.fix) { hasFixed = true; fixAfter(attributeNode); return; } complain(messages.expectedClosing, index); } } ); }); } ); 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 fixBefore(attributeNode) { const rawAttrBefore = _.get(attributeNode, "raws.spaces.attribute.before"); const { attrBefore, setAttrBefore } = rawAttrBefore ? { attrBefore: rawAttrBefore, setAttrBefore(fixed) { attributeNode.raws.spaces.attribute.before = fixed; } } : { attrBefore: _.get(attributeNode, "spaces.attribute.before", ""), setAttrBefore(fixed) { _.set(attributeNode, "spaces.attribute.before", fixed); } }; if (expectation === "always") { setAttrBefore(attrBefore.replace(/^\s*/, " ")); } else if (expectation === "never") { setAttrBefore(attrBefore.replace(/^\s*/, "")); } } function fixAfter(attributeNode) { let key; if (attributeNode.operator) { if (attributeNode.insensitive) { key = "insensitive"; } else { key = "value"; } } else { key = "attribute"; } const rawAfter = _.get(attributeNode, `raws.spaces.${key}.after`); const { after, setAfter } = rawAfter ? { after: rawAfter, setAfter(fixed) { attributeNode.raws.spaces[key].after = fixed; } } : { after: _.get(attributeNode, `spaces.${key}.after`, ""), setAfter(fixed) { _.set(attributeNode, `spaces.${key}.after`, fixed); } }; if (expectation === "always") { setAfter(after.replace(/\s*$/, " ")); } else if (expectation === "never") { setAfter(after.replace(/\s*$/, "")); } } }; rule.ruleName = ruleName; rule.messages = messages; module.exports = rule;