Repository: nodejs/node
Pull request: doc: correct module loading descriptions

The existing description is outdated, and exposes too many details that are subject to change.

- There is no point conceptualizing "two module loaders", in reality the boundary is blurred since the two invoke each other to support require(esm) and import(cjs). The distinction lies not in what kind of module is being requested/which loader is used, but only in how the the module request is initiated (via `require()` or `import()`). The inner working of the loaders are subject to change and not suitable to be documented.
- It should not mention monkey patching in the documentation, as publicly supported universal hooks are already provided, and so there's no need to single out any of them in terms of loader hooks support either.
- Remove the description about whether they are asynchronous or synchronous, which is also implementation detail subject to change.
- Add missing descriptions about how .ts, .mts and .cts are treated, and `.node` is also supported in import now.
- There is no need to specially mention .node treatment in cli.md, link to the explanations about loading from `import` in packages.md instead.

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api/cli.md
patch: @@ -25,30 +25,22 @@ For more info about `node inspect`, see the [debugger][] documentation.
 
 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.
+directory. That entry point string is then resolved as if it's been requested
+by `require()` from the current working directory. If no corresponding file
+is found, an error is thrown.
 
-If a file is found, its path will be passed to the
-[ES module loader][Modules loaders] under any of the following conditions:
+By default, the resolved path is also loaded as if it's been requested by `require()`,
+unless one of the conditions below apply—then it's loaded as if it's been requested
+by `import()`:
 
 * The program was started with a command-line flag that forces the entry
   point to be loaded with ECMAScript module loader, such as `--import`.
-* The file has an `.mjs` or `.wasm` extension.
+* The file has an `.mjs`, `.mts` or `.wasm` 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, the [ES module loader][Modules loaders] loads the program
-entry point, the `node` command will accept as input only files with `.js`,
-`.mjs`, or `.cjs` extensions. With the following flags, additional file
-extensions are enabled:
-
-* [`--experimental-addon-modules`][] for files with `.node` extension.
+See [module resolution and loading][] for more details.
 
 ## Options
 
