Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Support ES module format (ESM) in next.config.js #9607

Closed
natemoo-re opened this issue Dec 3, 2019 · 59 comments
Closed

Support ES module format (ESM) in next.config.js #9607

natemoo-re opened this issue Dec 3, 2019 · 59 comments
Labels
Webpack Related to Webpack with Next.js.
Milestone

Comments

@natemoo-re
Copy link
Contributor

natemoo-re commented Dec 3, 2019

Feature request

With Node 12.17.x landing native support for ES modules, I would expect next.config.js to support ES module format in addition to CJS.

When using type: module in package.json on Node v13.1.0 and an ESM next.config.js format, Next throws an error due to the config resolution logic relying on require.

Describe the solution you'd like

Next should follow Node’s module semantics and allow either CJS or ESM file, depending on user configuration.

Currently, next.config.js uses CJS with require and module.exports. ESM format would allow users to import dependencies and export default the config object.

This theoretically also opens the door to mirror Node’s explicit file extension pattern, which would suggest support for next.config.cjs and next.config.mjs files. This is likely controversial, but it is part of the language.

Describe alternatives you've considered

As statically analyzing the native module format of next.config.js likely introduces a fair amount of complexity, this could be used as an opportunity to introduce "superset" config files (ESM, TypeScript [see #5318, #8044]) which are transpiled to CJS, emitted to the distDir (.next), and read from that location. This would also allow users pinned versions of Node below 13 to benefit from ESM format or strongly typed config files (see @stencil/core for prior art.)

Additional context

The following error is thrown, minimum reproducible files below.

node:97960) Warning: require() of ES modules is not supported.
require() of /PROJECT_NAME/next.config.js from /PROJECT_NAME/node_modules/next/dist/next-server/server/config.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename next.config.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /PROJECT_NAME/package.json.
/PROJECT_NAME/next.config.js:1
import path from "path";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:983:16)
    at Module._compile (internal/modules/cjs/loader.js:1033:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1103:10)
    at Module.load (internal/modules/cjs/loader.js:914:32)
    at Function.Module._load (internal/modules/cjs/loader.js:822:14)
    at Module.require (internal/modules/cjs/loader.js:956:19)
    at require (internal/modules/cjs/helpers.js:74:18)
    at Object.loadConfig [as default] (/PROJECT_NAME/node_modules/next/dist/next-server/server/config.js:105:34)
    at new Server (/PROJECT_NAME/node_modules/next/dist/next-server/server/next-server.js:42:43)
    at new DevServer (/PROJECT_NAME/node_modules/next/dist/server/next-dev-server.js:1:2508)

package.json

{
  "type": "module"
  ...
}

next.config.js

// next.config.js
import withCSS from '@zeit/next-css';
export default withCSS();
@natemoo-re natemoo-re reopened this Dec 3, 2019
@natemoo-re natemoo-re changed the title Support esm next.config.js Support ES module format (ESM) in next.config.js Dec 3, 2019
@Timer Timer added this to the backlog milestone Dec 3, 2019
@redblue9771
Copy link

redblue9771 commented Dec 8, 2019

Node 13 is not yet suitable for production environments, and using the mjs extension is intrusive. Recently I needed a custom server to use better express middleware features, but I have no way to use ESM in any custom server related files. I hope to add an example about using ESM and nodemon when customizing the server.

@natemoo-re
Copy link
Contributor Author

While I’d love to see ESM support globally in Next, this issue is just about the next.config.js file. Custom servers can support ESM in a similar manner to the custom-server-typescript example.

Let’s not get into a discussion about the merits of .mjs or .cjs. I’m personally not a big fan, but since Node allows both, Next should support them as well.

@afsanefdaa
Copy link
Contributor

afsanefdaa commented Mar 25, 2020

I have the same issue. Do you have any idea how to fix it?

I previously used node 8.10.0 and now changed to 12.16.1 and when I yarn dev it keeps returning me the error in one of my npm packages.

SyntaxError: Cannot use import statement outside a module

I added type:module to package.json but it says:

> Build error occurred
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /.../next.config.js
require() of ES modules is not supported.
require() of /..../node_modules/next/dist/next-server/server/config.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename next.config.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /.../package.json.

@cooervo
Copy link

cooervo commented Apr 16, 2020

same here tried to migrate project to use import just to fail on next.config.js

@OctonianCat

This comment has been minimized.

@hypo-thesis

This comment has been minimized.

@029A-h
Copy link

029A-h commented Sep 15, 2020

Any progress in implementation of esm syntax so far?

@crshumate
Copy link

crshumate commented Sep 15, 2020

For anyone running up against this issue. I am using a custom server for complete flexibility in my build.

Node files are located in a top level dir called server and Next files are in a top level dir called src

/root
    - next.config.js
    - package.json - make no changes to this file
    /src
        /pages
    /server
        - package.json
            - this file just needs: {"type":"module"}
        - server.js

