Webpack打包产物含require语句,ESM环境Node.js运行报错求助
我有一个仅使用ESM规范的TypeScript项目,通过Webpack将代码打包为单个JavaScript文件。执行node ./dist/index.js时出现错误:
const external_minimist_namespaceObject = require("minimist"); ^ ReferenceError: require is not defined in ES module scope, you can use import instead This file is being treated as an ES module because it has a '.js' file extension and 'package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
我的package.json中已配置"type": "module",Webpack运行脚本为:"dist": "node --loader ts-node/esm node_modules/webpack-cli/bin/cli.js -c ./webpack.config.ts"。
以下是我的webpack.config.ts配置:
import path from 'node:path'; import fs from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import webpack from 'webpack'; import nodeExternals from 'webpack-node-externals'; import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; import WebpackShellPluginNext from 'webpack-shell-plugin-next'; const packageJsonData = await fs.readFile('package.json', 'utf-8'); const packageJsonObject = JSON.parse(packageJsonData); const version = packageJsonObject.version; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const configuration: webpack.Configuration = { context: __dirname, mode: 'production', target: 'node', entry: './src/index.ts', optimization: { minimize: false }, externals: [nodeExternals({ modulesDir: path.join(__dirname, 'node_modules') })], experiments: { outputModule: true }, module: { rules: [ { test: /\.ts$/, use: [ { loader: 'ts-loader', options: { configFile: 'tsconfig.build.json' }, }, ], exclude: /node_modules/, }, ], }, plugins: [ new WebpackShellPluginNext({ onBuildStart: { scripts: ['rimraf dist'], blocking: true, }, safe: true, }), new webpack.DefinePlugin({ __PACKAGE_VERSION__: JSON.stringify(version), }), ], resolve: { extensions: ['.ts'], plugins: [ new TsconfigPathsPlugin({ configFile: './tsconfig.base.json', extensions: ['.ts'], }), ], }, output: { filename: 'index.js', path: path.resolve(__dirname, 'dist'), library: { type: 'module' }, chunkFormat: 'module', }, }; export default configuration;
关键配置文件tsconfig.base.json内容如下:
{ "extends": "./tsconfig.paths.json", "compilerOptions": { "target": "ES2018", "module": "ESNext", "moduleResolution": "bundler", "noEmit": true, "baseUrl": "./", "allowJs": false, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "resolveJsonModule": true, "isolatedModules": true, "incremental": true, "removeComments": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "alwaysStrict": true, "noFallthroughCasesInSwitch": true, "noImplicitAny": true, "noImplicitOverride": true, "noImplicitReturns": true, "noImplicitThis": true, "noPropertyAccessFromIndexSignature": true, "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "typeRoots": ["./node_modules/@types", "./@types"] } }
Webpack打包成功,但无法用Node.js运行产物文件,请问为何打包产物中是require语句而非import,导致无法在ESM环境正常运行?
问题出在webpack-node-externals插件上——它默认会把外部依赖以CommonJS的require语法输出,哪怕你配置了ESM的打包模式。
要解决这个问题,按以下步骤调整配置:
- 修改webpack.config.ts中的externals配置:给
nodeExternals添加importType: 'module'参数,强制它生成ESM风格的导入语句:
externals: [ nodeExternals({ modulesDir: path.join(__dirname, 'node_modules'), importType: 'module', allowlist: [] // 若有需要打包进产物的外部依赖,在此添加,否则留空 }) ],
检查tsconfig.build.json配置:确保该文件的
module和moduleResolution与tsconfig.base.json保持一致,即"module": "ESNext"、"moduleResolution": "bundler",避免ts-loader编译出CommonJS代码。确认Webpack的ESM输出配置:你已经开启了
experiments.outputModule: true,这是Webpack输出ESM模块的关键开关,保持该配置即可。
修改后重新打包,产物中的外部依赖会替换为import语句,即可在ESM环境正常运行。
内容的提问来源于stack exchange,提问作者Tal Rofe

