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

Nextjs fails to detect ESM modules correctly when using exports in package.json #39375

Open
1 task done
Pagebakers opened this issue Aug 6, 2022 · 26 comments
Open
1 task done
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. Webpack Related to Webpack with Next.js.

Comments

@Pagebakers
Copy link

Pagebakers commented Aug 6, 2022

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 21.2.0: Sun Nov 28 20:28:41 PST 2021; root:xnu-8019.61.5~1/RELEASE_ARM64_T6000
Binaries:
  Node: 16.16.0
  npm: 8.11.0
  Yarn: 1.22.15
  pnpm: 7.5.2
Relevant packages:
  next: 12.2.4
  eslint-config-next: 12.2.4
  react: 18.2.0
  react-dom: 18.2.0

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

Next.js fails to resolve sub dependencies correctly and tries to load CJS from ESM modules, causing the build to fail.

SyntaxError: Named export 'theme' not found. The requested module '@chakra-ui/react' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

@chakra-ui/react a dependency of @saas-ui/react

Saas UI uses exports definition in package.json to define package entries and exports the ESM module as default.

{
  "exports": {
    ".": {
      "require": "./dist/index.js",
      "default": "./dist/index.modern.mjs"
    },
    "./src": {
      "default": "./src/index.ts"
    }
  },
  "main": "./dist/index.js",
  "module": "./dist/index.modern.mjs",
  "types": "./dist/index.d.ts"
}

Removing exports or changing default to import fixes the issue.

However I think Next.js should handle this correctly.

Expected Behavior

Next.js resolves all packages correctly as ESM.

Link to reproduction

https://github.com/msnegurski/test-saas-ui

To Reproduce

  1. Clone the repo.
  2. npm i
  3. npm run dev

Make sure @saas-ui/react@1.2.x is installed (not 1.3)

NEXT-1381

@Pagebakers
Copy link
Author

After some more testing the .mjs extension seems to cause issues, renaming the esm files to .js resolves the issues.

@balazsorban44
Copy link
Member

balazsorban44 commented Aug 8, 2022

Thanks, I was first going to suggest upgrading the packages to 1.3 as it seems to have been fixed, but I saw that you are the maintainer, so you are likely here to get an answer why saas-js/saas-ui@f6ec7dc was needed.

When ESM resolution is enabled, the work is handed off to the Node.js resolver:

The general rule is that conditions should be from most specific to least specific in object order.

Given that import and require are mutually exclusive on the same specificity level and default is just a fallback (the least specific), I assume require gets picked instead of default as it's higher specificity. I think it's correct to assume that require and import should always be used together.

All examples I have seen define both import and require.

Webpack's guidelines also suggest avoiding default:

Avoid the default export. It's handled differently between tooling.

Resources:

@Pagebakers
Copy link
Author

Pagebakers commented Aug 8, 2022

Thanks @balazsorban44

This issue is related to: chakra-ui/chakra-ui#6436

Chakra UI uses .js extension for ESM modules. Any other package that depends on Chakra UI and uses .mjs extension for ESM modules will run into this error. Renaming all .mjs files to .js in @saas-ui/react ultimately resolved it for now, but this is more a workaround then a solution imho.

I looked into framer as well, which had a similar issue open with Next.js last year. They are using require (.js) and default (.mjs) without any issues, so that doesn't seem to be the problem.

#30750

Here to proposed solution was to use .mjs.

I'm not sure what is going on, but anyway the compiler behaviour changes based on which extension is used for ESM modules, while the package.json definitions are the same, as well as the code of the modules.

@balazsorban44
Copy link
Member

As far as I can tell, Framer does not have require or default, but 🤔:

"type": "module",
"exports": {
  ".": "./build/index.js",
  "./package.json": "./package.json",
  "./*": "./build/*"
},

https://unpkg.com/browse/framer@2.1.3/package.json

@balazsorban44
Copy link
Member

balazsorban44 commented Aug 8, 2022

Sorry, probably the wrong package above. framer-motion has this:

"exports": {
".": {
  "require": "./dist/cjs/index.js",
  "import": {
    "types": "./dist/index.d.ts",
    "default": "./dist/es/index.mjs"
  },
  "default": "./dist/cjs/index.js"
  },
"./package.json": "./package.json"
},

https://unpkg.com/browse/framer-motion@7.0.0/package.json

So actually they use both import and require.

@balazsorban44 balazsorban44 added Webpack Related to Webpack with Next.js. type: needs investigation labels Aug 8, 2022
@Pagebakers
Copy link
Author

Ok, my bad I misread this yesterday.

Then we can agree using both 'import' and 'require' is the way to go, leaves us only with the question why the compiler behaves different with .js and .mjs file extensions for es modules.

@balazsorban44
Copy link
Member

balazsorban44 commented Aug 8, 2022

Do you mean saas-js/saas-ui@a43204d?

If you want to signal Node.js to treat your .js files as ESM, you need to set type: "module" at the top level.

The "type" field defines the module format that Node.js uses for all .js files that have that package.json file as their nearest parent.

