Relevant code:
package.json
...
"main": "dist/main.js",
"files": ["dist/", "package.json", "README.md"],
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/preset-env": "^7.2.0",
"@babel/preset-react": "^7.0.0",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "^23.6.0",
"babel-loader": "^8.0.4",
"babel-plugin-emotion": "^10.0.5",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.7.1",
"jest": "^23.6.0",
"jest-emotion": "^10.0.5",
"webpack": "^4.27.1",
"webpack-cli": "^3.1.2"
},
"dependencies": {
"@emotion/core": "^10.0.5",
"@emotion/styled": "^10.0.5",
"debounce": "^1.2.0",
"prop-types": "^15.6.2",
"react": "^16.6.3",
"react-dom": "^16.6.3"
},
"scripts": {
"build": "./node_modules/.bin/webpack --config=webpack.config.js",
"test": "./node_modules/.bin/jest"
}
...
webpack.config.js
const path = require("path")
module.exports = {
entry: "./src/index.js",
mode: "production",
output: {
path: path.resolve("./dist"),
filename: "[name].js",
libraryTarget: "commonjs2",
libraryExport: "default"
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: "babel-loader"
}
]
}
}
src/components/body/elements.js
import styled from "@emotion/styled"
import { css, jsx } from "@emotion/core"
export const Wrapper = styled.div`
background-color: red;
`
src/components/body/index.js
import { Wrapper } from "./elements"
class Body extends React.Component {
render() {
const {
children,
containerClassName,
layerIndex
} = this.props
return <Wrapper>{children}</Wrapper>
}
}
src/index.js
import React from "react"
import ReactDOM from "react-dom"
import PropTypes from "prop-types"
import { debounce } from "debounce"
import Body from "./components/body"
const isBrowser = typeof document !== "undefined" ? true : null
class SimpleModal extends React.Component {
static defaultProps = {
mountPointSelector: "body",
containerClassName: "SimpleModal",
layerPosition: "above",
defaultIndex: 100
}
_getLayerIndex = () => {
const { layerPosition, defaultIndex } = this.props
const allModals = this._getOtherModals()
const totalModals = allModals && allModals.length ? allModals.length : 0
var nextIndex = defaultIndex
if (layerPosition === "above") {
nextIndex += totalModals
}
if (layerPosition === "below") {
nextIndex -= totalModals
}
return nextIndex
}
_getOtherModals = (modal) => {
if (!isBrowser) {
return
}
const { containerClassName } = this.props
// Get all the elements on the page with the classname modal.
const otherModals = [].slice.call(
document.getElementsByClassName(containerClassName)
)
// If we got ourselves as an argument, rmeove it from the list of
// elements we return.
if (modal) {
return otherModals.filter((el) => {
return el !== modal
})
}
// Otherwise, return them all.
return otherModals
}
_getMountPoint = () => {
if (!isBrowser) {
return
}
const { mountPointSelector } = this.props
return document.querySelector(mountPointSelector)
}
render() {
const {
children,
isVisible,
containerClassName
} = this.props
// If the element is visible...
if (isVisible) {
if(!isBrowser){
return null
}
// Then, create the portal element in the DOM, under the BODY.
const mountPoint = this._getMountPoint()
const layerIndex = this._getLayerIndex()
const modal = (
<Body containerClassName={containerClassName} layerIndex={layerIndex}>
{children}
</Body>
)
return ReactDOM.createPortal(modal, mountPoint)
} else {
return null
}
}
}
export default SimpleModal
What you did: What happened:
I am attempting to rebuild a package that I maintain. However, the default component will function after being compiled by webpack. It will render correctly after a jest test though. Any attempt to use this package in another project results in a ReferenceError at runtime. I have pieced the \
Rendering \
Could u prepare a repository with the issue reproduced?
I will if the following isn't sufficient. I realize the module is named .browser, so I'm either doing something wrong (with SSR perhaps) or there's an error with the following module.
The issue seems to be with the function createCache located at https://github.com/emotion-js/emotion/blob/master/packages/cache/src/index.js
Uncompiled source
let createCache = (options?: Options): EmotionCache => {
if (options === undefined) options = {}
let key = options.key || 'css'
let stylisOptions
if (options.prefix !== undefined) {
stylisOptions = {
prefix: options.prefix
}
}
let stylis = new Stylis(stylisOptions)
if (process.env.NODE_ENV !== 'production') {
// $FlowFixMe
if (/[^a-z-]/.test(key)) {
throw new Error(
`Emotion key must only contain lower case alphabetical characters and - but "${key}" was passed`
)
}
}
let inserted = {}
// $FlowFixMe
let container: HTMLElement
if (isBrowser) {
container = options.container || document.head
const nodes = document.querySelectorAll(`style[data-emotion-${key}]`)
Array.prototype.forEach.call(nodes, (node: HTMLStyleElement) => {
const attrib = node.getAttribute(`data-emotion-${key}`)
// $FlowFixMe
attrib.split(' ').forEach(id => {
inserted[id] = true
})
if (node.parentNode !== container) {
container.appendChild(node)
}
})
}
let insert: (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
) => string | void
if (isBrowser) {
stylis.use(options.stylisPlugins)(ruleSheet)
insert = (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
): void => {
let name = serialized.name
Sheet.current = sheet
if (
process.env.NODE_ENV !== 'production' &&
serialized.map !== undefined
) {
let map = serialized.map
Sheet.current = {
insert: (rule: string) => {
sheet.insert(rule + map)
}
}
}
stylis(selector, serialized.styles)
if (shouldCache) {
cache.inserted[name] = true
}
}
} else {
stylis.use(removeLabel)
let serverStylisCache = rootServerStylisCache
if (options.stylisPlugins || options.prefix !== undefined) {
stylis.use(options.stylisPlugins)
// $FlowFixMe
serverStylisCache = getServerStylisCache(
options.stylisPlugins || rootServerStylisCache
)(options.prefix)
}
let getRules = (selector: string, serialized: SerializedStyles): string => {
let name = serialized.name
if (serverStylisCache[name] === undefined) {
serverStylisCache[name] = stylis(selector, serialized.styles)
}
return serverStylisCache[name]
}
insert = (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
): string | void => {
let name = serialized.name
let rules = getRules(selector, serialized)
if (cache.compat === undefined) {
// in regular mode, we don't set the styles on the inserted cache
// since we don't need to and that would be wasting memory
// we return them so that they are rendered in a style tag
if (shouldCache) {
cache.inserted[name] = true
}
if (
// using === development instead of !== production
// because if people do ssr in tests, the source maps showing up would be annoying
process.env.NODE_ENV === 'development' &&
serialized.map !== undefined
) {
return rules + serialized.map
}
return rules
} else {
// in compat mode, we put the styles on the inserted cache so
// that emotion-server can pull out the styles
// except when we don't want to cache it(just the Global component right now)
if (shouldCache) {
cache.inserted[name] = rules
} else {
return rules
}
}
}
}
if (process.env.NODE_ENV !== 'production') {
// https://esbench.com/bench/5bf7371a4cd7e6009ef61d0a
const commentStart = /\/\*/g
const commentEnd = /\*\//g
stylis.use((context, content) => {
switch (context) {
case -1: {
while (commentStart.test(content)) {
commentEnd.lastIndex = commentStart.lastIndex
if (commentEnd.test(content)) {
commentStart.lastIndex = commentEnd.lastIndex
continue
}
throw new Error(
'Your styles have an unterminated comment ("/*" without corresponding "*/").'
)
}
commentStart.lastIndex = 0
break
}
}
})
stylis.use((context, content, selectors) => {
switch (context) {
case 2: {
for (let i = 0, len = selectors.length; len > i; i++) {
// :last-child isn't included here since it's safe
// because a style element will never be the last element
let match = selectors[i].match(/:(first|nth|nth-last)-child/)
if (match !== null) {
console.error(
`The pseudo class "${
match[0]
}" is potentially unsafe when doing server-side rendering. Try changing it to "${
match[1]
}-of-type"`
)
}
}
break
}
}
})
}
const cache: EmotionCache = {
key,
sheet: new StyleSheet({
key,
container,
nonce: options.nonce,
speedy: options.speedy
}),
nonce: options.nonce,
inserted,
registered: {},
insert
}
return cache
}
Distributed source
_(./node_modules/@emotion/cache/dist/cache.browser.esm.js)_
var createCache = function createCache(options) {
if (options === undefined) options = {};
var key = options.key || 'css';
var stylisOptions;
if (options.prefix !== undefined) {
stylisOptions = {
prefix: options.prefix
};
}
var stylis = new Stylis(stylisOptions);
if (process.env.NODE_ENV !== 'production') {
// $FlowFixMe
if (/[^a-z-]/.test(key)) {
throw new Error("Emotion key must only contain lower case alphabetical characters and - but \"" + key + "\" was passed");
}
}
var inserted = {}; // $FlowFixMe
var container;
{
container = options.container || document.head;
var nodes = document.querySelectorAll("style[data-emotion-" + key + "]");
Array.prototype.forEach.call(nodes, function (node) {
var attrib = node.getAttribute("data-emotion-" + key); // $FlowFixMe
attrib.split(' ').forEach(function (id) {
inserted[id] = true;
});
if (node.parentNode !== container) {
container.appendChild(node);
}
});
}
var _insert;
{
stylis.use(options.stylisPlugins)(ruleSheet);
_insert = function insert(selector, serialized, sheet, shouldCache) {
var name = serialized.name;
Sheet.current = sheet;
if (process.env.NODE_ENV !== 'production' && serialized.map !== undefined) {
var map = serialized.map;
Sheet.current = {
insert: function insert(rule) {
sheet.insert(rule + map);
}
};
}
stylis(selector, serialized.styles);
if (shouldCache) {
cache.inserted[name] = true;
}
};
}
if (process.env.NODE_ENV !== 'production') {
// https://esbench.com/bench/5bf7371a4cd7e6009ef61d0a
var commentStart = /\/\*/g;
var commentEnd = /\*\//g;
stylis.use(function (context, content) {
switch (context) {
case -1:
{
while (commentStart.test(content)) {
commentEnd.lastIndex = commentStart.lastIndex;
if (commentEnd.test(content)) {
commentStart.lastIndex = commentEnd.lastIndex;
continue;
}
throw new Error('Your styles have an unterminated comment ("/*" without corresponding "*/").');
}
commentStart.lastIndex = 0;
break;
}
}
});
stylis.use(function (context, content, selectors) {
switch (context) {
case 2:
{
for (var i = 0, len = selectors.length; len > i; i++) {
// :last-child isn't included here since it's safe
// because a style element will never be the last element
var match = selectors[i].match(/:(first|nth|nth-last)-child/);
if (match !== null) {
console.error("The pseudo class \"" + match[0] + "\" is potentially unsafe when doing server-side rendering. Try changing it to \"" + match[1] + "-of-type\"");
}
}
break;
}
}
});
}
var cache = {
key: key,
sheet: new StyleSheet({
key: key,
container: container,
nonce: options.nonce,
speedy: options.speedy
}),
nonce: options.nonce,
inserted: inserted,
registered: {},
insert: _insert
};
return cache;
};
I'm receiving an error that and leaves the following stack trace:
Message: document is not defined
Stack trace: ReferenceError: document is not defined
at createCache (webpack:///./node_modules/@emotion/cache/dist/cache.browser.esm.js?:109:38)
at eval (webpack:///./node_modules/@emotion/core/dist/core.browser.esm.js?:32:149)
at Module../node_modules/@emotion/core/dist/core.browser.esm.js (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:110:1)
at __webpack_require__ (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:21:30)
at eval (webpack:///./node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js?:7:71)
at Module../node_modules/@emotion/styled-base/dist/styled-base.browser.esm.js (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:194:1)
at __webpack_require__ (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:21:30)
at eval (webpack:///./src/components/body/elements.js?:6:78)
at Module../src/components/body/elements.js (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:431:1)
at __webpack_require__ (/usr/home/main/mnt/da0/code/projects/library/alexseitsinger/sites/main/node_modules/@alexseitsinger/simple-modal/dist/main.js:21:30)
You have mentioned SSR - are those .browser files imported when trying to run this in node?
The project this is used in uses SSR. An example project layout can be found here. In the frontend/src folder is a render.js file. This is where the app is passed in and rendered for the server-side bundle. The client-side bundle is rendered from client.js. Both of these entry points import a module \@emotion/styled and @emotion/core to create styled components. Nothing unusual was done when upgrading to emotion 10 other changing the import statements used (following the documentation), but somehow these errors occur now.
Could u specify target: 'node' in your webpack config? https://webpack.js.org/concepts/targets/#usage
That did it. Thanks!
What about react-rails? I tried setting
environment.config.merge({
target: 'node'
});
in rails webpacker, but then I was getting require is not a function
i don't think changing a target is good option.
i'm trying to get emotion working with this SSR solution https://github.com/Limenius/ReactBundle, and as soon as I import emotion (without even using it) i get this ReferenceError: document is not defined error. like @dachinat said, changing the target in webpack is not an option because like him i then get require is not a function. I've tried using the older emotion SSR api's, doesn't seem to help... Any help or even clues are appreciated! thanks
I ran into the same problem when trying to use v10 with react-on-rails. v9 worked fine. Switching the webpack target unfortunately isn't an option for us. I haven't had a chance to dig into the source too much, but somewhere the test of whether we are in a browser or not changed between v9 and v10.
I ran into the same problem when trying to use v10 with react-on-rails. v9 worked fine. Switching the webpack target unfortunately isn't an option for us. I haven't had a chance to dig into the source too much, but somewhere the test of whether we are in a browser or not changed between v9 and v10.
Did you ever manage to solve this?
Most helpful comment
I ran into the same problem when trying to use v10 with react-on-rails. v9 worked fine. Switching the webpack target unfortunately isn't an option for us. I haven't had a chance to dig into the source too much, but somewhere the test of whether we are in a browser or not changed between v9 and v10.