Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make runtime label extraction opt-in #2815

Merged
merged 9 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading