Skip to content
  • Sponsor emotion-js/emotion

  • Notifications You must be signed in to change notification settings
  • Fork 1.1k

Commit 65a1eea

Browse files
srmaguraAndarist
andauthoredJun 13, 2024··
Make runtime label extraction opt-in (#2815)
* Make runtime labeling opt-in * Add test for EMOTION_RUNTIME_AUTO_LABEL * Document EMOTION_RUNTIME_AUTO_LABEL * Fix tests (maybe) * Fix test again * Fix eslint issue * hoist the `runtimeAutoLabel` check * move it back to allow for mocking --------- Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
1 parent fc74bee commit 65a1eea

File tree

5 files changed

+57
-7
lines changed

5 files changed

+57
-7
lines changed
 

‎.changeset/weak-cooks-compare.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@emotion/react': minor
3+
---
4+
5+
Automatic labeling at runtime is now an opt-in feature. Define `globalThis.EMOTION_RUNTIME_AUTO_LABEL = true` before Emotion gets initialized to enable it.

‎docs/labels.mdx

+14-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: 'Labels'
33
---
44

5-
Emotion adds a css property called `label`, the value of it is appended to the end of the class name, so it's more readable than a hash. `@emotion/babel-plugin` adds these labels automatically based on the variable name and other information, so they don't need to be manually specified.
5+
Emotion adds a CSS property called `label` which is appended to the generated class name to make it more readable. `@emotion/babel-plugin` adds these labels automatically based on the variable name and other information, so they don't need to be manually specified.
66

77
```jsx
88
// @live
@@ -29,3 +29,16 @@ render(
2929
</div>
3030
)
3131
```
32+
33+
## Automatic Labeling at Runtime
34+
35+
If you are not using `@emotion/babel-plugin`, you can still get automatic labels in development by setting the following global flag:
36+
37+
```js
38+
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true
39+
```
40+
41+
This feature is opt-in because:
42+
43+
- If you use server-side rendering and test your site in Safari, you may get spurious hydration warnings because the label computed on the server does not match the label computed in Safari.
44+
- This feature may degrade performance if the number of elements using the `css` prop is very large.

‎packages/react/__tests__/css.js

+26
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import createCache from '@emotion/cache'
1010
console.error = jest.fn()
1111
console.warn = jest.fn()
1212

13+
beforeEach(() => {
14+
delete globalThis.EMOTION_RUNTIME_AUTO_LABEL
15+
})
16+
1317
afterEach(() => {
1418
jest.clearAllMocks()
1519
safeQuerySelector('body').innerHTML = ''
@@ -185,7 +189,27 @@ test('speedy option from a custom cache is inherited for <Global/> styles', () =
185189
expect(safeQuerySelector('body style').textContent).toEqual('')
186190
})
187191

192+
it('does not autoLabel without babel or EMOTION_RUNTIME_AUTO_LABEL', () => {
193+
let SomeComp = props => {
194+
return (
195+
<div
196+
{...props}
197+
css={{
198+
color: 'hotpink'
199+
}}
200+
>
201+
something
202+
</div>
203+
)
204+
}
205+
const tree = renderer.create(<SomeComp />)
206+
207+
expect(tree.toJSON().props.className).toMatch(/css-[^-]+/)
208+
})
209+
188210
test('autoLabel without babel', () => {
211+
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true
212+
189213
let SomeComp = props => {
190214
return (
191215
<div
@@ -204,6 +228,8 @@ test('autoLabel without babel', () => {
204228
})
205229

206230
test('autoLabel without babel (sanitized)', () => {
231+
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true
232+
207233
let SomeComp$ = props => {
208234
return (
209235
<div {...props} css={{ color: 'hotpink' }}>

‎packages/react/__tests__/rehydration.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ beforeEach(() => {
5353
test("cache created in render doesn't cause a hydration mismatch", () => {
5454
safeQuerySelector('body').innerHTML = [
5555
'<div id="root">',
56-
'<style data-emotion="stl 1pdkrhd">.stl-1pdkrhd-App {color: hotpink;}</style>',
57-
'<div class="stl-1pdkrhd-App">Hello world!</div>',
56+
'<style data-emotion="stl 168r6j">.stl-1pdkrhd {color: hotpink;}</style>',
57+
'<div class="stl-168r6j">Hello world!</div>',
5858
'</div>'
5959
].join('')
6060

@@ -141,7 +141,7 @@ test('initializing another Emotion instance should not move already moved styles
141141
data-s=""
142142
>
143143
144-
.stl-1pdkrhd-App{color:hotpink;}
144+
.stl-168r6j{color:hotpink;}
145145
</style>
146146
</div>
147147
</head>
@@ -189,7 +189,7 @@ test('initializing another Emotion instance should not move already moved styles
189189
data-s=""
190190
>
191191
192-
.stl-1pdkrhd-App{color:hotpink;}
192+
.stl-168r6j{color:hotpink;}
193193
</style>
194194
</div>
195195
</head>

‎packages/react/src/emotion-element.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,16 @@ export const createEmotionProps = (
4040

4141
newProps[typePropName] = type
4242

43-
// For performance, only call getLabelFromStackTrace in development and when
44-
// the label hasn't already been computed
43+
// Runtime labeling is an opt-in feature because:
44+
// - It causes hydration warnings when using Safari and SSR
45+
// - It can degrade performance if there are a huge number of elements
46+
//
47+
// Even if the flag is set, we still don't compute the label if it has already
48+
// been determined by the Babel plugin.
4549
if (
4650
process.env.NODE_ENV !== 'production' &&
51+
typeof globalThis !== 'undefined' &&
52+
!!globalThis.EMOTION_RUNTIME_AUTO_LABEL &&
4753
!!props.css &&
4854
(typeof props.css !== 'object' ||
4955
typeof props.css.name !== 'string' ||

0 commit comments

Comments
 (0)
Please sign in to comment.