Skip to content

Commit 06206af

Browse files
authored
module: unflag --experimental-require-module
This unflags --experimental-require-module so require(esm) can be used without the flag. For now, when require() actually encounters an ESM, it will still emit an experimental warning. To opt out of the feature, --no-experimental-require-module can be used. There are some tests specifically testing ERR_REQUIRE_ESM. Some of them are repurposed to test --no-experimental-require-module. Some of them are modified to just expect loading require(esm) to work, when it's appropriate. PR-URL: #55085 Refs: #52697 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: LiviaMedeiros <livia@cirno.name> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Richard Lau <rlau@redhat.com>
1 parent 17fd327 commit 06206af

23 files changed

+135
-68
lines changed

doc/api/cli.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,10 @@ following permissions are restricted:
10341034
added:
10351035
- v22.0.0
10361036
- v20.17.0
1037+
changes:
1038+
- version: REPLACEME
1039+
pr-url: https://github.com/nodejs/node/pull/55085
1040+
description: This is now true by default.
10371041
-->
10381042

10391043
> Stability: 1.1 - Active Development
@@ -1659,6 +1663,24 @@ added: v16.6.0
16591663

16601664
Use this flag to disable top-level await in REPL.
16611665

1666+
### `--no-experimental-require-module`
1667+
1668+
<!-- YAML
1669+
added:
1670+
- v22.0.0
1671+
- v20.17.0
1672+
changes:
1673+
- version: REPLACEME
1674+
pr-url: https://github.com/nodejs/node/pull/55085
1675+
description: This is now false by default.
1676+
-->
1677+
1678+
> Stability: 1.1 - Active Development
1679+
1680+
Disable support for loading a synchronous ES module graph in `require()`.
1681+
1682+
See [Loading ECMAScript modules using `require()`][].
1683+
16621684
### `--no-experimental-websocket`
16631685

16641686
<!-- YAML
@@ -1875,9 +1897,7 @@ added:
18751897
- v20.17.0
18761898
-->
18771899

1878-
This flag is only useful when `--experimental-require-module` is enabled.
1879-
1880-
If the ES module being `require()`'d contains top-level await, this flag
1900+
If the ES module being `require()`'d contains top-level `await`, this flag
18811901
allows Node.js to evaluate the module, try to locate the
18821902
top-level awaits, and print their location to help users find them.
18831903

doc/api/errors.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,8 +2426,8 @@ object.
24262426

24272427
> Stability: 1 - Experimental
24282428
2429-
When trying to `require()` a [ES Module][] under `--experimental-require-module`,
2430-
a CommonJS to ESM or ESM to CommonJS edge participates in an immediate cycle.
2429+
When trying to `require()` a [ES Module][], a CommonJS to ESM or ESM to CommonJS edge
2430+
participates in an immediate cycle.
24312431
This is not allowed because ES Modules cannot be evaluated while they are
24322432
already being evaluated.
24332433

@@ -2441,8 +2441,8 @@ module, and should be done lazily in an inner function.
24412441

24422442
> Stability: 1 - Experimental
24432443
2444-
When trying to `require()` a [ES Module][] under `--experimental-require-module`,
2445-
the module turns out to be asynchronous. That is, it contains top-level await.
2444+
When trying to `require()` a [ES Module][], the module turns out to be asynchronous.
2445+
That is, it contains top-level await.
24462446

24472447
To see where the top-level await is, use
24482448
`--experimental-print-required-tla` (this would execute the modules
@@ -2452,12 +2452,20 @@ before looking for the top-level awaits).
24522452

24532453
### `ERR_REQUIRE_ESM`
24542454

2455-
> Stability: 1 - Experimental
2455+
<!-- YAML
2456+
changes:
2457+
- version: REPLACEME
2458+
pr-url: https://github.com/nodejs/node/pull/55085
2459+
description: require() now supports loading synchronous ES modules by default.
2460+
-->
2461+
2462+
> Stability: 0 - Deprecated
24562463
24572464
An attempt was made to `require()` an [ES Module][].
24582465

2459-
To enable `require()` for synchronous module graphs (without
2460-
top-level `await`), use `--experimental-require-module`.
2466+
This error has been deprecated since `require()` now supports loading synchronous
2467+
ES modules. When `require()` encounters an ES module that contains top-level
2468+
`await`, it will throw [`ERR_REQUIRE_ASYNC_MODULE`][] instead.
24612469

24622470
<a id="ERR_SCRIPT_EXECUTION_INTERRUPTED"></a>
24632471

