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
Freigeben: 2024-12-28 20:49:18
Original
716 Leute haben es durchsucht

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
Nach dem Login kopieren
Nach dem Login kopieren
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.
Nach dem Login kopieren
Nach dem Login kopieren

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
Nach dem Login kopieren
Nach dem Login kopieren

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.
Nach dem Login kopieren
Nach dem Login kopieren

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;
}
Nach dem Login kopieren

erfordern

cjs-Modul mit einem require()-Aufruf:

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

--format=cjs

Wir führen esbuild mit den folgenden Argumenten aus:

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

export function getVersion() {
  return version;
}
Nach dem Login kopieren

und den folgenden Code:

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

exports.getVersion = function() {
  return version;
}
Nach dem Login kopieren

statischer Import

Erzeugt Folgendes, was einwandfrei funktioniert:

esbuild --bundle --format=cjs --platform=node --outfile=bundle.cjs src/main.js
Nach dem Login kopieren

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);
})();
Nach dem Login kopieren

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);
})();
Nach dem Login kopieren

--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);
})();
Nach dem Login kopieren

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");
        ╵                     ~~~~~
Nach dem Login kopieren

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
Nach dem Login kopieren

--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);
})();
Nach dem Login kopieren

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.
Nach dem Login kopieren

erzeugt Folgendes, was einwandfrei funktioniert:

esbuild --bundle --format=esm --platform=node --outfile=bundle.mjs src/main.js
Nach dem Login kopieren

require() von node:*-Modulen

src/main.js

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

console.log(getVersion());
Nach dem Login kopieren

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());
Nach dem Login kopieren

Die Ausführung schlägt jedoch fehl:

import { getVersion } from "require";

console.log(getVersion());
Nach dem Login kopieren

--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)());
Nach dem Login kopieren

mit:

src/index.js

Error: Dynamic require of "node:process" is not supported
Nach dem Login kopieren

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
Nach dem Login kopieren

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!

Quelle:dev.to
Erklärung dieser Website
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
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage