diff --git a/doc/api/customization.md b/doc/api/customization.md new file mode 100644 index 000000000000..a636d164507e --- /dev/null +++ b/doc/api/customization.md @@ -0,0 +1,60 @@ +# Customization + +> Stability: 1.0 - Early development + +Node.js has multiple APIs for customizing behavior and can easily be invoked via +[`--import`][]. The package.json of such a plugin would look something like +this: + +```json +{ + "name": "example-nodejs-plugin", + "keywords": [ + "nodejs-plugin" + ], + "exports": { + "register": "./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; a plugin extending Node.js support for TypeScript +should contain `typescript` in its keywords; a customization package that does +not extend Node.js support for TypeScript should not contain `typescript` (such +as when the library is itself merely written in TypeScript). Incorrect keywords +will cause a package to be listed for things it does not do, aggravating users. + +## Setting up a plugin + +These steps configure Node.js with a customization plugin: + +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 use it, you have 2 options: + * Pass the `--import` directly in the shell command: + `node --import=example-nodejs-plugin/register" main.mjs`. This option works + well when the command is simple, but it can + quickly become unwieldy as the list of flags grows, and it requires you to + remember everything you need to pass. + * Create a [`.env`][`--env-file`] file containing an [`--import`][] for your + chosen plugin, like `NODE_OPTIONS="--import=example-nodejs-plugin/register"`. + This option avoids the command becoming unwieldly, and it's easier to + remember (a single, unchanging `--env-file` flag). + * 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 diff --git a/doc/api/module.md b/doc/api/module.md index f4338028abe3..a07e18cd4754 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -683,6 +683,47 @@ 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": { + "register": "./registration.mjs", + "typescript": "./hooks/typescript.mjs" + } +} +``` + +> **Note**: The `"typescript"` entry point is not required but it makes the next +> step (and possibly others) simpler. + +```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 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 @@ -1027,6 +1068,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 @@ -1048,5 +1090,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