-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
doc: clarify module system selection #41383
Changes from 1 commit
1b1159a
dd7140f
97515fd
c19d161
ce3edd0
be6f791
e9c0b15
4c697b7
2cea6a8
21b45bf
93d6a23
fe05b72
5de6698
b08ec87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,18 @@ module.exports = class Square { | |
}; | ||
``` | ||
|
||
The module system is implemented in the `require('module')` module. | ||
The CommonJS module system is implemented in the [`module` core module][]. | ||
|
||
## Enabling | ||
|
||
<!-- type=misc --> | ||
|
||
Node.js has two module systems: CommonJS modules and [ECMAScript modules][]. | ||
|
||
Authors can tell Node.js to use the ECMAScript modules loader | ||
via the `.mjs` file extension, the `package.json` [`"type"`][] field, or the | ||
[`--input-type`][] flag. Outside of those cases, Node.js will use the CommonJS | ||
module loader. See [Determining module system][] for more details. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps add a note again that the ES module loader can be accessed in any context via dynamic
aduh95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Accessing the main module | ||
|
||
|
@@ -1047,13 +1058,16 @@ This section was moved to | |
[ECMAScript Modules]: esm.md | ||
[GLOBAL_FOLDERS]: #loading-from-the-global-folders | ||
[`"main"`]: packages.md#main | ||
[`"type"`]: packages.md#type | ||
[`--input-type`]: cli.md#--input-typetype | ||
[`ERR_REQUIRE_ESM`]: errors.md#err_require_esm | ||
[`Error`]: errors.md#class-error | ||
[`__dirname`]: #__dirname | ||
[`__filename`]: #__filename | ||
[`import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports | ||
[`module.children`]: #modulechildren | ||
[`module.id`]: #moduleid | ||
[`module` core module]: module.md | ||
[`module` object]: #the-module-object | ||
[`package.json`]: packages.md#nodejs-packagejson-field-definitions | ||
[`path.dirname()`]: path.md#pathdirnamepath | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -51,12 +51,13 @@ along with a reference for the [`package.json`][] fields defined by Node.js. | |||||||||||||||||||||||
## Determining module system | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Node.js will treat the following as [ES modules][] when passed to `node` as the | ||||||||||||||||||||||||
initial input, or when referenced by `import` statements within ES module code: | ||||||||||||||||||||||||
initial input, or when referenced by `import` statements or `import()` | ||||||||||||||||||||||||
expressions: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files ending in `.mjs`. | ||||||||||||||||||||||||
* Files whose name ends in `.mjs`. | ||||||||||||||||||||||||
aduh95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files ending in `.js` when the nearest parent `package.json` file contains a | ||||||||||||||||||||||||
top-level [`"type"`][] field with a value of `"module"`. | ||||||||||||||||||||||||
* Files whose name ends in `.js` when the nearest parent `package.json` file | ||||||||||||||||||||||||
contains a top-level [`"type"`][] field with a value of `"module"`. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Strings passed in as an argument to `--eval`, or piped to `node` via `STDIN`, | ||||||||||||||||||||||||
with the flag `--input-type=module`. | ||||||||||||||||||||||||
|
@@ -67,12 +68,12 @@ field, or string input without the flag `--input-type`. This behavior is to | |||||||||||||||||||||||
preserve backward compatibility. However, now that Node.js supports both | ||||||||||||||||||||||||
CommonJS and ES modules, it is best to be explicit whenever possible. Node.js | ||||||||||||||||||||||||
will treat the following as CommonJS when passed to `node` as the initial input, | ||||||||||||||||||||||||
or when referenced by `import` statements within ES module code: | ||||||||||||||||||||||||
or when referenced by `import` statements or `import()` expressions: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files ending in `.cjs`. | ||||||||||||||||||||||||
* Files whose name ends in `.cjs`. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files ending in `.js` when the nearest parent `package.json` file contains a | ||||||||||||||||||||||||
top-level field [`"type"`][] with a value of `"commonjs"`. | ||||||||||||||||||||||||
* Files whose name ends in `.js` when the nearest parent `package.json` file | ||||||||||||||||||||||||
contains a top-level field [`"type"`][] with a value of `"commonjs"`. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Strings passed in as an argument to `--eval` or `--print`, or piped to `node` | ||||||||||||||||||||||||
via `STDIN`, with the flag `--input-type=commonjs`. | ||||||||||||||||||||||||
|
@@ -83,6 +84,23 @@ future-proof the package in case the default type of Node.js ever changes, and | |||||||||||||||||||||||
it will also make things easier for build tools and loaders to determine how the | ||||||||||||||||||||||||
files in the package should be interpreted. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Node.js will refuse to load the following when passed to `node` as the | ||||||||||||||||||||||||
initial input and the nearest parent `package.json` file contains a top-level | ||||||||||||||||||||||||
[`"type"`][] field with a value of `"module"`: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files whose name doesn't end in `.js`, `.mjs`, `.cjs`, or `.wasm`. | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than saying what Node won’t do, why not say what it will? I think it’s easier to understand the positive case than the negative:
Suggested change
(And please add a link to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The type field has no bearing on what node accepts - it accepts a .js file regardless, and has no effect on the others. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That isn't true, type affects if the ESM module loader is used for the entry point: node/lib/internal/modules/run_main.js Line 41 in 2cc7a91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the latter, that’s good to know, and should indeed be called out. For the former, are you saying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. under There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so it looks like CJS resolver is always hit prior to the the ESM resolver, but things are weird and spaghetti node/lib/internal/modules/run_main.js Line 70 in e46c680
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ljharb the comments in the file that are causing it explicitly state that removing extension searching can be done in the future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, this seems like it would break if someone mucked with require.extensions in weird ways and passed it to ESM There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comments were probably from when it was still experimental. Now that it no longer is, it doesn't seem like something that can be removed without a semver-major. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likely would be semver-major yes. |
||||||||||||||||||||||||
|
||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is perhaps where we should mention that ESM entry points need to be URLs.
Suggested change
I feel like others can come up with better wording; my point is just that something along these lines should be part of the docs, either in the ESM or CLI docs or probably both. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's.. not true? AFAIK you can only specify a path as CLI entry point, using a URL only works if it happens to coincide with a path. $ cd /tmp
$ touch file.mjs
$ node file:///tmp/file.mjs
node:internal/modules/cjs/loader:936
throw err;
^
Error: Cannot find module '/private/tmp/file:/file/path'
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:17:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
Node.js v17.3.0
$ node ./%66ile.mjs
node:internal/modules/cjs/loader:936
throw err;
^
Error: Cannot find module '/private/tmp/%66ile.mjs'
at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
at Function.Module._load (node:internal/modules/cjs/loader:778:27)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:17:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
Node.js v17.3.0 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Takes a deep breath the CLI never accepts a filepath or a url. It is always a string specifier resolved using file semantics against the cwd PRIOR to doing any loading stuff node/lib/internal/bootstrap/pre_execution.js Line 105 in e46c680
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in other words, it is true? This was what tripped me up in the tests PR, where the tests were failing only in Windows and it took me a long time to figure out why. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My understanding is it only accepts a CJS specifier (with the notable difference that if you don't specify node/lib/internal/modules/run_main.js Line 16 in 2cc7a91
At least this discussion shows that this part of the docs definitely needs some clarification, because I'm quite confused right now ^^
I think there's a confusion with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||
Passing to `node` as the initial input when the nearest parent `package.json` | ||||||||||||||||||||||||
file contains a top-level [`"type"`][] field with a value of `"commonjs"`, or | ||||||||||||||||||||||||
when referenced by `require()` calls: | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files whose name ends in `.node` are interpreted as | ||||||||||||||||||||||||
compiled addon modules loaded with `process.dlopen()`. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Files whose name ends in `.json` are parsed as JSON text files. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
* Any other files will be treated as a [CommonJS][] module. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
aduh95 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
### `package.json` and file extensions | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
Within a package, the [`package.json`][] [`"type"`][] field defines how | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just don’t think the docs should have any references to “the CommonJS modules loader” or the “ECMAScript modules loader”—no average user knows what those are. There’s just Node, and how it interprets source code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK but using the ES module loader has more effect than on simply JS code (it no longer accepts
.node
and.json
file as entry point, it no longer treats extensionless / unknown extension as JS files), which was the nuance I was trying to communicate here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add those other implications here, then. Most people reading these docs wouldn't know about those nuances just because the loader is mentioned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried to address that in ce3edd0, PTAL.