https://nodejs.org/api/packages.html#type

UPDATE:

Oh, it's the other way around. 🤔 You fixed it by going from .mjs to .js. Hmm.

Also, If using .mjs, the type config is ignored:

Regardless of the value of the "type" field, .mjs files are always treated as ES modules and .cjs files are always treated as CommonJS.

@Pagebakers
Copy link
Author

So if no .mjs are found (even though type = module , or 'import' is defined, it will always treat the package as cjs?

1 similar comment
@Pagebakers

This comment was marked as duplicate.

@balazsorban44
Copy link
Member

Not sure yet, will have to investigate further.

@activenode
Copy link

#39375 (comment)
So if no .mjs are found (even though type = module , or 'import' is defined, it will always treat the package as cjs?

Our research would confirm this. We have the following setup:

NextJS project that uses @our-company/lib

@our-company/lib uses @tabler/icons as dependency (and FYI, even if it's not a dependency but a peerDependency NextJS will act the same way).

@tabler/icons correctly defines this in it's package.json:

{
 "exports": { ".": { "import": "./icons-react/dist/index.esm.js" } }
}

Now the above ends with .js and not with .mjs. Technically this is perfectly fine.

Now what NextJS does is: If THAT package is requested from ANOTHER package (so not directly from the NextJS app but from a sub-package, in our case @our-company/lib) it will ALWAYS throw the error that the ESM file is a CJS file, probably judging by its extension .js instead of .mjs .

This is extremely weird and I'd be willing to help out here if I know in which file to start. I am actually wondering why so few people seem to have this issue. But I assume that people find workarounds.

Our findings are backed by the fact that if we rename those files to .mjs by hand it works.

@activenode
Copy link

I've done a bit more research on this and since the error is being triggered with this error log

ModuleJob._instantiate
  node:internal/modules/esm/module_job (124:21)
async ModuleJob.run
  node:internal/modules/esm/module_job (190:5)

I have the gut feeling that this problem might be rather a node-based problem than a framework-based problem which is interesting.

@activenode
Copy link

According to https://nodejs.org/api/esm.html .mjs is the proposed format and it seems to hit confusion in combination with exports: . Which, when type isn't explicitly module, currently (node 16,18,19) won't resolve what's defined in the exports: { import: "... "} as ESM but as CJS when it doesn't have that specific .mjs extension.

So maybe at this point of information / node status it's best to have this resolved in the related / imported libraries. But what's really interesting is why it works for direct dependencies (maybe because NextJS bundles those but it doesn't transform the sub-deps?)

@activenode
Copy link

Alright. I've researched this for quite some while now. What I reported above seems to hold true.

Now for people searching for a SOLUTION only I have exactly 2 proposals for you:

  1. Clean one: Go to the affected repository of the "problematic" library and make sure they export the ESM files with .mjs extension and/or have it set type: "module" in the package.json

  2. Workaround: Abuse your next.config.js:

const nextConfig = {
 experimental: {
    transpilePackages: ["problematic-package", "other-problematic-package", "etc", "..."],
  },
}

Warning: This isn't a clean solution so only do this when you are beyond desperate.

@Pagebakers
Copy link
Author

Pagebakers commented Jan 16, 2023

@activenode Great work, thank you.

The saga continues for me. Chakra UI renamed everything back to .mjs, and my library started having the same issues again, but reversed :)

I've released new bundles with .mjs extensions, which fixed it partially. But one dependency @react-aria/* uses .js for module bundles, so back at the start.

I have the gut feeling that this problem might be rather a node-based problem than a framework-based problem which is interesting.

I was wondering the same, looking at the logs nothing really points to Next.js

Also Webpack detects the packages correctly as ESM, even overriding the package type to ESM doesn't seem to work.

Your proposed work around doesn't seem to fix the issues with @react-aria for me. I'll keep digging.
I bundle Typescript src files with Saas UI at the moment, changing imports to @saas-ui/date-picker/src is a work around for now.

@Pagebakers
Copy link
Author

For reference. Adobe reverted their update to .mjs recently.

adobe/react-spectrum#3630

@activenode
Copy link

activenode commented Jan 17, 2023

For reference. Adobe reverted their update to .mjs recently.

adobe/react-spectrum#3630

Well, there's just standard people working everywhere.

But with revert you mean .js to .mjs or not? Because those diffs seem like it https://github.com/adobe/react-spectrum/pull/3630/files#diff-b0c5db6e52be47d05cc62e47a08b31818cfc69d80b48a1c395129dd885b91dd8

Which would make sense to me.

I get your pain and I feel it comes from the mix (which must be resolved depending on how the mix is setup - which is one of the described problems here).

At the end of the day it comes down to: If everything is using .mjs it doesn't matter the type of the package.json because then Node will correctly detect --> Make sure that the libs are using .mjs.
Going back reverting to .js just because of a specific setup feels like going back for false reasons (see Node ESM Link above)

What I still find interesting though is that it only appears (in my experience) with deps of deps not with deps but that could be explained as direct deps might run through the bundler whereas the deps of deps don't.

@abdulhdr1
Copy link

abdulhdr1 commented Feb 14, 2023

I was having the same problems with a @company/lib that implements the adobe stack (react-aria, @react-stately, @internationalized).
I found two possible workarounds for it:

  1. Change the libraries files:
    This is updating the package.json's lines:
"main": "dist/main.js",
"module": "dist/module.js",

to:

"exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/main.js"
    }
  }

as you may have noticed it also requires renaming module.js to module.mjs.
This fixes next.js/node resolving the wrong file.

  1. Client-side rendering
    I don't know why this happens or if it's a solution only to my use case but I found that importing the component from @company/lib using dynamic import and ssr: false also solve the issue.

@supersabillon
Copy link

Is tsconfig.js make sure moduleResolution is set to node16

@activenode
Copy link

Is tsconfig.js make sure moduleResolution is set to node16

Too few information: When does this help and where? Are you suggesting to set moduleResolution: node16 in the project (NextJS project) that is using troublesome (non-mjs peerdeps) libraries?

@marcofranssen
Copy link

marcofranssen commented Apr 26, 2023

Facing the same issue when bumping nextjs from 13.2.4 to 13.3.1 on liveReload.

It still works in 13.2.4, but breaks as soon I bump to 13.3.1.

error - file:///Users/marco/code/blog/node_modules/next-contentlayer/dist/hooks/useLiveReload.js:1
import { addMessageListener } from 'next/dist/client/dev/error-overlay/websocket.js';
         ^^^^^^^^^^^^^^^^^^
SyntaxError: Named export 'addMessageListener' not found. The requested module 'next/dist/client/dev/error-overlay/websocket.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'next/dist/client/dev/error-overlay/websocket.js';
const { addMessageListener } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:190:5) {
  digest: undefined
}

