diff --git a/lib/rules/no-descending-specificity/index.cjs b/lib/rules/no-descending-specificity/index.cjs index 4670d5d47e..8c094127c3 100644 --- a/lib/rules/no-descending-specificity/index.cjs +++ b/lib/rules/no-descending-specificity/index.cjs @@ -88,9 +88,11 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, (s) => { - checkSelector(resolvedSelector, s, ruleNode, comparisonContext); - }); + const selectorRoot = parseSelector(resolvedSelector, result, ruleNode); + + if (selectorRoot) { + checkSelector(resolvedSelector, selectorRoot, ruleNode, comparisonContext); + } } } }); diff --git a/lib/rules/no-descending-specificity/index.mjs b/lib/rules/no-descending-specificity/index.mjs index 780ecee0a0..0d3cb895f6 100644 --- a/lib/rules/no-descending-specificity/index.mjs +++ b/lib/rules/no-descending-specificity/index.mjs @@ -85,9 +85,11 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, (s) => { - checkSelector(resolvedSelector, s, ruleNode, comparisonContext); - }); + const selectorRoot = parseSelector(resolvedSelector, result, ruleNode); + + if (selectorRoot) { + checkSelector(resolvedSelector, selectorRoot, ruleNode, comparisonContext); + } } } }); diff --git a/lib/rules/no-duplicate-selectors/index.cjs b/lib/rules/no-duplicate-selectors/index.cjs index 3b34ae44b1..c962bb3adc 100644 --- a/lib/rules/no-duplicate-selectors/index.cjs +++ b/lib/rules/no-duplicate-selectors/index.cjs @@ -89,16 +89,14 @@ const rule = (primary, secondaryOptions) => { const selectorListParsed = []; if (shouldDisallowDuplicateInList) { - parseSelector(sortedSelectorList, result, ruleNode, (selectors) => { - selectors.each((s) => { - const selector = String(s); + parseSelector(sortedSelectorList, result, ruleNode)?.each((s) => { + const selector = String(s); - selectorListParsed.push(selector); + selectorListParsed.push(selector); - if (contextSelectorSet.get(selector)) { - previousDuplicatePosition = contextSelectorSet.get(selector); - } - }); + if (contextSelectorSet.get(selector)) { + previousDuplicatePosition = contextSelectorSet.get(selector); + } }); } else { previousDuplicatePosition = contextSelectorSet.get(sortedSelectorList); diff --git a/lib/rules/no-duplicate-selectors/index.mjs b/lib/rules/no-duplicate-selectors/index.mjs index 00fbdb4a5d..4584b6af05 100644 --- a/lib/rules/no-duplicate-selectors/index.mjs +++ b/lib/rules/no-duplicate-selectors/index.mjs @@ -86,16 +86,14 @@ const rule = (primary, secondaryOptions) => { const selectorListParsed = []; if (shouldDisallowDuplicateInList) { - parseSelector(sortedSelectorList, result, ruleNode, (selectors) => { - selectors.each((s) => { - const selector = String(s); + parseSelector(sortedSelectorList, result, ruleNode)?.each((s) => { + const selector = String(s); - selectorListParsed.push(selector); + selectorListParsed.push(selector); - if (contextSelectorSet.get(selector)) { - previousDuplicatePosition = contextSelectorSet.get(selector); - } - }); + if (contextSelectorSet.get(selector)) { + previousDuplicatePosition = contextSelectorSet.get(selector); + } }); } else { previousDuplicatePosition = contextSelectorSet.get(sortedSelectorList); diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.cjs b/lib/rules/selector-attribute-name-disallowed-list/index.cjs index 5836e879c0..b53dbea479 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/index.cjs +++ b/lib/rules/selector-attribute-name-disallowed-list/index.cjs @@ -41,26 +41,24 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const attributeName = attributeNode.qualifiedAttribute; + parseSelector(ruleNode.selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const attributeName = attributeNode.qualifiedAttribute; - if (!matchesStringOrRegExp(attributeName, primary)) { - return; - } + if (!matchesStringOrRegExp(attributeName, primary)) { + return; + } - const index = attributeNode.sourceIndex + attributeNode.offsetOf('attribute'); - const endIndex = index + attributeName.length; + const index = attributeNode.sourceIndex + attributeNode.offsetOf('attribute'); + const endIndex = index + attributeName.length; - report({ - message: messages.rejected, - messageArgs: [attributeName], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + report({ + message: messages.rejected, + messageArgs: [attributeName], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-name-disallowed-list/index.mjs b/lib/rules/selector-attribute-name-disallowed-list/index.mjs index f262d44c50..73a5fa9cd4 100644 --- a/lib/rules/selector-attribute-name-disallowed-list/index.mjs +++ b/lib/rules/selector-attribute-name-disallowed-list/index.mjs @@ -37,26 +37,24 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const attributeName = attributeNode.qualifiedAttribute; + parseSelector(ruleNode.selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const attributeName = attributeNode.qualifiedAttribute; - if (!matchesStringOrRegExp(attributeName, primary)) { - return; - } + if (!matchesStringOrRegExp(attributeName, primary)) { + return; + } - const index = attributeNode.sourceIndex + attributeNode.offsetOf('attribute'); - const endIndex = index + attributeName.length; + const index = attributeNode.sourceIndex + attributeNode.offsetOf('attribute'); + const endIndex = index + attributeName.length; - report({ - message: messages.rejected, - messageArgs: [attributeName], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + report({ + message: messages.rejected, + messageArgs: [attributeName], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-operator-allowed-list/index.cjs b/lib/rules/selector-attribute-operator-allowed-list/index.cjs index 7cdeee29e6..33aa188fae 100644 --- a/lib/rules/selector-attribute-operator-allowed-list/index.cjs +++ b/lib/rules/selector-attribute-operator-allowed-list/index.cjs @@ -44,26 +44,24 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { operator } = attributeNode; - - if (!operator || primaryValues.has(operator)) { - return; - } - - const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); - const endIndex = index + operator.length; - - report({ - message: messages.rejected, - messageArgs: [operator], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { operator } = attributeNode; + + if (!operator || primaryValues.has(operator)) { + return; + } + + const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); + const endIndex = index + operator.length; + + report({ + message: messages.rejected, + messageArgs: [operator], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-operator-allowed-list/index.mjs b/lib/rules/selector-attribute-operator-allowed-list/index.mjs index 97e2906e92..6669dcc050 100644 --- a/lib/rules/selector-attribute-operator-allowed-list/index.mjs +++ b/lib/rules/selector-attribute-operator-allowed-list/index.mjs @@ -40,26 +40,24 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { operator } = attributeNode; + parseSelector(selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { operator } = attributeNode; - if (!operator || primaryValues.has(operator)) { - return; - } + if (!operator || primaryValues.has(operator)) { + return; + } - const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); - const endIndex = index + operator.length; + const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); + const endIndex = index + operator.length; - report({ - message: messages.rejected, - messageArgs: [operator], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + report({ + message: messages.rejected, + messageArgs: [operator], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-operator-disallowed-list/index.cjs b/lib/rules/selector-attribute-operator-disallowed-list/index.cjs index f2572b5f03..655f2e88f0 100644 --- a/lib/rules/selector-attribute-operator-disallowed-list/index.cjs +++ b/lib/rules/selector-attribute-operator-disallowed-list/index.cjs @@ -44,26 +44,24 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { operator } = attributeNode; - - if (!operator || !primaryValues.has(operator)) { - return; - } - - const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); - const endIndex = index + operator.length; - - report({ - message: messages.rejected, - messageArgs: [operator], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { operator } = attributeNode; + + if (!operator || !primaryValues.has(operator)) { + return; + } + + const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); + const endIndex = index + operator.length; + + report({ + message: messages.rejected, + messageArgs: [operator], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-operator-disallowed-list/index.mjs b/lib/rules/selector-attribute-operator-disallowed-list/index.mjs index aa2cc7e89b..d13ebb5274 100644 --- a/lib/rules/selector-attribute-operator-disallowed-list/index.mjs +++ b/lib/rules/selector-attribute-operator-disallowed-list/index.mjs @@ -40,26 +40,24 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { operator } = attributeNode; + parseSelector(selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { operator } = attributeNode; - if (!operator || !primaryValues.has(operator)) { - return; - } + if (!operator || !primaryValues.has(operator)) { + return; + } - const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); - const endIndex = index + operator.length; + const index = attributeNode.sourceIndex + attributeNode.offsetOf('operator'); + const endIndex = index + operator.length; - report({ - message: messages.rejected, - messageArgs: [operator], - node: ruleNode, - index, - endIndex, - result, - ruleName, - }); + report({ + message: messages.rejected, + messageArgs: [operator], + node: ruleNode, + index, + endIndex, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-attribute-quotes/index.cjs b/lib/rules/selector-attribute-quotes/index.cjs index a344378c1f..1fde35c90f 100644 --- a/lib/rules/selector-attribute-quotes/index.cjs +++ b/lib/rules/selector-attribute-quotes/index.cjs @@ -47,51 +47,53 @@ const rule = (primary, _secondaryOptions, context) => { return; } - parseSelector(getRuleSelector(ruleNode), result, ruleNode, (selectorTree) => { - let selectorFixed = false; + const selectorTree = parseSelector(getRuleSelector(ruleNode), result, ruleNode); - selectorTree.walkAttributes((attributeNode) => { - const { operator, value, quoted } = attributeNode; + if (!selectorTree) return; - if (!operator || !value) { - return; - } + let selectorFixed = false; + + selectorTree.walkAttributes((attributeNode) => { + const { operator, value, quoted } = attributeNode; - if (!quoted && primary === 'always') { - if (context.fix) { - selectorFixed = true; - attributeNode.quoteMark = acceptedQuoteMark; - } else { - complain(messages.expected(value), attributeNode); - } + if (!operator || !value) { + return; + } + + if (!quoted && primary === 'always') { + if (context.fix) { + selectorFixed = true; + attributeNode.quoteMark = acceptedQuoteMark; + } else { + complain(messages.expected(value), attributeNode); } + } - if (quoted && primary === 'never') { - // some selectors require quotes to be valid; - // we pass in the raw string value, which contains the escape characters - // necessary to check if escaped characters are valid - // see: https://github.com/stylelint/stylelint/issues/4300 - if ( - !attributeNode.raws.value || - !isValidIdentifier(attributeNode.raws.value.slice(1, -1)) - ) { - return; - } - - if (context.fix) { - selectorFixed = true; - attributeNode.quoteMark = null; - } else { - complain(messages.rejected(value), attributeNode); - } + if (quoted && primary === 'never') { + // some selectors require quotes to be valid; + // we pass in the raw string value, which contains the escape characters + // necessary to check if escaped characters are valid + // see: https://github.com/stylelint/stylelint/issues/4300 + if ( + !attributeNode.raws.value || + !isValidIdentifier(attributeNode.raws.value.slice(1, -1)) + ) { + return; } - }); - if (selectorFixed) { - ruleNode.selector = selectorTree.toString(); + if (context.fix) { + selectorFixed = true; + attributeNode.quoteMark = null; + } else { + complain(messages.rejected(value), attributeNode); + } } }); + if (selectorFixed) { + ruleNode.selector = selectorTree.toString(); + } + /** * @param {string} message * @param {import('postcss-selector-parser').Attribute} attrNode diff --git a/lib/rules/selector-attribute-quotes/index.mjs b/lib/rules/selector-attribute-quotes/index.mjs index c208ba6ea4..5e81e8016f 100644 --- a/lib/rules/selector-attribute-quotes/index.mjs +++ b/lib/rules/selector-attribute-quotes/index.mjs @@ -43,51 +43,53 @@ const rule = (primary, _secondaryOptions, context) => { return; } - parseSelector(getRuleSelector(ruleNode), result, ruleNode, (selectorTree) => { - let selectorFixed = false; + const selectorTree = parseSelector(getRuleSelector(ruleNode), result, ruleNode); - selectorTree.walkAttributes((attributeNode) => { - const { operator, value, quoted } = attributeNode; + if (!selectorTree) return; - if (!operator || !value) { - return; - } + let selectorFixed = false; + + selectorTree.walkAttributes((attributeNode) => { + const { operator, value, quoted } = attributeNode; - if (!quoted && primary === 'always') { - if (context.fix) { - selectorFixed = true; - attributeNode.quoteMark = acceptedQuoteMark; - } else { - complain(messages.expected(value), attributeNode); - } + if (!operator || !value) { + return; + } + + if (!quoted && primary === 'always') { + if (context.fix) { + selectorFixed = true; + attributeNode.quoteMark = acceptedQuoteMark; + } else { + complain(messages.expected(value), attributeNode); } + } - if (quoted && primary === 'never') { - // some selectors require quotes to be valid; - // we pass in the raw string value, which contains the escape characters - // necessary to check if escaped characters are valid - // see: https://github.com/stylelint/stylelint/issues/4300 - if ( - !attributeNode.raws.value || - !isValidIdentifier(attributeNode.raws.value.slice(1, -1)) - ) { - return; - } - - if (context.fix) { - selectorFixed = true; - attributeNode.quoteMark = null; - } else { - complain(messages.rejected(value), attributeNode); - } + if (quoted && primary === 'never') { + // some selectors require quotes to be valid; + // we pass in the raw string value, which contains the escape characters + // necessary to check if escaped characters are valid + // see: https://github.com/stylelint/stylelint/issues/4300 + if ( + !attributeNode.raws.value || + !isValidIdentifier(attributeNode.raws.value.slice(1, -1)) + ) { + return; } - }); - if (selectorFixed) { - ruleNode.selector = selectorTree.toString(); + if (context.fix) { + selectorFixed = true; + attributeNode.quoteMark = null; + } else { + complain(messages.rejected(value), attributeNode); + } } }); + if (selectorFixed) { + ruleNode.selector = selectorTree.toString(); + } + /** * @param {string} message * @param {import('postcss-selector-parser').Attribute} attrNode diff --git a/lib/rules/selector-class-pattern/index.cjs b/lib/rules/selector-class-pattern/index.cjs index b2fa97a63c..10be72fa04 100644 --- a/lib/rules/selector-class-pattern/index.cjs +++ b/lib/rules/selector-class-pattern/index.cjs @@ -69,10 +69,14 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(nestedSelector, result, ruleNode, (s) => checkSelector(s, ruleNode)); + const selectorRoot = parseSelector(nestedSelector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot, ruleNode); } } else { - parseSelector(selector, result, ruleNode, (s) => checkSelector(s, ruleNode)); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot, ruleNode); } }); diff --git a/lib/rules/selector-class-pattern/index.mjs b/lib/rules/selector-class-pattern/index.mjs index 5ced4dfc5a..0bace317cb 100644 --- a/lib/rules/selector-class-pattern/index.mjs +++ b/lib/rules/selector-class-pattern/index.mjs @@ -66,10 +66,14 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(nestedSelector, result, ruleNode, (s) => checkSelector(s, ruleNode)); + const selectorRoot = parseSelector(nestedSelector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot, ruleNode); } } else { - parseSelector(selector, result, ruleNode, (s) => checkSelector(s, ruleNode)); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot, ruleNode); } }); diff --git a/lib/rules/selector-combinator-allowed-list/index.cjs b/lib/rules/selector-combinator-allowed-list/index.cjs index 7a0287d73f..a1201fa425 100644 --- a/lib/rules/selector-combinator-allowed-list/index.cjs +++ b/lib/rules/selector-combinator-allowed-list/index.cjs @@ -39,31 +39,29 @@ const rule = (primary) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (fullSelector) => { - fullSelector.walkCombinators((combinatorNode) => { - if (!isStandardSyntaxCombinator(combinatorNode)) { - return; - } - - const { value } = combinatorNode; - const normalizedValue = normalizeCombinator(value); - - if (primary.includes(normalizedValue)) { - return; - } - - const { sourceIndex: index, raws } = combinatorNode; - const endIndex = index + ((raws && raws.value) || value).length; - - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [normalizedValue], - node: ruleNode, - index, - endIndex, - }); + parseSelector(selector, result, ruleNode)?.walkCombinators((combinatorNode) => { + if (!isStandardSyntaxCombinator(combinatorNode)) { + return; + } + + const { value } = combinatorNode; + const normalizedValue = normalizeCombinator(value); + + if (primary.includes(normalizedValue)) { + return; + } + + const { sourceIndex: index, raws } = combinatorNode; + const endIndex = index + ((raws && raws.value) || value).length; + + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [normalizedValue], + node: ruleNode, + index, + endIndex, }); }); }); diff --git a/lib/rules/selector-combinator-allowed-list/index.mjs b/lib/rules/selector-combinator-allowed-list/index.mjs index b686ab241c..e7849f0196 100644 --- a/lib/rules/selector-combinator-allowed-list/index.mjs +++ b/lib/rules/selector-combinator-allowed-list/index.mjs @@ -35,31 +35,29 @@ const rule = (primary) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (fullSelector) => { - fullSelector.walkCombinators((combinatorNode) => { - if (!isStandardSyntaxCombinator(combinatorNode)) { - return; - } + parseSelector(selector, result, ruleNode)?.walkCombinators((combinatorNode) => { + if (!isStandardSyntaxCombinator(combinatorNode)) { + return; + } - const { value } = combinatorNode; - const normalizedValue = normalizeCombinator(value); + const { value } = combinatorNode; + const normalizedValue = normalizeCombinator(value); - if (primary.includes(normalizedValue)) { - return; - } + if (primary.includes(normalizedValue)) { + return; + } - const { sourceIndex: index, raws } = combinatorNode; - const endIndex = index + ((raws && raws.value) || value).length; + const { sourceIndex: index, raws } = combinatorNode; + const endIndex = index + ((raws && raws.value) || value).length; - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [normalizedValue], - node: ruleNode, - index, - endIndex, - }); + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [normalizedValue], + node: ruleNode, + index, + endIndex, }); }); }); diff --git a/lib/rules/selector-combinator-disallowed-list/index.cjs b/lib/rules/selector-combinator-disallowed-list/index.cjs index 17d3841ae9..9a49dcd515 100644 --- a/lib/rules/selector-combinator-disallowed-list/index.cjs +++ b/lib/rules/selector-combinator-disallowed-list/index.cjs @@ -39,31 +39,29 @@ const rule = (primary) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (fullSelector) => { - fullSelector.walkCombinators((combinatorNode) => { - if (!isStandardSyntaxCombinator(combinatorNode)) { - return; - } - - const { value } = combinatorNode; - const normalizedValue = normalizeCombinator(value); - - if (!primary.includes(normalizedValue)) { - return; - } - - const { sourceIndex: index, raws } = combinatorNode; - const endIndex = index + ((raws && raws.value) || value).length; - - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [normalizedValue], - node: ruleNode, - index, - endIndex, - }); + parseSelector(selector, result, ruleNode)?.walkCombinators((combinatorNode) => { + if (!isStandardSyntaxCombinator(combinatorNode)) { + return; + } + + const { value } = combinatorNode; + const normalizedValue = normalizeCombinator(value); + + if (!primary.includes(normalizedValue)) { + return; + } + + const { sourceIndex: index, raws } = combinatorNode; + const endIndex = index + ((raws && raws.value) || value).length; + + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [normalizedValue], + node: ruleNode, + index, + endIndex, }); }); }); diff --git a/lib/rules/selector-combinator-disallowed-list/index.mjs b/lib/rules/selector-combinator-disallowed-list/index.mjs index 5a23450690..6898587a32 100644 --- a/lib/rules/selector-combinator-disallowed-list/index.mjs +++ b/lib/rules/selector-combinator-disallowed-list/index.mjs @@ -35,31 +35,29 @@ const rule = (primary) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (fullSelector) => { - fullSelector.walkCombinators((combinatorNode) => { - if (!isStandardSyntaxCombinator(combinatorNode)) { - return; - } + parseSelector(selector, result, ruleNode)?.walkCombinators((combinatorNode) => { + if (!isStandardSyntaxCombinator(combinatorNode)) { + return; + } - const { value } = combinatorNode; - const normalizedValue = normalizeCombinator(value); + const { value } = combinatorNode; + const normalizedValue = normalizeCombinator(value); - if (!primary.includes(normalizedValue)) { - return; - } + if (!primary.includes(normalizedValue)) { + return; + } - const { sourceIndex: index, raws } = combinatorNode; - const endIndex = index + ((raws && raws.value) || value).length; + const { sourceIndex: index, raws } = combinatorNode; + const endIndex = index + ((raws && raws.value) || value).length; - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [normalizedValue], - node: ruleNode, - index, - endIndex, - }); + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [normalizedValue], + node: ruleNode, + index, + endIndex, }); }); }); diff --git a/lib/rules/selector-id-pattern/index.cjs b/lib/rules/selector-id-pattern/index.cjs index 2738eaa126..7087780f77 100644 --- a/lib/rules/selector-id-pattern/index.cjs +++ b/lib/rules/selector-id-pattern/index.cjs @@ -38,22 +38,20 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (fullSelector) => { - fullSelector.walkIds((selectorNode) => { - if (normalizedPattern.test(selectorNode.value)) { - return; - } + parseSelector(ruleNode.selector, result, ruleNode)?.walkIds((selectorNode) => { + if (normalizedPattern.test(selectorNode.value)) { + return; + } - const selector = String(selectorNode); + const selector = String(selectorNode); - report({ - result, - ruleName, - message: messages.expected, - messageArgs: [selector, primary], - node: ruleNode, - word: selector, - }); + report({ + result, + ruleName, + message: messages.expected, + messageArgs: [selector, primary], + node: ruleNode, + word: selector, }); }); }); diff --git a/lib/rules/selector-id-pattern/index.mjs b/lib/rules/selector-id-pattern/index.mjs index 4dd26b481c..63936a6bae 100644 --- a/lib/rules/selector-id-pattern/index.mjs +++ b/lib/rules/selector-id-pattern/index.mjs @@ -34,22 +34,20 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (fullSelector) => { - fullSelector.walkIds((selectorNode) => { - if (normalizedPattern.test(selectorNode.value)) { - return; - } - - const selector = String(selectorNode); - - report({ - result, - ruleName, - message: messages.expected, - messageArgs: [selector, primary], - node: ruleNode, - word: selector, - }); + parseSelector(ruleNode.selector, result, ruleNode)?.walkIds((selectorNode) => { + if (normalizedPattern.test(selectorNode.value)) { + return; + } + + const selector = String(selectorNode); + + report({ + result, + ruleName, + message: messages.expected, + messageArgs: [selector, primary], + node: ruleNode, + word: selector, }); }); }); diff --git a/lib/rules/selector-max-specificity/index.cjs b/lib/rules/selector-max-specificity/index.cjs index 2bb5d3fe9f..3fa3aba58d 100644 --- a/lib/rules/selector-max-specificity/index.cjs +++ b/lib/rules/selector-max-specificity/index.cjs @@ -245,19 +245,21 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, (selectorTree) => { - // Check if the selector specificity exceeds the allowed maximum - if (selectorSpecificity.compare(maxChildSpecificity(selectorTree), maxSpecificity) > 0) { - report({ - ruleName, - result, - node: ruleNode, - message: messages.expected, - messageArgs: [resolvedSelector, primary], - word: selector, - }); - } - }); + const selectorTree = parseSelector(resolvedSelector, result, ruleNode); + + if (!selectorTree) continue; + + // Check if the selector specificity exceeds the allowed maximum + if (selectorSpecificity.compare(maxChildSpecificity(selectorTree), maxSpecificity) > 0) { + report({ + ruleName, + result, + node: ruleNode, + message: messages.expected, + messageArgs: [resolvedSelector, primary], + word: selector, + }); + } } } }); diff --git a/lib/rules/selector-max-specificity/index.mjs b/lib/rules/selector-max-specificity/index.mjs index 98cba46c4a..40bbaa463d 100644 --- a/lib/rules/selector-max-specificity/index.mjs +++ b/lib/rules/selector-max-specificity/index.mjs @@ -246,19 +246,21 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, (selectorTree) => { - // Check if the selector specificity exceeds the allowed maximum - if (compare(maxChildSpecificity(selectorTree), maxSpecificity) > 0) { - report({ - ruleName, - result, - node: ruleNode, - message: messages.expected, - messageArgs: [resolvedSelector, primary], - word: selector, - }); - } - }); + const selectorTree = parseSelector(resolvedSelector, result, ruleNode); + + if (!selectorTree) continue; + + // Check if the selector specificity exceeds the allowed maximum + if (compare(maxChildSpecificity(selectorTree), maxSpecificity) > 0) { + report({ + ruleName, + result, + node: ruleNode, + message: messages.expected, + messageArgs: [resolvedSelector, primary], + word: selector, + }); + } } } }); diff --git a/lib/rules/selector-no-qualifying-type/index.cjs b/lib/rules/selector-no-qualifying-type/index.cjs index 7fd46ea6b3..5ccab88283 100644 --- a/lib/rules/selector-no-qualifying-type/index.cjs +++ b/lib/rules/selector-no-qualifying-type/index.cjs @@ -132,7 +132,9 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, checkSelector); + const selectorRoot = parseSelector(resolvedSelector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot); } /** diff --git a/lib/rules/selector-no-qualifying-type/index.mjs b/lib/rules/selector-no-qualifying-type/index.mjs index de70317015..25eada7dd6 100644 --- a/lib/rules/selector-no-qualifying-type/index.mjs +++ b/lib/rules/selector-no-qualifying-type/index.mjs @@ -129,7 +129,9 @@ const rule = (primary, secondaryOptions) => { continue; } - parseSelector(resolvedSelector, result, ruleNode, checkSelector); + const selectorRoot = parseSelector(resolvedSelector, result, ruleNode); + + if (selectorRoot) checkSelector(selectorRoot); } /** diff --git a/lib/rules/selector-no-vendor-prefix/index.cjs b/lib/rules/selector-no-vendor-prefix/index.cjs index 9f27b913ff..9a0cdd337a 100644 --- a/lib/rules/selector-no-vendor-prefix/index.cjs +++ b/lib/rules/selector-no-vendor-prefix/index.cjs @@ -52,32 +52,30 @@ const rule = (primary, secondaryOptions, context) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const { value } = pseudoNode; - - if (!isAutoprefixable.selector(value)) { - return; - } - - if (optionsMatches(secondaryOptions, 'ignoreSelectors', value)) { - return; - } - - if (context.fix) { - ruleNode.selector = isAutoprefixable.unprefix(selector); - - return; - } - - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const { value } = pseudoNode; + + if (!isAutoprefixable.selector(value)) { + return; + } + + if (optionsMatches(secondaryOptions, 'ignoreSelectors', value)) { + return; + } + + if (context.fix) { + ruleNode.selector = isAutoprefixable.unprefix(selector); + + return; + } + + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, }); }); }); diff --git a/lib/rules/selector-no-vendor-prefix/index.mjs b/lib/rules/selector-no-vendor-prefix/index.mjs index d26bc3ebeb..1fb3b1b3b8 100644 --- a/lib/rules/selector-no-vendor-prefix/index.mjs +++ b/lib/rules/selector-no-vendor-prefix/index.mjs @@ -48,32 +48,30 @@ const rule = (primary, secondaryOptions, context) => { const selector = ruleNode.selector; - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const { value } = pseudoNode; + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const { value } = pseudoNode; - if (!isAutoprefixable.selector(value)) { - return; - } + if (!isAutoprefixable.selector(value)) { + return; + } - if (optionsMatches(secondaryOptions, 'ignoreSelectors', value)) { - return; - } + if (optionsMatches(secondaryOptions, 'ignoreSelectors', value)) { + return; + } - if (context.fix) { - ruleNode.selector = isAutoprefixable.unprefix(selector); + if (context.fix) { + ruleNode.selector = isAutoprefixable.unprefix(selector); - return; - } + return; + } - report({ - result, - ruleName, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - }); + report({ + result, + ruleName, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, }); }); }); diff --git a/lib/rules/selector-not-notation/index.cjs b/lib/rules/selector-not-notation/index.cjs index 989df17fbc..ed5d38da95 100644 --- a/lib/rules/selector-not-notation/index.cjs +++ b/lib/rules/selector-not-notation/index.cjs @@ -82,48 +82,49 @@ const rule = (primary, _, context) => { if (!isStandardSyntaxSelector(selector)) return; - const fixedSelector = parseSelector(selector, result, ruleNode, (container) => { - container.walkPseudos((pseudo) => { - if (!isNot(pseudo)) return; - - if (primary === 'complex') { - const prev = pseudo.prev(); - const hasConsecutiveNot = prev && isNot(prev); - - if (!hasConsecutiveNot) return; - - if (context.fix) return fixComplex(prev); - } else { - const selectors = pseudo.nodes; - - if (isSimple(selectors)) return; - - const mustFix = - context.fix && - selectors.length > 1 && - selectors[1] && - (selectors[1].nodes.length === 0 || - selectors.every(({ nodes }) => nodes.length === 1)); - - if (mustFix) return fixSimple(pseudo); - } - - validateTypes.assert(pseudo.source && pseudo.source.end); - - report({ - message: messages.expected, - messageArgs: [primary], - node: ruleNode, - index: pseudo.sourceIndex, - endIndex: pseudo.source.end.column, - result, - ruleName, - }); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (!selectorRoot) return; + + selectorRoot.walkPseudos((pseudo) => { + if (!isNot(pseudo)) return; + + if (primary === 'complex') { + const prev = pseudo.prev(); + const hasConsecutiveNot = prev && isNot(prev); + + if (!hasConsecutiveNot) return; + + if (context.fix) return fixComplex(prev); + } else { + const selectors = pseudo.nodes; + + if (isSimple(selectors)) return; + + const mustFix = + context.fix && + selectors.length > 1 && + selectors[1] && + (selectors[1].nodes.length === 0 || selectors.every(({ nodes }) => nodes.length === 1)); + + if (mustFix) return fixSimple(pseudo); + } + + validateTypes.assert(pseudo.source && pseudo.source.end); + + report({ + message: messages.expected, + messageArgs: [primary], + node: ruleNode, + index: pseudo.sourceIndex, + endIndex: pseudo.source.end.column, + result, + ruleName, }); }); - if (context.fix && fixedSelector) { - ruleNode.selector = fixedSelector; + if (context.fix) { + ruleNode.selector = selectorRoot.toString(); } }); }; diff --git a/lib/rules/selector-not-notation/index.mjs b/lib/rules/selector-not-notation/index.mjs index 9cbede642b..e6576112a6 100644 --- a/lib/rules/selector-not-notation/index.mjs +++ b/lib/rules/selector-not-notation/index.mjs @@ -78,48 +78,49 @@ const rule = (primary, _, context) => { if (!isStandardSyntaxSelector(selector)) return; - const fixedSelector = parseSelector(selector, result, ruleNode, (container) => { - container.walkPseudos((pseudo) => { - if (!isNot(pseudo)) return; - - if (primary === 'complex') { - const prev = pseudo.prev(); - const hasConsecutiveNot = prev && isNot(prev); - - if (!hasConsecutiveNot) return; - - if (context.fix) return fixComplex(prev); - } else { - const selectors = pseudo.nodes; - - if (isSimple(selectors)) return; - - const mustFix = - context.fix && - selectors.length > 1 && - selectors[1] && - (selectors[1].nodes.length === 0 || - selectors.every(({ nodes }) => nodes.length === 1)); - - if (mustFix) return fixSimple(pseudo); - } - - assert(pseudo.source && pseudo.source.end); - - report({ - message: messages.expected, - messageArgs: [primary], - node: ruleNode, - index: pseudo.sourceIndex, - endIndex: pseudo.source.end.column, - result, - ruleName, - }); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (!selectorRoot) return; + + selectorRoot.walkPseudos((pseudo) => { + if (!isNot(pseudo)) return; + + if (primary === 'complex') { + const prev = pseudo.prev(); + const hasConsecutiveNot = prev && isNot(prev); + + if (!hasConsecutiveNot) return; + + if (context.fix) return fixComplex(prev); + } else { + const selectors = pseudo.nodes; + + if (isSimple(selectors)) return; + + const mustFix = + context.fix && + selectors.length > 1 && + selectors[1] && + (selectors[1].nodes.length === 0 || selectors.every(({ nodes }) => nodes.length === 1)); + + if (mustFix) return fixSimple(pseudo); + } + + assert(pseudo.source && pseudo.source.end); + + report({ + message: messages.expected, + messageArgs: [primary], + node: ruleNode, + index: pseudo.sourceIndex, + endIndex: pseudo.source.end.column, + result, + ruleName, }); }); - if (context.fix && fixedSelector) { - ruleNode.selector = fixedSelector; + if (context.fix) { + ruleNode.selector = selectorRoot.toString(); } }); }; diff --git a/lib/rules/selector-pseudo-class-allowed-list/index.cjs b/lib/rules/selector-pseudo-class-allowed-list/index.cjs index 197a231e83..a4a5bfe4ad 100644 --- a/lib/rules/selector-pseudo-class-allowed-list/index.cjs +++ b/lib/rules/selector-pseudo-class-allowed-list/index.cjs @@ -44,33 +44,31 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-elements - if (value.slice(0, 2) === '::') { - return; - } - - const name = value.slice(1); - - if (matchesStringOrRegExp(name, primary)) { - return; - } - - if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - word: value, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-elements + if (value.slice(0, 2) === '::') { + return; + } + + const name = value.slice(1); + + if (matchesStringOrRegExp(name, primary)) { + return; + } + + if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + word: value, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-class-allowed-list/index.mjs b/lib/rules/selector-pseudo-class-allowed-list/index.mjs index a4a89b518f..e73eb0db8c 100644 --- a/lib/rules/selector-pseudo-class-allowed-list/index.mjs +++ b/lib/rules/selector-pseudo-class-allowed-list/index.mjs @@ -40,33 +40,31 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-elements - if (value.slice(0, 2) === '::') { - return; - } - - const name = value.slice(1); - - if (matchesStringOrRegExp(name, primary)) { - return; - } - - if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - word: value, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-elements + if (value.slice(0, 2) === '::') { + return; + } + + const name = value.slice(1); + + if (matchesStringOrRegExp(name, primary)) { + return; + } + + if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + word: value, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-class-disallowed-list/index.cjs b/lib/rules/selector-pseudo-class-disallowed-list/index.cjs index 289cff1190..db7a8b0c7d 100644 --- a/lib/rules/selector-pseudo-class-disallowed-list/index.cjs +++ b/lib/rules/selector-pseudo-class-disallowed-list/index.cjs @@ -44,29 +44,27 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-elements - if (value.slice(0, 2) === '::') { - return; - } - - const name = value.slice(1); - - if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - word: value, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-elements + if (value.slice(0, 2) === '::') { + return; + } + + const name = value.slice(1); + + if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + word: value, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-class-disallowed-list/index.mjs b/lib/rules/selector-pseudo-class-disallowed-list/index.mjs index d86df6fa83..0e69fe5db5 100644 --- a/lib/rules/selector-pseudo-class-disallowed-list/index.mjs +++ b/lib/rules/selector-pseudo-class-disallowed-list/index.mjs @@ -40,29 +40,27 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; - // Ignore pseudo-elements - if (value.slice(0, 2) === '::') { - return; - } + // Ignore pseudo-elements + if (value.slice(0, 2) === '::') { + return; + } - const name = value.slice(1); + const name = value.slice(1); - if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } + if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } - report({ - word: value, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - result, - ruleName, - }); + report({ + word: value, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-class-no-unknown/index.cjs b/lib/rules/selector-pseudo-class-no-unknown/index.cjs index ee12bdecc9..6c6cb497cd 100644 --- a/lib/rules/selector-pseudo-class-no-unknown/index.cjs +++ b/lib/rules/selector-pseudo-class-no-unknown/index.cjs @@ -52,84 +52,82 @@ const rule = (primary, secondaryOptions) => { * @param {import('postcss').ChildNode} node */ function check(selector, node) { - parseSelector(selector, result, node, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; + parseSelector(selector, result, node)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; - if (!isStandardSyntaxSelector(value)) { - return; - } + if (!isStandardSyntaxSelector(value)) { + return; + } - if (isCustomSelector(value)) { - return; - } + if (isCustomSelector(value)) { + return; + } - if (value.startsWith('::')) { - return; - } + if (value.startsWith('::')) { + return; + } - const name = value.replace(/^:*/, '').toLowerCase(); + const name = value.replace(/^:*/, '').toLowerCase(); + + if (selectors.levelOneAndTwoPseudoElements.has(name)) { + return; + } - if (selectors.levelOneAndTwoPseudoElements.has(name)) { + if (optionsMatches(secondaryOptions, 'ignorePseudoClasses', pseudoNode.value.slice(1))) { + return; + } + + const hasVendorPrefix = vendor.prefix(name); + let index = null; + + if (typeGuards.isAtRule(node) && node.name === 'page') { + if (selectors.atRulePagePseudoClasses.has(name)) { return; } - if (optionsMatches(secondaryOptions, 'ignorePseudoClasses', pseudoNode.value.slice(1))) { + index = atRuleParamIndex(node) + pseudoNode.sourceIndex; + } else if (selectors.pseudoElements.has(name) && !hasVendorPrefix) { + index = pseudoNode.sourceIndex; + } else { + if (hasVendorPrefix || selectors.pseudoClasses.has(name)) { return; } - const hasVendorPrefix = vendor.prefix(name); - let index = null; + /** @type {import('postcss-selector-parser').Base} */ + let prevPseudoElement = pseudoNode; - if (typeGuards.isAtRule(node) && node.name === 'page') { - if (selectors.atRulePagePseudoClasses.has(name)) { - return; - } + do { + prevPseudoElement = /** @type {import('postcss-selector-parser').Base} */ ( + prevPseudoElement.prev() + ); - index = atRuleParamIndex(node) + pseudoNode.sourceIndex; - } else if (selectors.pseudoElements.has(name) && !hasVendorPrefix) { - index = pseudoNode.sourceIndex; - } else { - if (hasVendorPrefix || selectors.pseudoClasses.has(name)) { - return; + if (prevPseudoElement && prevPseudoElement.value.slice(0, 2) === '::') { + break; } + } while (prevPseudoElement); - /** @type {import('postcss-selector-parser').Base} */ - let prevPseudoElement = pseudoNode; + if (prevPseudoElement) { + const prevPseudoElementValue = prevPseudoElement.value.toLowerCase().slice(2); - do { - prevPseudoElement = /** @type {import('postcss-selector-parser').Base} */ ( - prevPseudoElement.prev() - ); - - if (prevPseudoElement && prevPseudoElement.value.slice(0, 2) === '::') { - break; - } - } while (prevPseudoElement); - - if (prevPseudoElement) { - const prevPseudoElementValue = prevPseudoElement.value.toLowerCase().slice(2); - - if ( - selectors.webkitScrollbarPseudoElements.has(prevPseudoElementValue) && - selectors.webkitScrollbarPseudoClasses.has(name) - ) { - return; - } + if ( + selectors.webkitScrollbarPseudoElements.has(prevPseudoElementValue) && + selectors.webkitScrollbarPseudoClasses.has(name) + ) { + return; } - - index = pseudoNode.sourceIndex; } - report({ - message: messages.rejected, - messageArgs: [value], - node, - index, - ruleName, - result, - word: value, - }); + index = pseudoNode.sourceIndex; + } + + report({ + message: messages.rejected, + messageArgs: [value], + node, + index, + ruleName, + result, + word: value, }); }); } diff --git a/lib/rules/selector-pseudo-class-no-unknown/index.mjs b/lib/rules/selector-pseudo-class-no-unknown/index.mjs index adfc514970..1e5fb69cd9 100644 --- a/lib/rules/selector-pseudo-class-no-unknown/index.mjs +++ b/lib/rules/selector-pseudo-class-no-unknown/index.mjs @@ -55,84 +55,82 @@ const rule = (primary, secondaryOptions) => { * @param {import('postcss').ChildNode} node */ function check(selector, node) { - parseSelector(selector, result, node, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; + parseSelector(selector, result, node)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; - if (!isStandardSyntaxSelector(value)) { - return; - } + if (!isStandardSyntaxSelector(value)) { + return; + } - if (isCustomSelector(value)) { - return; - } + if (isCustomSelector(value)) { + return; + } - if (value.startsWith('::')) { - return; - } + if (value.startsWith('::')) { + return; + } - const name = value.replace(/^:*/, '').toLowerCase(); + const name = value.replace(/^:*/, '').toLowerCase(); + + if (levelOneAndTwoPseudoElements.has(name)) { + return; + } - if (levelOneAndTwoPseudoElements.has(name)) { + if (optionsMatches(secondaryOptions, 'ignorePseudoClasses', pseudoNode.value.slice(1))) { + return; + } + + const hasVendorPrefix = vendor.prefix(name); + let index = null; + + if (isAtRule(node) && node.name === 'page') { + if (atRulePagePseudoClasses.has(name)) { return; } - if (optionsMatches(secondaryOptions, 'ignorePseudoClasses', pseudoNode.value.slice(1))) { + index = atRuleParamIndex(node) + pseudoNode.sourceIndex; + } else if (pseudoElements.has(name) && !hasVendorPrefix) { + index = pseudoNode.sourceIndex; + } else { + if (hasVendorPrefix || pseudoClasses.has(name)) { return; } - const hasVendorPrefix = vendor.prefix(name); - let index = null; + /** @type {import('postcss-selector-parser').Base} */ + let prevPseudoElement = pseudoNode; - if (isAtRule(node) && node.name === 'page') { - if (atRulePagePseudoClasses.has(name)) { - return; - } + do { + prevPseudoElement = /** @type {import('postcss-selector-parser').Base} */ ( + prevPseudoElement.prev() + ); - index = atRuleParamIndex(node) + pseudoNode.sourceIndex; - } else if (pseudoElements.has(name) && !hasVendorPrefix) { - index = pseudoNode.sourceIndex; - } else { - if (hasVendorPrefix || pseudoClasses.has(name)) { - return; + if (prevPseudoElement && prevPseudoElement.value.slice(0, 2) === '::') { + break; } + } while (prevPseudoElement); - /** @type {import('postcss-selector-parser').Base} */ - let prevPseudoElement = pseudoNode; + if (prevPseudoElement) { + const prevPseudoElementValue = prevPseudoElement.value.toLowerCase().slice(2); - do { - prevPseudoElement = /** @type {import('postcss-selector-parser').Base} */ ( - prevPseudoElement.prev() - ); - - if (prevPseudoElement && prevPseudoElement.value.slice(0, 2) === '::') { - break; - } - } while (prevPseudoElement); - - if (prevPseudoElement) { - const prevPseudoElementValue = prevPseudoElement.value.toLowerCase().slice(2); - - if ( - webkitScrollbarPseudoElements.has(prevPseudoElementValue) && - webkitScrollbarPseudoClasses.has(name) - ) { - return; - } + if ( + webkitScrollbarPseudoElements.has(prevPseudoElementValue) && + webkitScrollbarPseudoClasses.has(name) + ) { + return; } - - index = pseudoNode.sourceIndex; } - report({ - message: messages.rejected, - messageArgs: [value], - node, - index, - ruleName, - result, - word: value, - }); + index = pseudoNode.sourceIndex; + } + + report({ + message: messages.rejected, + messageArgs: [value], + node, + index, + ruleName, + result, + word: value, }); }); } diff --git a/lib/rules/selector-pseudo-element-allowed-list/index.cjs b/lib/rules/selector-pseudo-element-allowed-list/index.cjs index d6801d0486..404a527988 100644 --- a/lib/rules/selector-pseudo-element-allowed-list/index.cjs +++ b/lib/rules/selector-pseudo-element-allowed-list/index.cjs @@ -44,36 +44,34 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-classes - if (value.charAt(1) !== ':') { - return; - } - - const name = value.slice(2); - - // Pseudo is in allowlist - if (matchesStringOrRegExp(name, primary)) { - return; - } - - // Unprefixed-pseudo is in allowlist - if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-classes + if (value.charAt(1) !== ':') { + return; + } + + const name = value.slice(2); + + // Pseudo is in allowlist + if (matchesStringOrRegExp(name, primary)) { + return; + } + + // Unprefixed-pseudo is in allowlist + if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-element-allowed-list/index.mjs b/lib/rules/selector-pseudo-element-allowed-list/index.mjs index c545ae0d1c..659ba466e7 100644 --- a/lib/rules/selector-pseudo-element-allowed-list/index.mjs +++ b/lib/rules/selector-pseudo-element-allowed-list/index.mjs @@ -40,36 +40,34 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-classes - if (value.charAt(1) !== ':') { - return; - } - - const name = value.slice(2); - - // Pseudo is in allowlist - if (matchesStringOrRegExp(name, primary)) { - return; - } - - // Unprefixed-pseudo is in allowlist - if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-classes + if (value.charAt(1) !== ':') { + return; + } + + const name = value.slice(2); + + // Pseudo is in allowlist + if (matchesStringOrRegExp(name, primary)) { + return; + } + + // Unprefixed-pseudo is in allowlist + if (matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-element-colon-notation/index.cjs b/lib/rules/selector-pseudo-element-colon-notation/index.cjs index a2d0f75cb7..21df79f62c 100644 --- a/lib/rules/selector-pseudo-element-colon-notation/index.cjs +++ b/lib/rules/selector-pseudo-element-colon-notation/index.cjs @@ -52,43 +52,45 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const fixedSelector = parseSelector(selector, result, ruleNode, (selectors$1) => { - selectors$1.walkPseudos((pseudo) => { - const pseudoElement = pseudo.value.replaceAll(':', ''); - - if (!selectors.levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) { - return; - } - - const isDouble = pseudo.value.startsWith('::'); - - if (primary === 'single' && !isDouble) { - return; - } - - if (primary === 'double' && isDouble) { - return; - } - - if (context.fix) { - pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement })); - - return; - } - - report({ - message: messages.expected(primary), - node: ruleNode, - index: pseudo.sourceIndex, - endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1), - result, - ruleName, - }); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (!selectorRoot) return; + + selectorRoot.walkPseudos((pseudo) => { + const pseudoElement = pseudo.value.replaceAll(':', ''); + + if (!selectors.levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) { + return; + } + + const isDouble = pseudo.value.startsWith('::'); + + if (primary === 'single' && !isDouble) { + return; + } + + if (primary === 'double' && isDouble) { + return; + } + + if (context.fix) { + pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement })); + + return; + } + + report({ + message: messages.expected(primary), + node: ruleNode, + index: pseudo.sourceIndex, + endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1), + result, + ruleName, }); }); - if (context.fix && fixedSelector) { - ruleNode.selector = fixedSelector; + if (context.fix) { + ruleNode.selector = selectorRoot.toString(); } }); }; diff --git a/lib/rules/selector-pseudo-element-colon-notation/index.mjs b/lib/rules/selector-pseudo-element-colon-notation/index.mjs index 44efcb5cc7..e5eb645d82 100644 --- a/lib/rules/selector-pseudo-element-colon-notation/index.mjs +++ b/lib/rules/selector-pseudo-element-colon-notation/index.mjs @@ -48,43 +48,45 @@ const rule = (primary, _secondaryOptions, context) => { return; } - const fixedSelector = parseSelector(selector, result, ruleNode, (selectors) => { - selectors.walkPseudos((pseudo) => { - const pseudoElement = pseudo.value.replaceAll(':', ''); - - if (!levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) { - return; - } - - const isDouble = pseudo.value.startsWith('::'); - - if (primary === 'single' && !isDouble) { - return; - } - - if (primary === 'double' && isDouble) { - return; - } - - if (context.fix) { - pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement })); - - return; - } - - report({ - message: messages.expected(primary), - node: ruleNode, - index: pseudo.sourceIndex, - endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1), - result, - ruleName, - }); + const selectorRoot = parseSelector(selector, result, ruleNode); + + if (!selectorRoot) return; + + selectorRoot.walkPseudos((pseudo) => { + const pseudoElement = pseudo.value.replaceAll(':', ''); + + if (!levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) { + return; + } + + const isDouble = pseudo.value.startsWith('::'); + + if (primary === 'single' && !isDouble) { + return; + } + + if (primary === 'double' && isDouble) { + return; + } + + if (context.fix) { + pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement })); + + return; + } + + report({ + message: messages.expected(primary), + node: ruleNode, + index: pseudo.sourceIndex, + endIndex: pseudo.sourceIndex + (isDouble ? 2 : 1), + result, + ruleName, }); }); - if (context.fix && fixedSelector) { - ruleNode.selector = fixedSelector; + if (context.fix) { + ruleNode.selector = selectorRoot.toString(); } }); }; diff --git a/lib/rules/selector-pseudo-element-disallowed-list/index.cjs b/lib/rules/selector-pseudo-element-disallowed-list/index.cjs index 367fb47274..38dec706a4 100644 --- a/lib/rules/selector-pseudo-element-disallowed-list/index.cjs +++ b/lib/rules/selector-pseudo-element-disallowed-list/index.cjs @@ -44,30 +44,28 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - // Ignore pseudo-classes - if (value.charAt(1) !== ':') { - return; - } - - const name = value.slice(2); - - if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } - - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - result, - ruleName, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + // Ignore pseudo-classes + if (value.charAt(1) !== ':') { + return; + } + + const name = value.slice(2); + + if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } + + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-element-disallowed-list/index.mjs b/lib/rules/selector-pseudo-element-disallowed-list/index.mjs index 6ecfcf03de..9b9bd4bc8b 100644 --- a/lib/rules/selector-pseudo-element-disallowed-list/index.mjs +++ b/lib/rules/selector-pseudo-element-disallowed-list/index.mjs @@ -40,30 +40,28 @@ const rule = (primary) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; - // Ignore pseudo-classes - if (value.charAt(1) !== ':') { - return; - } + // Ignore pseudo-classes + if (value.charAt(1) !== ':') { + return; + } - const name = value.slice(2); + const name = value.slice(2); - if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { - return; - } + if (!matchesStringOrRegExp(vendor.unprefixed(name), primary)) { + return; + } - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - word: value, - result, - ruleName, - }); + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + word: value, + result, + ruleName, }); }); }); diff --git a/lib/rules/selector-pseudo-element-no-unknown/index.cjs b/lib/rules/selector-pseudo-element-no-unknown/index.cjs index 9534f24958..668dc56d80 100644 --- a/lib/rules/selector-pseudo-element-no-unknown/index.cjs +++ b/lib/rules/selector-pseudo-element-no-unknown/index.cjs @@ -56,38 +56,36 @@ const rule = (primary, secondaryOptions) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - if (!isStandardSyntaxSelector(value)) { - return; - } - - // Ignore pseudo-classes - if (value.slice(0, 2) !== '::') { - return; - } - - if (optionsMatches(secondaryOptions, 'ignorePseudoElements', pseudoNode.value.slice(2))) { - return; - } - - const name = value.slice(2); - - if (vendor.prefix(name) || selectors.pseudoElements.has(name.toLowerCase())) { - return; - } - - report({ - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - index: pseudoNode.sourceIndex, - ruleName, - result, - word: value, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + if (!isStandardSyntaxSelector(value)) { + return; + } + + // Ignore pseudo-classes + if (value.slice(0, 2) !== '::') { + return; + } + + if (optionsMatches(secondaryOptions, 'ignorePseudoElements', pseudoNode.value.slice(2))) { + return; + } + + const name = value.slice(2); + + if (vendor.prefix(name) || selectors.pseudoElements.has(name.toLowerCase())) { + return; + } + + report({ + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + index: pseudoNode.sourceIndex, + ruleName, + result, + word: value, }); }); }); diff --git a/lib/rules/selector-pseudo-element-no-unknown/index.mjs b/lib/rules/selector-pseudo-element-no-unknown/index.mjs index b08066459e..8f72fad78f 100644 --- a/lib/rules/selector-pseudo-element-no-unknown/index.mjs +++ b/lib/rules/selector-pseudo-element-no-unknown/index.mjs @@ -52,38 +52,36 @@ const rule = (primary, secondaryOptions) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkPseudos((pseudoNode) => { - const value = pseudoNode.value; - - if (!isStandardSyntaxSelector(value)) { - return; - } - - // Ignore pseudo-classes - if (value.slice(0, 2) !== '::') { - return; - } - - if (optionsMatches(secondaryOptions, 'ignorePseudoElements', pseudoNode.value.slice(2))) { - return; - } - - const name = value.slice(2); - - if (vendor.prefix(name) || pseudoElements.has(name.toLowerCase())) { - return; - } - - report({ - message: messages.rejected, - messageArgs: [value], - node: ruleNode, - index: pseudoNode.sourceIndex, - ruleName, - result, - word: value, - }); + parseSelector(selector, result, ruleNode)?.walkPseudos((pseudoNode) => { + const value = pseudoNode.value; + + if (!isStandardSyntaxSelector(value)) { + return; + } + + // Ignore pseudo-classes + if (value.slice(0, 2) !== '::') { + return; + } + + if (optionsMatches(secondaryOptions, 'ignorePseudoElements', pseudoNode.value.slice(2))) { + return; + } + + const name = value.slice(2); + + if (vendor.prefix(name) || pseudoElements.has(name.toLowerCase())) { + return; + } + + report({ + message: messages.rejected, + messageArgs: [value], + node: ruleNode, + index: pseudoNode.sourceIndex, + ruleName, + result, + word: value, }); }); }); diff --git a/lib/rules/selector-type-case/index.cjs b/lib/rules/selector-type-case/index.cjs index 06289f2191..d336e31b42 100644 --- a/lib/rules/selector-type-case/index.cjs +++ b/lib/rules/selector-type-case/index.cjs @@ -69,59 +69,57 @@ const rule = (primary, secondaryOptions, context) => { return; } - parseSelector(selector, result, ruleNode, (selectorAST) => { - selectorAST.walkTags((tag) => { - if (!isStandardSyntaxTypeSelector(tag)) { - return; - } + parseSelector(selector, result, ruleNode)?.walkTags((tag) => { + if (!isStandardSyntaxTypeSelector(tag)) { + return; + } - if (selectors.mixedCaseSvgTypeSelectors.has(tag.value)) { - return; - } + if (selectors.mixedCaseSvgTypeSelectors.has(tag.value)) { + return; + } - if (optionsMatches(secondaryOptions, 'ignoreTypes', tag.value)) { - return; - } + if (optionsMatches(secondaryOptions, 'ignoreTypes', tag.value)) { + return; + } - const sourceIndex = tag.sourceIndex; - const value = tag.value; + const sourceIndex = tag.sourceIndex; + const value = tag.value; - const expectedValue = primary === 'lower' ? value.toLowerCase() : value.toUpperCase(); + const expectedValue = primary === 'lower' ? value.toLowerCase() : value.toUpperCase(); - if (value === expectedValue) { - return; - } + if (value === expectedValue) { + return; + } + + if (context.fix) { + if (hasComments) { + hasComments = + hasComments.slice(0, sourceIndex) + + expectedValue + + hasComments.slice(sourceIndex + value.length); - if (context.fix) { - if (hasComments) { - hasComments = - hasComments.slice(0, sourceIndex) + - expectedValue + - hasComments.slice(sourceIndex + value.length); - - if (ruleNode.raws.selector == null) { - throw new Error('The `raw` property must be present'); - } - - ruleNode.raws.selector.raw = hasComments; - } else { - ruleNode.selector = - ruleNode.selector.slice(0, sourceIndex) + - expectedValue + - ruleNode.selector.slice(sourceIndex + value.length); + if (ruleNode.raws.selector == null) { + throw new Error('The `raw` property must be present'); } - return; + ruleNode.raws.selector.raw = hasComments; + } else { + ruleNode.selector = + ruleNode.selector.slice(0, sourceIndex) + + expectedValue + + ruleNode.selector.slice(sourceIndex + value.length); } - report({ - message: messages.expected, - messageArgs: [value, expectedValue], - node: ruleNode, - index: sourceIndex, - ruleName, - result, - }); + return; + } + + report({ + message: messages.expected, + messageArgs: [value, expectedValue], + node: ruleNode, + index: sourceIndex, + ruleName, + result, }); }); }); diff --git a/lib/rules/selector-type-case/index.mjs b/lib/rules/selector-type-case/index.mjs index 79805e6ab9..d21d692d67 100644 --- a/lib/rules/selector-type-case/index.mjs +++ b/lib/rules/selector-type-case/index.mjs @@ -65,59 +65,57 @@ const rule = (primary, secondaryOptions, context) => { return; } - parseSelector(selector, result, ruleNode, (selectorAST) => { - selectorAST.walkTags((tag) => { - if (!isStandardSyntaxTypeSelector(tag)) { - return; - } + parseSelector(selector, result, ruleNode)?.walkTags((tag) => { + if (!isStandardSyntaxTypeSelector(tag)) { + return; + } - if (mixedCaseSvgTypeSelectors.has(tag.value)) { - return; - } + if (mixedCaseSvgTypeSelectors.has(tag.value)) { + return; + } - if (optionsMatches(secondaryOptions, 'ignoreTypes', tag.value)) { - return; - } + if (optionsMatches(secondaryOptions, 'ignoreTypes', tag.value)) { + return; + } - const sourceIndex = tag.sourceIndex; - const value = tag.value; + const sourceIndex = tag.sourceIndex; + const value = tag.value; - const expectedValue = primary === 'lower' ? value.toLowerCase() : value.toUpperCase(); + const expectedValue = primary === 'lower' ? value.toLowerCase() : value.toUpperCase(); - if (value === expectedValue) { - return; - } + if (value === expectedValue) { + return; + } + + if (context.fix) { + if (hasComments) { + hasComments = + hasComments.slice(0, sourceIndex) + + expectedValue + + hasComments.slice(sourceIndex + value.length); - if (context.fix) { - if (hasComments) { - hasComments = - hasComments.slice(0, sourceIndex) + - expectedValue + - hasComments.slice(sourceIndex + value.length); - - if (ruleNode.raws.selector == null) { - throw new Error('The `raw` property must be present'); - } - - ruleNode.raws.selector.raw = hasComments; - } else { - ruleNode.selector = - ruleNode.selector.slice(0, sourceIndex) + - expectedValue + - ruleNode.selector.slice(sourceIndex + value.length); + if (ruleNode.raws.selector == null) { + throw new Error('The `raw` property must be present'); } - return; + ruleNode.raws.selector.raw = hasComments; + } else { + ruleNode.selector = + ruleNode.selector.slice(0, sourceIndex) + + expectedValue + + ruleNode.selector.slice(sourceIndex + value.length); } - report({ - message: messages.expected, - messageArgs: [value, expectedValue], - node: ruleNode, - index: sourceIndex, - ruleName, - result, - }); + return; + } + + report({ + message: messages.expected, + messageArgs: [value, expectedValue], + node: ruleNode, + index: sourceIndex, + ruleName, + result, }); }); }); diff --git a/lib/rules/selector-type-no-unknown/index.cjs b/lib/rules/selector-type-no-unknown/index.cjs index 7fb9c2a33f..dd240aa801 100644 --- a/lib/rules/selector-type-no-unknown/index.cjs +++ b/lib/rules/selector-type-no-unknown/index.cjs @@ -63,55 +63,53 @@ const rule = (primary, secondaryOptions) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkTags((tagNode) => { - if (!isStandardSyntaxTypeSelector(tagNode)) { - return; - } - - if ( - optionsMatches(secondaryOptions, 'ignore', 'custom-elements') && - isCustomElement(tagNode.value) - ) { - return; - } - - if ( - optionsMatches(secondaryOptions, 'ignore', 'default-namespace') && - !(typeof tagNode.namespace === 'string') - ) { - return; - } - - if (optionsMatches(secondaryOptions, 'ignoreNamespaces', tagNode.namespace)) { - return; - } - - if (optionsMatches(secondaryOptions, 'ignoreTypes', tagNode.value)) { - return; - } - - const tagName = tagNode.value; - const tagNameLowerCase = tagName.toLowerCase(); - - if ( - selectors.htmlTypeSelectors.has(tagNameLowerCase) || - // SVG tags are case-sensitive - svgTags.includes(tagName) || - mathMLTags.includes(tagNameLowerCase) - ) { - return; - } - - report({ - message: messages.rejected, - messageArgs: [tagName], - node: ruleNode, - index: tagNode.sourceIndex, - ruleName, - result, - word: tagName, - }); + parseSelector(selector, result, ruleNode)?.walkTags((tagNode) => { + if (!isStandardSyntaxTypeSelector(tagNode)) { + return; + } + + if ( + optionsMatches(secondaryOptions, 'ignore', 'custom-elements') && + isCustomElement(tagNode.value) + ) { + return; + } + + if ( + optionsMatches(secondaryOptions, 'ignore', 'default-namespace') && + !(typeof tagNode.namespace === 'string') + ) { + return; + } + + if (optionsMatches(secondaryOptions, 'ignoreNamespaces', tagNode.namespace)) { + return; + } + + if (optionsMatches(secondaryOptions, 'ignoreTypes', tagNode.value)) { + return; + } + + const tagName = tagNode.value; + const tagNameLowerCase = tagName.toLowerCase(); + + if ( + selectors.htmlTypeSelectors.has(tagNameLowerCase) || + // SVG tags are case-sensitive + svgTags.includes(tagName) || + mathMLTags.includes(tagNameLowerCase) + ) { + return; + } + + report({ + message: messages.rejected, + messageArgs: [tagName], + node: ruleNode, + index: tagNode.sourceIndex, + ruleName, + result, + word: tagName, }); }); }); diff --git a/lib/rules/selector-type-no-unknown/index.mjs b/lib/rules/selector-type-no-unknown/index.mjs index ac7fcf952a..5ff9518f56 100644 --- a/lib/rules/selector-type-no-unknown/index.mjs +++ b/lib/rules/selector-type-no-unknown/index.mjs @@ -60,55 +60,53 @@ const rule = (primary, secondaryOptions) => { return; } - parseSelector(selector, result, ruleNode, (selectorTree) => { - selectorTree.walkTags((tagNode) => { - if (!isStandardSyntaxTypeSelector(tagNode)) { - return; - } - - if ( - optionsMatches(secondaryOptions, 'ignore', 'custom-elements') && - isCustomElement(tagNode.value) - ) { - return; - } - - if ( - optionsMatches(secondaryOptions, 'ignore', 'default-namespace') && - !(typeof tagNode.namespace === 'string') - ) { - return; - } - - if (optionsMatches(secondaryOptions, 'ignoreNamespaces', tagNode.namespace)) { - return; - } - - if (optionsMatches(secondaryOptions, 'ignoreTypes', tagNode.value)) { - return; - } - - const tagName = tagNode.value; - const tagNameLowerCase = tagName.toLowerCase(); - - if ( - htmlTypeSelectors.has(tagNameLowerCase) || - // SVG tags are case-sensitive - svgTags.includes(tagName) || - mathMLTags.includes(tagNameLowerCase) - ) { - return; - } - - report({ - message: messages.rejected, - messageArgs: [tagName], - node: ruleNode, - index: tagNode.sourceIndex, - ruleName, - result, - word: tagName, - }); + parseSelector(selector, result, ruleNode)?.walkTags((tagNode) => { + if (!isStandardSyntaxTypeSelector(tagNode)) { + return; + } + + if ( + optionsMatches(secondaryOptions, 'ignore', 'custom-elements') && + isCustomElement(tagNode.value) + ) { + return; + } + + if ( + optionsMatches(secondaryOptions, 'ignore', 'default-namespace') && + !(typeof tagNode.namespace === 'string') + ) { + return; + } + + if (optionsMatches(secondaryOptions, 'ignoreNamespaces', tagNode.namespace)) { + return; + } + + if (optionsMatches(secondaryOptions, 'ignoreTypes', tagNode.value)) { + return; + } + + const tagName = tagNode.value; + const tagNameLowerCase = tagName.toLowerCase(); + + if ( + htmlTypeSelectors.has(tagNameLowerCase) || + // SVG tags are case-sensitive + svgTags.includes(tagName) || + mathMLTags.includes(tagNameLowerCase) + ) { + return; + } + + report({ + message: messages.rejected, + messageArgs: [tagName], + node: ruleNode, + index: tagNode.sourceIndex, + ruleName, + result, + word: tagName, }); }); }); diff --git a/lib/rules/string-no-newline/index.cjs b/lib/rules/string-no-newline/index.cjs index df4f1cb34b..e78fb6ab9f 100644 --- a/lib/rules/string-no-newline/index.cjs +++ b/lib/rules/string-no-newline/index.cjs @@ -59,36 +59,34 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { value, quoteMark } = attributeNode; - - if (!value || !reNewLine.test(value)) { - return; - } - - const openIndex = [ - // length of our attribute - attributeNode.attribute, - // length of our operator , ie '=' - attributeNode.operator || '', - ].reduce( - (index, str) => index + str.length, - // index of the start of our attribute node in our source - // plus 1 for the opening quotation mark - attributeNode.sourceIndex + 1, - ); - - const valueLength = value.length + (quoteMark || '').length * 2; - - report({ - message: messages.rejected, - node: ruleNode, - index: openIndex, - endIndex: openIndex + valueLength, - result, - ruleName, - }); + parseSelector(ruleNode.selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { value, quoteMark } = attributeNode; + + if (!value || !reNewLine.test(value)) { + return; + } + + const openIndex = [ + // length of our attribute + attributeNode.attribute, + // length of our operator , ie '=' + attributeNode.operator || '', + ].reduce( + (index, str) => index + str.length, + // index of the start of our attribute node in our source + // plus 1 for the opening quotation mark + attributeNode.sourceIndex + 1, + ); + + const valueLength = value.length + (quoteMark || '').length * 2; + + report({ + message: messages.rejected, + node: ruleNode, + index: openIndex, + endIndex: openIndex + valueLength, + result, + ruleName, }); }); } diff --git a/lib/rules/string-no-newline/index.mjs b/lib/rules/string-no-newline/index.mjs index 5f2d35ab01..4c5e555b40 100644 --- a/lib/rules/string-no-newline/index.mjs +++ b/lib/rules/string-no-newline/index.mjs @@ -56,36 +56,34 @@ const rule = (primary) => { return; } - parseSelector(ruleNode.selector, result, ruleNode, (selectorTree) => { - selectorTree.walkAttributes((attributeNode) => { - const { value, quoteMark } = attributeNode; - - if (!value || !reNewLine.test(value)) { - return; - } - - const openIndex = [ - // length of our attribute - attributeNode.attribute, - // length of our operator , ie '=' - attributeNode.operator || '', - ].reduce( - (index, str) => index + str.length, - // index of the start of our attribute node in our source - // plus 1 for the opening quotation mark - attributeNode.sourceIndex + 1, - ); - - const valueLength = value.length + (quoteMark || '').length * 2; - - report({ - message: messages.rejected, - node: ruleNode, - index: openIndex, - endIndex: openIndex + valueLength, - result, - ruleName, - }); + parseSelector(ruleNode.selector, result, ruleNode)?.walkAttributes((attributeNode) => { + const { value, quoteMark } = attributeNode; + + if (!value || !reNewLine.test(value)) { + return; + } + + const openIndex = [ + // length of our attribute + attributeNode.attribute, + // length of our operator , ie '=' + attributeNode.operator || '', + ].reduce( + (index, str) => index + str.length, + // index of the start of our attribute node in our source + // plus 1 for the opening quotation mark + attributeNode.sourceIndex + 1, + ); + + const valueLength = value.length + (quoteMark || '').length * 2; + + report({ + message: messages.rejected, + node: ruleNode, + index: openIndex, + endIndex: openIndex + valueLength, + result, + ruleName, }); }); } diff --git a/lib/utils/__tests__/parseSelectorAST.test.mjs b/lib/utils/__tests__/parseSelector.test.mjs similarity index 59% rename from lib/utils/__tests__/parseSelectorAST.test.mjs rename to lib/utils/__tests__/parseSelector.test.mjs index becf7001fa..afcba93a8f 100644 --- a/lib/utils/__tests__/parseSelectorAST.test.mjs +++ b/lib/utils/__tests__/parseSelector.test.mjs @@ -1,12 +1,12 @@ import postcss from 'postcss'; -import parseSelectorAST from '../parseSelectorAST.mjs'; +import parseSelector from '../parseSelector.mjs'; -describe('parseSelectorAST', () => { +describe('parseSelector', () => { it('parses selectors into an AST', async () => { const result = await postcss().process('a .foo, b::before {}', { from: undefined }); const rule = result.root.first; - const ast = parseSelectorAST(rule.selector, result, rule); + const ast = parseSelector(rule.selector, result, rule); expect(ast).toBeTruthy(); expect(result.warnings()).toHaveLength(0); @@ -15,7 +15,7 @@ describe('parseSelectorAST', () => { it('returns `undefined` when there is no selector', async () => { const result = await postcss().process('{}', { from: undefined }); const rule = result.root.first; - const ast = parseSelectorAST(rule.selector, result, rule); + const ast = parseSelector(rule.selector, result, rule); expect(ast).toBeUndefined(); expect(result.warnings()).toHaveLength(0); @@ -24,7 +24,7 @@ describe('parseSelectorAST', () => { it('warns on invalid selectors, but does not throw', async () => { const result = await postcss().process('[&] {}', { from: undefined }); const rule = result.root.first; - const ast = parseSelectorAST(rule.selector, result, rule); + const ast = parseSelector(rule.selector, result, rule); expect(ast).toBeUndefined(); expect( @@ -45,4 +45,17 @@ describe('parseSelectorAST', () => { }, ]); }); + + it('keeps the callback support for backward compatibility', async () => { + expect.assertions(3); + + const result = await postcss().process('a {}', { from: undefined }); + const rule = result.root.first; + const processed = parseSelector(rule.selector, result, rule, (selectorRoot) => { + expect(selectorRoot).toHaveProperty('type', 'root'); + expect(selectorRoot).toHaveProperty('nodes[0].nodes[0].value', 'a'); + }); + + expect(typeof processed).toBe('string'); + }); }); diff --git a/lib/utils/flattenNestedSelectorsForRule.cjs b/lib/utils/flattenNestedSelectorsForRule.cjs index 40980467e3..ddcda0b1d8 100644 --- a/lib/utils/flattenNestedSelectorsForRule.cjs +++ b/lib/utils/flattenNestedSelectorsForRule.cjs @@ -5,7 +5,7 @@ const resolveNestedSelector = require('postcss-resolve-nested-selector'); const getRuleSelector = require('./getRuleSelector.cjs'); const isStandardSyntaxSelector = require('./isStandardSyntaxSelector.cjs'); -const parseSelectorAST = require('./parseSelectorAST.cjs'); +const parseSelector = require('./parseSelector.cjs'); /** * @typedef {import('postcss-selector-parser').Selector} Selector @@ -15,7 +15,7 @@ const parseSelectorAST = require('./parseSelectorAST.cjs'); * @returns {Array<{selector: Selector, resolvedSelectors: Array}>} */ function flattenNestedSelectorsForRule(rule, result) { - const ownAST = parseSelectorAST(getRuleSelector(rule), result, rule); + const ownAST = parseSelector(getRuleSelector(rule), result, rule); if (!ownAST) return []; @@ -27,7 +27,7 @@ function flattenNestedSelectorsForRule(rule, result) { for (const resolvedSelector of resolvedSelectors) { if (!isStandardSyntaxSelector(resolvedSelector)) return []; - const resolvedRoot = parseSelectorAST(resolvedSelector, result, rule); + const resolvedRoot = parseSelector(resolvedSelector, result, rule); if (!resolvedRoot) { continue; diff --git a/lib/utils/flattenNestedSelectorsForRule.mjs b/lib/utils/flattenNestedSelectorsForRule.mjs index e03b6ae308..a3a51fc1a7 100644 --- a/lib/utils/flattenNestedSelectorsForRule.mjs +++ b/lib/utils/flattenNestedSelectorsForRule.mjs @@ -2,7 +2,7 @@ import resolveNestedSelector from 'postcss-resolve-nested-selector'; import getRuleSelector from './getRuleSelector.mjs'; import isStandardSyntaxSelector from './isStandardSyntaxSelector.mjs'; -import parseSelectorAST from './parseSelectorAST.mjs'; +import parseSelector from './parseSelector.mjs'; /** * @typedef {import('postcss-selector-parser').Selector} Selector @@ -12,7 +12,7 @@ import parseSelectorAST from './parseSelectorAST.mjs'; * @returns {Array<{selector: Selector, resolvedSelectors: Array}>} */ export default function flattenNestedSelectorsForRule(rule, result) { - const ownAST = parseSelectorAST(getRuleSelector(rule), result, rule); + const ownAST = parseSelector(getRuleSelector(rule), result, rule); if (!ownAST) return []; @@ -24,7 +24,7 @@ export default function flattenNestedSelectorsForRule(rule, result) { for (const resolvedSelector of resolvedSelectors) { if (!isStandardSyntaxSelector(resolvedSelector)) return []; - const resolvedRoot = parseSelectorAST(resolvedSelector, result, rule); + const resolvedRoot = parseSelector(resolvedSelector, result, rule); if (!resolvedRoot) { continue; diff --git a/lib/utils/parseSelector.cjs b/lib/utils/parseSelector.cjs index dfd0c9f457..96410d08e7 100644 --- a/lib/utils/parseSelector.cjs +++ b/lib/utils/parseSelector.cjs @@ -8,12 +8,20 @@ const selectorParser = require('postcss-selector-parser'); * @param {string} selector * @param {import('stylelint').PostcssResult} result * @param {import('postcss').Node} node - * @param {(root: import('postcss-selector-parser').Root) => void} callback - * @returns {string | undefined} + * @param {(root: import('postcss-selector-parser').Root) => void} [callback] - Deprecated. It will be removed in the future. + * @returns {import('postcss-selector-parser').Root | undefined} */ function parseSelector(selector, result, node, callback) { + if (!selector) return undefined; + try { - return selectorParser(callback).processSync(selector); + // TODO: Remove `callback` in the future. See #7647. + if (callback) { + // @ts-expect-error -- TS2322: Type 'string' is not assignable to type 'Root'. + return selectorParser(callback).processSync(selector); + } + + return selectorParser().astSync(selector); } catch (err) { result.warn(`Cannot parse selector (${err})`, { node, stylelintType: 'parseError' }); diff --git a/lib/utils/parseSelector.mjs b/lib/utils/parseSelector.mjs index 52e1a0350a..68c875ce4f 100644 --- a/lib/utils/parseSelector.mjs +++ b/lib/utils/parseSelector.mjs @@ -4,12 +4,20 @@ import selectorParser from 'postcss-selector-parser'; * @param {string} selector * @param {import('stylelint').PostcssResult} result * @param {import('postcss').Node} node - * @param {(root: import('postcss-selector-parser').Root) => void} callback - * @returns {string | undefined} + * @param {(root: import('postcss-selector-parser').Root) => void} [callback] - Deprecated. It will be removed in the future. + * @returns {import('postcss-selector-parser').Root | undefined} */ export default function parseSelector(selector, result, node, callback) { + if (!selector) return undefined; + try { - return selectorParser(callback).processSync(selector); + // TODO: Remove `callback` in the future. See #7647. + if (callback) { + // @ts-expect-error -- TS2322: Type 'string' is not assignable to type 'Root'. + return selectorParser(callback).processSync(selector); + } + + return selectorParser().astSync(selector); } catch (err) { result.warn(`Cannot parse selector (${err})`, { node, stylelintType: 'parseError' }); diff --git a/lib/utils/parseSelectorAST.cjs b/lib/utils/parseSelectorAST.cjs deleted file mode 100644 index 639f58c473..0000000000 --- a/lib/utils/parseSelectorAST.cjs +++ /dev/null @@ -1,25 +0,0 @@ -// 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'); - -/** - * @param {string} selector - * @param {import('stylelint').PostcssResult} result - * @param {import('postcss').Node} node - * @returns {import('postcss-selector-parser').Root | undefined} - */ -function parseSelectorAST(selector, result, node) { - if (!selector) return undefined; - - try { - return selectorParser().astSync(selector); - } catch (err) { - result.warn(`Cannot parse selector (${err})`, { node, stylelintType: 'parseError' }); - - return undefined; - } -} - -module.exports = parseSelectorAST; diff --git a/lib/utils/parseSelectorAST.mjs b/lib/utils/parseSelectorAST.mjs deleted file mode 100644 index 51da9c5051..0000000000 --- a/lib/utils/parseSelectorAST.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import selectorParser from 'postcss-selector-parser'; - -/** - * @param {string} selector - * @param {import('stylelint').PostcssResult} result - * @param {import('postcss').Node} node - * @returns {import('postcss-selector-parser').Root | undefined} - */ -export default function parseSelectorAST(selector, result, node) { - if (!selector) return undefined; - - try { - return selectorParser().astSync(selector); - } catch (err) { - result.warn(`Cannot parse selector (${err})`, { node, stylelintType: 'parseError' }); - - return undefined; - } -}