Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@emotion/styled/base imports don't work with native ES modules #2413

Closed
matt-allan opened this issue Jun 28, 2021 · 4 comments · Fixed by #2819
Closed

@emotion/styled/base imports don't work with native ES modules #2413

matt-allan opened this issue Jun 28, 2021 · 4 comments · Fixed by #2819

Comments

@matt-allan
Copy link

matt-allan commented Jun 28, 2021

Current behavior:

I'm trying to use native ECMAScript modules in Node.JS in a project that uses @emotion/styled and @emotion/babel-preset-css-prop.

When @emotion/babel-preset-css-prop runs it injects an import from @emotion/styled/base which looks like this:

import _styled from "@emotion/styled/base";

Since this is a directory import, and directory imports only work with CommonJS, it fails with an ERR_UNSUPPORTED_DIR_IMPORT' error.

To reproduce:

You can reproduce the issue with this Github repository.

The main points are:

  1. Enable native ES modules
  2. Use @emotion/styled
  3. Transpile with @emotion/babel-preset-css-prop and make sure @babel/preset-env is configured with {modules: false}
  4. Try to run the resulting file with node: node lib/index.js

Expected behavior:

The import works.

Environment information:

  • react version: 17.0.2
  • @emotion/react version: 11.4.0
  • @emotion/styled version: 11.3.0
  • @emotion/babel-preset-css-prop version: 11.2.0
  • @emotion/babel-plugin version: 11.3.0

I think this is probably the same issue as #2121 since Next.JS also sets {modules: false} for @babel/preset-env and is presumably trying to run Emotion in Node for SSR.

@matt-allan
Copy link
Author

I'm able to workaround the issue with this Babel config:

// babel.config.cjs

// Get the actual path to the ESM module, i.e. `@emotion/styled/base/dist/emotion-styled-base.esm.js`.
const path = require('path');
const emotionPkg = require('@emotion/styled/base/package.json');
const styledBaseImport = [
  '@emotion/styled/base/' + path.normalize(emotionPkg.module),
  'default',
];

module.exports = {
  presets: [
    [
      '@emotion/babel-preset-css-prop',
      {
        autoLabel: 'always',
        labelFormat: '[local]',
        useBuiltIns: false,
        throwIfNamespace: true,
        importMap: {
          '@emotion/styled': {
            default: {
              canonicalImport: ['@emotion/styled', 'default'],
              styledBaseImport,
            },
          },
        },
      },
    ],
  ]
}

@tobua
Copy link

tobua commented Jul 5, 2021

Ran into the same issue with @emotion/babel-plugin and ES Modules. The importMap workaround did the trick:

{
  plugins: [
    [
      '@emotion',
      {
        importMap: {
          '@emotion/styled': {
            default: {
              canonicalImport: ['@emotion/styled', 'default'],
              styledBaseImport: [
                '@emotion/styled/base/dist/emotion-styled-base.esm.js',
                'default',
              ],
            },
          },
        },
      },
    ],
  ],
}

@Andarist
Copy link
Member

Actually, this wasn't fixed by #2819 but I would consider this issue here to be a duplicate of #2730 and this going to close this one here.

@ssbarbee
Copy link

ssbarbee commented Nov 1, 2023

Ended up writing custom babel transformer to resolve this issue as it appeared while running some tests in a repo.

// emotionStyledHotfix.js
module.exports = function ({ types: t }) {
  return {
    visitor: {
      CallExpression(path) {
        if (
          path.node.callee.name === 'require' &&
          path.node.arguments[0].type === 'StringLiteral' &&
          path.node.arguments[0].value === '@emotion/styled' &&
          !path.node.alreadyTransformed
        ) {
          const newRequire = t.callExpression(t.identifier('require'), [
            t.stringLiteral('@emotion/styled'),
          ]);
          newRequire.alreadyTransformed = true;
          const newExpression = t.memberExpression(newRequire, t.identifier('default'));
          path.replaceWith(newExpression);
        }
      },
    },
  };
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants