Как решить «[ERR_REQUIRE_ESM]: require () модуля ES» для случая Webpack?

Я попытался импортировать w3c-html-валидатор, но получил ошибку

Ошибка [ERR_REQUIRE_ESM]: require() модуля ES D:(...)\node_m odules\w3c-html-validator\node_modules\chalk\source\index.js из D:(...)\node_modules\w3c-html-validator\dist\w3c-html-validator.umd.cjs не поддерживается.

Похоже, эта проблема связана с мелом, зависимостью w3c-html-validator.

Я использую Webpack с webpack-node-externals. Благодаря этому ключевые слова export/import доступны, даже несмотря на то, что вывод представляет собой CommonJS и предназначен для выполнения простым Node.js. Это должно быть конкретное решение для этого случая, а не просто «добавь type: module к своему package.json».

Если возможно, я хочу избежать добавления type: module в package.json, потому что это может вызвать другие ошибки. В настоящее время у меня около 30 зависимостей (см. приложение), и ни одна из них не требует type: module.

Приложение

Настройка веб-пакета

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"
      }
    },

    /* [ Theory ] Valid non-undefined values are only "development", "production" and "none". */
    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: {
            /* [ Theory ] 'ForkTypeScriptCheckerWebpackPlugin' will execute the type checking. */
            transpileOnly: true
          }
        }
      ]
    },

    resolve: {
      extensions: [ ".ts", ".js" ],
      alias: { /* ... */ }
    },

    plugins: [
      new Webpack.DefinePlugin({
        __IS_DEVELOPMENT_BUILDING_MODE__,
        __IS_PRODUCTION_BUILDING_MODE__
      }),
      new ForkTypeScriptCheckerWebpackPlugin({
        typescript: {
          /* [ Theory ] The default value is 'context', but the 'tsconfig.json' is 1 level above 'context'. */
          configFile: Path.resolve(__dirname, "tsconfig.json")
        }
      }),
      new ESLintPlugin({
        extensions: [ "js", "ts" ],
        failOnWarning: __IS_PRODUCTION_BUILDING_MODE__
      })
    ]
  };
}

Текущие зависимости

{
  "dependencies": {
    "@typescript-eslint/eslint-plugin": "5.57.0",
    "@typescript-eslint/parser": "5.57.0",
    "@vue/compiler-sfc": "3.2.47",
    "@webdiscus/pug-loader": "2.10.4",
    "@yamato-daiwa/es-extensions": 1.6.9,
    "@yamato-daiwa/es-extensions-nodejs": "1.6.0-alpha.9",
    "@yamato-daiwa/style_guides": "0.0.47",
    "access-sniff": "3.2.0",
    "autoprefixer": "10.4.14",
    "browser-sync": "2.27.11",
    "cheerio": "1.0.0-rc.1",
    "css-loader": "6.7.3",
    "cssnano": "5.1.14",
    "eslint": "8.37.0",
    "eslint-plugin-import": "2.27.5",
    "eslint-plugin-node": "11.1.0",
    "eslint-webpack-plugin": "4.0.0",
    "fork-ts-checker-webpack-plugin": "7.3.0",
    "glob": "7.2.0",
    "gulp": "4.0.2",
    "gulp-data": "1.3.1",
    "gulp-debug": "4.0.0",
    "gulp-html-prettify": "0.0.1",
    "gulp-if": "3.0.0",
    "gulp-imagemin": "7.1.0",
    "gulp-intercept": "0.1.0",
    "gulp-plumber": "1.2.1",
    "gulp-postcss": "9.0.1",
    "gulp-pug": "5.0.0",
    "gulp-sourcemaps": "3.0.0",
    "gulp-stylus": "2.7.1",
    "imagemin-pngquant": "9.0.2",
    "json5-loader": "4.0.1",
    "minimatch": "5.1.1",
    "node-notifier": "10.0.1",
    "prettier": "2.8.7",
    "pug-lint": "2.6.0",
    "pug-plain-loader": "1.1.0",
    "rev-hash": "4.0.0",
    "stlint": "1.0.65",
    "stream-combiner2": "1.1.1",
    "style-loader": "3.3.1",
    "stylus-loader": "7.1.0",
    "ts-loader": "9.4.2",
    "vinyl": "2.2.1",
    "vue": "3.2.47",
    "vue-loader": "17.0.1",
    "vue-style-loader": "4.1.3",
    "vue-tsc": "1.2.0",
    "w3c-html-validator": "1.3.3",
    "webpack": "5.76.2",
    "webpack-node-externals": "3.0.0",
    "webpack-stream": "7.0.0",
    "worker-loader": "3.0.8",
    "yaml-loader": "0.8.0"
  },
  "devDependencies": {
    "@types/browser-sync": "2.26.3",
    "@types/cheerio": "0.22.31",
    "@types/cssnano": "5.0.0",
    "@types/glob": "7.2.0",
    "@types/gulp": "4.0.10",
    "@types/gulp-debug": "2.0.32",
    "@types/gulp-html-prettify": "0.0.2",
    "@types/gulp-if": "0.0.34",
    "@types/gulp-imagemin": "8.0.1",
    "@types/gulp-intercept": "0.1.1",
    "@types/gulp-plumber": "0.0.33",
    "@types/gulp-postcss": "8.0.2",
    "@types/gulp-sourcemaps": "0.0.35",
    "@types/gulp-stylus": "2.7.4",
    "@types/node": "18.13.0",
    "@types/node-notifier": "8.0.2",
    "@types/pug": "2.0.6",
    "@types/webpack-node-externals": "2.5.3",
    "@types/webpack-stream": "3.2.12",
    "ts-node": "10.9.1",
    "typescript": "5.0.3",
    "webpack-cli": "5.0.1"
  }
}

Зачем использовать webpack с плагином webpack-node-externals? Этот плагин предотвращает объединение всех node_modules вместе с вашим исходным кодом, который, по-видимому, является источником вашей проблемы.

VonC 16.05.2023 11:11

@VonC Потому что объединение зависимостей Node.js может вызвать множество предупреждений. Рекомендуется использовать webpack-node-external.

Takeshi Tokugawa YD 16.05.2023 13:43

Вы пробовали просто определить внешние объекты без webpack-node-externals?

Slbox 18.05.2023 00:43

@Slbox определяет внешние объекты без webpack-node-externals? Не могли бы вы поделиться ссылкой с примером?

Takeshi Tokugawa YD 18.05.2023 03:48

Попробуйте использовать другую версию, которая не зависит от мела, которая представляет собой чистый esm начиная с версии 5. Лучше выберите свои зависимости или поймите, как работать с границами esm/cjs.

morganney 20.05.2023 15:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
103
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если проблема заключается в том, что вы используете webpack-node-externals, возможно, вам повезет с кодом, как показано ниже.

Обратите внимание, что это решение зависит от того, знаете ли вы, какие модули являются нативными зависимостями (что не займет много времени, чтобы выяснить это).

Вы можете обновить начало своей конфигурации следующим образом:

return {
    target: "node",
    externals: ['sharp', 'another-native-node-dep'], // Add your native dependency names to the array and remove my examples
    context: SOURCE_CODE_ROOT_DIRECTORY_ABSOLUTE_PATH,
    entry: { EntryPoint: "./EntryPoint.ts" },

Спасибо за ответ. К сожалению, прекращение использования webpack-node-externals вызвало другую ошибку, "development" = 'development' SyntaxError: Invalid left-hand side in assignment. Это относится к коду, автоматически сгенерированному Webpack. Похоже, просто перестать использовать "webpack-node-externals" невозможно, потому что в принципе он работает нормально.

Takeshi Tokugawa YD 19.05.2023 04:45
Ответ принят как подходящий

Как вы заметили, проблема в том, что w3c-html-validator использует ESM-версию chalk. По сути, сопровождающий w3c-html-validator неправильно построил вариант UMD/CJS, пытаясь require одну из собственных зависимостей ESM 🙃.

Простое решение: понизьте версию w3c-html-validator до v0.8.1, которая использует версию chalk для CommonJS.

Решение Babel: добавьте babel-loader в сборку веб-пакета и транспилируйте зависимость chalk, чтобы преобразовать import/export в require/module.exports, то есть ESM в CJS.

Ваша конфигурация babel должна выглядеть примерно так (добавьте @babel/preset-env, если ее еще нет в ваших devDependencies):

{
  "presets": [
    ["@babel/preset-env", {
      "modules": "commonjs"
    }]
  ]
}

Ваша обновленная конфигурация веб-пакета должна включать что-то вроде этого:

module: {
  rules: [
    {
      test: /\.(t|j)sx?$/,
      include: filename => {
        return /node_modules/chalk/.test(filename)
      },
      use: ['babel-loader']
    }
  ]
}

Другие вопросы по теме