Any thoughts?

  • I don't have any exports in my own package.json
  • The latest canary installed at 26-04-2023 20:12 CET still has the same bug.

@Pagebakers
Copy link
Author

@marcofranssen having this issue too with 13.2.4 now with a fresh checkout of my project.

I had to upgrade @swc/helpers in order to resolve another bug. #48593

Seems to related to swc's resolver?

@pencilcheck
Copy link
Contributor

pencilcheck commented May 2, 2023

Fixed it with those two steps

  1. [OPTIONAL] Change tsconfig.json, from node to node16 (moduleResolution)
    "module": "esnext",
    "moduleResolution": "node16",
    "resolveJsonModule": true,

Looks like this step isn't needed. but feel free to change it if you like.

  1. Follow instructions here to fix the issue: https://nextjs.org/docs/messages/import-esm-externals

  2. Add transpilePackages in next.config.mjs

  transpilePackages: ["@nivo/line", "@nivo/colors", "d3-color"],

I'm on next 13.1.2

@rafant97
Copy link

After some more testing the .mjs extension seems to cause issues, renaming the esm files to .js resolves the issues.

Thank you very much, it works for me

penberg added a commit to libsql/isomorphic-ts that referenced this issue Sep 18, 2023
Pálmi Þór Valgeirsson is reporting the following import failure on
Vercel with the Bun runtime:

[11:48:09.012] Error [ERR_REQUIRE_ESM]: require() of ES Module /vercel/path0/node_modules/node-fetch/src/index.js from /vercel/path0/node_modules/@libsql/isomorphic-fetch/node.cjs not supported.
[11:48:09.012] Instead change the require of index.js in /vercel/path0/node_modules/@libsql/isomorphic-fetch/node.cjs to a dynamic import() which is available in all CommonJS modules.

This seems to be a known issue on Vercel:

vercel/next.js#39375

With the suggested workaround of:

> After some more testing the .mjs extension seems to cause issues, renaming the esm files to .js resolves the issues.
gabrielmfern added a commit to gabrielmfern/resend-node that referenced this issue Nov 3, 2023
I have tested this out on a repo by copying the new dist into the node_modules and can
confirm this solves the issues that were happening with builds for next.

see vercel/next.js#39375 for more info
gabrielmfern added a commit to gabrielmfern/resend-node that referenced this issue Nov 8, 2023
I have tested this out on a repo by copying the new dist into the node_modules and can
confirm this solves the issues that were happening with builds for next.

see vercel/next.js#39375 for more info
@dimidob
Copy link

dimidob commented Feb 3, 2024

What worked in my case was to remove the curly braces from the import statement in the quoted JS file: "/var/www/nextjs/node_modules/next-contentlayer/dist/hooks/useLiveReload.js"


info - Collecting page data .file:///var/www/nextjs/node_modules/next-contentlayer/dist/hooks/useLiveReload.js:1
import { addMessageListener } from 'next/dist/client/dev/error-overlay/websocket.js';
^^^^^^^^^^^^^^^^^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template. linear: next Confirmed issue that is tracked by the Next.js team. Webpack Related to Webpack with Next.js.
Projects
None yet
Development

No branches or pull requests

10 participants