diff options
author | guybedford <guybedford@gmail.com> | 2017-09-03 14:20:06 +0300 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2017-10-18 09:25:36 +0300 |
commit | 7337a40491da073e8d5eb8acf54d447004d8a418 (patch) | |
tree | 572205f3561856def7d86d3ac27179fff04eb8bd /doc | |
parent | 0e2db0e64e6ce6fc3cc1ea3eeaeee6d2ce797267 (diff) |
module: resolve and instantiate loader pipeline hooks
This enables a --loader flag for Node, which can provide custom
"resolve" and "dynamicInstantiate" methods for custom ES module
loading.
In the process, module providers have been converted from classes
into functions and the module APIs have been made to pass URL strings
over objects.
PR-URL: https://github.com/nodejs/node/pull/15445
Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Diffstat (limited to 'doc')
-rw-r--r-- | doc/api/esm.md | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/doc/api/esm.md b/doc/api/esm.md index 7b684e99a27..bc25c88b9a8 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -98,4 +98,111 @@ fs.readFile('./foo.txt', (err, body) => { }); ``` +## Loader hooks + +<!-- type=misc --> + +To customize the default module resolution, loader hooks can optionally be +provided via a `--loader ./loader-name.mjs` argument to Node. + +When hooks are used they only apply to ES module loading and not to any +CommonJS modules loaded. + +### Resolve hook + +The resolve hook returns the resolved file URL and module format for a +given module specifier and parent file URL: + +```js +import url from 'url'; + +export async function resolve(specifier, parentModuleURL, defaultResolver) { + return { + url: new URL(specifier, parentModuleURL).href, + format: 'esm' + }; +} +``` + +The default NodeJS ES module resolution function is provided as a third +argument to the resolver for easy compatibility workflows. + +In addition to returning the resolved file URL value, the resolve hook also +returns a `format` property specifying the module format of the resolved +module. This can be one of `"esm"`, `"cjs"`, `"json"`, `"builtin"` or +`"addon"`. + +For example a dummy loader to load JavaScript restricted to browser resolution +rules with only JS file extension and Node builtin modules support could +be written: + +```js +import url from 'url'; +import path from 'path'; +import process from 'process'; + +const builtins = new Set( + Object.keys(process.binding('natives')).filter((str) => + /^(?!(?:internal|node|v8)\/)/.test(str)) +); +const JS_EXTENSIONS = new Set(['.js', '.mjs']); + +export function resolve(specifier, parentModuleURL/*, defaultResolve */) { + if (builtins.has(specifier)) { + return { + url: specifier, + format: 'builtin' + }; + } + if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) { + // For node_modules support: + // return defaultResolve(specifier, parentModuleURL); + throw new Error( + `imports must begin with '/', './', or '../'; '${specifier}' does not`); + } + const resolved = new url.URL(specifier, parentModuleURL); + const ext = path.extname(resolved.pathname); + if (!JS_EXTENSIONS.has(ext)) { + throw new Error( + `Cannot load file with non-JavaScript file extension ${ext}.`); + } + return { + url: resolved.href, + format: 'esm' + }; +} +``` + +With this loader, running: + +``` +NODE_OPTIONS='--experimental-modules --loader ./custom-loader.mjs' node x.js +``` + +would load the module `x.js` as an ES module with relative resolution support +(with `node_modules` loading skipped in this example). + +### Dynamic instantiate hook + +To create a custom dynamic module that doesn't correspond to one of the +existing `format` interpretations, the `dynamicInstantiate` hook can be used. +This hook is called only for modules that return `format: "dynamic"` from +the `resolve` hook. + +```js +export async function dynamicInstantiate(url) { + return { + exports: ['customExportName'], + execute: (exports) => { + // get and set functions provided for pre-allocated export names + exports.customExportName.set('value'); + } + }; +} +``` + +With the list of module exports provided upfront, the `execute` function will +then be called at the exact point of module evalutation order for that module +in the import tree. + [Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md |