As noted in the structure above, place an empty package.json file in a subdir with the node/express/etc files.
In this package.json simply add {"type":"module"}
Ensure all the requires are changed to ES6 import statements and it should build fine.

@sagirk
Copy link

sagirk commented Sep 16, 2020

As noted in the structure above, place an empty package.json file in a subdir with the node/express/etc files.
In this package.json simply add {"type":"module"}
Ensure all the requires are changed to ES6 import statements and it should build fine.

Nice! 👍

If you want to retain server.js in the root, though, simply rename it from server.js to server.mjs for the time being. Works without requiring a type entry in package.json.

@Rowno
Copy link
Contributor

Rowno commented Oct 7, 2020

Since Node.js 12.17.0 ES modules have been enabled by default (a flag is no longer required). So ES modules are now available in an LTS version of Node.js:
https://nodejs.org/en/blog/release/v12.17.0/

Also it looks like you can use normal .js file extensions if you add "type": "module" to your package.json:
https://nodejs.org/docs/latest-v12.x/api/esm.html#esm_package_json_type_field

@andreasg123
Copy link

Is the use of next-transpile-modules still the only way to include modules that only provide ESM? I tried switching my project to "type": "module" but I was prompted to rename next.config.js to .cjs. The latter wasn't picked up as a config file, resurfacing an issue with "fs" in another module.

In case it is relevant, my project uses TypeScript. I had to include babel-jest to be able to use Jest.

My final goal is to use next export because the production server doesn't run Node.

@paulshorey
Copy link

I ran into a similar issue. If any of the folders inside my Next.js project use {type:"module"}, then next build process can't require() from them. It says "Must use import to load ES Module".

The silly thing is that Webpack/React uses ES Module format (import instead of require). So this is very strange. Next.js server really should use ESM by default, because it's working with an ESM filesystem.

@GitHubFilipe
Copy link

Im running into this issue too.
A .js script that I'm importing in my next.config.js, uses "import {...} from '...' directives to call some API functions, thus throwing the error:
SyntaxError: Cannot use import statement outside a module

no matter how I turn it, the combination just doesn't work.

@belgattitude
Copy link
Contributor

For information, ky will move to esm only... next-transpile-modules can be used meanwhile, but esm would be really appreciated.

sindresorhus/ky#322 (comment)

@Janpot
Copy link
Contributor

Janpot commented Feb 20, 2021

