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

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

Dec 28, 2024 pm 08:49 PM

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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

前端热敏纸小票打印遇到乱码问题怎么办? 前端热敏纸小票打印遇到乱码问题怎么办? Apr 04, 2025 pm 02:42 PM

前端热敏纸小票打印的常见问题与解决方案在前端开发中,小票打印是一个常见的需求。然而,很多开发者在实...

神秘的JavaScript:它的作用以及为什么重要 神秘的JavaScript:它的作用以及为什么重要 Apr 09, 2025 am 12:07 AM

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

谁得到更多的Python或JavaScript? 谁得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript开发者的薪资没有绝对的高低,具体取决于技能和行业需求。1.Python在数据科学和机器学习领域可能薪资更高。2.JavaScript在前端和全栈开发中需求大,薪资也可观。3.影响因素包括经验、地理位置、公司规模和特定技能。

如何实现视差滚动和元素动画效果,像资生堂官网那样?
或者:
怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? 如何实现视差滚动和元素动画效果,像资生堂官网那样? 或者: 怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? Apr 04, 2025 pm 05:36 PM

实现视差滚动和元素动画效果的探讨本文将探讨如何实现类似资生堂官网(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript难以学习吗? JavaScript难以学习吗? Apr 03, 2025 am 12:20 AM

学习JavaScript不难,但有挑战。1)理解基础概念如变量、数据类型、函数等。2)掌握异步编程,通过事件循环实现。3)使用DOM操作和Promise处理异步请求。4)避免常见错误,使用调试技巧。5)优化性能,遵循最佳实践。

JavaScript的演变:当前的趋势和未来前景 JavaScript的演变:当前的趋势和未来前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? 如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? Apr 04, 2025 pm 05:09 PM

如何在JavaScript中将具有相同ID的数组元素合并到一个对象中?在处理数据时,我们常常会遇到需要将具有相同ID�...

Zustand异步操作:如何确保useStore获取的最新状态? Zustand异步操作:如何确保useStore获取的最新状态? Apr 04, 2025 pm 02:09 PM

zustand异步操作中的数据更新问题在使用zustand状态管理库时,经常会遇到异步操作导致数据更新不及时的问题。�...

See all articles