Heim >Web-Frontend >js-Tutorial >Node.js und esbuild: Vorsicht vor der Vermischung von cjs und esm

Node.js und esbuild: Vorsicht vor der Vermischung von cjs und esm

Patricia Arquette
Patricia ArquetteOriginal
2024-12-28 20:49:18719Durchsuche

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

TL;DR

Wenn Sie esbuild verwenden, um Code mit --platform=node zu bündeln, der von NPM-Paketen mit einer Mischung aus CJS- und ESM-Einstiegspunkten abhängt, verwenden Sie die folgende Faustregel:

  • Wenn Sie --bundle verwenden, setzen Sie --format auf cjs. Dies funktioniert in allen Fällen, außer bei ESM-Modulen mit Wait auf oberster Ebene.
    • --format=esm kann verwendet werden, erfordert jedoch eine Polyfüllung wie diese.
  • Wenn Sie --packages=external verwenden, setzen Sie --format auf esm.

Wenn Sie sich über den Unterschied zwischen CJS und ESM wundern, werfen Sie einen Blick auf Node.js: Eine kurze Geschichte von CJS, Bundlern und ESM.

Symptom

Beim Ausführen von gebündeltem esbuild-Code mit --platform=node ist möglicherweise einer der folgenden Laufzeitfehler aufgetreten:

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.

Ursache

Dies liegt an einer der folgenden Einschränkungen:

  • esbuilds ESM-zu-CJS-Transformationen (und umgekehrt).
  • Node.js CJS/ESM-Interoperabilität.

Analyse

esbuild verfügt über begrenzte Transformationsmöglichkeiten zwischen ESM und CJS. Darüber hinaus werden einige Szenarios zwar von esbuild unterstützt, aber nicht von Node.js selbst. Ab esbuild@0.24.0 fasst die folgende Tabelle zusammen, was unterstützt wird:

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

* Unterstützt von esbuild, aber nicht von Node.js

** Bezieht sich auf npm-Pakete oder relative Pfaddateien.

*** Benutzermodule werden mit einigen Einschränkungen unterstützt: __dirname und __filename werden ohne Polyfill nicht unterstützt.

**** node:*-Module können mit derselben Polyfüllung unterstützt werden.

Was folgt, ist eine detaillierte Beschreibung dieser Szenarien ohne die Verwendung von Polyfills:


npm-Pakete

Wir verwenden die folgenden Beispiel-NPM-Pakete:

statischer Import

ESM-Modul mit statischem Import:

Error: Dynamic require of "<module_name>" is not supported

dynamischer Import

esm-Modul mit einem dynamischen import() innerhalb einer asynchronen Funktion:

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.

Top-Level-Warten

ESM-Modul mit einem dynamischen import() und einem Wait auf oberster Ebene:

import { version } from "node:process";

export function getVersion() {
  return version;
}

erfordern

cjs-Modul mit einem require()-Aufruf:

export async function getVersion() {
  const { version } = await import("node:process");
  return version;
}

--format=cjs

Wir führen esbuild mit den folgenden Argumenten aus:

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

export function getVersion() {
  return version;
}

und den folgenden Code:

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

exports.getVersion = function() {
  return version;
}

statischer Import

Erzeugt Folgendes, was einwandfrei funktioniert:

esbuild --bundle --format=cjs --platform=node --outfile=bundle.cjs src/main.js

dynamischer Import()

Erzeugt Folgendes, was einwandfrei funktioniert:

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

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

  console.log(version);
})();

Beachten Sie, dass der dynamische import() nicht in einen require() umgewandelt wird, da er auch in CJS-Modulen zulässig ist.

Top-Niveau erwartet Sie

esbuild schlägt mit folgendem Fehler fehl:

// 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=external

Die Verwendung von --packages=external ist mit allen npm-Paketen erfolgreich:

// (...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);
})();

produziert:

[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");
        ╵                     ~~~~~

Sie können jedoch alle nicht ausgeführt werden, da Nodes.js es CJS-Modulen nicht erlaubt, ESM-Module zu importieren:

esbuild --packages=external --format=cjs --platform=node --outfile=bundle.cjs src/main.js

--format=esm

Wir führen esbuild jetzt mit den folgenden Argumenten aus:

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

require() von Benutzermodulen

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.

erzeugt Folgendes, was einwandfrei funktioniert:

esbuild --bundle --format=esm --platform=node --outfile=bundle.mjs src/main.js

require() von node:*-Modulen

src/main.js

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

console.log(getVersion());

erzeugt Folgendes:

// (...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());

Die Ausführung schlägt jedoch fehl:

import { getVersion } from "require";

console.log(getVersion());

--packages=external

Die Verwendung von --packages=external ist mit allen NPM-Paketen erfolgreich, einschließlich derer mit CJS-Einstiegspunkten. Zum Beispiel:

// (...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)());

mit:

src/index.js

Error: Dynamic require of "node:process" is not supported

erzeugt eine nahezu wörtliche Ausgabe, die einwandfrei funktioniert, da ESM-Module NPM-Pakete mit CJS-Einstiegspunkten importieren können:

esbuild --packages=external --format=esm --platform=node --outfile=bundle.mjs src/main.js

Abschluss

Ich hoffe, dass Sie diesen Beitrag hilfreich finden, um jetzt und in Zukunft Fehler bei Esbuild-Ausgaben zu beheben. Teilen Sie mir unten Ihre Gedanken mit!

Das obige ist der detaillierte Inhalt vonNode.js und esbuild: Vorsicht vor der Vermischung von cjs und esm. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn