Skip to content

Commit

Permalink
Fix incorrect column and escaped colons for `selector-pseudo-element-…
Browse files Browse the repository at this point in the history
…colon-notation` rule (stylelint#5879)
  • Loading branch information
ybiquitous authored and kawaguchi1102 committed Feb 6, 2022
1 parent accfc91 commit 1eb8fba
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ testRule({
{
code: 'a::grammar-error { color: pink; }',
},
{
code: 'a\\:before { color: pink; }',
},
{
code: 'li::marker { font-variant-numeric: tabular-nums; }',
},
Expand Down Expand Up @@ -65,42 +68,49 @@ testRule({
fixed: 'a:before { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a::bEfOrE { color: pink; }',
fixed: 'a:bEfOrE { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a::BEFORE { color: pink; }',
fixed: 'a:BEFORE { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a::after { color: pink; }',
fixed: 'a:after { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a::first-line { color: pink; }',
fixed: 'a:first-line { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a::first-letter { color: pink; }',
fixed: 'a:first-letter { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
code: 'a\\:before-none::before { color: pink; }',
fixed: 'a\\:before-none:before { color: pink; }',
message: messages.expected('single'),
line: 1,
column: 15,
},
{
code: 'a::before, a::after, a::first-letter { color: pink; }',
Expand All @@ -109,17 +119,17 @@ testRule({
{
message: messages.expected('single'),
line: 1,
column: 3,
column: 2,
},
{
message: messages.expected('single'),
line: 1,
column: 14,
column: 13,
},
{
message: messages.expected('single'),
line: 1,
column: 24,
column: 23,
},
],
},
Expand Down
73 changes: 35 additions & 38 deletions lib/rules/selector-pseudo-element-colon-notation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule');
const keywordSets = require('../../reference/keywordSets');
const parseSelector = require('../../utils/parseSelector');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const styleSearch = require('style-search');
const validateOptions = require('../../utils/validateOptions');

const ruleName = 'selector-pseudo-element-colon-notation';
Expand All @@ -29,6 +29,14 @@ const rule = (primary, _secondaryOptions, context) => {
return;
}

let fixedColon = '';

if (primary === 'single') {
fixedColon = ':';
} else if (primary === 'double') {
fixedColon = '::';
}

root.walkRules((ruleNode) => {
if (!isStandardSyntaxRule(ruleNode)) {
return;
Expand All @@ -41,53 +49,42 @@ const rule = (primary, _secondaryOptions, context) => {
return;
}

/** @type {Array<{ ruleNode: import('postcss').Rule, startIndex: number }>} */
const fixPositions = [];
const fixedSelector = parseSelector(selector, result, ruleNode, (selectors) => {
selectors.walkPseudos((pseudo) => {
const pseudoElement = pseudo.value.replace(/:/g, '');

// match only level 1 and 2 pseudo elements
const pseudoElementsWithColons = [...keywordSets.levelOneAndTwoPseudoElements].map(
(x) => `:${x}`,
);
if (!keywordSets.levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase())) {
return;
}

styleSearch({ source: selector.toLowerCase(), target: pseudoElementsWithColons }, (match) => {
const prevCharIsColon = selector[match.startIndex - 1] === ':';
const isDouble = pseudo.value.startsWith('::');

if (primary === 'single' && !prevCharIsColon) {
return;
}
if (primary === 'single' && !isDouble) {
return;
}

if (primary === 'double' && prevCharIsColon) {
return;
}
if (primary === 'double' && isDouble) {
return;
}

if (context.fix) {
fixPositions.unshift({ ruleNode, startIndex: match.startIndex });
if (context.fix) {
pseudo.replaceWith(pseudo.clone({ value: fixedColon + pseudoElement }));

return;
}
return;
}

report({
message: messages.expected(primary),
node: ruleNode,
index: match.startIndex,
result,
ruleName,
report({
message: messages.expected(primary),
node: ruleNode,
index: pseudo.sourceIndex,
result,
ruleName,
});
});
});

if (fixPositions.length) {
// If expecting : then we found :: so remove one of the colons
// If expecting :: then we found : so add one extra colon
const expectedSingle = primary === 'single';
const offset = expectedSingle ? 1 : 0;
const extraColon = expectedSingle ? '' : ':';

for (const fixPosition of fixPositions) {
ruleNode.selector =
ruleNode.selector.substring(0, fixPosition.startIndex - offset) +
extraColon +
ruleNode.selector.substring(fixPosition.startIndex);
}
if (context.fix && fixedSelector) {
ruleNode.selector = fixedSelector;
}
});
};
Expand Down
4 changes: 2 additions & 2 deletions lib/utils/parseSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const selectorParser = require('postcss-selector-parser');
module.exports = function parseSelector(selector, result, node, callback) {
try {
return selectorParser(callback).processSync(selector);
} catch {
result.warn('Cannot parse selector', { node, stylelintType: 'parseError' });
} catch (err) {
result.warn(`Cannot parse selector (${err})`, { node, stylelintType: 'parseError' });
}
};

0 comments on commit 1eb8fba

Please sign in to comment.