Skip to content

Commit

Permalink
Fix selector-max-id end positions (#7571)
Browse files Browse the repository at this point in the history
  • Loading branch information
romainmenke committed Mar 26, 2024
1 parent 412ae2b commit f69c57b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .changeset/tasty-lamps-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"stylelint": patch
---

Fixed: `selector-max-id` end positions
92 changes: 61 additions & 31 deletions lib/rules/selector-max-id/__tests__/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ testRule({
},
{
code: ':not(#foo) {}',
message: messages.expected('#foo', 0),
message: messages.expected(':not(#foo)', 0),
line: 1,
column: 6,
column: 1,
endLine: 1,
endColumn: 10,
endColumn: 11,
},
],
});
Expand Down Expand Up @@ -236,11 +236,11 @@ testRule({
{
code: '#foo { &:hover > #bar #baz {} }',
description: 'nested selectors: greater than max ID selectors',
message: messages.expected('#foo:hover > #bar #baz', 2),
message: messages.expected('&:hover > #bar #baz', 2),
line: 1,
column: 8,
endLine: 1,
endColumn: 30,
endColumn: 27,
},
{
code: '#foo #bar #baz { @media print { #bar {} } }',
Expand All @@ -254,51 +254,51 @@ testRule({
endColumn: 15,
},
{
message: messages.expected('#foo #bar #baz #bar', 2),
message: messages.expected('#bar', 2),
line: 1,
column: 33,
endLine: 1,
endColumn: 40,
endColumn: 37,
},
],
},
{
code: '.foo { @media print { #bar #baz #foo {} } }',
description: 'nested compound selector: greater than max ID selectors, nested @media query',
message: messages.expected('.foo #bar #baz #foo', 2),
message: messages.expected('#bar #baz #foo', 2),
line: 1,
column: 23,
endLine: 1,
endColumn: 40,
endColumn: 37,
},
{
code: '#foo #bar { #baz { #quux {} } }',
description: 'nested compound selector: greater than max ID selectors',
warnings: [
{
message: messages.expected('#foo #bar #baz', 2),
message: messages.expected('#baz', 2),
line: 1,
column: 13,
endLine: 1,
endColumn: 30,
endColumn: 17,
},
{
message: messages.expected('#foo #bar #baz #quux', 2),
message: messages.expected('#quux', 2),
line: 1,
column: 20,
endLine: 1,
endColumn: 28,
endColumn: 25,
},
],
},
{
code: '#foo { #baz { #quux {} } }',
description: 'nested compound selector: greater than max ID selectors',
message: messages.expected('#foo #baz #quux', 2),
message: messages.expected('#quux', 2),
line: 1,
column: 15,
endLine: 1,
endColumn: 23,
endColumn: 20,
},
],
});
Expand All @@ -322,20 +322,20 @@ testRule({
{
code: ':--foo(#foo) {}',
description: 'custom pseudo-class selector: greater than max ID selectors',
message: messages.expected('#foo', 0),
message: messages.expected(':--foo(#foo)', 0),
line: 1,
column: 8,
column: 1,
endLine: 1,
endColumn: 12,
endColumn: 13,
},
{
code: ':--foo(#foo #bar #baz) {}',
description: 'custom pseudo-class selector: greater than max ID selectors',
message: messages.expected('#foo #bar #baz', 0),
message: messages.expected(':--foo(#foo #bar #baz)', 0),
line: 1,
column: 8,
column: 1,
endLine: 1,
endColumn: 22,
endColumn: 23,
},
],
});
Expand All @@ -353,11 +353,11 @@ testRule({
reject: [
{
code: 'a:not(#foo #bar #baz) {}',
message: messages.expected('#foo #bar #baz', 2),
message: messages.expected('a:not(#foo #bar #baz)', 2),
line: 1,
column: 7,
column: 1,
endLine: 1,
endColumn: 21,
endColumn: 22,
},
],
});
Expand Down Expand Up @@ -415,72 +415,96 @@ testRule({
message: messages.expected('.bar #foo', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 10,
},
{
code: '.bar > #foo { @include test {} }',
message: messages.expected('.bar > #foo', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 12,
},
{
code: '#foo.bar { @include test {} }',
message: messages.expected('#foo.bar', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 9,
},
{
code: '.foo, .bar, #foo.baz { @include test {} }',
message: messages.expected('#foo.baz', 0),
line: 1,
column: 13,
endLine: 1,
endColumn: 21,
},
{
code: '#foo [lang^=en] { @include test {} }',
message: messages.expected('#foo [lang^=en]', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 16,
},
{
code: '#foo[lang^=en] { @include test {} }',
message: messages.expected('#foo[lang^=en]', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 15,
},
{
code: '* #foo { @include test {} }',
message: messages.expected('* #foo', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 7,
},
{
code: '#foo * { @include test {} }',
message: messages.expected('#foo *', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 7,
},
{
code: '*#foo { @include test {} }',
message: messages.expected('*#foo', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 6,
},
{
code: '#foo:hover { @include test {} }',
message: messages.expected('#foo:hover', 0),
line: 1,
column: 1,
endLine: 1,
endColumn: 11,
},
{
code: ':not(#foo) { @include test {} }',
message: messages.expected('#foo', 0),
message: messages.expected(':not(#foo)', 0),
line: 1,
column: 6,
column: 1,
endLine: 1,
endColumn: 11,
},
{
code: '@include test { #foo {} }',
message: messages.expected('#foo', 0),
line: 1,
column: 17,
endLine: 1,
endColumn: 21,
},
],
});
Expand Down Expand Up @@ -543,20 +567,26 @@ testRule({
message: messages.expected('#foo #bar #baz', 2),
line: 1,
column: 1,
endLine: 1,
endColumn: 15,
},
{
message: messages.expected('#foo #bar #baz #bar', 2),
message: messages.expected('#bar', 2),
line: 1,
column: 34,
endLine: 1,
endColumn: 38,
},
],
},
{
code: '.foo { @if ($p == 1) { #bar #baz #foo {} } }',
description: 'nested compound selector: greater than max ID selectors, nested @if statement',
message: messages.expected('.foo #bar #baz #foo', 2),
message: messages.expected('#bar #baz #foo', 2),
line: 1,
column: 24,
endLine: 1,
endColumn: 38,
},
],
});
Expand All @@ -579,11 +609,11 @@ testRule({
{
code: 'a:matches(#foo) {}',
description: 'selector within non-ignored pseudo class',
message: messages.expected('#foo', 0),
message: messages.expected('a:matches(#foo)', 0),
line: 1,
column: 11,
column: 1,
endLine: 1,
endColumn: 15,
endColumn: 16,
},
],
});
37 changes: 19 additions & 18 deletions lib/rules/selector-max-id/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
// please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
'use strict';

const resolvedNestedSelector = require('postcss-resolve-nested-selector');
const validateTypes = require('../../utils/validateTypes.cjs');
const flattenNestedSelectorsForRule = require('../../utils/flattenNestedSelectorsForRule.cjs');
const isContextFunctionalPseudoClass = require('../../utils/isContextFunctionalPseudoClass.cjs');
const isNonNegativeInteger = require('../../utils/isNonNegativeInteger.cjs');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule.cjs');
const isStandardSyntaxSelector = require('../../utils/isStandardSyntaxSelector.cjs');
const optionsMatches = require('../../utils/optionsMatches.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');
Expand Down Expand Up @@ -49,18 +49,19 @@ const rule = (primary, secondaryOptions) => {
}

/**
* @param {import('postcss-selector-parser').Container<string | undefined>} resolvedSelectorNode
* @param {import('postcss-selector-parser').Container<string | undefined>} selectorNode
* @param {import('postcss').Rule} ruleNode
*/
function checkSelector(selectorNode, ruleNode) {
const count = selectorNode.reduce((total, childNode) => {
function checkSelector(resolvedSelectorNode, selectorNode, ruleNode) {
const count = resolvedSelectorNode.reduce((total, childNode) => {
// Only traverse inside actual selectors and context functional pseudo-classes that aren't ignored
if (
childNode.type === 'selector' ||
isCheckedContextFunctionalPseudoClass(childNode) ||
isUnignoredContextFunctionalPseudoClass(childNode)
) {
checkSelector(childNode, ruleNode);
checkSelector(childNode, selectorNode, ruleNode);
}

if (childNode.type === 'id') total += 1;
Expand All @@ -69,15 +70,17 @@ const rule = (primary, secondaryOptions) => {
}, 0);

if (selectorNode.type !== 'root' && selectorNode.type !== 'pseudo' && count > primary) {
const selector = selectorNode.toString();
const index = selectorNode.first?.sourceIndex ?? 0;
const selectorStr = selectorNode.toString().trim();

report({
ruleName,
result,
node: ruleNode,
message: messages.expected,
messageArgs: [selector, primary],
word: selector,
messageArgs: [selectorStr, primary],
index,
endIndex: index + selectorStr.length,
});
}
}
Expand Down Expand Up @@ -105,17 +108,15 @@ const rule = (primary, secondaryOptions) => {
}

root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return;
}
if (!isStandardSyntaxRule(ruleNode)) return;

for (const selector of ruleNode.selectors) {
for (const resolvedSelector of resolvedNestedSelector(selector, ruleNode)) {
parseSelector(resolvedSelector, result, ruleNode, (container) =>
checkSelector(container, ruleNode),
);
}
}
if (!isStandardSyntaxSelector(ruleNode.selector)) return;

flattenNestedSelectorsForRule(ruleNode, result).forEach(({ selector, resolvedSelectors }) => {
resolvedSelectors.forEach((resolvedSelector) => {
checkSelector(resolvedSelector, selector, ruleNode);
});
});
});
};
};
Expand Down

0 comments on commit f69c57b

Please sign in to comment.