Ошибка "Error [ERR_REQUIRE_ESM]: require() of ES Module" в случае Webpack + "webpack-node-externals"
Описание проблемы
Собранная Webpack-ом Node.js-утилита (основана на Gulp и Webpack) выдаёт ошибку при запуске:
D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:14040
module.exports = require("gulp-imagemin");
^
Error [ERR_REQUIRE_ESM]: require() of ES Module D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\node_modules\gulp-imagemin\index.js from D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js not supported.
Instead change the require of index.js in D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js to a dynamic import() which is available in all CommonJS modules.
at gulp-imagemin (D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:14040:18)
at __webpack_require__ (D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:14370:42)
at ./ProjectBuilding/AssetsProcessing/Images/ImagesProcessor.ts (D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:1093:41)
at __webpack_require__ (D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:14370:42)
at ./ProjectBuilder.ts (D:\IntelliJ IDEA\InHouseDevelopment\Example\MainPackage\EntryPoint.js:166:43) {
code: 'ERR_REQUIRE_ESM'
}
Node.js v22.0.0
Как можно понять из сообщения об ошибке, проблема связана с gulp-imagemin. Однако есть и другие пакеты, которые могут вызывать подобную ошибку, например w3c-html-validator, причём ошибку вызывает не он сам, его зависимость - chalk.
Ремарка
Непонятные какие-то вещи происходят в Node.js с модулями.
С одной стороны, CommonJS-модули являются модулями по умолчанию (может я что-то пропустил в новых версиях Node.js, но вроде как чтобы активировать ESM-режим, нужно добавить в package.json "type": "module"
).
С другой стороны, осуществляется агитация по переходу на ECMAScript-модули. Так или иначе, если у нас много зависимостей, то в "node_modules" будет в итоге мешанина из CommonJS-модулей и ESM-модулей. И ладно бы системы сборки проектов знали, что с этим делать, но похоже, не всегда знают.
В репозитории gulp-imagemin даже есть билет, озаглавленный "8.0.0 - ESM only? Serious?", что указывает на то, что ECMAScript-модули - пока ещё отнюдь не сама собой разумеющаяся вещь.
Особенности моего случая
Моя утилита собирается Webpack-ом, а для подключения одних файлов в другие используется ключевое слово import
. Благодаря настройке target: "node"
, Webpack знает, во что надо собирать код.
Следуя рекомендациям, я использую webpack-node-externals, чтобы не подшивать Node.js-зависимости в Webpack-сборке (а подшивать их - не вариант: будет много предупреждений, если не ошибок). Таким образом, ошибка происходит "Error [ERR_REQUIRE_ESM]: require() of ES Module" происходит уже при запуске собранной утилиты, когда Node.js пытается импортировать (но не факт, что через ключевое слово import
) модули пакета "gulp-imagemin".
Листинг Webpack-пресета
import Webpack from "webpack";
import Path from "path";
import NodeExternalsPlugin from "webpack-node-externals";
import ForkTypeScriptCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import ESLintPlugin from "eslint-webpack-plugin";
import type { ArbitraryObject } from "@yamato-daiwa/es-extensions";
export default function generateConfiguration(
_environment: ArbitraryObject, commandArguments: ArbitraryObject
): Webpack.Configuration {
const SOURCE_CODE_ROOT_DIRECTORY_ABSOLUTE_PATH: string = Path.resolve(__dirname, "Source");
const __IS_DEVELOPMENT_BUILDING_MODE__: boolean = commandArguments.mode === "development";
const __IS_PRODUCTION_BUILDING_MODE__: boolean = commandArguments.mode === "production";
return {
target: "node",
context: SOURCE_CODE_ROOT_DIRECTORY_ABSOLUTE_PATH,
entry: { EntryPoint: "./EntryPoint.ts" },
output: {
path: __dirname,
filename: "[name].js",
library: {
type: "commonjs"
}
},
mode: __IS_DEVELOPMENT_BUILDING_MODE__ ? "development" : "production",
watch: __IS_DEVELOPMENT_BUILDING_MODE__,
optimization: {
emitOnErrors: __IS_DEVELOPMENT_BUILDING_MODE__,
minimize: __IS_PRODUCTION_BUILDING_MODE__
},
node: {
__dirname: false
},
devtool: false,
externals: [
NodeExternalsPlugin({
allowlist: [ "rev-hash" ]
})
],
module: {
rules: [
{
test: /\.ts$/u,
loader: "ts-loader",
options: {
transpileOnly: true
}
}
]
},
resolve: {
extensions: [ ".ts", ".js" ],
alias: {
// ...
}
},
plugins: [
new Webpack.DefinePlugin({
__IS_DEVELOPMENT_BUILDING_MODE__,
__IS_PRODUCTION_BUILDING_MODE__
}),
new ForkTypeScriptCheckerWebpackPlugin({
typescript: {
configFile: Path.resolve(__dirname, "tsconfig.json")
}
}),
new ESLintPlugin({
extensions: [ "js", "ts" ],
failOnWarning: __IS_PRODUCTION_BUILDING_MODE__
})
]
};
}