@@ -4081,7 +4073,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
 [#42511]: https://github.com/nodejs/node/issues/42511
 [Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
 [Chromium's policy for locally trusted certificates]: https://chromium.googlesource.com/chromium/src/+/main/net/data/ssl/chrome_root_store/faq.md#does-the-chrome-certificate-verifier-consider-local-trust-decisions
-[CommonJS]: modules.md
 [CommonJS module]: modules.md
 [DEP0025 warning]: deprecations.md#dep0025-requirenodesys
 [ECMAScript module]: esm.md#modules-ecmascript-modules
@@ -4091,7 +4082,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
 [Loading ECMAScript modules using `require()`]: modules.md#loading-ecmascript-modules-using-require
 [Module customization hooks]: module.md#customization-hooks
 [Module customization hooks: enabling]: module.md#enabling
-[Modules loaders]: packages.md#modules-loaders
+[Module resolution and loading]: packages.md#module-resolution-and-loading
 [Navigator API]: globals.md#navigator
 [Node.js issue tracker]: https://github.com/nodejs/node/issues
 [OSSL_PROVIDER-legacy]: https://www.openssl.org/docs/man3.0/man7/OSSL_PROVIDER-legacy.html
@@ -4117,7 +4108,6 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
 [`--disable-sigusr1`]: #--disable-sigusr1
 [`--env-file-if-exists`]: #--env-file-if-existsfile
 [`--env-file`]: #--env-filefile
-[`--experimental-addon-modules`]: #--experimental-addon-modules
 [`--experimental-sea-config`]: single-executable-applications.md#generating-single-executable-preparation-blobs
 [`--heap-prof-dir`]: #--heap-prof-dir
 [`--import`]: #--importmodule

filename: doc/api/packages.md
patch: @@ -142,46 +142,56 @@ CommonJS. This includes the following:
 * Lexical redeclarations of the CommonJS wrapper variables (`require`, `module`,
   `exports`, `__dirname`, `__filename`).
 
-### 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 can only be used to [load ECMAScript modules from CommonJS modules][] if
-  the module graph is synchronous (that contains no top-level `await`).
-  When used to load a JavaScript text file that is not an ECMAScript module,
-  the file will be loaded as a CommonJS module.
-
-There is the ECMAScript module loader:
-
-* It is asynchronous, unless it's being used to load modules for `require()`.
-* 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 type attribute is required.
-* 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 `cjs-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.
+### Module resolution and loading
+
+Node.js has two types of module resolution and loading, chosen based on how the module is requested.
+
+When a module is requested via `require()` (available by default in CommonJS modules,
+and can be dynamically generated using `createRequire()` in both CommonJS and ES Modules):
+
+* Resolution:
+  * The resolution initiated by `require()` supports [folders as modules][].
+  * When resolving a specifier, if no exact match is found, `require()` will try to add
+    extensions (`.js`, `.json`, and finally `.node`) and then attempt to resolve
+    [folders as modules][].
+  * It does not support URLs as specifiers by default.
+* Loading:
+  * `.json` files are treated as JSON text files.
+  * `.node` files are interpreted as compiled addon modules loaded with `process.dlopen()`.
+  * `.ts`, `.mts` and `.cts` files are treated as [TypeScript][] text files.
+  * Files with any other extension, or without extensions, are treated as JavaScript
+    text files.
+  * `require()` can only be used to [load ECMAScript modules from CommonJS modules][] if
+    the [ECMAScript module][ES Module] _and its dependencies_ are synchronous
+    (i.e. they do not contain top-level `await`).
+
+When a module is requested via static `import` statements (only available in ES Modules)
+or `import()` expressions (available in both CommonJS and ES Modules):
+
+* Resolution:
+  * The resolution of `import`/`import()` does not support folders as modules,
+    directory indexes (e.g. `'./startup/index.js'`) must be fully specified.
+  * It does not perform extension searching. A file extension must be provided
+    when the specifier is a relative or absolute file URL.
+  * It supports `file://` and `data:` URLs as specifiers by default.
+* Loading:
+  * `.json` files are treated as JSON text files. When importing JSON modules,
+    an import type attribute is required (e.g.
+    `import json from './data.json' with { type: 'json' }`).
+  * `.node` files are interpreted as compiled addon modules loaded with
+    `process.dlopen()`, if [`--experimental-addon-modules`][] is enabled.
+  * `.ts`, `.mts` and `.cts` files are treated as [TypeScript][] text files.
+  * It accepts only `.js`, `.mjs`, and `.cjs` extensions for JavaScript text
+    files.
+  * `.wasm` files are treated as [WebAssembly modules][].
+  * Any other file extensions will result in a  [`ERR_UNKNOWN_FILE_EXTENSION`][] error.
+    Additional file extensions can be facilitated via [customization hooks][].
+  * `import`/`import()` can be used to load JavaScript [CommonJS modules][commonjs].
+    Such modules are passed through the `cjs-module-lexer` to try to identify named
+    exports, which are available if they can be determined through static analysis.
+
+Regardless of how a module is requested, the resolution and loading process can be customized
+using [customization hooks][].
 
 ### `package.json` and file extensions
 
@@ -1151,21 +1161,25 @@ This field defines [subpath imports][] for the current package.
 [Node.js documentation for this section]: https://github.com/nodejs/node/blob/HEAD/doc/api/packages.md#conditions-definitions
 [Runtime Keys]: https://runtime-keys.proposal.wintercg.org/
 [Syntax detection]: #syntax-detection
+[TypeScript]: typescript.md
+[WebAssembly modules]: esm.md#wasm-modules
 [WinterCG]: https://wintercg.org/
 [`"exports"`]: #exports
 [`"imports"`]: #imports
 [`"main"`]: #main
 [`"name"`]: #name
 [`"type"`]: #type
 [`--conditions` / `-C` flag]: #resolving-user-conditions
+[`--experimental-addon-modules`]: cli.md#--experimental-addon-modules
 [`--no-addons` flag]: cli.md#--no-addons
 [`ERR_PACKAGE_PATH_NOT_EXPORTED`]: errors.md#err_package_path_not_exported
+[`ERR_UNKNOWN_FILE_EXTENSION`]: errors.md#err_unknown_file_extension
 [`package.json`]: #nodejs-packagejson-field-definitions
+[customization hooks]: module.md#customization-hooks
 [entry points]: #package-entry-points
 [folders as modules]: modules.md#folders-as-modules
 [import maps]: https://github.com/WICG/import-maps
 [load ECMAScript modules from CommonJS modules]: modules.md#loading-ecmascript-modules-using-require
-[loader hooks]: esm.md#loaders
 [packages folder mapping]: https://github.com/WICG/import-maps#packages-via-trailing-slashes
 [self-reference]: #self-referencing-a-package-using-its-name
 [subpath exports]: #subpath-exports