// NOTICE: This file is generated by Rollup. To modify it, // please instead edit the ESM counterpart and rebuild with Rollup (npm run build). 'use strict'; const selectorParser = require('postcss-selector-parser'); const validateTypes = require('../../utils/validateTypes.cjs'); const getRuleSelector = require('../../utils/getRuleSelector.cjs'); const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule.cjs'); const parseSelector = require('../../utils/parseSelector.cjs'); const report = require('../../utils/report.cjs'); const ruleMessages = require('../../utils/ruleMessages.cjs'); const validateOptions = require('../../utils/validateOptions.cjs'); const { isAttribute, isClassName, isIdentifier, isPseudoClass, isTag, isUniversal } = selectorParser; const ruleName = 'selector-not-notation'; const messages = ruleMessages(ruleName, { expected: (type) => `Expected ${type} :not() pseudo-class notation`, }); const meta = { url: 'https://stylelint.io/user-guide/rules/selector-not-notation', fixable: true, }; /** @typedef {import('postcss-selector-parser').Node} Node */ /** @typedef {import('postcss-selector-parser').Selector} Selector */ /** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */ /** @typedef {import('postcss-selector-parser').Root} Root */ /** @typedef {import('postcss').Rule} Rule */ /** @typedef {import('stylelint').Range} Range */ /** * @param {Node} node * @returns {boolean} */ const isSimpleSelector = (node) => isPseudoClass(node) || isAttribute(node) || isClassName(node) || isUniversal(node) || isIdentifier(node) || isTag(node); /** * @param {Node | undefined} node * @returns {node is Pseudo} */ const isNot = (node) => isPseudoClass(node) && node.value !== undefined && node.value.toLowerCase() === ':not'; /** * @param {Selector[]} list * @returns {boolean} */ const isSimple = (list) => { if (list.length > 1) return false; validateTypes.assert(list[0], 'list is never empty'); const [first, second] = list[0].nodes; if (!first) return true; if (second) return false; return isSimpleSelector(first) && !isNot(first); }; /** @type {import('stylelint').CoreRules[ruleName]} */ const rule = (primary) => { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: primary, possible: ['simple', 'complex'], }); if (!validOptions) return; root.walkRules((ruleNode) => { if (!isStandardSyntaxRule(ruleNode)) return; const selector = ruleNode.selector; if (!selector.includes(':not(')) return; const selectorRoot = parseSelector(getRuleSelector(ruleNode), result, ruleNode); if (!selectorRoot) return; selectorRoot.walkPseudos((pseudo) => { if (!isNot(pseudo)) return; const index = pseudo.sourceIndex; const endIndex = index + pseudo.toString().trim().length; let fix; if (primary === 'complex') { const prev = pseudo.prev(); const hasConsecutiveNot = prev && isNot(prev); if (!hasConsecutiveNot) return; fix = () => fixComplex(prev, ruleNode, selectorRoot); } else { const selectors = pseudo.nodes; if (isSimple(selectors)) return; const second = selectors.length > 1 && selectors[1]; const isFixable = second && (!second.nodes.length || selectors.every(({ nodes }) => nodes.length === 1)); if (isFixable) fix = () => fixSimple(pseudo, ruleNode, selectorRoot); } report({ message: messages.expected, messageArgs: [primary], node: ruleNode, result, ruleName, index, endIndex, fix: { apply: fix, node: ruleNode, }, }); }); }); }; }; /** * @param {Pseudo} not * @returns {Node} */ const getLastConsecutiveNot = ({ parent, sourceIndex }) => { validateTypes.assert(parent); const nodes = parent.nodes; const index = nodes.findIndex((node) => node.sourceIndex >= sourceIndex && !isNot(node)); const node = index === -1 ? nodes[nodes.length - 1] : nodes[index - 1]; validateTypes.assert(node); return node; }; /** * @param {Pseudo} not * @param {Rule} ruleNode * @param {Root} selectorRoot */ function fixSimple(not, ruleNode, selectorRoot) { const simpleSelectors = not.nodes .filter(({ nodes }) => nodes[0] && isSimpleSelector(nodes[0])) .map((s) => { validateTypes.assert(s.nodes[0]); s.nodes[0].rawSpaceBefore = ''; s.nodes[0].rawSpaceAfter = ''; return s; }); const firstSelector = simpleSelectors.shift(); validateTypes.assert(firstSelector); validateTypes.assert(not.parent); not.empty(); not.nodes.push(firstSelector); for (const s of simpleSelectors) { const last = getLastConsecutiveNot(not); const clone = last.clone({ nodes: [s] }); clone.rawSpaceBefore = ''; not.parent.insertAfter(last, clone); } ruleNode.selector = selectorRoot.toString(); } /** * @param {Pseudo} previousNot * @param {Rule} ruleNode * @param {Root} selectorRoot */ function fixComplex(previousNot, ruleNode, selectorRoot) { const indentAndTrimRight = (/** @type {Selector[]} */ selectors) => { for (const s of selectors) { validateTypes.assert(s.nodes[0]); s.nodes[0].rawSpaceBefore = ' '; s.nodes[0].rawSpaceAfter = ''; } }; const [head, ...tail] = previousNot.nodes; let node = previousNot.next(); if (head == null || head.nodes.length === 0) return; validateTypes.assert(head.nodes[0]); head.nodes[0].rawSpaceBefore = ''; head.nodes[0].rawSpaceAfter = ''; indentAndTrimRight(tail); while (isNot(node)) { const selectors = node.nodes; const prev = node; indentAndTrimRight(selectors); previousNot.nodes = previousNot.nodes.concat(selectors); node = node.next(); prev.remove(); } ruleNode.selector = selectorRoot.toString(); } rule.ruleName = ruleName; rule.messages = messages; rule.meta = meta; module.exports = rule;