Skip to content
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: add Customization section and subsections to related sub systems #50419

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions doc/api/customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Customization

> Stability: 1.0 - Early development

Node.js has multiple APIs for customizing behaviour and can easily be invoked
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Node.js has multiple APIs for customizing behaviour and can easily be invoked
Node.js has multiple APIs for customizing behavior and can easily be invoked

Copy link
Member Author

@JakobJingleheimer JakobJingleheimer Oct 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gah. I think VS Code uses my system language for spell check. (This project has confused the heck outta spell check on my iPhone)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use this extension: https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker

It allows you to have custom settings by workspace, so you can create a Node workspace and configure cSpell within that workspace to use US English.

via [`--import`][]: A `"nodejs-customization"` [entry point][`"exports"`] for a
package loaded via [`--import`][] is automatically run at startup. The
package.json of such a plugin would look something like this:

```json
{
"name": "example-nodejs-plugin",
"keywords": [
"nodejs-plugin"
],
"exports": {
"nodejs-customization": "./registration.mjs"
}
}
```

Setting the keyword `nodejs-plugin` is important for users to find the package
(which may be automated). It should also contain the official name(s) for the
support it provides; for a plugin extending support for typescript would contain
`typescript` in its keywords; avoid red-herrings such as `typescript` as a
keyword when the library does not extend support for typescript but is merely
itself written in typescript. Failing to avoid these red-herrings will cause
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
support it provides; for a plugin extending support for typescript would contain
`typescript` in its keywords; avoid red-herrings such as `typescript` as a
keyword when the library does not extend support for typescript but is merely
itself written in typescript. Failing to avoid these red-herrings will cause
support it provides; a plugin adding Node.js support for TypeScript should contain
`typescript` in its keywords, to avoid matches such as `typescript` as a
keyword when the library does not add support for TypeScript but is merely
itself written in TypeScript. Using nonstandard keywords will cause

your package to be listed for things it does not do, aggravating users.

These steps configure Node.js with a customization plugin:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section mixes instructions for package authors writing a plugin with instructions for using such a plugin. I feel like those should be separate sections, as end users aren’t going to care about how a plugin should be authored.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a heading ("Setting up a plugin") between 28 and 30

Copy link
Member

@GeoffreyBooth GeoffreyBooth Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably put the “Using a plugin” section first, as it applies to more people. (Not sure if “setting up” means authoring or consuming.)


1. Install the plugin via your preferred package manager. For finding the
plugin, package managers provide often a CLI utility, such as [`npm
search`][], as well as a website.
2. Once installed, in order to get Node.js to automatically use it, create a
[`.env`][`--env-file`] file containing an [`--import`][] for your chosen
plugin, like `NODE_OPTIONS="--import=example-nodejs-plugin"`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we’re using the standardized export that you mentioned above, this would be

Suggested change
plugin, like `NODE_OPTIONS="--import=example-nodejs-plugin"`.
plugin, like `NODE_OPTIONS="--import=example-nodejs-plugin/nodejs-customization"`.

Copy link
Member Author

@JakobJingleheimer JakobJingleheimer Oct 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OH!! Actually, this is an interesting point, and I think absolves us from a not-great entry-point name: We "control" this --import, so if the --import itself specifies the entry-point, I think that means it's safe to use whatever we want (so this would be okay):

{
  "name": "example-nodejs-plugin",
  "exports": {
    "register": "whatever.mjs"
  },
  "keywords": ["nodejs-plugin", ""]
}
NODE_OPTIONS="--import=example-nodejs-plugin/register"

The problem before would exist if node automatically calls the entry-point whenever any package is --imported; but if we include the entry-point in the --import's specifier, then we're safe (and this only happens when the user manually puts it there, or in future, when the wizard automates it).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but that’s also why it’s not that important that it be standardized. I think the only benefit of standardization is so that the wizard we eventually create knows what export to use.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[…] the wizard we eventually create knows what export to use.

Yes, exactly.

3. Include the env file flag on subsequent runs, like
`node --env-file=.env main.mjs`.

## APIs

Customizable systems each expose a `register` function, whereby the
customization plugin is registered into Node.js.

* [`node:module`][module.register]

[`"exports"`]: packages.md#exports
[`--env-file`]: cli.md#--env-file
[`--import`]: cli.md#--import
[`npm search`]: https://docs.npmjs.com/cli/v10/commands/npm-search
[module.register]: module.md#moduleregisterspecifier-parenturl-options
40 changes: 40 additions & 0 deletions doc/api/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,44 @@ export async function load(url, context, nextLoad) {
In a more advanced scenario, this can also be used to transform an unsupported
source to a supported one (see [Examples](#examples) below).

### External formats

Node.js natively understands a handful of formats (see the table in [`load`
hook][load hook]). Non-native or external formats require transpilation to
something Node.js understands, and many packages exist to handle this. A simple
example of such a package would look something like this:

```json
{
"name": "example-nodejs-plugin",
"keywords": [
"nodejs-plugin",
"typescript"
],
"exports": {
"nodejs-customization": "./registration.mjs",
"typescript": "./hooks/typescript.mjs"
}
}
```

```mjs
import { register } from 'node:module';

register('example-nodejs-plugin/typescript');
```

`typescript.mjs` would contain [customization hooks][hooks]:

* A [`resolve` hook][resolve hook] that sets `format` for applicable modules to
the format it handles, such as `'typescript'`.
* A [`load` hook][load hook] that transpiles the external format (as signalled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* A [`load` hook][load hook] that transpiles the external format (as signalled
* A [`load` hook][load hook] that transpiles the external format (as signaled

by its `resolve` hook) to something Node.js understands.
* Optionally, an [`initialize` hook][`initialize`].

See [Node.js Customization][Customization] for information about registering and
persisting these plugins.

### Examples

The various module customization hooks can be used together to accomplish
Expand Down Expand Up @@ -1027,6 +1065,7 @@ returned object contains the following keys:

[CommonJS]: modules.md
[Conditional exports]: packages.md#conditional-exports
[Customization]: customization.md
[Customization hooks]: #customization-hooks
[ES Modules]: esm.md
[HTTPS and HTTP imports]: esm.md#https-and-http-imports
Expand All @@ -1048,5 +1087,6 @@ returned object contains the following keys:
[load hook]: #loadurl-context-nextload
[module wrapper]: modules.md#the-module-wrapper
[realm]: https://tc39.es/ecma262/#realm
[resolve hook]: #resolvespecifier-context-nextresolve
[source map include directives]: https://sourcemaps.info/spec.html#h.lmz475t4mvbx
[transferrable objects]: worker_threads.md#portpostmessagevalue-transferlist