Skip to content

Commit

Permalink
is-pseudo : handle more complex selector patterns (#923)
Browse files Browse the repository at this point in the history
* is-pseudo : handle more complex selector patterns

* clarify
  • Loading branch information
romainmenke authored Apr 9, 2023
1 parent 62a8134 commit 51b3bc6
Show file tree
Hide file tree
Showing 18 changed files with 423 additions and 35 deletions.
8 changes: 7 additions & 1 deletion plugins/postcss-is-pseudo-class/.tape.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ postcssTape(plugin)({
},
'basic:oncomplex:warning': {
message: "warns on complex selectors",
warnings: 10,
warnings: 8,
options: {
onComplexSelector: 'warning'
}
Expand All @@ -42,6 +42,12 @@ postcssTape(plugin)({
preserve: false
}
},
'compound-after-complex-is': {
message: "can handle compound selectors after complex selectors in :is()",
options: {
preserve: false
}
},
'complex': {
message: "supports complex selectors",
options: {
Expand Down
4 changes: 4 additions & 0 deletions plugins/postcss-is-pseudo-class/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changes to PostCSS Is Pseudo Class

### Unreleased (minor)

- Add support for more complex selector patterns. In particular anything where `:is()` is in the left-most compound selector.

### 3.1.1 (February 8, 2023)

- Reduce the amount of duplicate fallback CSS.
Expand Down
2 changes: 1 addition & 1 deletion plugins/postcss-is-pseudo-class/dist/index.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion plugins/postcss-is-pseudo-class/dist/index.mjs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function isPseudoInFirstCompound(selector: any): boolean;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { sortCompoundSelectorsInsideComplexSelector } from './compound-selector-
import { childAdjacentChild } from './complex/child-adjacent-child';
import { isInCompoundWithOneOtherElement } from './complex/is-in-compound';
import type { Container } from 'postcss-selector-parser';
import { isPseudoInFirstCompound } from './complex/is-pseudo-in-first-compound';

export default function complexSelectors(selectors: Array<string>, pluginOptions: { onComplexSelector?: 'warning' }, warnOnComplexSelector: () => void, warnOnPseudoElements: () => void) {
return selectors.flatMap((selector) => {
Expand Down Expand Up @@ -80,7 +81,8 @@ export default function complexSelectors(selectors: Array<string>, pluginOptions

if (
childAdjacentChild(pseudo.parent) ||
isInCompoundWithOneOtherElement(pseudo.parent)
isInCompoundWithOneOtherElement(pseudo.parent) ||
isPseudoInFirstCompound(pseudo.parent)
) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
// :-csstools-matches(.a > .b) + :-csstools-matches(.c > .d)
// equivalent to
// .a.c > .b + .d
//
// and
//
// :-csstools-matches(.a > .b) ~ :-csstools-matches(.c > .d)
// equivalent to
// .a.c ~ .b + .d
//
// because adjacent elements have the same parent element.
export function childAdjacentChild(selector): boolean {
if (!selector || !selector.nodes) {
Expand All @@ -19,7 +26,7 @@ export function childAdjacentChild(selector): boolean {
}

// adjacent combinator
if (!selector.nodes[1] || selector.nodes[1].type !== 'combinator' || selector.nodes[1].value !== '+') {
if (!selector.nodes[1] || selector.nodes[1].type !== 'combinator' || (selector.nodes[1].value !== '+' && selector.nodes[1].value !== '~')) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// :-csstools-matches(.a > .b) > .c
// equivalent to
// .a > .b > .c
// because `:is()` is in the left-most compound selector
export function isPseudoInFirstCompound(selector): boolean {
if (!selector || !selector.nodes) {
return false;
}
if (selector.type !== 'selector') {
return false;
}

let isPseudoIndex = -1;
for (let i = 0; i < selector.nodes.length; i++) {
const node = selector.nodes[i];
if (node.type === 'combinator') {
return false;
}

if (node.type === 'pseudo' && node.value === ':-csstools-matches') {
if (!node.nodes || node.nodes.length !== 1) {
return false;
}

isPseudoIndex = i;
break;
}
}

if (isPseudoIndex === -1) {
return false;
}

const before = selector.nodes.slice(0, isPseudoIndex);
const isPseudo = selector.nodes[isPseudoIndex];
const after = selector.nodes.slice(isPseudoIndex + 1);

before.forEach((node) => {
isPseudo.nodes[0].append(node.clone());
});

after.forEach((node) => {
isPseudo.nodes[0].append(node.clone());
});

isPseudo.replaceWith(...isPseudo.nodes);

before.forEach((node) => {
node.remove();
});

after.forEach((node) => {
node.remove();
});

return true;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import parser from 'postcss-selector-parser';

export function sortCompoundSelectorsInsideComplexSelector(node) {
if (!node || !node.nodes) {
if (!node || !node.nodes || node.nodes.length === 1) {
return;
}

Expand Down Expand Up @@ -61,8 +61,27 @@ export function sortCompoundSelectorsInsideComplexSelector(node) {
}

for (let i = sortedCompoundSelectors.length - 1; i >= 0; i--) {
const previous = sortedCompoundSelectors[i - 1];

sortedCompoundSelectors[i].remove();
node.prepend(sortedCompoundSelectors[i]);

if (previous && previous.type === 'tag' && sortedCompoundSelectors[i].type === 'tag') {
const wrapped = parser.pseudo({
value: ':is',
nodes: [
parser.selector({
value: '',
nodes: [
sortedCompoundSelectors[i],
],
}),
],
});

node.prepend(wrapped);
} else {
node.prepend(sortedCompoundSelectors[i]);
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions plugins/postcss-is-pseudo-class/test/basic.css
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ foo[baz=":is(.some, .other)"], .ok {
order: 36;
}

:is(input, button):is(select, textarea) {
order: 37;
}

:is(input, button):is(:hover, :focus) {
to-clone: 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,31 +74,31 @@ foo[baz=":is(.some, .other)"], .ok {
order: 10;
}

.pre:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.pre + :is(:focus > .beta) {
order: 11;
}

.pre :is(.alpha > .beta)+ :is(:focus > .beta) {
order: 12;
}

.post:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.post + :is(:focus > .beta) {
order: 13;
}

:is(.alpha > .beta) .post + :is(:focus > .beta) {
.alpha > .beta .post + :is(:focus > .beta) {
order: 14;
}

:is(.alpha ~ .delta):focus > .beta + .beta {
.alpha ~ .delta:focus > .beta + .beta {
order: 15;
}

.alpha:focus > .beta + .beta, .alpha:focus > .beta + .two, .one:focus > .two + .beta, .one:focus > .two + .two {
order: 16;
}

:is(.alpha > .beta) ~ :is(:focus > .beta) {
.alpha:focus > .beta ~ .beta {
order: 17;
}

Expand All @@ -110,7 +110,7 @@ foo[baz=":is(.some, .other)"], .ok {
order: 19;
}

.pre.alpha:is(.one > .two) {
.one > .two.pre.alpha {
order: 20;
}

Expand Down Expand Up @@ -238,6 +238,22 @@ ol:hover > li.foo, ol:hover > li.bar, ol:focus > li.foo, ol:focus > li.bar, ul:h
order: 36;
}

input:is(select) {
order: 37;
}

input:is(textarea) {
order: 37;
}

button:is(select) {
order: 37;
}

button:is(textarea) {
order: 37;
}

input:hover, input:focus, button:hover, button:focus {
to-clone: 1;
}
28 changes: 22 additions & 6 deletions plugins/postcss-is-pseudo-class/test/basic.expect.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,31 +74,31 @@ foo[baz=":is(.some, .other)"], .ok {
order: 10;
}

.pre:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.pre + :is(:focus > .beta) {
order: 11;
}

.pre :is(.alpha > .beta)+ :is(:focus > .beta) {
order: 12;
}

.post:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.post + :is(:focus > .beta) {
order: 13;
}

:is(.alpha > .beta) .post + :is(:focus > .beta) {
.alpha > .beta .post + :is(:focus > .beta) {
order: 14;
}

:is(.alpha ~ .delta):focus > .beta + .beta {
.alpha ~ .delta:focus > .beta + .beta {
order: 15;
}

.alpha:focus > .beta + .beta, .alpha:focus > .beta + .two, .one:focus > .two + .beta, .one:focus > .two + .two {
order: 16;
}

:is(.alpha > .beta) ~ :is(:focus > .beta) {
.alpha:focus > .beta ~ .beta {
order: 17;
}

Expand All @@ -110,7 +110,7 @@ foo[baz=":is(.some, .other)"], .ok {
order: 19;
}

.pre.alpha:is(.one > .two) {
.one > .two.pre.alpha {
order: 20;
}

Expand Down Expand Up @@ -238,6 +238,22 @@ ol:hover > li.foo, ol:hover > li.bar, ol:focus > li.foo, ol:focus > li.bar, ul:h
order: 36;
}

input:is(select) {
order: 37;
}

input:is(textarea) {
order: 37;
}

button:is(select) {
order: 37;
}

button:is(textarea) {
order: 37;
}

input:hover, input:focus, button:hover, button:focus {
to-clone: 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,31 +74,31 @@ foo[baz=":is(.some, .other)"], .ok {
order: 10;
}

.pre:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.pre + :is(:focus > .beta) {
order: 11;
}

.pre :is(.alpha > .beta)+ :is(:focus > .beta) {
order: 12;
}

.post:is(.alpha > .beta) + :is(:focus > .beta) {
.alpha > .beta.post + :is(:focus > .beta) {
order: 13;
}

:is(.alpha > .beta) .post + :is(:focus > .beta) {
.alpha > .beta .post + :is(:focus > .beta) {
order: 14;
}

:is(.alpha ~ .delta):focus > .beta + .beta {
.alpha ~ .delta:focus > .beta + .beta {
order: 15;
}

.alpha:focus > .beta + .beta, .alpha:focus > .beta + .two, .one:focus > .two + .beta, .one:focus > .two + .two {
order: 16;
}

:is(.alpha > .beta) ~ :is(:focus > .beta) {
.alpha:focus > .beta ~ .beta {
order: 17;
}

Expand All @@ -110,7 +110,7 @@ foo[baz=":is(.some, .other)"], .ok {
order: 19;
}

.pre.alpha:is(.one > .two) {
.one > .two.pre.alpha {
order: 20;
}

Expand Down Expand Up @@ -238,6 +238,22 @@ ol:hover > li.foo, ol:hover > li.bar, ol:focus > li.foo, ol:focus > li.bar, ul:h
order: 36;
}

input:is(select) {
order: 37;
}

input:is(textarea) {
order: 37;
}

button:is(select) {
order: 37;
}

button:is(textarea) {
order: 37;
}

input:hover, input:focus, button:hover, button:focus {
to-clone: 1;
}
Loading

0 comments on commit 51b3bc6

Please sign in to comment.