Skip to content

Commit be6bc32

Browse files
committed
module: support __cjsUnwrapDefault interop flag in require(esm)
1 parent 01bf4a1 commit be6bc32

File tree

48 files changed

+274
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+274
-6
lines changed

β€Ždoc/api/modules.mdβ€Ž

Lines changed: 51 additions & 2 deletions

β€Žlib/internal/modules/cjs/loader.jsβ€Ž

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,10 +1416,13 @@ function loadESMFromCJS(mod, filename) {
14161416
// createRequiredModuleFacade() to `wrap` which is a ModuleWrap wrapping
14171417
// over the original module.
14181418

1419-
// We don't do this to modules that don't have default exports to avoid
1420-
// the unnecessary overhead. If __esModule is already defined, we will
1421-
// also skip the extension to allow users to override it.
1422-
if (!ObjectHasOwn(namespace, 'default') || ObjectHasOwn(namespace, '__esModule')) {
1419+
// We don't do this to modules that are marked as CJS ESM or that
1420+
// don't have default exports to avoid the unnecessary overhead.
1421+
// If __esModule is already defined, we will also skip the extension
1422+
// to allow users to override it.
1423+
if (namespace.__cjsUnwrapDefault) {
1424+
mod.exports = namespace.default;
1425+
} else if (!ObjectHasOwn(namespace, 'default') || ObjectHasOwn(namespace, '__esModule')) {
14231426
mod.exports = namespace;
14241427
} else {
14251428
mod.exports = createRequiredModuleFacade(wrap);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Flags: --experimental-require-module
2+
import { mustCall } from '../common/index.mjs';
3+
import assert from 'assert';
4+
import { directRequireFixture, importFixture } from '../fixtures/pkgexports.mjs';
5+
6+
const tests = {
7+
'string': 'cjs',
8+
'object': { a: 'cjs a', b: 'cjs b' },
9+
'fauxesmdefault': { default: 'faux esm default' },
10+
'fauxesmmixed': { default: 'faux esm default', a: 'faux esm a', b: 'faux esm b' },
11+
'fauxesmnamed': { a: 'faux esm a', b: 'faux esm b' }
12+
};
13+
14+
// This test demonstrates interop between CJS and CJS represented as ESM
15+
// under the new `export const __cjsModule = true` pattern, for the above cases.
16+
for (const [test, exactShape] of Object.entries(tests)) {
17+
// Each case represents a CJS dependency, which has the expected shape in CJS:
18+
assert.deepStrictEqual(directRequireFixture(`interop-cjsdep-${test}`), exactShape);
19+
20+
// Each dependency is reexported through CJS as if it is a library being consumed,
21+
// which in CJS is fully shape-preserving:
22+
assert.deepStrictEqual(directRequireFixture(`interop-cjs/${test}`), exactShape);
23+
24+
// Now we have ESM conversions of these dependencies, using __cjsModule = true,
25+
// staring with the conversion of those dependencies into ESM under require(esm):
26+
assert.deepStrictEqual(directRequireFixture(`interop-cjsdep-${test}-esm`), exactShape);
27+
28+
// When importing these ESM conversions, from require(esm), we should preserve the shape:
29+
assert.deepStrictEqual(directRequireFixture(`interop-cjs/${test}-esm`), exactShape);
30+
31+
// Now if the importer itself is converted into ESM, it should still be able to load the original
32+
// CJS and reexport it, preserving the shape:
33+
assert.deepStrictEqual(directRequireFixture(`interop-cjs-esm/${test}`), exactShape);
34+
35+
// And then if we have the converted CJS to ESM importing from converted CJS to ESM,
36+
// that should also work:
37+
assert.deepStrictEqual(directRequireFixture(`interop-cjs-esm/${test}-esm`), exactShape);
38+
39+
// Finally, the CJS ESM representation under `import()` should match all these cases equivalently,
40+
// where the CJS module is exported as the default export:
41+
const esmCjsImport = await importFixture(`interop-cjsdep-${test}`);
42+
assert.deepStrictEqual(esmCjsImport.default, exactShape);
43+
44+
assert.deepStrictEqual((await importFixture(`interop-cjsdep-${test}`)).default, exactShape);
45+
assert.deepStrictEqual((await importFixture(`interop-cjs/${test}`)).default, exactShape);
46+
assert.deepStrictEqual((await importFixture(`interop-cjsdep-${test}-esm`)).default, exactShape);
47+
assert.deepStrictEqual((await importFixture(`interop-cjs/${test}-esm`)).default, exactShape);
48+
assert.deepStrictEqual((await importFixture(`interop-cjs-esm/${test}`)).default, exactShape);
49+
assert.deepStrictEqual((await importFixture(`interop-cjs-esm/${test}-esm`)).default, exactShape);
50+
51+
}

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmdefault-esm.jsβ€Ž

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmdefault.jsβ€Ž

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmmixed-esm.jsβ€Ž

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmmixed.jsβ€Ž

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmnamed-esm.jsβ€Ž

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/fauxesmnamed.jsβ€Ž

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žtest/fixtures/node_modules/interop-cjs-esm/object-esm.jsβ€Ž

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
Β (0)