@@ -4061,6 +4069,7 @@ Type stripping is not supported for files descendent of a `node_modules` directo
40614069
[`ERR_INVALID_ARG_TYPE`]: #err_invalid_arg_type
40624070
[`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`]: #err_missing_message_port_in_transfer_list
40634071
[`ERR_MISSING_TRANSFERABLE_IN_TRANSFER_LIST`]: #err_missing_transferable_in_transfer_list
4072+
[`ERR_REQUIRE_ASYNC_MODULE`]: #err_require_async_module
40644073
[`EventEmitter`]: events.md#class-eventemitter
40654074
[`MessagePort`]: worker_threads.md#class-messageport
40664075
[`Object.getPrototypeOf`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf

doc/api/esm.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ compatibility.
468468
### `require`
469469
470470
The CommonJS module `require` currently only supports loading synchronous ES
471-
modules when `--experimental-require-module` is enabled.
471+
modules (that is, ES modules that do not use top-level `await`).
472472
473473
See [Loading ECMAScript modules using `require()`][] for details.
474474

doc/api/modules.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,18 @@ relative, and based on the real path of the files making the calls to
170170

171171
## Loading ECMAScript modules using `require()`
172172

173+
<!-- YAML
174+
changes:
175+
- version: REPLACEME
176+
pr-url: https://github.com/nodejs/node/pull/55085
177+
description: require() now supports loading synchronous ES modules by default.
178+
-->
179+
173180
The `.mjs` extension is reserved for [ECMAScript Modules][].
174-
Currently, if the flag `--experimental-require-module` is not used, loading
175-
an ECMAScript module using `require()` will throw a [`ERR_REQUIRE_ESM`][]
176-
error, and users need to use [`import()`][] instead. See
177-
[Determining module system][] section for more info
181+
See [Determining module system][] section for more info
178182
regarding which files are parsed as ECMAScript modules.
179183

180-
If `--experimental-require-module` is enabled, and the ECMAScript module being
181-
loaded by `require()` meets the following requirements:
184+
`require()` only supports loading ECMAScript modules that meet the following requirements:
182185

183186
* The module is fully synchronous (contains no top-level `await`); and
184187
* One of these conditions are met:
@@ -187,8 +190,8 @@ loaded by `require()` meets the following requirements:
187190
3. The file has a `.js` extension, the closest `package.json` does not contain
188191
`"type": "commonjs"`, and the module contains ES module syntax.
189192

190-
`require()` will load the requested module as an ES Module, and return
191-
the module namespace object. In this case it is similar to dynamic
193+
If the ES Module being loaded meet the requirements, `require()` can load it and
194+
return the module namespace object. In this case it is similar to dynamic
192195
`import()` but is run synchronously and returns the name space object
193196
directly.
194197

@@ -207,7 +210,7 @@ class Point {
207210
export default Point;
208211
```
209212

210-
A CommonJS module can load them with `require()` under `--experimental-detect-module`:
213+
A CommonJS module can load them with `require()`:
211214

212215
```cjs
213216
const distance = require('./distance.mjs');
@@ -236,13 +239,19 @@ conventions. Code authored directly in CommonJS should avoid depending on it.
236239
If the module being `require()`'d contains top-level `await`, or the module
237240
graph it `import`s contains top-level `await`,
238241
[`ERR_REQUIRE_ASYNC_MODULE`][] will be thrown. In this case, users should
239-
load the asynchronous module using `import()`.
242+
load the asynchronous module using [`import()`][].
240243

241244
If `--experimental-print-required-tla` is enabled, instead of throwing
242245
`ERR_REQUIRE_ASYNC_MODULE` before evaluation, Node.js will evaluate the
243246
module, try to locate the top-level awaits, and print their location to
244247
help users fix them.
245248

249+
Support for loading ES modules using `require()` is currently
250+
experimental and can be disabled using `--no-experimental-require-module`.
251+
When `require()` actually encounters an ES module for the
252+
first time in the process, it will emit an experimental warning. The
253+
warning is expected to be removed when this feature stablizes.
254+
246255
## All together
247256

248257
<!-- type=misc -->
@@ -272,8 +281,7 @@ require(X) from module at path Y
272281
273282
MAYBE_DETECT_AND_LOAD(X)
274283
1. If X parses as a CommonJS module, load X as a CommonJS module. STOP.
275-
2. Else, if `--experimental-require-module` is
276-
enabled, and the source code of X can be parsed as ECMAScript module using
284+
2. Else, if the source code of X can be parsed as ECMAScript module using
277285
<a href="esm.md#resolver-algorithm-specification">DETECT_MODULE_SYNTAX defined in
278286
the ESM resolver</a>,
279287
a. Load X as an ECMAScript module. STOP.
@@ -1190,7 +1198,6 @@ This section was moved to
11901198
[`"main"`]: packages.md#main
11911199
[`"type"`]: packages.md#type
11921200
[`ERR_REQUIRE_ASYNC_MODULE`]: errors.md#err_require_async_module
1193-
[`ERR_REQUIRE_ESM`]: errors.md#err_require_esm
11941201
[`ERR_UNSUPPORTED_DIR_IMPORT`]: errors.md#err_unsupported_dir_import
11951202
[`MODULE_NOT_FOUND`]: errors.md#module_not_found
11961203
[`__dirname`]: #__dirname

doc/api/packages.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ There is the CommonJS module loader:
172172
* It treats all files that lack `.json` or `.node` extensions as JavaScript
173173
text files.
174174
* It can only be used to [load ECMASCript modules from CommonJS modules][] if
175-
the module graph is synchronous (that contains no top-level `await`) when
176-
`--experimental-require-module` is enabled.
175+
the module graph is synchronous (that contains no top-level `await`).
177176
When used to load a JavaScript text file that is not an ECMAScript module,
178177
the file will be loaded as a CommonJS module.
179178

@@ -662,8 +661,7 @@ specific to least specific as conditions should be defined:
662661
* `"require"` - matches when the package is loaded via `require()`. The
663662
referenced file should be loadable with `require()` although the condition
664663
matches regardless of the module format of the target file. Expected
665-
formats include CommonJS, JSON, native addons, and ES modules
666-
if `--experimental-require-module` is enabled. _Always mutually
664+
formats include CommonJS, JSON, native addons, and ES modules. _Always mutually
667665
exclusive with `"import"`._
668666
* `"module-sync"` - matches no matter the package is loaded via `import`,
669667
`import()` or `require()`. The format is expected to be ES modules that does

lib/internal/modules/cjs/loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,6 @@ function initializeCJS() {
438438
Module._extensions['.ts'] = loadTS;
439439
}
440440
if (getOptionValue('--experimental-require-module')) {
441-
emitExperimentalWarning('Support for loading ES Module in require()');
442441
Module._extensions['.mjs'] = loadESMFromCJS;
443442
if (tsEnabled) {
444443
Module._extensions['.mts'] = loadESMFromCJS;
@@ -1386,6 +1385,7 @@ function loadESMFromCJS(mod, filename) {
13861385
// ESM won't be accessible via process.mainModule.
13871386
setOwnProperty(process, 'mainModule', undefined);
13881387
} else {
1388+
emitExperimentalWarning('Support for loading ES Module in require()');
13891389
const {
13901390
wrap,
13911391
namespace,

lib/internal/modules/esm/load.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,11 +131,6 @@ async function defaultLoad(url, context = kEmptyObject) {
131131

132132
validateAttributes(url, format, importAttributes);
133133

134-
// Use the synchronous commonjs translator which can deal with cycles.
135-
if (format === 'commonjs' && getOptionValue('--experimental-require-module')) {
136-
format = 'commonjs-sync';
137-
}
138-
139134
if (getOptionValue('--experimental-strip-types') &&
140135
(format === 'module-typescript' || format === 'commonjs-typescript') &&
141136
isUnderNodeModules(url)) {
@@ -191,11 +186,6 @@ function defaultLoadSync(url, context = kEmptyObject) {
191186

192187
validateAttributes(url, format, importAttributes);
193188

194-
// Use the synchronous commonjs translator which can deal with cycles.
195-
if (format === 'commonjs' && getOptionValue('--experimental-require-module')) {
196-
format = 'commonjs-sync';
197-
}
198-
199189
return {
200190
__proto__: null,
201191
format,

lib/internal/modules/esm/loader.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,15 +373,15 @@ class ModuleLoader {
373373

374374
defaultLoadSync ??= require('internal/modules/esm/load').defaultLoadSync;
375375
const loadResult = defaultLoadSync(url, { format, importAttributes });
376-
const {
377-
format: finalFormat,
378-
source,
379-
} = loadResult;
376+
377+
// Use the synchronous commonjs translator which can deal with cycles.
378+
const finalFormat = loadResult.format === 'commonjs' ? 'commonjs-sync' : loadResult.format;
380379

381380
if (finalFormat === 'wasm') {
382381
assert.fail('WASM is currently unsupported by require(esm)');
383382
}
384383

384+
const { source } = loadResult;
385385
const isMain = (parentURL === undefined);
386386
const wrap = this.#translate(url, finalFormat, source, isMain);
387387
assert(wrap instanceof ModuleWrap, `Translator used for require(${url}) should not be async`);

lib/internal/modules/esm/module_job.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ class ModuleJobSync extends ModuleJobBase {
361361
}
362362

363363
runSync() {
364+
// TODO(joyeecheung): add the error decoration logic from the async instantiate.
364365
this.module.instantiateSync();
365366
setHasStartedUserESMExecution();
366367
const namespace = this.module.evaluateSync();

lib/internal/modules/esm/utils.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,15 @@ function initializeDefaultConditions() {
7575
const userConditions = getOptionValue('--conditions');
7676
const noAddons = getOptionValue('--no-addons');
7777
const addonConditions = noAddons ? [] : ['node-addons'];
78-
78+
const moduleConditions = getOptionValue('--experimental-require-module') ? ['module-sync'] : [];
7979
defaultConditions = ObjectFreeze([
8080
'node',
8181
'import',
82+
...moduleConditions,
8283
...addonConditions,
8384
...userConditions,
8485
]);
8586
defaultConditionsSet = new SafeSet(defaultConditions);
86-
if (getOptionValue('--experimental-require-module')) {
87-
defaultConditionsSet.add('module-sync');
88-
}
8987
}
9088

9189
/**

0 commit comments

Comments
 (0)