-
Notifications
You must be signed in to change notification settings - Fork 27k
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
Issue with backend-side ES6 imports with "type":"module" with an express/nextjs setup #24334
Comments
So the TLDR is that NextJS does not really support native ESM right now (they should... but not yet). It is possible, but you have to hack around it in a few places. The main issue is that NextJS compiles everything into CJS and builds each page separately. When the code is expecting ESM things start to break... I managed to get ESM running on my custom server, so here is my checklist of things to fix to support ESM:
Code snippet to add to your
So NextJS with native ESM is possible! But it is a lot and probably overwhelming. So proceed at your own risk until NextJS does a better job of supporting it by default. |
@JacobLey Is there a simpler answer now that |
Hmm I admittedly haven't fully tested the capabilities but I have been following these features. In general I think NextJS supports ESM in I think I mentioned it above, but it is worth noting a general caveat to my instructions is my goal is to write code that runs automatically. If you are someone that is planning to deploy your NextJS in more basic ways like via Vercel or Anyways, in order of my points above:
I'll try upgrading my local version and see what changes are still necessary, and report back |
This is exceptional, thank you for your effort! I seem to have this mostly working but I had to remove |
So I upgraded to NextJS 12.0.4 I think my assumptions above are generally correct, ESM "locally" isn't natively supported, but modules are.
My "redacted" webpack config in next.config.js (takes into account notes I made above): 'use strict';
const fs = require('fs');
const Path = require('path');
let wrotePackageJson = false;
module.exports = {
pageExtensions: ['js'],
webpack: (defaultConfig, opts) => {
if (!wrotePackageJson) {
fs.mkdirSync(Path.join(opts.dir, '.next'), { recursive: true });
fs.writeFileSync(Path.join(opts.dir, '.next/package.json'), '{ "type": "commonjs" } ');
wrotePackageJson = true;
}
defaultConfig.experiments = {
...defaultConfig.experiments,
topLevelAwait: true,
};
if (opts.dev && !opts.isServer) {
defaultConfig.module.rules.push({
test: /\.js$/,
type: 'javascript/auto',
resolve: {
fullySpecified: false,
},
});
}
if (opts.isServer) {
const [, major, minor] = /^v(\d+).(\d+).\d+$/.exec(process.version);
defaultConfig.target = `node${major}.${minor}`;
defaultConfig.output.environment = {
arrowFunction: true,
bigIntLiteral: true,
const: true,
destructuring: true,
dynamicImport: true,
forOf: true,
module: true,
};
defaultConfig.externals.unshift(async ({ request }) => {
if (request.startsWith('@<custom-package>/')) {
// Local package (does not resolve to node_modules/**), must use import
return `import ${request}`;
}
if (request.startsWith('.') && /.[cm]?js$/.test(request)) {
// Local JS file
if (opts.dev) {
// In development bundle "local" files to best support hot reload.
return;
}
if (request.startsWith('..')) {
// Transpiled version gets placed two directories deeper
return `import ../../${request}`;
}
}
});
}
return defaultConfig;
},
}; Yes I had to remove My directory setup looks something like:
|
Fix: #33637 (still work-in-progress at the time of this comment) |
- [x] Add failing test for development / production - [x] Add failing test for client-side JavaScript - [x] Write `.next/package.json` with `"type": "commonjs" - [x] Fix issue with client-side JavaScript showing `module` is not defined Production works after these changes. Development breaks on module not existing because of the Fast Refresh loader. Working with @sokra to add alternatives to what is being used in the loader to webpack so that it can be updated. Fixes #23029, Fixes #24334 ## Bug - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint`
Happy to confirm that #33637 worked for me in a side project 🎉 I just upgraded to Miraculas times, thanks a lot @timneutkens! ES Modules FTW 🚀 |
@kachkaev, were you able to deploy your project to Vercel? Everything is working for me locally and in CI/CD, but I'm getting this error in my Vercel functions:
I see this page when trying to access the site: |
Opened a new issue here: #34412 which will also solve your case @michaelhays. |
@michaelhays The project in which I have |
I wanted to follow up on my example webpack config above #24334 (comment) ESM is now supported which is great, so my config is now just export default {
pageExtensions: ['js'],
webpack: (defaultConfig, opts) => {
defaultConfig.experiments = {
...defaultConfig.experiments,
topLevelAwait: true,
};
if (opts.isServer) {
const [, major, minor] = /^v(\d+).(\d+).\d+$/.exec(process.version);
defaultConfig.target = `node${major}.${minor}`;
defaultConfig.externals.unshift(async ({ request }) => {
if (request.startsWith('@<custom-package>/')) {
// Local package (does not resolve to node_modules/**), must use import
return `import ${request}`;
}
if (request.startsWith('.') && /.[cm]?js$/.test(request)) {
// Local JS file
if (opts.dev) {
// In development bundle "local" files to best support hot reload.
return;
}
if (request.startsWith('..')) {
// Transpiled version gets placed two directories deeper
return `import ../../${request}`;
}
}
});
}
return defaultConfig;
},
}; So huge progress! NextJS now properly identifies when ESM is used vs CommonJS, so the extra I still had to include My "issue"** with NextJS is it is still fully transpiling the server-side code. In my case, all code is already generated and I don't need a CJS version of my code. Transpilation actually creates duplicate code when the same libraries are loaded in the custom server, and breaks when expecting things like unique Symbol to equal itself. I think for the sake of this issue, it is definitely resolved. But worth maintaining the caveat that NextJS does not use the original files. If you are writing a custom server and re-use some files, keep an eye out for this edge case. ** This "issue" is pretty particular to my use case. I expect >95% of NextJS users will not run into this. If you are just writing ESM because you prefer the syntax and continue relying on NextJS for transpilation then it should work seamlessly! |
This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you. |
What version of Next.js are you using?
10.1.3
What version of Node.js are you using?
15.9.0
What browser are you using?
Chrome
What operating system are you using?
MacOS
How are you deploying your application?
Running locally via express
Describe the Bug
I have an Express.js server that sets up Next.js, and I want to use ES6 modules with my backend.
My package.json has the line
"type": "module"
, which enables ES6 module support in Node.js. Everything is imported fine, but when I try to load a page, I get the following exception:Indeed, looking at
.next/server/pages/_document.js
, it has avar installedModules = require('../ssr-module-cache.js');
directive, which is against the rules for"type": "module"
.This seems to imply that I can not use Next.js with ES6 syntax in Node - which is too bad!
Expected Behavior
I think what I would expect is that Next.js would compile in a way that's compatible with ES6 modules - i.e. when
"type": "module"
is enabled, it relies onimport
, notrequire
To Reproduce
I've created a minimal setup where I'm able to get this to reproduce:
package.json
index.js
And also in pages/main.jsx:
With this setup, after a
yarn install
andyarn start
, I see the error above. I left therequire
style that works fine w/o a"type": "module"
directive in comments so it's quick to test that this Express+Next.js setup is in fact functional.The text was updated successfully, but these errors were encountered: