esm: `--experimental-default-type` flag to flip module defaults
This PR contains work by @LiviaMedeiros, @JakobJingleheimer, @bmeck adapted from #49531, #49540, #49295, #41552, #31388.
This PR implements most of the proposal in #49432. It creates a new flag, --experimental-default-type
, which supports values commonjs
(the default) or module
. Under --experimental-default-type=module
, Node.js interprets the following as ES modules:
-
String input provided via
--eval
or STDIN, if--input-type
is unspecified. -
Files ending in
.js
or with no extension, if there is nopackage.json
file present in the same folder or any parent folder. -
Files ending in
.js
or with no extension, if the nearest parentpackage.json
field lacks atype
field; unless the folder is inside anode_modules
folder.
Extensionless files are interpreted as Wasm if --experimental-wasm-modules
is passed and the file contains the “magic bytes” Wasm header.
The ESM loader is used for all entry points, and we don’t pass through Module.runMain
. Under this flag, we no longer support monkey-patching of that method by files loaded via --require
.
This PR does not implement the “parse the entry point as a URL” part of #49432, as there are still some design decisions to be made around that and it’s substantial enough to warrant being implemented as a separate PR.
This is related to #49629 and linked PRs, which include the above behaviors for extensionless files but don’t require a flag. I think we can potentially ship that change on its own, separate from making this flag the new default behavior, because extensionless files currently error in module
scopes. I’d prefer to ship it behind the --experimental-default-type
flag first to work out any kinks before splitting it out to not require any flag.
If people don’t mind, I’d like if this PR thread can focus on the implementation. If anyone has any thoughts on the design or intentions behind this, there’s already a detailed thread for that at #49432.
cc @nodejs/loaders @nodejs/wasi @nodejs/startup @nodejs/tsc
Notable change
The new flag --experimental-default-type
can be used to flip the default module system used by Node.js. Input that is already explicitly defined as ES modules or CommonJS, such as by a package.json
"type"
field or .mjs
/.cjs
file extension or the --input-type
flag, is unaffected. What is currently implicitly CommonJS would instead be interpreted as ES modules under --experimental-default-type=module
:
-
String input provided via
--eval
or STDIN, if--input-type
is unspecified. -
Files ending in
.js
or with no extension, if there is nopackage.json
file present in the same folder or any parent folder. -
Files ending in
.js
or with no extension, if the nearest parentpackage.json
field lacks atype
field; unless the folder is inside anode_modules
folder.
In addition, extensionless files are interpreted as Wasm if --experimental-wasm-modules
is passed and the file contains the “magic bytes” Wasm header.