Skip to content

Commit 3d672ac

Browse files
srmaguraAndarist
andauthored
Fix getLabelFromStackTrace issue related to class components in Firefox (#2615)
* Fix getLabelFromStackTrace edge case in Firefox Closes #2614. * Update .changeset/spotty-clouds-listen.md Co-authored-by: Mateusz Burzyński <[email protected]>
1 parent b0be5c0 commit 3d672ac

File tree

3 files changed

+182
-7
lines changed

3 files changed

+182
-7
lines changed

.changeset/spotty-clouds-listen.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@emotion/react': patch
3+
---
4+
5+
Fix an edge case where runtime label extraction in class components led to invalid class names in Firefox. This only affected the development build of Emotion.

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

+168
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,171 @@ beginWork$1@http://localhost:3000/static/js/vendors~main.chunk.js:27384:27`
551551
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
552552
})
553553
})
554+
555+
/**
556+
* E.g.
557+
*
558+
* ```
559+
* import React from 'react'
560+
*
561+
* var __extends =
562+
* (this && this.__extends) ||
563+
* (function () {
564+
* var extendStatics = function (d, b) {
565+
* extendStatics =
566+
* Object.setPrototypeOf ||
567+
* ({ __proto__: [] } instanceof Array &&
568+
* function (d, b) {
569+
* d.__proto__ = b
570+
* }) ||
571+
* function (d, b) {
572+
* for (var p in b)
573+
* if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]
574+
* }
575+
* return extendStatics(d, b)
576+
* }
577+
* return function (d, b) {
578+
* extendStatics(d, b)
579+
* function __() {
580+
* this.constructor = d
581+
* }
582+
* d.prototype =
583+
* b === null ? Object.create(b) : ((__.prototype = b.prototype), new __())
584+
* }
585+
* })()
586+
*
587+
* export const MyComponent$9 = (function (_super) {
588+
* __extends(MyComponent$9, _super)
589+
* function MyComponent$9() {
590+
* var _this = (_super !== null && _super.apply(this, arguments)) || this
591+
* return _this
592+
* }
593+
* MyComponent$9.prototype.render = function () {
594+
* // Defining a variable to prevent Proper Tail Call
595+
* const el = <div css={{ color: 'red' }} />
596+
* return el
597+
* }
598+
* return MyComponent$9
599+
* })(React.PureComponent)
600+
* ```
601+
*/
602+
describe('class component transpiled to ES 5', () => {
603+
test('Chrome', () => {
604+
const stackTrace = `Error
605+
at createEmotionProps (emotion-element-10a9af6f.browser.esm.js?d0a2:168)
606+
at jsxDEV (emotion-react-jsx-dev-runtime.browser.esm.js?cf67:18)
607+
at MyComponent$9.render (MyComponent9.js?2fbf:37)
608+
at finishClassComponent (react-dom.development.js?3c4a:17160)
609+
at updateClassComponent (react-dom.development.js?3c4a:17110)
610+
at beginWork (react-dom.development.js?3c4a:18620)
611+
at beginWork$1 (react-dom.development.js?3c4a:23179)
612+
at performUnitOfWork (react-dom.development.js?3c4a:22154)
613+
at workLoopSync (react-dom.development.js?3c4a:22130)
614+
at performSyncWorkOnRoot (react-dom.development.js?3c4a:21756)`
615+
616+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
617+
})
618+
619+
test('Firefox', () => {
620+
const stackTrace = `createEmotionProps@webpack-internal:///../../packages/react/dist/emotion-element-10a9af6f.browser.esm.js:189:42
621+
jsxDEV@webpack-internal:///../../packages/react/jsx-dev-runtime/dist/emotion-react-jsx-dev-runtime.browser.esm.js:35:230
622+
MyComponent$9</MyComponent$9.prototype.render@webpack-internal:///./pages/MyComponent9.js:62:82
623+
finishClassComponent@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:17163:18
624+
updateClassComponent@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:17110:44
625+
beginWork@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:18620:16
626+
beginWork$1@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:23179:14
627+
performUnitOfWork@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22154:12
628+
workLoopSync@webpack-internal:///../../node_modules/react-dom/cjs/react-dom.development.js:22130:22`
629+
630+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
631+
})
632+
633+
test('Safari', () => {
634+
// No idea why the function name is just blank in this stack trace
635+
const stackTrace = `createEmotionProps@http://localhost:3000/static/js/main.chunk.js:973:49
636+
jsxDEV@http://localhost:3000/static/js/main.chunk.js:1609:247
637+
@http://localhost:3000/static/js/main.chunk.js:2926:93
638+
finishClassComponent@http://localhost:3000/static/js/vendors~main.chunk.js:21433:41
639+
updateClassComponent@http://localhost:3000/static/js/vendors~main.chunk.js:21386:48
640+
beginWork$1@http://localhost:3000/static/js/vendors~main.chunk.js:27364:27
641+
performUnitOfWork@http://localhost:3000/static/js/vendors~main.chunk.js:26352:27
642+
workLoopSync@http://localhost:3000/static/js/vendors~main.chunk.js:26328:43
643+
performSyncWorkOnRoot@http://localhost:3000/static/js/vendors~main.chunk.js:25946:25`
644+
645+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
646+
})
647+
648+
test('SSR', () => {
649+
const stackTrace = `Error
650+
at Object.createEmotionProps (webpack-internal:///../../packages/react/dist/emotion-element-491a37fd.cjs.dev.js:201:42)
651+
at jsxDEV (webpack-internal:///../../packages/react/jsx-dev-runtime/dist/emotion-react-jsx-dev-runtime.cjs.dev.js:22:75)
652+
at MyComponent$9.render (webpack-internal:///./pages/MyComponent9.js:60:82)
653+
at processChild (/Users/sammagura/Documents/emotion/node_modules/react-dom/cjs/react-dom-server.node.development.js:3134:18)
654+
at resolve (/Users/sammagura/Documents/emotion/node_modules/react-dom/cjs/react-dom-server.node.development.js:2960:5)
655+
at ReactDOMServerRenderer.render (/Users/sammagura/Documents/emotion/node_modules/react-dom/cjs/react-dom-server.node.development.js:3435:22)
656+
at ReactDOMServerRenderer.read (/Users/sammagura/Documents/emotion/node_modules/react-dom/cjs/react-dom-server.node.development.js:3373:29)
657+
at Object.renderToString (/Users/sammagura/Documents/emotion/node_modules/react-dom/cjs/react-dom-server.node.development.js:3988:27)
658+
at Object.renderPage (/Users/sammagura/Documents/emotion/node_modules/next/dist/server/render.js:621:45)
659+
at Object.defaultGetInitialProps (/Users/sammagura/Documents/emotion/node_modules/next/dist/server/render.js:301:51)`
660+
661+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
662+
})
663+
})
664+
665+
/**
666+
* https://github.com/emotion-js/emotion/issues/2614
667+
*
668+
* Not sure how to reproduce this other than this repro project:
669+
* https://github.com/srmagura/emotion-issue-2614
670+
*/
671+
describe('issue #2614 - class component transpiled to ES 5', () => {
672+
test('Chrome', () => {
673+
const stackTrace = `Error
674+
at createEmotionProps (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-699e6908.browser.esm.js:183)
675+
at Module.jsx (webpack-internal:///./node_modules/@emotion/react/dist/emotion-react.browser.esm.js:127)
676+
at Loader.render (webpack-internal:///./node_modules/react-spinners/ScaleLoader.js:56)
677+
at finishClassComponent (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:17485)
678+
at updateClassComponent (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:17435)
679+
at beginWork (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:19073)
680+
at beginWork$1 (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:23935)
681+
at performUnitOfWork (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22771)
682+
at workLoopSync (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22702)
683+
at renderRootSync (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22665)`
684+
685+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
686+
})
687+
688+
test('Firefox', () => {
689+
const stackTrace = `createEmotionProps@webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-699e6908.browser.esm.js:183:40
690+
jsx@webpack-internal:///./node_modules/@emotion/react/dist/emotion-react.browser.esm.js:127:105
691+
Loader.prototype.render@webpack-internal:///./node_modules/react-spinners/ScaleLoader.js:56:35
692+
finishClassComponent@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:17485:31
693+
updateClassComponent@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:17435:44
694+
beginWork@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:19073:16
695+
beginWork$1@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:23935:14
696+
performUnitOfWork@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22771:12
697+
workLoopSync@webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:22702:22`
698+
699+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
700+
})
701+
702+
test('Safari', () => {
703+
// No idea why the function name is blank and there are no file locations
704+
const stackTrace = `createEmotionProps@
705+
jsx@
706+
@
707+
finishClassComponent@
708+
updateClassComponent@
709+
beginWork$1@
710+
performUnitOfWork@
711+
workLoopSync@
712+
renderRootSync@
713+
performSyncWorkOnRoot@
714+
scheduleUpdateOnFiber@
715+
updateContainer@`
716+
717+
expect(getLabelFromStackTrace(stackTrace)).toBeUndefined()
718+
})
719+
720+
// No SSR stack trace since this comes from a Gatsby project
721+
})

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

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
// @flow
22

3+
const getLastPart = (functionName: string): string => {
4+
// The match may be something like 'Object.createEmotionProps' or
5+
// 'Loader.prototype.render'
6+
const parts = functionName.split('.')
7+
return parts[parts.length - 1]
8+
}
9+
310
const getFunctionNameFromStackTraceLine = (line: string): ?string => {
411
// V8
512
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-
}
13+
if (match) return getLastPart(match[1])
1214

1315
// Safari / Firefox
1416
match = /^([A-Za-z0-9$.]+)@/.exec(line)
15-
if (match) return match[1]
17+
if (match) return getLastPart(match[1])
1618

1719
return undefined
1820
}

0 commit comments

Comments
 (0)