Skip to content

Commit 9a66324

Browse files
Allow flattening of multiple selectors into separate rules to better deduplicate and sort styles. (#1830)
* Allow flatenning of multiple selectors into separate rules to better deduplicate and sort styles * Update babel-plugin types * changeset * As this has been tested, enable `flattenMultipleSelectors` by default instead.
1 parent cceffda commit 9a66324

File tree

18 files changed

+483
-70
lines changed

18 files changed

+483
-70
lines changed

.changeset/lazy-bats-remain.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
'@compiled/parcel-transformer-external': none
3+
'@compiled/babel-component-extracted-fixture': none
4+
'@compiled/parcel-transformer': none
5+
'@compiled/babel-component-fixture': none
6+
'@compiled/css': minor
7+
---
8+
9+
Adds a possibly breaking change to flatten multiple selectors into separate rules to better deduplicate and sort styles.
10+
11+
You can disable this by setting `flattenMultipleSelectors: false` in Babel and other config.
12+
13+
For example:
14+
15+
```tsx
16+
css({
17+
'&:hover, &:focus': {
18+
color: 'red',
19+
},
20+
});
21+
```
22+
23+
Is transformed into the same code as this would be:
24+
25+
```tsx
26+
css({
27+
'&:hover': { color: 'red' },
28+
'&:focus': { color: 'red' },
29+
});
30+
```
31+
32+
Without this, pseudo-selectors aren't sorted properly in some scenarios.

.changeset/lovely-rockets-hammer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@compiled/babel-plugin': minor
3+
---
4+
5+
Adds option `flattenMultipleSelectors` (defaults to `true`) to allow flattening of multiple selectors into separate rules to better deduplicate and sort styles.

fixtures/babel-component-extracted/.babelrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44
["@babel/preset-react", { "runtime": "automatic" }]
55
],
66
"plugins": [
7-
["@compiled/babel-plugin", { "importReact": false, "optimizeCss": false }],
7+
[
8+
"@compiled/babel-plugin",
9+
{ "importReact": false, "optimizeCss": false, "flattenMultipleSelectors": true }
10+
],
811
[
912
"@compiled/babel-plugin-strip-runtime",
1013
{ "extractStylesToDirectory": { "source": "src", "dest": "dist" } }

fixtures/babel-component-extracted/src/index.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,20 @@ import { styled, css } from '@compiled/react';
33
const Button = styled.button({
44
color: 'blue',
55
fontSize: '30px',
6-
border: '2px solid blue',
6+
border: '2px solid transparent',
77
padding: '32px',
88
backgroundColor: 'yellow',
9+
10+
'&:hover': {
11+
borderColor: 'blue',
12+
backgroundColor: 'blue',
13+
color: 'white',
14+
},
15+
16+
'&:hover, &:focus': {
17+
backgroundColor: 'blue',
18+
color: 'white',
19+
},
920
});
1021

1122
export default function BabelComponent({ children }) {

fixtures/babel-component/.babelrc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
["@babel/preset-env", { "targets": { "browsers": "last 1 version" } }],
44
["@babel/preset-react", { "runtime": "automatic" }]
55
],
6-
"plugins": [["@compiled/babel-plugin", { "importReact": false, "optimizeCss": false }]]
6+
"plugins": [
7+
[
8+
"@compiled/babel-plugin",
9+
{ "importReact": false, "optimizeCss": false, "flattenMultipleSelectors": true }
10+
]
11+
]
712
}

fixtures/babel-component/src/index.jsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,20 @@ import { styled } from '@compiled/react';
33
const Button = styled.button`
44
color: blue;
55
font-size: 30px;
6-
border: 2px solid blue;
6+
border: 2px solid transparent;
77
padding: 32px;
8+
9+
&:hover {
10+
border-color: blue;
11+
background-color: blue;
12+
color: white;
13+
}
14+
15+
&:hover,
16+
&:focus {
17+
background-color: blue;
18+
color: white;
19+
}
820
`;
921

1022
export default function BabelComponent({ children }) {

packages/babel-plugin/src/__tests__/module-traversal.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,11 @@ describe('module traversal', () => {
366366
</BackgroundWithSelector>;
367367
`);
368368

369-
expect(actual).toInclude(
370-
'._15rzbf54 #joined-selector, ._1khrbf54 .red{background-color:green}'
371-
);
369+
// This gets split into two rules due to flattenMultipleSelectors
370+
expect(actual).toIncludeMultiple([
371+
'._15rzbf54 #joined-selector{background-color:green}',
372+
'._1khrbf54 .red{background-color:green}',
373+
]);
372374
});
373375

374376
describe('should call onIncludedFiles with the filepath', () => {

packages/babel-plugin/src/keyframes/__tests__/call-expression.test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describe('keyframes', () => {
8282
const _3 = "._j7hqb4f3{animation-name:k1wmcptp}";
8383
const _2 = "._5sagymdr{animation-duration:2s}";
8484
const _ =
85-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
85+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
8686
const fadeOut = null;
8787
<CC>
8888
<CS>{[_, _2, _3, _4]}</CS>
@@ -100,7 +100,7 @@ describe('keyframes', () => {
100100
expect(actual).toMatchInlineSnapshot(`
101101
"const _2 = "._y44vjvcp{animation:k1wmcptp 2s ease-in-out}";
102102
const _ =
103-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
103+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
104104
const fadeOut = null;
105105
<CC>
106106
<CS>{[_, _2]}</CS>
@@ -857,7 +857,7 @@ describe('keyframes', () => {
857857
const _3 = "._j7hqb4f3{animation-name:k1wmcptp}";
858858
const _2 = "._5sagymdr{animation-duration:2s}";
859859
const _ =
860-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
860+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
861861
const fadeOut = null;
862862
<CC>
863863
<CS>{[_, _2, _3, _4]}</CS>
@@ -875,7 +875,7 @@ describe('keyframes', () => {
875875
expect(actual).toMatchInlineSnapshot(`
876876
"const _2 = "._y44vjvcp{animation:k1wmcptp 2s ease-in-out}";
877877
const _ =
878-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
878+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
879879
const fadeOut = null;
880880
<CC>
881881
<CS>{[_, _2]}</CS>
@@ -918,7 +918,7 @@ describe('keyframes', () => {
918918

919919
expect(actual).toMatchInlineSnapshot(`
920920
"const _4 =
921-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
921+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
922922
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
923923
const _2 = "._j7hqb4f3{animation-name:k1wmcptp}";
924924
const _ = "._5sagymdr{animation-duration:2s}";
@@ -952,7 +952,7 @@ describe('keyframes', () => {
952952

953953
expect(actual).toMatchInlineSnapshot(`
954954
"const _2 =
955-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
955+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
956956
const _ = "._y44vjvcp{animation:k1wmcptp 2s ease-in-out}";
957957
const fadeOut = null;
958958
const StyledComponent = forwardRef(
@@ -1028,7 +1028,7 @@ describe('keyframes', () => {
10281028

10291029
expect(actual).toMatchInlineSnapshot(`
10301030
"const _4 =
1031-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1031+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
10321032
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
10331033
const _2 = "._j7hqb4f3{animation-name:k1wmcptp}";
10341034
const _ = "._5sagymdr{animation-duration:2s}";
@@ -1062,7 +1062,7 @@ describe('keyframes', () => {
10621062

10631063
expect(actual).toMatchInlineSnapshot(`
10641064
"const _2 =
1065-
"@keyframes k1wmcptp{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1065+
"@keyframes k1wmcptp{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
10661066
const _ = "._y44vjvcp{animation:k1wmcptp 2s ease-in-out}";
10671067
const fadeOut = null;
10681068
const StyledComponent = forwardRef(
@@ -1191,7 +1191,7 @@ describe('keyframes', () => {
11911191
const _3 = "._j7hq1c6j{animation-name:khheuil}";
11921192
const _2 = "._5sagymdr{animation-duration:2s}";
11931193
const _ =
1194-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1194+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
11951195
const fadeOut = null;
11961196
<CC>
11971197
<CS>{[_, _2, _3, _4]}</CS>
@@ -1209,7 +1209,7 @@ describe('keyframes', () => {
12091209
expect(actual).toMatchInlineSnapshot(`
12101210
"const _2 = "._y44v1go4{animation:khheuil 2s ease-in-out}";
12111211
const _ =
1212-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1212+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
12131213
const fadeOut = null;
12141214
<CC>
12151215
<CS>{[_, _2]}</CS>
@@ -1255,7 +1255,7 @@ describe('keyframes', () => {
12551255
const _3 = "._j7hq1c6j{animation-name:khheuil}";
12561256
const _2 = "._5sagymdr{animation-duration:2s}";
12571257
const _ =
1258-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1258+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
12591259
const fadeOut = null;
12601260
<CC>
12611261
<CS>{[_, _2, _3, _4]}</CS>
@@ -1273,7 +1273,7 @@ describe('keyframes', () => {
12731273
expect(actual).toMatchInlineSnapshot(`
12741274
"const _2 = "._y44v1go4{animation:khheuil 2s ease-in-out}";
12751275
const _ =
1276-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1276+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
12771277
const fadeOut = null;
12781278
<CC>
12791279
<CS>{[_, _2]}</CS>
@@ -1316,7 +1316,7 @@ describe('keyframes', () => {
13161316

13171317
expect(actual).toMatchInlineSnapshot(`
13181318
"const _4 =
1319-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1319+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
13201320
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
13211321
const _2 = "._j7hq1c6j{animation-name:khheuil}";
13221322
const _ = "._5sagymdr{animation-duration:2s}";
@@ -1350,7 +1350,7 @@ describe('keyframes', () => {
13501350

13511351
expect(actual).toMatchInlineSnapshot(`
13521352
"const _2 =
1353-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1353+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
13541354
const _ = "._y44v1go4{animation:khheuil 2s ease-in-out}";
13551355
const fadeOut = null;
13561356
const StyledComponent = forwardRef(
@@ -1426,7 +1426,7 @@ describe('keyframes', () => {
14261426

14271427
expect(actual).toMatchInlineSnapshot(`
14281428
"const _4 =
1429-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1429+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
14301430
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
14311431
const _2 = "._j7hq1c6j{animation-name:khheuil}";
14321432
const _ = "._5sagymdr{animation-duration:2s}";
@@ -1460,7 +1460,7 @@ describe('keyframes', () => {
14601460

14611461
expect(actual).toMatchInlineSnapshot(`
14621462
"const _2 =
1463-
"@keyframes khheuil{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
1463+
"@keyframes khheuil{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
14641464
const _ = "._y44v1go4{animation:khheuil 2s ease-in-out}";
14651465
const fadeOut = null;
14661466
const StyledComponent = forwardRef(

packages/babel-plugin/src/keyframes/__tests__/tagged-template-expression.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ describe('keyframes transforms a tagged template expression', () => {
8181
const _3 = "._j7hqa2t1{animation-name:k1a3bdtb}";
8282
const _2 = "._5sagymdr{animation-duration:2s}";
8383
const _ =
84-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
84+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
8585
const fadeOut = null;
8686
<CC>
8787
<CS>{[_, _2, _3, _4]}</CS>
@@ -99,7 +99,7 @@ describe('keyframes transforms a tagged template expression', () => {
9999
expect(actual).toMatchInlineSnapshot(`
100100
"const _2 = "._y44v1e4p{animation:k1a3bdtb 2s ease-in-out}";
101101
const _ =
102-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
102+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
103103
const fadeOut = null;
104104
<CC>
105105
<CS>{[_, _2]}</CS>
@@ -145,7 +145,7 @@ describe('keyframes transforms a tagged template expression', () => {
145145
const _3 = "._j7hqa2t1{animation-name:k1a3bdtb}";
146146
const _2 = "._5sagymdr{animation-duration:2s}";
147147
const _ =
148-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
148+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
149149
const fadeOut = null;
150150
<CC>
151151
<CS>{[_, _2, _3, _4]}</CS>
@@ -163,7 +163,7 @@ describe('keyframes transforms a tagged template expression', () => {
163163
expect(actual).toMatchInlineSnapshot(`
164164
"const _2 = "._y44v1e4p{animation:k1a3bdtb 2s ease-in-out}";
165165
const _ =
166-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
166+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
167167
const fadeOut = null;
168168
<CC>
169169
<CS>{[_, _2]}</CS>
@@ -806,7 +806,7 @@ describe('keyframes transforms a tagged template expression', () => {
806806

807807
expect(actual).toMatchInlineSnapshot(`
808808
"const _4 =
809-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
809+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
810810
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
811811
const _2 = "._j7hqa2t1{animation-name:k1a3bdtb}";
812812
const _ = "._5sagymdr{animation-duration:2s}";
@@ -840,7 +840,7 @@ describe('keyframes transforms a tagged template expression', () => {
840840

841841
expect(actual).toMatchInlineSnapshot(`
842842
"const _2 =
843-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
843+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
844844
const _ = "._y44v1e4p{animation:k1a3bdtb 2s ease-in-out}";
845845
const fadeOut = null;
846846
const StyledComponent = forwardRef(
@@ -916,7 +916,7 @@ describe('keyframes transforms a tagged template expression', () => {
916916

917917
expect(actual).toMatchInlineSnapshot(`
918918
"const _4 =
919-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
919+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
920920
const _3 = "._1pgl1ytf{animation-timing-function:ease-in-out}";
921921
const _2 = "._j7hqa2t1{animation-name:k1a3bdtb}";
922922
const _ = "._5sagymdr{animation-duration:2s}";
@@ -950,7 +950,7 @@ describe('keyframes transforms a tagged template expression', () => {
950950

951951
expect(actual).toMatchInlineSnapshot(`
952952
"const _2 =
953-
"@keyframes k1a3bdtb{0%,25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
953+
"@keyframes k1a3bdtb{0%{opacity:1}25%{opacity:1}25%{opacity:0.75}50%{opacity:0.5}to{opacity:0}}";
954954
const _ = "._y44v1e4p{animation:k1a3bdtb 2s ease-in-out}";
955955
const fadeOut = null;
956956
const StyledComponent = forwardRef(

0 commit comments

Comments
 (0)