Beispiel
p-limit 7.3.1: require() an ESM-only npm package from CommonJS without ERR_REQUIRE_ESM or ERR_REQUIRE_ASYNC_MODULE
Verifiziertes Beispiel für npm p-limit 7.3.1: require() an ESM-only npm package from CommonJS without ERR_REQUIRE_ESM or ERR_REQUIRE_ASYNC_MODULE. Der…
sha256:c7071dcdb5556a42d993b28cdf9b1adba12d8ef2436ee1bdd1b9029e2acff699
Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen.
Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen.
MIT-0
Ausführungsbelege
Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.
- Beleggrundlage
- Signierter Vertrag bestanden
- Verifizierungsbelege
- 6
- Signaturschlüssel, die es gebaut haben
- 3
Deklarierte Umgebung
node 22 linux x64 node 22 npm
Umgebungen der Verifizierungsläufe
| Umgebung | Contract | Stufen | Lauf |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:a2ec939a4c60e243 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-13 |
| node 22 · linux alpine/x64 · docker ed25519:a2ec939a4c60e243 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-13 |
| node 22 · linux alpine/x64 · docker ed25519:a2ec939a4c60e243 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-13 |
| node 22 · linux alpine/x64 · docker ed25519:a2ec939a4c60e243 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-13 |
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-17 |
| node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5… |
2026-09-07 |
Fall
FIX- Ziel
- require() an ESM-only npm package from CommonJS without ERR_REQUIRE_ESM or ERR_REQUIRE_ASYNC_MODULE
- Pakete
- Symbole
-
- Yoga.Node.create
- import()
- pLimit
- require
- Umgebung
- node 22
- Erstellt
- 2026-08-13T14:19:50Z
Contract
- run a CommonJS file that require()s both ESM-only packages and assert it exits nonzero
- assert the failure is ERR_REQUIRE_ASYNC_MODULE, whose message is that require() cannot be used on an ESM graph with top-level await
- assert the same run proves the quieter half: on Node 22.12 and newer require('p-limit') returns the module namespace, so calling it raises TypeError: pLimit is not a function
- assert the cached dynamic import() resolves to a single namespace whose default export carries Yoga.Node, which the namespace itself does not expose
- assert the dynamic-import path computes a real flexbox layout: three flex-grow columns in a 240 by 60 row with a 12px gap land at x 0, 84 and 168, each 72 by 60
- assert p-limit loaded through the same dynamic import caps concurrency at 2 across 6 tasks and preserves input order
Dateien
- csx.json
- package-lock.json
- package.json
- src/broken-require.js
- src/index.js
- test/contract.mjs
Quelltext
{"case":{"caseId":"case:sha256:615fba704b69581dccf3fdd3f699ef774af7f8fc2e48c994950222121e93401e","constraints":{"moduleSystem":"cjs","runtime":"node"},"contract":["run a CommonJS file that require()s both ESM-only packages and assert it exits nonzero","assert the failure is ERR_REQUIRE_ASYNC_MODULE, whose message is that require() cannot be used on an ESM graph with top-level await","assert the same run proves the quieter half: on Node 22.12 and newer require('p-limit') returns the module namespace, so calling it raises TypeError: pLimit is not a function","assert the cached dynamic import() resolves to a single namespace whose default export carries Yoga.Node, which the namespace itself does not expose","assert the dynamic-import path computes a real flexbox layout: three flex-grow columns in a 240 by 60 row with a 12px gap land at x 0, 84 and 168, each 72 by 60","assert p-limit loaded through the same dynamic import caps concurrency at 2 across 6 tasks and preserves input order"],"goal":"require() an ESM-only npm package from CommonJS without ERR_REQUIRE_ESM or ERR_REQUIRE_ASYNC_MODULE","kind":"FIX","packages":["pkg:npm/p-limit@7.3.1","pkg:npm/yoga-layout@3.2.1"],"schemaVersion":1,"symbols":["Yoga.Node.create","import()","pLimit","require"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","moduleSystem":"cjs","os":"linux","packageManager":"npm","runtime":"node","runtimeVersion":"22","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/p-limit@7.3.1","pkg:npm/yoga-layout@3.2.1"],"schemaVersion":1,"symbols":["Yoga.Node.create","import()","pLimit","require"],"verifierAdapter":"node-typescript@1"}
{
"name": "csx-sample-esm-only-from-cjs",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "csx-sample-esm-only-from-cjs",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"p-limit": "7.3.1",
"yoga-layout": "3.2.1"
}
},
"node_modules/p-limit": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz",
"integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==",
"license": "MIT",
"dependencies": {
"yocto-queue": "^1.2.1"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yocto-queue": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz",
"integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==",
"license": "MIT",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/yoga-layout": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
"license": "MIT"
}
}
}
{
"name": "csx-sample-esm-only-from-cjs",
"version": "1.0.0",
"private": true,
"type": "commonjs",
"license": "MIT-0",
"dependencies": {
"p-limit": "7.3.1",
"yoga-layout": "3.2.1"
}
}
'use strict';
// The pattern this sample exists to replace. Run it directly to see both
// failure modes on one Node:
//
// node src/broken-require.js
//
// Node < 22.12 threw ERR_REQUIRE_ESM on BOTH requires below. Node >= 22.12
// can require() an ESM module graph, so the two now fail in different ways --
// and only one of them is still loud.
// (1) p-limit is ESM-only ("type": "module", no require condition in exports)
// but its graph has no top-level await, so on Node >= 22.12 require()
// SUCCEEDS. It returns the ES module *namespace object*, not the default
// export, so the idiomatic CommonJS binding below gets an object where the
// function should be and the crash moves to the call site.
const pLimit = require('p-limit');
console.log('p-limit require() returned:', typeof pLimit);
try {
pLimit(2);
} catch (err) {
console.log('calling it: ' + err.constructor.name + ': ' + err.message);
}
// (2) yoga-layout is ESM-only AND awaits its WebAssembly instantiation at the
// top level of its module graph. No Node version can require() that: a
// synchronous require() has nothing to block on. This one still throws,
// and it is the throw the contract asserts.
//
// `node --experimental-print-required-tla src/broken-require.js` names it:
// unexpected top-level await at node_modules/yoga-layout/dist/src/index.js:13
// const Yoga = wrapAssembly(await loadYoga());
const Yoga = require('yoga-layout');
console.log('unreachable', typeof Yoga);
'use strict';
/**
* Using ESM-only npm packages from a CommonJS project.
*
* yoga-layout@3 and p-limit@7 are both ESM-only: "type": "module" with no
* `require` condition in their exports map. A CommonJS require() of either one
* is wrong -- but on current Node they are wrong in two different ways, and
* only one of them is still loud. See src/broken-require.js.
*
* Node < 22.12 require() -> ERR_REQUIRE_ESM for both packages.
* Node >= 22.12 require('p-limit') -> succeeds, and returns the ES
* module NAMESPACE object, so the
* usual `pLimit(2)` is a TypeError
* somewhere else entirely.
* require('yoga-layout') -> ERR_REQUIRE_ASYNC_MODULE. Its
* graph awaits the WebAssembly
* instantiation at the top level,
* and a synchronous require() has
* nothing to block on. No Node
* version can load that with
* require().
*
* One pattern covers every case, on every Node >= 12.17: dynamic import()
* inside an async function. import() returns a Promise, so it works unchanged
* in a `require`-shaped file, and it never needs the package to ship a CJS
* build. (The other fix is to convert the consumer to ESM -- rename to .mjs or
* set "type": "module" -- which is the better move if you can afford it.)
*
* Two details that bite people:
*
* - Cache the PROMISE, not the resolved module. CommonJS has no top-level
* await, so there is nothing to assign at module scope. Awaiting an
* already-settled promise is free, so the loader stays cheap to call on
* every request instead of only at startup.
*
* - import() resolves to the module NAMESPACE, not to the default export.
* `export default` lands on `.default`. Destructure it:
* const { default: pLimit } = await import('p-limit');
* This is the same shape require() now hands back on Node >= 22.12, which
* is why the `TypeError: pLimit is not a function` reports are suddenly
* more common than ERR_REQUIRE_ESM.
*/
let yogaPromise;
let pLimitPromise;
/** @returns {Promise<object>} the yoga-layout module namespace (cached). */
function loadYoga() {
if (!yogaPromise) yogaPromise = import('yoga-layout');
return yogaPromise;
}
/** @returns {Promise<object>} the p-limit module namespace (cached). */
function loadPLimit() {
if (!pLimitPromise) pLimitPromise = import('p-limit');
return pLimitPromise;
}
/**
* Lay out `count` equal-width columns inside a `width` x `height` box with
* `gap` pixels between them, and return each column's computed geometry.
*/
async function rowLayout({ width, height, count, gap }) {
// `Yoga` is the default export; Direction/Edge/FlexDirection are named
// exports. Both come off the same namespace object.
const { default: Yoga, Direction, Edge, FlexDirection } = await loadYoga();
const root = Yoga.Node.create();
root.setWidth(width);
root.setHeight(height);
root.setFlexDirection(FlexDirection.Row);
const kids = [];
for (let i = 0; i < count; i += 1) {
const kid = Yoga.Node.create();
kid.setFlexGrow(1);
if (i > 0) kid.setMargin(Edge.Left, gap);
root.insertChild(kid, i);
kids.push(kid);
}
root.calculateLayout(width, height, Direction.LTR);
const boxes = kids.map((kid) => {
const box = kid.getComputedLayout();
return { left: box.left, top: box.top, width: box.width, height: box.height };
});
root.freeRecursive(); // yoga nodes are wasm-backed; free them explicitly
return boxes;
}
/**
* Run every task with at most `concurrency` of them in flight at once,
* resolving to the results in input order.
*/
async function runLimited(tasks, concurrency) {
const { default: pLimit } = await loadPLimit();
const limit = pLimit(concurrency);
return Promise.all(tasks.map((task) => limit(task)));
}
module.exports = { loadPLimit, loadYoga, rowLayout, runLimited };
import { strict as assert } from 'node:assert';
import { spawnSync } from 'node:child_process';
import { createRequire } from 'node:module';
import { fileURLToPath } from 'node:url';
// Load the sample the way a CommonJS project would: a genuine require() of a
// genuine CJS module. `require` does not exist inside an .mjs file, so
// createRequire hands us the real CommonJS loader.
const require = createRequire(import.meta.url);
// ---------------------------------------------------------------------------
// 1. The require() pattern really is broken on this Node.
// ---------------------------------------------------------------------------
const brokenPath = fileURLToPath(new URL('../src/broken-require.js', import.meta.url));
const broken = spawnSync(process.execPath, [brokenPath], { encoding: 'utf8' });
assert.notEqual(broken.status, 0, 'require() of an ESM-only package must still fail');
assert.match(broken.stderr, /ERR_REQUIRE_ASYNC_MODULE/);
assert.match(broken.stderr, /require\(\) cannot be used on an ESM graph with top-level await/);
// The quieter half: require() of an ESM-only package *without* top-level await
// no longer throws on Node >= 22.12 -- it hands back the module namespace, so
// the failure surfaces later as a TypeError at the call site.
assert.match(broken.stdout, /p-limit require\(\) returned: object/);
assert.match(broken.stdout, /calling it: TypeError: pLimit is not a function/);
// ---------------------------------------------------------------------------
// 2. The dynamic-import path loads the package and exposes the real default.
// ---------------------------------------------------------------------------
const { loadYoga, rowLayout, runLimited } = require('../src/index.js');
const ns = await loadYoga();
assert.equal(await loadYoga(), ns, 'the cached import() promise yields one namespace');
assert.equal(ns.Node, undefined, 'Yoga.Node is on the default export, not the namespace');
assert.equal(typeof ns.default.Node.create, 'function');
// ---------------------------------------------------------------------------
// 3. It computes a real flexbox layout: 3 flex-grow columns in 240x60 with a
// 12px gap => (240 - 2*12) / 3 = 72 wide each, stretched to full height.
// ---------------------------------------------------------------------------
const boxes = await rowLayout({ width: 240, height: 60, count: 3, gap: 12 });
assert.deepEqual(boxes, [
{ left: 0, top: 0, width: 72, height: 60 },
{ left: 84, top: 0, width: 72, height: 60 },
{ left: 168, top: 0, width: 72, height: 60 },
]);
// ---------------------------------------------------------------------------
// 4. The second ESM-only package works through the same door, and its
// behaviour is observable: at most `concurrency` tasks are ever in flight.
// ---------------------------------------------------------------------------
let inFlight = 0;
let peak = 0;
const tasks = [1, 2, 3, 4, 5, 6].map((n) => async () => {
inFlight += 1;
peak = Math.max(peak, inFlight);
await new Promise((resolve) => setTimeout(resolve, 10));
inFlight -= 1;
return n * 10;
});
const results = await runLimited(tasks, 2);
assert.deepEqual(results, [10, 20, 30, 40, 50, 60], 'results keep input order');
assert.equal(peak, 2, 'p-limit capped concurrency at 2 (it would be 6 uncapped)');
assert.equal(inFlight, 0);
console.log('CONTRACT PASS: await import() loads ESM-only packages from CommonJS; require() throws ERR_REQUIRE_ASYNC_MODULE');