Skip to content

Commit

Permalink
Improved tree-shakeability of the @emotion/react package (#2101)
Browse files Browse the repository at this point in the history
Co-authored-by: Mitchell Hamilton <mitchell@hamil.town>
  • Loading branch information
Andarist and emmatown committed Nov 16, 2020
1 parent 6b4b2a0 commit 0e465d1
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 78 deletions.
5 changes: 5 additions & 0 deletions .changeset/hungry-suns-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/react': patch
---

Improved tree-shakeability of the package. The main benefit is that bundlers should be able now to drop [`hoist-non-react-statics`](https://github.com/mridgway/hoist-non-react-statics) if you don't actually use our `withTheme` export.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"main": "dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.cjs.js",
"module": "dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.esm.js",
"umd:main": "dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.umd.min.js",
"browser": {
"./dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.cjs.js": "./dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.browser.cjs.js",
"./dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.esm.js": "./dist/emotion-react-isolated-hoist-non-react-statics-do-not-use-this-in-your-code.browser.esm.js"
},
"preconstruct": {
"umdName": "emotionHoistNonReactStatics"
}
}
5 changes: 4 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
"dist",
"jsx-runtime",
"jsx-dev-runtime",
"isolated-hoist-non-react-statics-do-not-use-this-in-your-code",
"types/*.d.ts",
"macro.js",
"macro.d.ts",
"macro.js.flow"
],
"sideEffects": false,
"author": "mitchellhamilton <mitchell@mitchellhamilton.me>",
"license": "MIT",
"scripts": {
Expand Down Expand Up @@ -65,7 +67,8 @@
"entrypoints": [
"./index.js",
"./jsx-runtime.js",
"./jsx-dev-runtime.js"
"./jsx-dev-runtime.js",
"./isolated-hoist-non-react-statics-do-not-use-this-in-your-code.js"
],
"umdName": "emotionReact"
}
Expand Down
153 changes: 79 additions & 74 deletions packages/react/src/emotion-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,90 +59,95 @@ export const createEmotionProps = (type: React.ElementType, props: Object) => {
return newProps
}

let Emotion = withEmotionCache<any, any>((props, cache, ref) => {
let cssProp = props.css

// so that using `css` from `emotion` and passing the result to the css prop works
// not passing the registered cache to serializeStyles because it would
// make certain babel optimisations not possible
if (typeof cssProp === 'string' && cache.registered[cssProp] !== undefined) {
cssProp = cache.registered[cssProp]
}

let type = props[typePropName]
let registeredStyles = [cssProp]
let className = ''
let Emotion = /* #__PURE__ */ withEmotionCache<any, any>(
(props, cache, ref) => {
let cssProp = props.css

if (typeof props.className === 'string') {
className = getRegisteredStyles(
cache.registered,
registeredStyles,
props.className
)
} else if (props.className != null) {
className = `${props.className} `
}
// so that using `css` from `emotion` and passing the result to the css prop works
// not passing the registered cache to serializeStyles because it would
// make certain babel optimisations not possible
if (
typeof cssProp === 'string' &&
cache.registered[cssProp] !== undefined
) {
cssProp = cache.registered[cssProp]
}

let serialized = serializeStyles(
registeredStyles,
undefined,
typeof cssProp === 'function' || Array.isArray(cssProp)
? React.useContext(ThemeContext)
: undefined
)
let type = props[typePropName]
let registeredStyles = [cssProp]
let className = ''

if (
process.env.NODE_ENV !== 'production' &&
serialized.name.indexOf('-') === -1
) {
let labelFromStack = props[labelPropName]
if (labelFromStack) {
serialized = serializeStyles([
serialized,
'label:' + labelFromStack + ';'
])
if (typeof props.className === 'string') {
className = getRegisteredStyles(
cache.registered,
registeredStyles,
props.className
)
} else if (props.className != null) {
className = `${props.className} `
}
}
const rules = insertStyles(cache, serialized, typeof type === 'string')
className += `${cache.key}-${serialized.name}`

const newProps = {}
for (let key in props) {
let serialized = serializeStyles(
registeredStyles,
undefined,
typeof cssProp === 'function' || Array.isArray(cssProp)
? React.useContext(ThemeContext)
: undefined
)

if (
hasOwnProperty.call(props, key) &&
key !== 'css' &&
key !== typePropName &&
(process.env.NODE_ENV === 'production' || key !== labelPropName)
process.env.NODE_ENV !== 'production' &&
serialized.name.indexOf('-') === -1
) {
newProps[key] = props[key]
let labelFromStack = props[labelPropName]
if (labelFromStack) {
serialized = serializeStyles([
serialized,
'label:' + labelFromStack + ';'
])
}
}
}
newProps.ref = ref
newProps.className = className

const ele = React.createElement(type, newProps)
if (!isBrowser && rules !== undefined) {
let serializedNames = serialized.name
let next = serialized.next
while (next !== undefined) {
serializedNames += ' ' + next.name
next = next.next
const rules = insertStyles(cache, serialized, typeof type === 'string')
className += `${cache.key}-${serialized.name}`

const newProps = {}
for (let key in props) {
if (
hasOwnProperty.call(props, key) &&
key !== 'css' &&
key !== typePropName &&
(process.env.NODE_ENV === 'production' || key !== labelPropName)
) {
newProps[key] = props[key]
}
}
return (
<>
<style
{...{
[`data-emotion`]: `${cache.key} ${serializedNames}`,
dangerouslySetInnerHTML: { __html: rules },
nonce: cache.sheet.nonce
}}
/>
{ele}
</>
)
newProps.ref = ref
newProps.className = className

const ele = React.createElement(type, newProps)
if (!isBrowser && rules !== undefined) {
let serializedNames = serialized.name
let next = serialized.next
while (next !== undefined) {
serializedNames += ' ' + next.name
next = next.next
}
return (
<>
<style
{...{
[`data-emotion`]: `${cache.key} ${serializedNames}`,
dangerouslySetInnerHTML: { __html: rules },
nonce: cache.sheet.nonce
}}
/>
{ele}
</>
)
}
return ele
}
return ele
})
)

if (process.env.NODE_ENV !== 'production') {
Emotion.displayName = 'EmotionCssPropInternal'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// this file isolates this package that is not tree-shakeable
// and allows it to be dropped - if it stays unused
// it happens thanks to sideEffects: false in our package.json
import hoistNonReactStatics from 'hoist-non-react-statics'

// have to wrap it in a proxy function because Rollup is too damn smart
// and if this module doesn't actually contain any logic of its own
// then Rollup just use 'hoist-non-react-statics' directly in other chunks
export default (targetComponent, sourceComponent) =>
hoistNonReactStatics(targetComponent, sourceComponent)
6 changes: 3 additions & 3 deletions packages/react/src/theming.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// @flow
import * as React from 'react'
import weakMemoize from '@emotion/weak-memoize'
import hoistNonReactStatics from 'hoist-non-react-statics'
import hoistNonReactStatics from './isolated-hoist-non-react-statics-do-not-use-this-in-your-code'

export const ThemeContext = React.createContext<Object>({})
export const ThemeContext = /* #__PURE__ */ React.createContext<Object>({})

export const useTheme = () => React.useContext(ThemeContext)

Expand Down Expand Up @@ -34,7 +34,7 @@ const getTheme = (outerTheme: Object, theme: Object | (Object => Object)) => {
return { ...outerTheme, ...theme }
}

let createCacheWithTheme = weakMemoize(outerTheme => {
let createCacheWithTheme = /* #__PURE__ */ weakMemoize(outerTheme => {
return weakMemoize(theme => {
return getTheme(outerTheme, theme)
})
Expand Down

0 comments on commit 0e465d1

Please sign in to comment.