首页 > web前端 > js教程 > Node.js 和 esbuild:小心混合使用 cjs 和 esm

Node.js 和 esbuild:小心混合使用 cjs 和 esm

Patricia Arquette
发布: 2024-12-28 20:49:18
原创
698 人浏览过

Node.js and esbuild: beware of mixing cjs and esm

长话短说

当使用 esbuild 将代码与 --platform=node 捆绑在一起(依赖于混合了 cjs 和 esm 入口点的 npm 包)时,请使用以下经验法则:

  • 使用--bundle时,将--format设置为cjs。这适用于除具有顶级等待的 esm 模块之外的所有情况。
    • --format=esm 可以使用,但需要一个像这样的polyfill。
  • 使用--packages=external时,将--format设置为esm。

如果您想知道 cjs 和 esm 之间的区别,请查看 Node.js:cjs、捆绑程序和 esm 的简史。

症状

使用 --platform=node 执行 esbuild 捆绑代码时,您可能会遇到以下运行时错误之一:

Error: Dynamic require of "<module_name>" is not supported
登录后复制
登录后复制
Error [ERR_REQUIRE_ESM]: require() of ES Module (...) from (...) not supported.
Instead change the require of (...) in (...) to a dynamic import() which is available in all CommonJS modules.
登录后复制
登录后复制

原因

这是因为以下限制之一:

  • esbuild 的 esm 到 cjs(反之亦然)转换。
  • Node.js cjs/esm 互操作性。

分析

esbuild 在 esm 和 cjs 之间的转换能力有限。此外,某些场景虽然受 esbuild 支持,但 Node.js 本身并不支持。从 esbuild@0.24.0 开始,下表总结了支持的内容:

Format Scenario Supported?
cjs static import Yes
cjs dynamic import() Yes
cjs top-level await No
cjs --packages=external of esm entry point No*
esm require() of user modules** Yes***
esm require() of node:* modules No****
esm --packages=external of cjs entry point Yes

* esbuild 支持,但 Node.js 不支持

** 指 npm 包或相对路径文件。

*** 支持用户模块,但有一些注意事项:如果没有腻子填充,则不支持 __dirname 和 __filename。

**** 节点:* 模块可以使用相同的 polyfill 支持。

以下是这些场景的详细描述,不使用任何填充:


npm 包

我们将使用以下示例 npm 包:

静态导入

具有静态导入的esm模块:

Error: Dynamic require of "<module_name>" is not supported
登录后复制
登录后复制

动态导入

esm 模块在异步函数中具有动态 import():

Error [ERR_REQUIRE_ESM]: require() of ES Module (...) from (...) not supported.
Instead change the require of (...) in (...) to a dynamic import() which is available in all CommonJS modules.
登录后复制
登录后复制

顶级等待

具有动态 import() 和顶级等待的 esm 模块:

import { version } from "node:process";

export function getVersion() {
  return version;
}
登录后复制

要求

带有 require() 调用的 cjs 模块:

export async function getVersion() {
  const { version } = await import("node:process");
  return version;
}
登录后复制

--格式=cjs

我们将使用以下参数运行 esbuild:

const { version } = await import("node:process");

export function getVersion() {
  return version;
}
登录后复制

和以下代码:

const { version } = require("node:process");

exports.getVersion = function() {
  return version;
}
登录后复制

静态导入

产生以下运行良好的内容:

esbuild --bundle --format=cjs --platform=node --outfile=bundle.cjs src/main.js
登录后复制

动态导入()

产生以下运行良好的内容:

import { getVersion } from "{npm-package}";

(async () => {
  // version can be `string` or `Promise<string>`
  const version = await getVersion();

  console.log(version);
})();
登录后复制

注意动态 import() 没有转换为 require(),因为它在 cjs 模块中也是允许的。

顶级等待

esbuild 失败并出现以下错误:

// node_modules/static-import/index.js
var import_node_process = require("node:process");
function getVersion() {
  return import_node_process.version;
}

// src/main.js
(async () => {
  const version2 = await getVersion();
  console.log(version2);
})();
登录后复制

--packages=外部

对所有 npm 包使用 --packages=external 都会成功:

// (...esbuild auto-generated helpers...)

// node_modules/dynamic-import/index.js
async function getVersion() {
  const { version } = await import("node:process");
  return version;
}

// src/main.js
(async () => {
  const version = await getVersion();
  console.log(version);
})();
登录后复制

产生:

[ERROR] Top-level await is currently not supported with the "cjs" output format

    node_modules/top-level-await/index.js:1:20:
      1 │ const { version } = await import("node:process");
        ╵                     ~~~~~
登录后复制

但是,它们都无法运行,因为 Nodes.js 不允许 cjs 模块导入 esm 模块:

esbuild --packages=external --format=cjs --platform=node --outfile=bundle.cjs src/main.js
登录后复制

--格式=esm

我们现在将使用以下参数运行 esbuild:

var npm_package_import = require("{npm-package}");
(async () => {
  const version = await (0, npm_package_import.getVersion)();
  console.log(version);
})();
登录后复制

用户模块的 require()

src/main.js

/(...)/bundle.cjs:1
var import_static_import = require("static-import");
                           ^

Error [ERR_REQUIRE_ESM]: require() of ES Module /(...)/node_modules/static-import/index.js from /(...)/bundle.cjs not supported.
Instead change the require of index.js in /(...)/bundle.cjs to a dynamic import() which is available in all CommonJS modules.
登录后复制

产生以下运行良好的结果:

esbuild --bundle --format=esm --platform=node --outfile=bundle.mjs src/main.js
登录后复制

节点的 require():* 模块

src/main.js

const { getVersion } = require("static-import");

console.log(getVersion());
登录后复制

产生以下内容:

// (...esbuild auto-generated helpers...)

// node_modules/static-import/index.js
var static_import_exports = {};
__export(static_import_exports, {
  getVersion: () => getVersion
});
import { version } from "node:process";
function getVersion() {
  return version;
}
var init_static_import = __esm({
  "node_modules/static-import/index.js"() {
  }
});

// src/main.js
var { getVersion: getVersion2 } = (init_static_import(), __toCommonJS(static_import_exports));
console.log(getVersion2());
登录后复制

但是,它无法运行:

import { getVersion } from "require";

console.log(getVersion());
登录后复制

--packages=外部

对所有 npm 包使用 --packages=external 都会成功,包括那些带有 cjs 入口点的包。例如:

// (...esbuild auto-generated helpers...)

var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
  if (typeof require !== "undefined") return require.apply(this, arguments);
  throw Error('Dynamic require of "' + x + '" is not supported');
});

// (...esbuild auto-generated helpers...)

// node_modules/require/index.js
var require_require = __commonJS({
  "node_modules/require/index.js"(exports) {
    var { version } = __require("node:process");
    exports.getVersion = function() {
      return version;
    };
  }
});

// src/main.js
var import_require = __toESM(require_require());
console.log((0, import_require.getVersion)());
登录后复制

与:

src/index.js

Error: Dynamic require of "node:process" is not supported
登录后复制

产生几乎逐字输出,运行得很好,因为 esm 模块可以使用 cjs 入口点导入 npm 包:

esbuild --packages=external --format=esm --platform=node --outfile=bundle.mjs src/main.js
登录后复制

结论

我希望您发现这篇文章对于现在和将来解决 esbuild 输出问题很有用。请在下面告诉我你的想法!

以上是Node.js 和 esbuild:小心混合使用 cjs 和 esm的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板