Skip to content

Commit 560dd8b

Browse files
authored
[shared] clean up compound pseudo selector priority logic (facebook#1474)
1 parent 232b01c commit 560dd8b

2 files changed

Lines changed: 89 additions & 28 deletions

File tree

packages/@stylexjs/babel-plugin/__tests__/transform-stylex-create-test.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,7 @@ describe('@stylexjs/babel-plugin', () => {
19791979
`);
19801980
});
19811981

1982+
// Generates invalid CSS, need to revisit this API
19821983
test('"::before" containing pseudo-classes', () => {
19831984
const { code, metadata } = transform(`
19841985
import * as stylex from '@stylexjs/stylex';
@@ -2025,6 +2026,78 @@ describe('@stylexjs/babel-plugin', () => {
20252026
}
20262027
`);
20272028
});
2029+
2030+
test('legacy compound ":hover::after" selector as single key', () => {
2031+
const { code, metadata } = transform(`
2032+
import * as stylex from '@stylexjs/stylex';
2033+
export const styles = stylex.create({
2034+
foo: {
2035+
':hover::after': {
2036+
color: 'red',
2037+
},
2038+
},
2039+
});
2040+
`);
2041+
expect(code).toMatchInlineSnapshot(`
2042+
"import * as stylex from '@stylexjs/stylex';
2043+
export const styles = {
2044+
foo: {
2045+
kF1atM: "x1gfyp89",
2046+
$$css: true
2047+
}
2048+
};"
2049+
`);
2050+
expect(metadata).toMatchInlineSnapshot(`
2051+
{
2052+
"stylex": [
2053+
[
2054+
"x1gfyp89",
2055+
{
2056+
"ltr": ".x1gfyp89:hover::after{color:red}",
2057+
"rtl": null,
2058+
},
2059+
8130,
2060+
],
2061+
],
2062+
}
2063+
`);
2064+
});
2065+
2066+
test('compound ":hover::after" selector as single key', () => {
2067+
const { metadata } = transform(`
2068+
import * as stylex from '@stylexjs/stylex';
2069+
export const styles = stylex.create({
2070+
foo: {
2071+
color: {
2072+
default: 'red',
2073+
':hover::after': 'blue',
2074+
},
2075+
},
2076+
});
2077+
`);
2078+
expect(metadata).toMatchInlineSnapshot(`
2079+
{
2080+
"stylex": [
2081+
[
2082+
"x1e2nbdu",
2083+
{
2084+
"ltr": ".x1e2nbdu{color:red}",
2085+
"rtl": null,
2086+
},
2087+
3000,
2088+
],
2089+
[
2090+
"x6wc952",
2091+
{
2092+
"ltr": ".x6wc952:hover::after{color:blue}",
2093+
"rtl": null,
2094+
},
2095+
8130,
2096+
],
2097+
],
2098+
}
2099+
`);
2100+
});
20282101
});
20292102

20302103
describe('object values: queries', () => {

packages/@stylexjs/shared/src/utils/property-priorities.js

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -734,26 +734,22 @@ const RELATIONAL_SELECTORS = {
734734
/^:where\(\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\s+~\s+\*,\s+:has\(~\s\.[0-9a-zA-Z_-]+(:[a-zA-Z-]+)\)\)$/,
735735
};
736736

737-
// Matches pseudo-elements (::after) and pseudo-classes (:hover, :nth-child(2))
738737
const PSEUDO_PART_REGEX = /::[a-zA-Z-]+|:[a-zA-Z-]+(?:\([^)]*\))?/g;
739738

740-
// Calculate priority for compound pseudo selectors like :hover::after
739+
// We only handle chains of simple pseudo-classes and pseudo-elements and opt out of functional pseudo-classes
741740
function getCompoundPseudoPriority(key: string): number | void {
742-
const pseudoParts = key.match(PSEUDO_PART_REGEX);
743-
if (pseudoParts && pseudoParts.length > 1) {
744-
let total = 0;
745-
for (const part of pseudoParts) {
746-
if (part.startsWith('::')) {
747-
total += PSEUDO_ELEMENT_PRIORITY;
748-
} else {
749-
const prop = part.includes('(')
750-
? part.slice(0, part.indexOf('('))
751-
: part;
752-
total += PSEUDO_CLASS_PRIORITIES[prop] ?? 40;
753-
}
754-
}
755-
return total;
741+
const parts = key.match(PSEUDO_PART_REGEX);
742+
if (!parts || parts.length <= 1 || parts.some((p) => p.includes('('))) return;
743+
744+
let total = 0;
745+
746+
for (const part of parts) {
747+
total += part.startsWith('::')
748+
? PSEUDO_ELEMENT_PRIORITY
749+
: (PSEUDO_CLASS_PRIORITIES[part] ?? 40);
756750
}
751+
752+
return total;
757753
}
758754

759755
export function getAtRulePriority(key: string): number | void {
@@ -776,10 +772,6 @@ export function getAtRulePriority(key: string): number | void {
776772

777773
export function getPseudoElementPriority(key: string): number | void {
778774
if (key.startsWith('::')) {
779-
const compoundPriority = getCompoundPseudoPriority(key);
780-
if (compoundPriority != null) {
781-
return compoundPriority;
782-
}
783775
return PSEUDO_ELEMENT_PRIORITY;
784776
}
785777
}
@@ -816,14 +808,7 @@ export function getPseudoClassPriority(key: string): number | void {
816808
}
817809

818810
if (key.startsWith(':')) {
819-
const compoundPriority = getCompoundPseudoPriority(key);
820-
if (compoundPriority != null) {
821-
return compoundPriority;
822-
}
823-
824-
const prop = key.includes('(')
825-
? key.slice(0, key.indexOf('('))
826-
: key;
811+
const prop = key.split('(')[0];
827812

828813
return PSEUDO_CLASS_PRIORITIES[prop] ?? 40;
829814
}
@@ -848,6 +833,9 @@ export default function getPriority(key: string): number {
848833
const atRulePriority = getAtRulePriority(key);
849834
if (atRulePriority) return atRulePriority;
850835

836+
const compoundPriority = getCompoundPseudoPriority(key);
837+
if (compoundPriority != null) return compoundPriority;
838+
851839
const pseudoElementPriority = getPseudoElementPriority(key);
852840
if (pseudoElementPriority) return pseudoElementPriority;
853841

0 commit comments

Comments
 (0)