emotion-js/emotion

Issues using emotion/styled with ES Modules

maddijoyce opened this issue · 14 comments

Current behavior:

When using import styled from "@emotion/styled", in a package setup to use esm, styled has a single key default

To reproduce:

  1. Clone https://github.com/maddijoyce/emotion-esm-issue
  2. Run npm test

Expected behavior:

As in the above example, using import styled from "@emotion/styled", styled should have styled.h1, styled.h2, etc

Environment information:

  • node version: 16.13.2
  • @emotion/styled version: 11.8.1

Some additional background:

Issue first discovered here - jestjs/jest#12571

The exports filed in emotion's package.json is invalid for Node.js. Explanation: https://github.com/sheremet-va/dual-packaging

@Andarist This issue is not resolved after #2819 (@emotion/styled@11.10.0) and it should not be closed.

$ node --version
v18.6.0
$ npm --version
8.13.2
$ git clone https://github.com/maddijoyce/emotion-esm-issue
$ cd emotion-esm-issue
$ rm package-lock.json  # remove lock file so that npm can install the latest version
$ npm install 
$ npm list
emotion-esm-issue@1.0.0 /private/tmp/emotion-esm-issue
└── @emotion/styled@11.10.0
$ npm run test 
npm run test

> emotion-esm-issue@1.0.0 test
> node index.js

[ 'default' ]
This should not be the case, this should be h1, h2, etc
So that we can do `styled.h1...`
Right now we would need to do `styled.default.h1...`

By adding some console.log under node_modules/@emotion/styled/dist/emotion-styled.esm.js and node_modules/@emotion/styled/dist/emotion-styled.cjs.js, you can see clearly that Node.js is still importing the CJS version of emotion. CommonJS doesn't include real default export, that's why we are seeing this error.

$ echo 'console.log("node_modules/@emotion/styled/dist/emotion-styled.cjs.js")' >> node_modules/@emotion/styled/dist/emotion-styled.cjs.js
$ echo 'console.log("node_modules/@emotion/styled/dist/emotion-styled.esm.js")' >> node_modules/@emotion/styled/dist/emotion-styled.esm.js
$ npm run test

> emotion-esm-issue@1.0.0 test
> node index.js

node_modules/@emotion/styled/dist/emotion-styled.cjs.js
[ 'default' ]
This should not be the case, this should be h1, h2, etc
So that we can do `styled.h1...`
Right now we would need to do `styled.default.h1...`

As someone already mentioned, we need to use .mjs for esm build.

Yes, you are right - my bad. This issue here won't be fixed soon though. The only viable strategy for us that would "fix" this would be switching to named exports.

Switching (adding) named exports should be a practicable solution for now.

Eventually, I think it's better to fix this on the preconstruct side, by bundling .mjs files for ESM build. Maybe this would require a new major version of preconstruct, but it will resolve this issue for not only emotion but also other projects using preconstruct.

Switching (adding) named exports should be a practicable solution for now.

Eventually, I think it's better to fix this on the preconstruct side, by bundling .mjs files for ESM build. Maybe this would require a new major version of preconstruct, but it will resolve this issue for not only emotion but also other projects using preconstruct.

You're right, see #2819 (comment)

Switching (adding) named exports should be a practicable solution for now.

We'd be open to accepting a PR that would add a named export "alias" for all our default exports.

Eventually, I think it's better to fix this on the preconstruct side, by bundling .mjs files for ESM build. Maybe this would require a new major version of preconstruct, but it will resolve this issue for not only emotion but also other projects using preconstruct.

The problem is not on the preconstruct side here but instead in each project using it. Changing to .mjs or adding an import condition is not something that can be easily done within a minor version of a package. Even if releasing a major version would be an option - you still have to consider the compatibility with the rest of the ecosystem.

I'm having the same issue with emotion/cache. Is there a workaround in the meantime?

The workaround is to do this:

import _createCache from '@emotion/cache'
const createCache = _createCache.default
buzz commented

I'm having the same issue with emotion/cache. Is there a workaround in the meantime?

Another solution is to patch the package locally. See example for @reduxjs/toolkit.

Btw. pnpm has built-in support for patching packages.

Hi, how could I fix the problem with .cjs file?

SyntaxError: The requested module '@emotion/react/jsx-runtime' is expected to be of type CommonJS, which does not support named exports. CommonJS modules can be imported by importing the default export. For example: import pkg from '@emotion/react/jsx-runtime'; const { jsx: _jsx } = pkg;

I'm having this issue.

For those who are not using babel and don't want to wait for the full support of ESM there is a workaround that I tested in my codebase:

I use Typescript, so this is important for me that everything works properly and there are no type errors

import _styled from '@emotion/styled';
const styled = _styled.default || _styled;

For those who came here trying to make work emotion-js + vite (which using ESM by default) here is solution:

import react from '@vitejs/plugin-react';
import { UserConfig } from 'vite';
import { cjsInterop } from 'vite-plugin-cjs-interop';


const config: UserConfig = {
  plugins: [
    cjsInterop({
      dependencies: [
        '@emotion/styled/base',
        '@emotion/*',
      ],
    }),
    react({
        jsxRuntime: 'automatic',
        jsxImportSource: '@emotion/react',
        babel: {
          plugins: [
            'babel-plugin-graphql-tag',
            'babel-plugin-macros',
            ['@emotion/babel-plugin', {}],
          ],
        },
      },
    ),
  ],
};

export default config;

The cjsInterop plugin is a crucial part, this plugin actually do what @vtereshyn wrote in the comment above

@thekip that helped me. thank you ❤️

@thekip it helped me a lot. Thanks :)