Skip to content

Commit

Permalink
Make runtime label extraction opt-in (#2815)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
srmagura and Andarist authored Jun 13, 2024
1 parent fc74bee commit 65a1eea
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-cooks-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/react': minor
---

Automatic labeling at runtime is now an opt-in feature. Define `globalThis.EMOTION_RUNTIME_AUTO_LABEL = true` before Emotion gets initialized to enable it.
15 changes: 14 additions & 1 deletion docs/labels.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: 'Labels'
---

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.
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.

```jsx
// @live
Expand All @@ -29,3 +29,16 @@ render(
</div>
)
```

## Automatic Labeling at Runtime

If you are not using `@emotion/babel-plugin`, you can still get automatic labels in development by setting the following global flag:

```js
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true
```

This feature is opt-in because:

- 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.
- This feature may degrade performance if the number of elements using the `css` prop is very large.
26 changes: 26 additions & 0 deletions packages/react/__tests__/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import createCache from '@emotion/cache'
console.error = jest.fn()
console.warn = jest.fn()

beforeEach(() => {
delete globalThis.EMOTION_RUNTIME_AUTO_LABEL
})

afterEach(() => {
jest.clearAllMocks()
safeQuerySelector('body').innerHTML = ''
Expand Down Expand Up @@ -185,7 +189,27 @@ test('speedy option from a custom cache is inherited for <Global/> styles', () =
expect(safeQuerySelector('body style').textContent).toEqual('')
})

it('does not autoLabel without babel or EMOTION_RUNTIME_AUTO_LABEL', () => {
let SomeComp = props => {
return (
<div
{...props}
css={{
color: 'hotpink'
}}
>
something
</div>
)
}
const tree = renderer.create(<SomeComp />)

expect(tree.toJSON().props.className).toMatch(/css-[^-]+/)
})

test('autoLabel without babel', () => {
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true

let SomeComp = props => {
return (
<div
Expand All @@ -204,6 +228,8 @@ test('autoLabel without babel', () => {
})

test('autoLabel without babel (sanitized)', () => {
globalThis.EMOTION_RUNTIME_AUTO_LABEL = true

let SomeComp$ = props => {
return (
<div {...props} css={{ color: 'hotpink' }}>
Expand Down
8 changes: 4 additions & 4 deletions packages/react/__tests__/rehydration.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ beforeEach(() => {
test("cache created in render doesn't cause a hydration mismatch", () => {
safeQuerySelector('body').innerHTML = [
'<div id="root">',
'<style data-emotion="stl 1pdkrhd">.stl-1pdkrhd-App {color: hotpink;}</style>',
'<div class="stl-1pdkrhd-App">Hello world!</div>',
'<style data-emotion="stl 168r6j">.stl-1pdkrhd {color: hotpink;}</style>',
'<div class="stl-168r6j">Hello world!</div>',
'</div>'
].join('')

Expand Down Expand Up @@ -141,7 +141,7 @@ test('initializing another Emotion instance should not move already moved styles
data-s=""
>
.stl-1pdkrhd-App{color:hotpink;}
.stl-168r6j{color:hotpink;}
</style>
</div>
</head>
Expand Down Expand Up @@ -189,7 +189,7 @@ test('initializing another Emotion instance should not move already moved styles
data-s=""
>
.stl-1pdkrhd-App{color:hotpink;}
.stl-168r6j{color:hotpink;}
</style>
</div>
</head>
Expand Down
10 changes: 8 additions & 2 deletions packages/react/src/emotion-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,16 @@ export const createEmotionProps = (

newProps[typePropName] = type

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

0 comments on commit 65a1eea

Please sign in to comment.