Skip to content

Commit da3f244

Browse files
committed
Merge branch 'main' into ts-migration
2 parents f3ef8cb + 57be9e8 commit da3f244

35 files changed

+4978
-11493
lines changed

.changeset/sweet-hotels-explain.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@emotion/react': patch
3+
---
4+
5+
Changed the implementation of the runtime label extraction in elements using the css prop (that only happens in development) to one that should yield more consistent results across browsers. This fixes some minor issues with React reporting hydration mismatches that wouldn't happen in production.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@
253253
"react-native": "^0.63.2",
254254
"react-primitives": "^0.8.1",
255255
"react-router-dom": "^4.2.2",
256-
"react-scripts": "1.1.5",
257256
"react-test-renderer": "16.8.6",
258257
"react18": "npm:react@alpha",
259258
"react18-dom": "npm:react-dom@alpha",

packages/react/__tests__/get-label-from-stack-trace.js

Lines changed: 553 additions & 0 deletions
Large diffs are not rendered by default.

packages/react/src/emotion-element.js

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import { ThemeContext } from './theming'
44
import { getRegisteredStyles, insertStyles } from '@emotion/utils'
55
import { hasOwnProperty, isBrowser } from './utils'
66
import { serializeStyles } from '@emotion/serialize'
7-
8-
// those identifiers come from error stacks, so they have to be valid JS identifiers
9-
// thus we only need to replace what is a valid character for JS, but not for CSS
10-
const sanitizeIdentifier = (identifier /*: string */) =>
11-
identifier.replace(/\$/g, '-')
7+
import { getLabelFromStackTrace } from './get-label-from-stack-trace'
128

139
let typePropName = '__EMOTION_TYPE_PLEASE_DO_NOT_USE__'
1410

@@ -39,21 +35,17 @@ export const createEmotionProps = (
3935

4036
newProps[typePropName] = type
4137

42-
if (process.env.NODE_ENV !== 'production') {
43-
const error = new Error()
44-
if (error.stack) {
45-
// chrome
46-
let match = error.stack.match(
47-
/at (?:Object\.|Module\.|)(?:jsx|createEmotionProps).*\n\s+at (?:Object\.|)([A-Z][A-Za-z0-9$]+) /
48-
)
49-
if (!match) {
50-
// safari and firefox
51-
match = error.stack.match(/.*\n([A-Z][A-Za-z0-9$]+)@/)
52-
}
53-
if (match) {
54-
newProps[labelPropName] = sanitizeIdentifier(match[1])
55-
}
56-
}
38+
// For performance, only call getLabelFromStackTrace in development and when
39+
// the label hasn't already been computed
40+
if (
41+
process.env.NODE_ENV !== 'production' &&
42+
!!props.css &&
43+
(typeof props.css !== 'object' ||
44+
typeof props.css.name !== 'string' ||
45+
props.css.name.indexOf('-') === -1)
46+
) {
47+
const label = getLabelFromStackTrace(new Error().stack)
48+
if (label) newProps[labelPropName] = label
5749
}
5850

5951
return newProps
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// @flow
2+
3+
const getFunctionNameFromStackTraceLine = line => {
4+
// V8
5+
let match = /^\s+at\s+([A-Za-z0-9$.]+)\s/.exec(line)
6+
7+
if (match) {
8+
// The match may be something like 'Object.createEmotionProps'
9+
const parts = match[1].split('.')
10+
return parts[parts.length - 1]
11+
}
12+
13+
// Safari / Firefox
14+
match = /^([A-Za-z0-9$.]+)@/.exec(line)
15+
if (match) return match[1]
16+
17+
return undefined
18+
}
19+
20+
const internalReactFunctionNames = /* #__PURE__ */ new Set([
21+
'renderWithHooks',
22+
'processChild',
23+
'finishClassComponent',
24+
'renderToString'
25+
])
26+
27+
// These identifiers come from error stacks, so they have to be valid JS
28+
// identifiers, thus we only need to replace what is a valid character for JS,
29+
// but not for CSS.
30+
const sanitizeIdentifier = identifier => identifier.replace(/\$/g, '-')
31+
32+
export const getLabelFromStackTrace = stackTrace => {
33+
if (!stackTrace) return undefined
34+
35+
const lines = stackTrace.split('\n')
36+
37+
for (let i = 0; i < lines.length; i++) {
38+
const functionName = getFunctionNameFromStackTraceLine(lines[i])
39+
40+
// The first line of V8 stack traces is just "Error"
41+
if (!functionName) continue
42+
43+
// If we reach one of these, we have gone too far and should quit
44+
if (internalReactFunctionNames.has(functionName)) break
45+
46+
// The component name is the first function in the stack that starts with an
47+
// uppercase letter
48+
if (/^[A-Z]/.test(functionName)) return sanitizeIdentifier(functionName)
49+
}
50+
51+
return undefined
52+
}

playgrounds/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Emotion Development Playgrounds
22

3-
These are intended to be places to experiment with behaviour that is hard to do in tests or a CodeSandbox. These are not intended to be perfect examples of how you would write emotion code with these other libraries as they will generally be focussed on edge cases.
3+
These are intended to be places to experiment with behaviour that is hard to do in tests or a CodeSandbox. These are not intended to be perfect examples of how you would write emotion code with these other libraries as they will generally be focused on edge cases.
44

55
## Getting Started
66

playgrounds/cra/.env

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Necessary because we might have a different version of babel-jest, .etc than
2+
# what react-scripts wants
3+
SKIP_PREFLIGHT_CHECK=true
4+
5+
# Uncomment if you want to test stuff with the old JSX transform.
6+
# You also need to change the `@jsxImportSource @emotion/react` line
7+
# to `@jsx jsx` and import `jsx` from @emotion/react.
8+
# DISABLE_NEW_JSX_TRANSFORM=true

playgrounds/cra/.gitignore

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
14+
# misc
15+
.DS_Store
16+
.env.local
17+
.env.development.local
18+
.env.test.local
19+
.env.production.local
20+
21+
npm-debug.log*
22+
yarn-debug.log*
23+
yarn-error.log*

0 commit comments

Comments
 (0)