-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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: packages docs feedback #35370
doc: packages docs feedback #35370
Changes from all commits
d054ee0
4f9495c
0598dc9
7df9cd7
896b1ff
2a19db1
3a35054
272a18c
1ebef9a
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 | ||||
---|---|---|---|---|---|---|
|
@@ -4,11 +4,13 @@ | |||||
|
||||||
## Introduction | ||||||
|
||||||
A package is a folder described by a `package.json` file. | ||||||
A package is a folder tree described by a `package.json` file. The package | ||||||
consists of the folder containing the `package.json` file and all subfolders | ||||||
until the next folder containing another `package.json` file, or a folder | ||||||
named `node_modules`. | ||||||
|
||||||
A folder containing a `package.json` file, and all subfolders below that folder | ||||||
until the next folder containing another `package.json` file, are a _package | ||||||
scope_. | ||||||
This page provides guidance for package authors writing `package.json` files | ||||||
along with a reference for the [`package.json`][] fields defined by Node.js. | ||||||
|
||||||
## Determining module system | ||||||
|
||||||
|
@@ -18,7 +20,7 @@ initial input, or when referenced by `import` statements within ES module code: | |||||
* Files ending in `.mjs`. | ||||||
|
||||||
* Files ending in `.js` when the nearest parent `package.json` file contains a | ||||||
top-level field [`"type"`][] with a value of `"module"`. | ||||||
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`. | ||||||
|
@@ -45,29 +47,25 @@ 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. | ||||||
|
||||||
### Package scope and file extensions | ||||||
### `package.json` and file extensions | ||||||
|
||||||
A folder containing a `package.json` file, and all subfolders below that folder | ||||||
until the next folder containing another [`package.json`][], are a | ||||||
_package scope_. Package scopes do not carry through `node_modules` folders. | ||||||
|
||||||
Within a package scope, the [`package.json`][] [`"type"`][] field defines how | ||||||
Within a package, the [`package.json`][] [`"type"`][] field defines how | ||||||
Node.js should interpret `.js` files. If a `package.json` file does not have a | ||||||
`"type"` field, `.js` files are treated as [CommonJS][]. | ||||||
|
||||||
A `package.json` `"type"` value of `"module"` tells Node.js to interpret `.js` | ||||||
files within that package scope as using [ES module][] syntax. | ||||||
files within that package as using [ES module][] syntax. | ||||||
|
||||||
The package scope applies not only to initial entry points (`node my-app.js`) | ||||||
The `"type"` field applies not only to initial entry points (`node my-app.js`) | ||||||
but also to files referenced by `import` statements and `import()` expressions. | ||||||
|
||||||
```js | ||||||
// my-app.js, in an ES module package scope because there is a package.json | ||||||
// my-app.js, treated as an ES module because there is a package.json | ||||||
// file in the same folder with "type": "module". | ||||||
|
||||||
import './startup/init.js'; | ||||||
// Loaded as ES module since ./startup contains no package.json file, | ||||||
// and therefore inherits the ES module package scope from one level up. | ||||||
// and therefore inherits the "type" value from one level up. | ||||||
|
||||||
import 'commonjs-package'; | ||||||
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json | ||||||
|
@@ -79,10 +77,10 @@ import './node_modules/commonjs-package/index.js'; | |||||
``` | ||||||
|
||||||
Files ending with `.mjs` are always loaded as [ES modules][] regardless of | ||||||
package scope. | ||||||
the nearest parent `package.json`. | ||||||
|
||||||
Files ending with `.cjs` are always loaded as [CommonJS][] regardless of package | ||||||
scope. | ||||||
Files ending with `.cjs` are always loaded as [CommonJS][] regardless of the | ||||||
nearest parent `package.json`. | ||||||
|
||||||
```js | ||||||
import './legacy-file.cjs'; | ||||||
|
@@ -93,17 +91,17 @@ import 'commonjs-package/src/index.mjs'; | |||||
``` | ||||||
|
||||||
The `.mjs` and `.cjs` extensions may be used to mix types within the same | ||||||
package scope: | ||||||
package: | ||||||
|
||||||
* Within a `"type": "module"` package scope, Node.js can be instructed to | ||||||
* Within a `"type": "module"` package, Node.js can be instructed to | ||||||
interpret a particular file as [CommonJS][] by naming it with a `.cjs` | ||||||
extension (since both `.js` and `.mjs` files are treated as ES modules within | ||||||
a `"module"` package scope). | ||||||
a `"module"` package). | ||||||
|
||||||
* Within a `"type": "commonjs"` package scope, Node.js can be instructed to | ||||||
* Within a `"type": "commonjs"` package, Node.js can be instructed to | ||||||
interpret a particular file as an [ES module][] by naming it with an `.mjs` | ||||||
extension (since both `.js` and `.cjs` files are treated as CommonJS within a | ||||||
`"commonjs"` package scope). | ||||||
`"commonjs"` package). | ||||||
|
||||||
### `--input-type` flag | ||||||
|
||||||
|
@@ -211,10 +209,10 @@ To set the main entry point for a package, it is advisable to define both | |||||
} | ||||||
``` | ||||||
|
||||||
The benefit of doing this is that when using the [`"exports"`][] field all | ||||||
subpaths of the package will no longer be available to importers under | ||||||
`require('pkg/subpath.js')`, and instead they will get a new error, | ||||||
`ERR_PACKAGE_PATH_NOT_EXPORTED`. | ||||||
When defining the [`"exports"`][] field, all subpaths of the package will be | ||||||
encapsulated and no longer available to importers. For example, | ||||||
`require('pkg/subpath.js')` would throw an [`ERR_PACKAGE_PATH_NOT_EXPORTED`][] | ||||||
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.
Suggested change
|
||||||
error. | ||||||
|
||||||
This encapsulation of exports provides more reliable guarantees | ||||||
about package interfaces for tools and when handling semver upgrades for a | ||||||
|
@@ -295,24 +293,6 @@ treating the right hand side target pattern as a `**` glob against the list of | |||||
files within the package. Because `node_modules` paths are forbidden in exports | ||||||
targets, this expansion is dependent on only the files of the package itself. | ||||||
|
||||||
### Package exports fallbacks | ||||||
|
||||||
> Stability: 1 - Experimental | ||||||
|
||||||
For possible new specifier support in future, array fallbacks are | ||||||
supported for all invalid specifiers: | ||||||
|
||||||
```json | ||||||
{ | ||||||
"exports": { | ||||||
"./submodule": ["not:valid", "./submodule.js"] | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
Since `"not:valid"` is not a valid specifier, `"./submodule.js"` is used | ||||||
instead as the fallback, as if it were the only target. | ||||||
|
||||||
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. As per @isaacs's comment this also removes documentation on package fallbacks. This may or may not be controversial to the modules group though but I tend to agree with the sentiment of simplifying the outward documentation for this already-complex field. |
||||||
### Exports sugar | ||||||
|
||||||
> Stability: 1 - Experimental | ||||||
|
@@ -560,9 +540,10 @@ could be intended only for other environments such as browsers. Such a package | |||||
would be usable by any version of Node.js, since `import` can refer to CommonJS | ||||||
files; but it would not provide any of the advantages of using ES module syntax. | ||||||
|
||||||
A package could also switch from CommonJS to ES module syntax in a breaking | ||||||
change version bump. This has the disadvantage that the newest version | ||||||
of the package would only be usable in ES module-supporting versions of Node.js. | ||||||
A package could also switch from CommonJS to ES module syntax in a [breaking | ||||||
change](https://semver.org/) version bump. This has the disadvantage that the | ||||||
newest version of the package would only be usable in ES module-supporting | ||||||
versions of Node.js. | ||||||
Comment on lines
-563
to
+546
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. Should we add a note to help users find the relevant info? Somebody who's new to Node.js may ask themself what are the ESM-supporting versions. 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. It sounds like a section on compatibility via a new PR would be useful to users actually. Perhaps esm.md could get a history or we could add a module compatibility section that mentions what levels of ESM support can be expected where. 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. TODO for myself: add a reference to the support table on #35395 if this PR lands first. |
||||||
|
||||||
Every pattern has tradeoffs, but there are two broad approaches that satisfy the | ||||||
following conditions: | ||||||
|
@@ -773,6 +754,19 @@ This section describes the fields used by the Node.js runtime. Other tools (such | |||||
as [npm](https://docs.npmjs.com/creating-a-package-json-file)) may use | ||||||
additional fields which are ignored by Node.js and not documented here. | ||||||
|
||||||
The following fields in `package.json` files are used in Node.js: | ||||||
|
||||||
* [`"name"`][] - Relevant when using named imports within a package. Also used | ||||||
by package managers as the name of the package. | ||||||
* [`"type"`][] - The package type determining whether to load `.js` files as | ||||||
CommonJS or ES modules. | ||||||
* [`"exports"`][] - Package exports and conditional exports. When present, | ||||||
limits which submodules may be loaded from within the package. | ||||||
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.
Suggested change
|
||||||
* [`"main"`][] - The default module when loading the package, if exports is not | ||||||
specified, and in versions of Node.js prior to the introduction of exports. | ||||||
* [`"imports"`][] - Package imports, for use by modules within the package | ||||||
itself. | ||||||
|
||||||
### `"name"` | ||||||
<!-- YAML | ||||||
added: | ||||||
|
@@ -815,7 +809,7 @@ changes: | |||||
* Type: {string} | ||||||
|
||||||
The `"type"` field defines the module format that Node.js will use for all | ||||||
`.js` files within a particular `package.json` file’s [package scope][]. | ||||||
`.js` files that have that `package.json` file as their nearest parent. | ||||||
|
||||||
Files ending with `.js` will be loaded as ES modules when the nearest parent | ||||||
`package.json` file contains a top-level field `"type"` with a value of | ||||||
|
@@ -839,8 +833,8 @@ node my-app.js # Runs as ES module | |||||
|
||||||
If the nearest parent `package.json` lacks a `"type"` field, or contains | ||||||
`"type": "commonjs"`, `.js` files are treated as [CommonJS][]. If the volume | ||||||
root is reached and no `package.json` is found, Node.js defers to the default | ||||||
treatment as [CommonJS][]. | ||||||
root is reached and no `package.json` is found, `.js` files are treated as | ||||||
[CommonJS][]. | ||||||
|
||||||
`import` statements of `.js` files are treated as ES modules if the nearest | ||||||
parent `package.json` contains `"type": "module"`. | ||||||
|
@@ -957,26 +951,27 @@ where `import '#dep'` would now get the resolution of the external package | |||||
`dep-node-native` (including its exports in turn), and instead get the local | ||||||
file `./dep-polyfill.js` relative to the package in other environments. | ||||||
|
||||||
Unlike the exports field, import maps permit mapping to external packages | ||||||
because this provides an important use case for conditional loading and also can | ||||||
be done without the risk of cycles, unlike for exports. | ||||||
Unlike the `"exports"` field, import maps permit mapping to external packages, | ||||||
providing an important use case for conditional loading scenarios. | ||||||
|
||||||
Apart from the above, the resolution rules for the imports field are otherwise | ||||||
analogous to the exports field. | ||||||
|
||||||
[Babel]: https://babeljs.io/ | ||||||
[Conditional exports]: #packages_conditional_exports | ||||||
[CommonJS]: modules.html | ||||||
[entry points]: #packages_package_entry_points | ||||||
[`esm`]: https://github.com/standard-things/esm#readme | ||||||
[`ERR_PACKAGE_PATH_NOT_EXPORTED`]: errors.html#errors_err_package_path_not_exported | ||||||
[ES modules]: esm.html | ||||||
[ES module]: esm.html | ||||||
[`esm`]: https://github.com/standard-things/esm#readme | ||||||
guybedford marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
[`"exports"`]: #packages_exports | ||||||
[`"main"`]: #packages_main | ||||||
[`"name"`]: #packages_name | ||||||
[`"imports"`]: #packages_imports | ||||||
[`"type"`]: #packages_type | ||||||
[entry points]: #packages_package_entry_points | ||||||
[`package.json`]: #packages_node_js_package_json_field_definitions | ||||||
[package scope]: #packages_package_scope_and_file_extensions | ||||||
[self-reference]: #packages_self_referencing_a_package_using_its_name | ||||||
[subpath exports]: #packages_subpath_exports | ||||||
[the full specifier path]: modules_esm.html#modules_esm_mandatory_file_extensions | ||||||
[the dual CommonJS/ES module packages section]: #packages_dual_commonjs_es_module_packages | ||||||
[`"type"`]: #packages_type |
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.
...or perhaps: