0

我正在构建一个应用程序,使用Nx进行模块化,使用Geckos进行服务器部分。使用 Nx 的 Node 默认 Webpack 5 配置,我在运行应用程序时收到此错误:

/Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:8
module.exports = require("@geckos.io/server");
                 ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/node_modules/@geckos.io/server/lib/index.js from /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js not supported.
Instead change the require of index.js in /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js to a dynamic import() which is available in all CommonJS modules.
(...)

以下是重现的步骤:

$ npx create-nx-workspace@latest nx-geckos-test
Need to install the following packages:
  create-nx-workspace@latest
Ok to proceed? (y) y
✔ What to create in the new workspace · express
✔ Application name                    · geckos-server-app
✔ Use Nx Cloud? (It's free and doesn't require registration.) · No

>  NX  Nx is creating your v13.4.1 workspace.

  To make sure the command works reliably in all environments, and that the preset is applied correctly,
  Nx will run "npm install" several times. Please wait.

✔ Installing dependencies with npm
✔ Nx has successfully created the workspace.
$ npm run start

> nx-geckos-test@0.0.0 start
> nx serve

> nx run geckos-server-app:serve
chunk (runtime: main) main.js (main) 552 bytes [entry] [rendered]
webpack compiled successfully (26ac6f288b082854)
Debugger listening on ws://localhost:9229/70a1d9b4-af2b-4fc9-90f7-f3502e5fdf2e
Debugger listening on ws://localhost:9229/70a1d9b4-af2b-4fc9-90f7-f3502e5fdf2e
For help, see: https://nodejs.org/en/docs/inspector
Issues checking in progress...
Listening at http://localhost:3333/api

在这个阶段,默认的 Express 应用程序构建并运行。

现在我将apps/geckos-server-app/src/main.tsREADME 中的内容替换为 Geckos 服务器示例:

import geckos from '@geckos.io/server'

const io = geckos()

io.listen(3000) // default port is 9208

io.onConnection(channel => {
  channel.onDisconnect(() => {
    console.log(`${channel.id} got disconnected`)
  })

  channel.on('chat message', data => {
    console.log(`got ${data} from "chat message"`)
    // emit the "chat message" data to all channels in the same room
    io.room(channel.roomId).emit('chat message', data)
  })
})

...并使用安装 geckos 服务器包

$ npm install @geckos.io/server

added 4 packages, and audited 785 packages in 7s

87 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

好的,是时候再次运行它了:

$ npm run start

> nx-geckos-test@0.0.0 start
> nx serve


> nx run geckos-server-app:serve
chunk (runtime: main) main.js (main) 610 bytes [entry] [rendered]
webpack compiled successfully (90920432a9d246ea)
Debugger listening on ws://localhost:9229/dc6d9e1a-1a15-432f-a46c-d2ac83b923ea
Debugger listening on ws://localhost:9229/dc6d9e1a-1a15-432f-a46c-d2ac83b923ea
For help, see: https://nodejs.org/en/docs/inspector

/Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:8
module.exports = require("@geckos.io/server");
                 ^
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/node_modules/@geckos.io/server/lib/index.js from /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js not supported.
Instead change the require of index.js in /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js to a dynamic import() which is available in all CommonJS modules.
    at Object.@geckos.io/server (/Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:8:18)
    at __webpack_require__ (/Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:32:41)
    at /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:45:18
    at /Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:59:3
    at Object.<anonymous> (/Users/hgrzeskowiak/hugo-game/fresh-nx-geckos/nx-geckos-test/dist/apps/geckos-server-app/main.js:64:12)

我测试了很多 Webpack 配置覆盖并type: "module"在 package.json 中使用,但无济于事。无论我更改什么 webpack 设置,输出 main.js 看起来都基本相同。我尝试在应用程序的 webpack 覆盖配置中更改的内容:

module.exports = function(webpackConfig, context) {
  webpackConfig.target = "async-node16";
  webpackConfig.output.libraryTarget = "module";
  webpackConfig.output.library = {type: "module"};
  webpackConfig.output.chunkFormat = "module";
  webpackConfig.experiments.outputModule = true;
  console.log("webpack config:");
  console.log(webpackConfig);
  console.log("module rules:");
  console.log(webpackConfig.module.rules);
  return webpackConfig;
}

控制台登录配置验证脚本是否正在运行。

当我在转译输出中手动更改为时,该应用程序可以工作requireimport但这不是一个可持续的解决方案。

我怀疑这个错误与 Geckos 是一个 ESM 包有关,而 Webpack 只使用require而不是import它应该使用的,但我找不到任何改变这种行为的方法。

有任何想法吗?

  • 节点版本 16.13.0
  • Nx 版本 13.4.1
  • Webpack(Nx 的传递 dep):5.65.0
4

1 回答 1

0

这是我能找到的最干净的解决方案,它适用于 4.6 之前的 Type Script 版本(应该会添加对此功能的支持)。这实质上是将 ESM 支持修补到具有较旧 TypeScript 版本(包括撰写本文时的最新版本)的 WebPack 中。

一个自定义 Webpack 插件,将外部模块的类型更改为import.

// TypeScript before version 4.6 does not support transpiling ESM imports to
// `import()`, but uses `require()` instead. NodeJS does not support the use
// of `require() for ECMAScript modules (ESM).
//
// Good reads:
// https://www.typescriptlang.org/docs/handbook/esm-node.html
// https://devblogs.microsoft.com/typescript/announcing-typescript-4-5/#esm-nodejs

/**
 * A Webpack 5 plugin that can be passed a list of packages that are of type
 * ESM. The typescript compiler will then be instructed to use the `import`
 * external type.
 */
class ESMLoader {
  static defaultOptions = {
    esmPackages: "all"
  };

  constructor(options = {}) {
    this.options = { ...ESMLoader.defaultOptions, ...options };
  }

  apply(compiler) {
    compiler.hooks.compilation.tap(
      "ECMAScript Module (ESM) Loader. Turns require() into import()",
      (
        compilation
      ) => {
        compilation.hooks.buildModule.tap("Hello World Plugin", (module) => {
            if (
              module.type === "javascript/dynamic" &&
              (
                this.options.esmPackages === "all" ||
                this.options.esmPackages.includes(module.request)
              )
            ) {
              // All types documented at
              // https://webpack.js.org/configuration/externals/#externalstype
              module.externalType = "import";
            }
          }
        )
        ;
      }
    );
  }
}

module.exports = function(webpackConfig, context) {
  // NodeJS supports dynamic imports since version 12.
  webpackConfig.target = "node16";
  webpackConfig.plugins.push(
    // Manually specify the ESM modules or leave blank for using `import()`
    // on all packages.
    //new ESMLoader()
    new ESMLoader({esmPackages: "@geckos.io/server"})
  );
  return webpackConfig;
};

可以通过以下方式在 Nx 应用程序上设置自定义 webpack 配置project.json

{
  "targets": {
    "build": {
      "executor": "@nrwl/node:build",
      "options": {
       ...
        "webpackConfig": "apps/your-app/webpackConfig.js"
      },
      ...

此解决方案还需要tsconfig.json将模块类型设置为比现代更现代的类型commonjs,例如es2020

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "../../dist/out-tsc",
    "module": "es2020",
    "types": ["node"],
  },
  "exclude": ["**/*.spec.ts", "**/*.test.ts"],
  "include": ["**/*.ts"]
}

完成此操作后,可以使用通常的nx serve.

于 2021-12-26T15:26:49.967 回答