webpack: Built 'module' bundle errors with "require is not defined", but 'commonjs' bundle errors with "Cannot use import"
Bug report
Done.
What is the current behavior?
I’m trying to bundle an ES module node app into a single file, with the exception of the sharp
library which has problems with Webpack. This is a node server app, not browser.
All of my code are modules, and I don’t use require()
. Only third=party dependencies might.
In webpack.config.js
, ff I set the target
to node
, node18.3
or async-node18.3
, I get the following error:
(output
config):
filename: '[name].bundle.mjs',
path: path.resolve(__dirname, 'dist'),
sourceMapFilename: '[name].bundle.map',
chunkFormat: 'module',
$ node ./dist/index.bundle.mjs
<remove source outout for brevity>
ReferenceError: require is not defined
at 67361 (file:///Path/to/app/dist/index.bundle.mjs:2:4368640)
at __webpack_require__ (file:///Path/to/app/dist/index.bundle.mjs:2:5256005)
at file:///Path/to/app/dist/index.bundle.mjs:2:4313644
at __webpack_require__.a (file:///Path/to/app/dist/index.bundle.mjs:2:5256751)
at 63607 (file:///Path/to/app/dist/index.bundle.mjs:2:4313499)
at __webpack_require__ (file:///Path/to/app/dist/index.bundle.mjs:2:5256005)
at file:///Path/to/app/dist/index.bundle.mjs:2:5258585
at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:409:24)
Node.js v18.3.0
If I change target to es2020
and keep output
as-is, webpack fails with (290 errors reduced for brevity):
ERROR in /Path/to/app/node_modules/mongoose/lib/browserDocument.js 8:21-51
Module not found: Error: Can't resolve 'events' in '/Path/to/app/node_modules/mongoose/lib'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "events": require.resolve("events/") }'
- install 'events'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "events": false }
@ Path/to/app/node_modules/mongoose/lib/document_provider.js 9:24-55
@ Path/to/app/node_modules/mongoose/lib/index.js 927:38-68
@ Path/to/app/node_modules/mongoose/index.js 9:17-34
@ ./node_modules/hapi-mongoose-connect-next/lib/index.js 3:15-34
@ ./src/index.ts 6:0-61 31:14-33
Module not found: Error: Can't resolve 'util' in '/Path/to/app/node_modules/mongoose/lib'
Module not found: Error: Can't resolve 'assert' in '/Path/to/app/node_modules/mongoose/lib/cast'
Module not found: Error: Can't resolve 'util' in '/Path/to/app/node_modules/mongoose/lib/cursor'
Module not found: Error: Can't resolve 'stream' in '/Path/to/app/node_modules/mongoose/lib/cursor'
Module not found: Error: Can't resolve 'tty' in '/Path/to/app/node_modules/mongoose/node_modules/debug/src'
Module not found: Error: Can't resolve 'os' in '/Path/to/app/node_modules/mongoose/node_modules/ip/lib'
Module not found: Error: Can't resolve 'dns' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/auth'
Module not found: Error: Can't resolve 'crypto' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/auth'
Module not found: Error: Can't resolve 'http' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/auth'
Module not found: Error: Can't resolve 'url' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/auth'
Module not found: Error: Can't resolve 'net' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap'
Module not found: Error: Can't resolve 'tls' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap'
Module not found: Error: Can't resolve 'zlib' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib/cmap/wire_protocol'
Module not found: Error: Can't resolve 'dns' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib'
Module not found: Error: Can't resolve 'fs' in '/Path/to/app/node_modules/mongoose/node_modules/mongodb/lib'
Module not found: Error: Can't resolve 'path' in '/Path/to/app/node_modules/mongoose/node_modules/saslprep/lib'
Module not found: Error: Can't resolve 'https' in '/Path/to/app/node_modules/@hapi/hapi/lib'
Module not found: Error: Can't resolve 'vm' in '/Path/to/app/node_modules/fast-redact/lib'
Module not found: Error: Can't resolve 'net' in '/Path/to/app/node_modules/http-proxy-agent/dist'
Module not found: Error: Can't resolve 'child_process' in '/Path/to/app/node_modules/jsdom/lib/jsdom/living/xhr'
290 errors have detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.
webpack 5.65.0 compiled with 290 errors and 21 warnings in 32411 ms
If I change target
back to node
but change output to commonjs
, I get:
filename: '[name].bundle.cjs',
path: path.resolve(__dirname, 'dist'),
sourceMapFilename: '[name].bundle.map',
chunkFormat: 'commonjs',
$ node ./dist/index.bundle.cjs
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
at wrapSafe (node:internal/modules/cjs/loader:1033:15)
at Module._compile (node:internal/modules/cjs/loader:1069:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Module._load (node:internal/modules/cjs/loader:827:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
at node:internal/main/run_main_module:17:47
Node.js v18.3.0
I seem to be stuck in a catch-22 here. I can’t compile to commonjs because I use the following in one of my modules:
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
And I can’t remove import.meta.url
because I’m using modules and I need to polyfill __filename
and __dirname
. I’ve gone though the documentation, and I don’t see what I’m missing. So, I can only assume this is a webpack bug.
Contents of ./dist
with target: 'node'
:
$ ls -al ./dist
total 44512
drwxr-xr-x 6 me me 192 Jul 6 23:57 .
drwxr-xr-x 22 me me 704 Jul 6 21:57 ..
-rw-r--r-- 1 me me 17330789 Jul 6 23:57 index.bundle.map
-rw-r--r-- 1 me me 5417703 Jul 6 23:57 index.bundle.mjs
-rw-r--r-- 1 me me 34784 Jul 6 23:57 index.bundle.mjs.LICENSE.txt
drwxr-xr-x 4 me me 128 Jul 6 23:57 resources
If the current behavior is a bug, please provide the steps to reproduce.
package.json
:
"type": "module",
"main": "./dist/index.bundle.mjs",
"module": "./dist/index.bundle.mjs",
"sideEffects": false,
"overrides": {
"@mapbox/node-pre-gyp": "1.0.9"
},
tsconfig.json
:
{
"compilerOptions": {
"outDir": "./dist",
"rootDir": "src",
"allowJs": true,
"target": "es2021",
"module": "es2022",
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"types": ["node", "jest"],
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
"lib": ["es2021"],
},
"include": [
"src/index.ts",
"src/**/*",
"src/resources"
],
"exclude": ["node_modules"]
}
Complete webpack.config.js
:
import path from 'path';
import webpack from 'webpack';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
target: 'async-node18.3',
entry: {
index: './src/index.ts'
},
devtool: 'source-map',
node: {
global: true,
__filename: true,
__dirname: true,
},
module: {
rules: [
{
test: /\.html$/i,
loader: "html-loader",
},
{
test: /\.[jt]sx?$/,
resolve: {
fullySpecified: false,
},
},
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{ test: /\.node$/, use: "node-loader" }
],
},
experiments: {
topLevelAwait: true,
outputModule: true,
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: '[name].bundle.mjs',
path: path.resolve(__dirname, 'dist'),
sourceMapFilename: '[name].bundle.map',
chunkFormat: 'module',
environment: {
arrowFunction: true,
bigIntLiteral: false,
const: true,
destructuring: true,
dynamicImport: true,
forOf: true,
module: true,
optionalChaining: true,
templateLiteral: true,
}
},
plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /canvas/,
contextRegExp: /jsdom$/
}),
new webpack.ContextReplacementPlugin(/require_optional/)
],
externals: {
'sharp': 'commonjs sharp'
}
};
What is the expected behavior?
The expect behavior is that javascript modules bundled as a single file module should have all require
references replaced, or modules compiled as commonjs should do something to convert import.meta.*
.
Other relevant information: webpack version: 5.73.0 Node.js version: 18.3.0 Operating System: macOS 12.4 Additional tools: typescript: 4.6.4, ts-loader: 9.3.0, webpack-cli: 4.10.0
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Comments: 23 (9 by maintainers)
webpack can output only commojs for Node.js right now, please use
@alexander-akait I solved problem with .node files by adding
node-loader
and@ignoramous Yeah, I fixed this locally in your repo, but it is still have some problems with loading
.node
files, but it is not related to webpack itself 😄@ignoramous let’s open a new issue, yes, it is a bug, shorty -
__non_webpack_require__
replaced onrequire
, but for ESM we should replace it oncreateRequire(import.meta.url)
, it happens only when you want to output ESMThanks. A fix for this would be nice, since some native Node modules use
bindings
which hits this bug. I confirmed that this happens withrollup
too.Same issue 🙋🏽♂️
@alexander-akait The real target is node v18. This is a server-side application. To me, I don’t care if it compiles down to commonjs (
.cjs
) or module (.mjs
), so long as it’s bundled with minimum code and preferably onlysharp
as an externalnode_modules
package. Ultimately, I’ll be creating a docker image and running this in a container, so getting things down to the smallest size is desired.All of my code is esm ts. No usage of
require
, unless a dependency uses that. Node shouldn’t care, except when it’s in the top-level js, right? Webpack bundles everything into a single top-level js, including the dependencies that userequire
. Am I correct in my understanding in that’s how it’s working?Case 1:
target
:node
output.filename
:'[name].bundle.cjs'
output.chunkFormat
:'commonjs'
,output.environment
:undefined
Output:
index.bundle.cjs
(node runs as commonjs) Result:SyntaxError: Cannot use import statement outside a module
Case 2:
target
:node
output.filename
:'[name].bundle.mjs'
output.chunkFormat
:'module'
,output.environment
:{ arrowFunction: true, bigIntLiteral: false, const: true, destructuring: true, dynamicImport: true, forOf: true, module: true, optionalChaining: true, templateLiteral: true, }
Output:
index.bundle.mjs
(node runs as module) Result:ReferenceError: require is not defined
Case 3:
target
:es2022
output.filename
:'[name].bundle.mjs'
output.chunkFormat
:'module'
,output.environment
:{ arrowFunction: true, bigIntLiteral: false, const: true, destructuring: true, dynamicImport: true, forOf: true, module: true, optionalChaining: true, templateLiteral: true, }
resolve.fallback
:{"events": false, "util": false, "assert": false, "stream": false, "tty": false, "os": false, "dns": false, "crypto": false, "http": false, "url": false, "net": false, "tls": false, "zlib": false, "fs": false, "path": false, "https": false, "vm": false, "net": false, "child_process": false}
Output:
index.bundle.mjs
(node runs as module) Result:TypeError: Class extends value undefined is not a constructor or null