From a6d5e0ad8f5b998a533c64935a649d054fad6bc0 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 17 Jan 2022 11:36:19 +0100 Subject: [PATCH] doc: clarify module system selection Refs: https://github.com/nodejs/node/pull/41345#discussion_r777115823 PR-URL: https://github.com/nodejs/node/pull/41383 Reviewed-By: Guy Bedford Reviewed-By: Geoffrey Booth --- doc/api/cli.md | 44 ++++++++++++++++++++++---- doc/api/esm.md | 12 ++++--- doc/api/modules.md | 35 ++++++++++++++++++++- doc/api/packages.md | 63 ++++++++++++++++++++++++++++++++----- doc/api/synopsis.md | 2 +- tools/doc/links-mapper.json | 4 +-- 6 files changed, 137 insertions(+), 23 deletions(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index 63bd7aaa702609..76c122fc16dd0b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1,4 +1,4 @@ -# Command-line options +# Command-line API @@ -11,9 +11,9 @@ To view this documentation as a manual page in a terminal, run `man node`. ## Synopsis -`node [options] [V8 options] [script.js | -e "script" | -] [--] [arguments]` +`node [options] [V8 options] [ | -e "script" | -] [--] [arguments]` -`node inspect [script.js | -e "script" | :] …` +`node inspect [ | -e "script" | :] …` `node --v8-options` @@ -21,6 +21,33 @@ Execute without arguments to start the [REPL][]. For more info about `node inspect`, see the [debugger][] documentation. +## Program entry point + +The program entry point is a specifier-like string. If the string is not an +absolute path, it's resolved as a relative path from the current working +directory. That path is then resolved by [CommonJS][] module loader. If no +corresponding file is found, an error is thrown. + +If a file is found, its path will be passed to the [ECMAScript module loader][] +under any of the following conditions: + +* The program was started with a command-line flag that forces the entry + point to be loaded with ECMAScript module loader. +* The file has an `.mjs` extension. +* The file does not have a `.cjs` extension, and the nearest parent + `package.json` file contains a top-level [`"type"`][] field with a value of + `"module"`. + +Otherwise, the file is loaded using the CommonJS module loader. See +[Modules loaders][] for more details. + +### ECMAScript modules loader entry point caveat + +When loading [ECMAScript module loader][] loads the program entry point, the `node` +command will only accept as input only files with `.js`, `.mjs`, or `.cjs` +extensions; and with `.wasm` extensions when +[`--experimental-wasm-modules`][] is enabled. + ## Options -Specify the `module` of a custom experimental [ECMAScript Module loader][]. -`module` may be either a path to a file, or an ECMAScript Module name. +Specify the `module` of a custom experimental [ECMAScript module loader][]. +`module` may be any string accepted as an [`import` specifier][]. ### `--experimental-policy` @@ -1931,15 +1958,19 @@ $ node --max-old-space-size=1536 index.js ``` [Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/ -[ECMAScript Module loader]: esm.md#loaders +[CommonJS]: modules.md +[ECMAScript module loader]: esm.md#loaders +[Modules loaders]: packages.md#modules-loaders [OSSL_PROVIDER-legacy]: https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html [REPL]: repl.md [ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage [Source Map]: https://sourcemaps.info/spec.html [Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity [V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html +[`"type"`]: packages.md#type [`--cpu-prof-dir`]: #--cpu-prof-dir [`--diagnostic-dir`]: #--diagnostic-dirdirectory +[`--experimental-wasm-modules`]: #--experimental-wasm-modules [`--heap-prof-dir`]: #--heap-prof-dir [`--openssl-config`]: #--openssl-configfile [`--redirect-warnings`]: #--redirect-warningsfile @@ -1952,6 +1983,7 @@ $ node --max-old-space-size=1536 index.js [`dns.lookup()`]: dns.md#dnslookuphostname-options-callback [`dns.setDefaultResultOrder()`]: dns.md#dnssetdefaultresultorderorder [`dnsPromises.lookup()`]: dns.md#dnspromiseslookuphostname-options +[`import` specifier]: esm.md#import-specifiers [`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn [`tls.DEFAULT_MAX_VERSION`]: tls.md#tlsdefault_max_version [`tls.DEFAULT_MIN_VERSION`]: tls.md#tlsdefault_min_version diff --git a/doc/api/esm.md b/doc/api/esm.md index 54f576261c2387..3b74ff03bc60c1 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -94,12 +94,12 @@ provides interoperability between them and its original module format, -Node.js treats JavaScript code as CommonJS modules by default. -Authors can tell Node.js to treat JavaScript code as ECMAScript modules +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. See -[Modules: Packages](packages.md#determining-module-system) for more -details. +[`--input-type`][] flag. Outside of those cases, Node.js will use the CommonJS +module loader. See [Determining module system][] for more details. @@ -1443,6 +1443,7 @@ success! [CommonJS]: modules.md [Conditional exports]: packages.md#conditional-exports [Core modules]: modules.md#core-modules +[Determining module system]: packages.md#determining-module-system [Dynamic `import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports [ES Module Integration Proposal for WebAssembly]: https://github.com/webassembly/esm-integration [Import Assertions]: #import-assertions @@ -1454,6 +1455,7 @@ success! [WHATWG JSON modules specification]: https://html.spec.whatwg.org/#creating-a-json-module-script [`"exports"`]: packages.md#exports [`"type"`]: packages.md#type +[`--input-type`]: cli.md#--input-typetype [`ArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer [`SharedArrayBuffer`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer [`TypedArray`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray diff --git a/doc/api/modules.md b/doc/api/modules.md index e8f0788ceba960..ce8b1d5f123e23 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -61,7 +61,38 @@ 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 + + + +Node.js has two module systems: CommonJS modules and [ECMAScript modules][]. + +By default, Node.js will treat the following as CommonJS modules: + +* Files with a `.cjs` extension; + +* Files with a `.js` extension when the nearest parent `package.json` file + contains a top-level field [`"type"`][] with a value of `"commonjs"`. + +* Files with a `.js` extension when the nearest parent `package.json` file + doesn't contain a top-level field [`"type"`][]. Package authors should include + the [`"type"`][] field, even in packages where all sources are CommonJS. Being + explicit about the `type` of the package will make things easier for build + tools and loaders to determine how the files in the package should be + interpreted. + +* Files with an extension that is not `.mjs`, `.cjs`, `.json`, `.node`, or `.js` + (when the nearest parent `package.json` file contains a top-level field + [`"type"`][] with a value of `"module"`, those files will be recognized as + CommonJS modules only if they are being `require`d, not when used as the + command-line entry point of the program). + +See [Determining module system][] for more details. + +Calling `require()` always use the CommonJS module loader. Calling `import()` +always use the ECMAScript module loader. ## Accessing the main module @@ -1047,6 +1078,7 @@ This section was moved to [ECMAScript Modules]: esm.md [GLOBAL_FOLDERS]: #loading-from-the-global-folders [`"main"`]: packages.md#main +[`"type"`]: packages.md#type [`ERR_REQUIRE_ESM`]: errors.md#err_require_esm [`Error`]: errors.md#class-error [`__dirname`]: #__dirname @@ -1054,6 +1086,7 @@ This section was moved to [`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 diff --git a/doc/api/packages.md b/doc/api/packages.md index 9529685b953127..e75549256bf2fc 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -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 with an `.mjs` extension. -* Files ending in `.js` when the nearest parent `package.json` file contains a - top-level [`"type"`][] field with a value of `"module"`. +* Files with a `.js` extension 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,13 @@ 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, `import()` expressions, or +`require()` expressions: -* Files ending in `.cjs`. +* Files with a `.cjs` extension. -* Files ending in `.js` when the nearest parent `package.json` file contains a - top-level field [`"type"`][] with a value of `"commonjs"`. +* Files with a `.js` extension 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 +85,48 @@ 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. +### Modules loaders + +Node.js has two systems for resolving a specifier and loading modules. + +There is the CommonJS module loader: + +* It is fully synchronous. +* It is responsible for handling `require()` calls. +* It is monkey patchable. +* It supports [folders as modules][]. +* When resolving a specifier, if no exact match is found, it will try to add + extensions (`.js`, `.json`, and finally `.node`) and then attempt to resolve + [folders as modules][]. +* It treats `.json` as JSON text files. +* `.node` files are interpreted as compiled addon modules loaded with + `process.dlopen()`. +* It treats all files that lack `.json` or `.node` extensions as JavaScript + text files. +* It cannot be used to load ECMAScript modules (although it is possible to + [load ECMASCript modules from CommonJS modules][]). When used to load a + JavaScript text file that is not an ECMAScript module, it loads it as a + CommonJS module. + +There is the ECMAScript module loader: + +* It is asynchronous. +* It is responsible for handling `import` statements and `import()` expressions. +* It is not monkey patchable, can be customized using [loader hooks][]. +* It does not support folders as modules, directory indexes (e.g. + `'./startup/index.js'`) must be fully specified. +* It does no extension searching. A file extension must be provided + when the specifier is a relative or absolute file URL. +* It can load JSON modules, but an import assertion is required (behind + `--experimental-json-modules` flag). +* It accepts only `.js`, `.mjs`, and `.cjs` extensions for JavaScript text + files. +* It can be used to load JavaScript CommonJS modules. Such modules + are passed through the `es-module-lexer` to try to identify named exports, + which are available if they can be determined through static analysis. + Imported CommonJS modules have their URLs converted to absolute + paths and are then loaded via the CommonJS module loader. + ### `package.json` and file extensions Within a package, the [`package.json`][] [`"type"`][] field defines how @@ -1236,6 +1280,9 @@ This field defines [subpath imports][] for the current package. [`esm`]: https://github.com/standard-things/esm#readme [`package.json`]: #nodejs-packagejson-field-definitions [entry points]: #package-entry-points +[folders as modules]: modules.md#folders-as-modules +[load ECMASCript modules from CommonJS modules]: modules.md#the-mjs-extension +[loader hooks]: esm.md#loaders [self-reference]: #self-referencing-a-package-using-its-name [subpath exports]: #subpath-exports [subpath imports]: #subpath-imports diff --git a/doc/api/synopsis.md b/doc/api/synopsis.md index cbc1888e201ba2..79b881952475f6 100644 --- a/doc/api/synopsis.md +++ b/doc/api/synopsis.md @@ -88,6 +88,6 @@ Now, open any preferred web browser and visit `http://127.0.0.1:3000`. If the browser displays the string `Hello, World!`, that indicates the server is working. -[Command-line options]: cli.md#command-line-options +[Command-line options]: cli.md#options [Installing Node.js via package manager]: https://nodejs.org/en/download/package-manager/ [web server]: http.md diff --git a/tools/doc/links-mapper.json b/tools/doc/links-mapper.json index 7232539ca461f5..211ba70c0b5991 100644 --- a/tools/doc/links-mapper.json +++ b/tools/doc/links-mapper.json @@ -1,6 +1,6 @@ { "doc/api/synopsis.md": { - "command line options": "cli.html#command-line-options", + "command line options": "cli.html#options", "web server": "http.html" } -} \ No newline at end of file +}