emotion icon indicating copy to clipboard operation
emotion copied to clipboard

Issues using emotion/styled with ES Modules

Open maddijoyce opened this issue 3 years ago • 9 comments
trafficstars

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 - https://github.com/facebook/jest/issues/12571

maddijoyce avatar Apr 19 '22 22:04 maddijoyce

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

ocavue avatar Jun 19 '22 13:06 ocavue

@Andarist This issue is not resolved after #2819 (@emotion/[email protected]) 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
[email protected] /private/tmp/emotion-esm-issue
└── @emotion/[email protected]
$ npm run test 
npm run test

> [email protected] 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

> [email protected] 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.

ocavue avatar Jul 31 '22 12:07 ocavue

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.

Andarist avatar Jul 31 '22 12:07 Andarist

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.

ocavue avatar Jul 31 '22 12:07 ocavue

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 https://github.com/emotion-js/emotion/pull/2819#issuecomment-1195164371

wight554 avatar Jul 31 '22 22:07 wight554

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.

Andarist avatar Aug 01 '22 07:08 Andarist

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

mithodin avatar Aug 04 '22 06:08 mithodin

The workaround is to do this:

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

Andarist avatar Aug 04 '22 16:08 Andarist

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.

buzz avatar Sep 13 '22 11:09 buzz

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.

joaquinbentancor avatar Oct 26 '22 14:10 joaquinbentancor

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;

vtereshyn avatar Nov 01 '22 11:11 vtereshyn

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

timofei-iatsenko avatar Dec 07 '22 09:12 timofei-iatsenko

@thekip that helped me. thank you ❤️

bobaaaaa avatar Feb 27 '23 08:02 bobaaaaa

@thekip it helped me a lot. Thanks :)

gauravbordoloi avatar Jul 20 '23 18:07 gauravbordoloi

this is still not fully working even with latest emotion packages. What is the update here?

ssbarbee avatar Nov 01 '23 15:11 ssbarbee