Skip to content

Commit b48cf31

Browse files
aduh95UlisesGascon
authored andcommitted
esm: bypass CJS loader in default load under --default-type=module
This allows user to opt-out from using the monkey-patchable CJS loader, even to load CJS modules. PR-URL: #50004 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 4cf10a1 commit b48cf31

File tree

5 files changed

+105
-30
lines changed

5 files changed

+105
-30
lines changed

doc/api/module.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,8 @@ Omitting vs providing a `source` for `'commonjs'` has very different effects:
622622
registered hooks. This behavior for nullish `source` is temporary — in the
623623
future, nullish `source` will not be supported.
624624
625-
The Node.js internal `load` implementation, which is the value of `next` for the
625+
When `node` is run with `--experimental-default-type=commonjs`, the Node.js
626+
internal `load` implementation, which is the value of `next` for the
626627
last hook in the `load` chain, returns `null` for `source` when `format` is
627628
`'commonjs'` for backward compatibility. Here is an example hook that would
628629
opt-in to using the non-default behavior:

lib/internal/modules/esm/load.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const policy = getOptionValue('--experimental-policy') ?
1818
null;
1919
const experimentalNetworkImports =
2020
getOptionValue('--experimental-network-imports');
21+
const defaultType =
22+
getOptionValue('--experimental-default-type');
2123

2224
const { Buffer: { from: BufferFrom } } = require('buffer');
2325

@@ -140,7 +142,7 @@ async function defaultLoad(url, context = kEmptyObject) {
140142
// Now that we have the source for the module, run `defaultGetFormat` again in case we detect ESM syntax.
141143
format ??= await defaultGetFormat(urlInstance, contextToPass);
142144

143-
if (format === 'commonjs' && contextToPass !== context) {
145+
if (format === 'commonjs' && contextToPass !== context && defaultType !== 'module') {
144146
// For backward compatibility reasons, we need to discard the source in
145147
// order for the CJS loader to re-fetch it.
146148
source = null;
+98-28
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,101 @@
11
import { spawnPromisified } from '../common/index.mjs';
22
import * as fixtures from '../common/fixtures.mjs';
33
import { describe, it } from 'node:test';
4-
import { match, strictEqual } from 'node:assert';
5-
6-
describe('--experimental-default-type=module should not affect the interpretation of files with unknown extensions',
7-
{ concurrency: true }, () => {
8-
it('should error on an entry point with an unknown extension', async () => {
9-
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
10-
'--experimental-default-type=module',
11-
fixtures.path('es-modules/package-type-module/extension.unknown'),
12-
]);
13-
14-
match(stderr, /ERR_UNKNOWN_FILE_EXTENSION/);
15-
strictEqual(stdout, '');
16-
strictEqual(code, 1);
17-
strictEqual(signal, null);
18-
});
19-
20-
it('should error on an import with an unknown extension', async () => {
21-
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
22-
'--experimental-default-type=module',
23-
fixtures.path('es-modules/package-type-module/imports-unknownext.mjs'),
24-
]);
25-
26-
match(stderr, /ERR_UNKNOWN_FILE_EXTENSION/);
27-
strictEqual(stdout, '');
28-
strictEqual(code, 1);
29-
strictEqual(signal, null);
30-
});
31-
});
4+
import { deepStrictEqual, match, strictEqual } from 'node:assert';
5+
6+
describe('--experimental-default-type=module', { concurrency: true }, () => {
7+
describe('should not affect the interpretation of files with unknown extensions', { concurrency: true }, () => {
8+
it('should error on an entry point with an unknown extension', async () => {
9+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
10+
'--experimental-default-type=module',
11+
fixtures.path('es-modules/package-type-module/extension.unknown'),
12+
]);
13+
14+
match(stderr, /ERR_UNKNOWN_FILE_EXTENSION/);
15+
strictEqual(stdout, '');
16+
strictEqual(code, 1);
17+
strictEqual(signal, null);
18+
});
19+
20+
it('should error on an import with an unknown extension', async () => {
21+
const { code, signal, stdout, stderr } = await spawnPromisified(process.execPath, [
22+
'--experimental-default-type=module',
23+
fixtures.path('es-modules/package-type-module/imports-unknownext.mjs'),
24+
]);
25+
26+
match(stderr, /ERR_UNKNOWN_FILE_EXTENSION/);
27+
strictEqual(stdout, '');
28+
strictEqual(code, 1);
29+
strictEqual(signal, null);
30+
});
31+
});
32+
33+
it('should affect CJS .js files (imported, required, entry points)', async () => {
34+
const result = await spawnPromisified(process.execPath, [
35+
'--experimental-default-type=module',
36+
fixtures.path('es-modules/package-type-commonjs/echo-require-cache.js'),
37+
]);
38+
39+
deepStrictEqual(result, {
40+
code: 0,
41+
stderr: '',
42+
stdout: 'undefined\n',
43+
signal: null,
44+
});
45+
});
46+
47+
it('should affect .cjs files that are imported', async () => {
48+
const result = await spawnPromisified(process.execPath, [
49+
'--experimental-default-type=module',
50+
'-e',
51+
`import ${JSON.stringify(fixtures.fileURL('es-module-require-cache/echo.cjs'))}`,
52+
]);
53+
54+
deepStrictEqual(result, {
55+
code: 0,
56+
stderr: '',
57+
stdout: 'undefined\n',
58+
signal: null,
59+
});
60+
});
61+
62+
it('should affect entry point .cjs files (with no hooks)', async () => {
63+
const { stderr, stdout, code } = await spawnPromisified(process.execPath, [
64+
'--experimental-default-type=module',
65+
fixtures.path('es-module-require-cache/echo.cjs'),
66+
]);
67+
68+
strictEqual(stderr, '');
69+
match(stdout, /^undefined\n$/);
70+
strictEqual(code, 0);
71+
});
72+
73+
it('should affect entry point .cjs files (when any hooks is registered)', async () => {
74+
const result = await spawnPromisified(process.execPath, [
75+
'--experimental-default-type=module',
76+
'--import',
77+
'data:text/javascript,import{register}from"node:module";register("data:text/javascript,");',
78+
fixtures.path('es-module-require-cache/echo.cjs'),
79+
]);
80+
81+
deepStrictEqual(result, {
82+
code: 0,
83+
stderr: '',
84+
stdout: 'undefined\n',
85+
signal: null,
86+
});
87+
});
88+
89+
it('should not affect CJS from input-type', async () => {
90+
const { stderr, stdout, code } = await spawnPromisified(process.execPath, [
91+
'--experimental-default-type=module',
92+
'--input-type=commonjs',
93+
'-p',
94+
'require.cache',
95+
]);
96+
97+
strictEqual(stderr, '');
98+
match(stdout, /^\[Object: null prototype\] \{\}\n$/);
99+
strictEqual(code, 0);
100+
});
101+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(require.cache);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log(require.cache);

0 commit comments

Comments
 (0)