Created an RFC (#22381) and started an implementation (#22153) for this

@jerrygreen
Copy link
Contributor

ESM is cool, but what about typescript? Will that be .mts? 😅

@natemoo-re
Copy link
Contributor Author

In all seriousness, TypeScript probably isn't that big of a leap from here. Async config loading was the biggest problem but is addressed in #22153. Node is currently stabilizing the experimental loaders API, which will expose hooks to transform non-standard file formats like TypeScript.

@Richienb
Copy link

Richienb commented Aug 26, 2021

Since Next.js v11.1.0, you can use the esmExternals option by adding the following to your next.config.js:

{
	experimental: {
		esmExternals: "loose",
	},
}

Here's an example project.

@ivan-kleshnin
Copy link
Contributor

ivan-kleshnin commented Aug 26, 2021

@Richienb did you find how to enable directory imports? Index imports aren't supported in ESM so far and it's unclear whether some magic Node flag can override that. It's totally inconvenient to develop without index.ts(x) files 😞

@Richienb
Copy link

Last time I checked, directory imports are not actually an ESM feature and thus would not work. You are able to import by file paths however.

@ivan-kleshnin
Copy link
Contributor

ivan-kleshnin commented Aug 26, 2021

@Richienb there's a flag --experimental-specifier-resolution=node but I wasn't able to make it work with NextJS.

$ NODE_OPTIONS="--experimental-specifier-resolution=node" yarn dev
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "file:///Users/.../node_modules/next/dist/bin/next" 

@IzioDev
Copy link

IzioDev commented Aug 26, 2021

It looks like the esmExternals experimental flag does not work in my case.

Here is what I'm trying to import import cliTruncate from "cli-truncate"; from https://www.npmjs.com/package/cli-truncate.
This package use "type": "module" in it's package.json.

If I comment out the import line, the Next.Js web application runs just fine. But when I uncomment it, this error occurs :

Unhandled Runtime Error
ReferenceError: module is not defined

Next.Js version : 11.1.0.

The weird part is that if I trust the stack trace, the error comes from: ../../node_modules/@fortawesome/fontawesome-svg-core/index.es.js

I just clicked on the first invocation in the stack trace :
image

image

PS: I'm using yarn workspaces and the Next.Js application is under /packages/website, so the node_modules folder is at the root directory /node_modules. I don't know if the error is related to this since I'm a complete newbie with ESM.

@ghost
Copy link

ghost commented Sep 4, 2021

same here:

nextjs: 11.1.0

I want use a remark plugins , some code like this :


import {visit} from 'unist-util-visit'
...


@nedkelly
Copy link

I'm having the same issue with react-markdown.

TypeScript
Next.js 11.1.0
Yarn 2

I tried adding esmExternals in next.config.js but still no joy.

Using Dynamic import fixes it though:

const ReactMarkdown = dynamic(() => import('react-markdown'));

@equinusocio
Copy link

equinusocio commented Sep 14, 2021

I'm having the same issue with react-markdown.

TypeScript

Next.js 11.1.0

Yarn 2

I tried adding esmExternals in next.config.js but still no joy.

Using Dynamic import fixes it though:

const ReactMarkdown = dynamic(() => import('react-markdown'));

@nedkelly are you doing this inside next config?

@city17
Copy link

city17 commented Sep 23, 2021

Same problem here, unable to get remark-slug or rehype-slug working due to this error.

@stringa
Copy link

stringa commented Sep 23, 2021

Just use

const a = import('react-markdown') where ever you want to use the dependency

@equinusocio
Copy link

equinusocio commented Sep 23, 2021

Just use

const a = import('react-markdown') where ever you want to use the dependency

You can't use import inside next config..

@nedkelly
Copy link

I'm having the same issue with react-markdown.
TypeScript
Next.js 11.1.0
Yarn 2
I tried adding esmExternals in next.config.js but still no joy.
Using Dynamic import fixes it though:

const ReactMarkdown = dynamic(() => import('react-markdown'));

@nedkelly are you doing this inside next config?

No, and to be honest, using dynamic import is a little unstable, it sometimes fails to load so it's not really a fix. And it doesn't work in next.config.js.

@alecmev
Copy link
Contributor

alecmev commented Sep 23, 2021

No disrespect to the participants, but I think too many people misunderstand this issue, resulting in all sorts of irrelevant ESM problems being reported here. Can commenting be limited to collaborators, please?

@equinusocio
Copy link

No disrespect to the participants, but I think too many people misunderstand this issue, resulting in all sorts of irrelevant ESM problems being reported here. Can commenting be limited to collaborators, please?

So we are all stupid. Ok. Anyway the issue is pretty clear, any nextjs plugin which are now modules are totally unusable because nextjs config file doesn't support modules.

@Richienb
Copy link

Richienb commented Sep 24, 2021

No, No. He's Got a Point

This issue was for allowing the next.config.js to be written with ESM. The most recent conversation is for allowing ESM modules to be loaded within the source code.

The best thing to do is to move this part of the conversation to a seperate issue and guide people who want to talk about that specific thing there.

To people who are having trouble loading modules written in ESM: create a reproduction repository and create a new bug report. As for general discussion about it, we now have #29348.

@equinusocio
Copy link

That's totally different from saying "let's close the comments". Anyway, this is the issue i want to follow.

@ctjlewis
Copy link
Contributor

ctjlewis commented Sep 24, 2021

Because Next configs are modules which must be loaded, it will take adding full ESM support to Next to support this for the reasons mentioned in this comment:

#27876 (comment)

Do not add "type": "module" to your package.json (yet)

The esmExternals experimental flag only adds support for ESM externals, i.e., you can import ESM dependencies. You cannot bundle your Next project as an ES module yet.

The same reason that Next cannot load an ESM config (see #9607) is the same reason it cannot load your project if you set "type": "module". If you mark your project as an ES module with "type": "module", Next will break while trying to load next.config.js and compiled JS in .next—these are currently loaded via hard-coded require(...) statements, and trying to require() an ES module throws ERR_REQUIRE_ESM:

userConfigModule = require(path)

It will actually take upgrading Next core itself to ESM to add full ESM support (as widely requested, e.g. in #9607), which I will explain in detail in a separate issue. It is worth it to undertake that effort, but for now it will suffice to fix the resolution issues below IMO, and let Next back-transpile ESM to CJS for deploy.

I have an experimental branch going to explore this, but Jest ESM support is shoddy and ultimately blocking progress.

@rinarakaki
Copy link

@ctjlewis Thank you for sharing your exploration. Based on it, do you think anything else will be straightforward in rewriting CJS to ESM when Jest's ESM support is solved?


@ctjlewis
Copy link
Contributor

ctjlewis commented Sep 25, 2021

@rnarkk There are a few obstacles (some related to TS itself, which we may be able to workaround with Node12 resolution as of next release) to getting Next to that point, which I will detail in a separate issue and link here, but the way I'd propose implementing it, you would just need to set "type": "module" or use .mjs extension to signal ESM, and Next would handle the rest (as long as you feed it valid ESM).

For TS projects, you'd want to configure your local project settings to use the new Node12 resolution, so that it forces you to write complete ESM-compatible import specifiers (or use something like tszip to rewrite them for you, which is what I do).

@timneutkens
Copy link
Member

I've opened a PR that changes from require() to import() for config loading and adds support for next.config.mjs. I'll keep this issue open as there's still more work to do for "type": "module" in package.json but this change should help until then: #29935

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Webpack Related to Webpack with Next.js.
Projects
None yet
Development

No branches or pull requests