@@ -289,6 +289,128 @@ READ_PACKAGE_JSON(_packageURL_)
289289> 1. Throw an _Invalid Package Configuration_ error.
290290> 1. Return the parsed JSON source of the file at _pjsonURL_.
291291
292+ ## Experimental Loader hooks
293+
294+ **Note: This API is currently being redesigned and will still change.**.
295+
296+ <!-- type=misc -->
297+
298+ To customize the default module resolution, loader hooks can optionally be
299+ provided via a ` -- loader ./ loader- name .mjs ` argument to Node.js.
300+
301+ When hooks are used they only apply to ES module loading and not to any
302+ CommonJS modules loaded.
303+
304+ ### Resolve hook
305+
306+ The resolve hook returns the resolved file URL and module format for a
307+ given module specifier and parent file URL:
308+
309+ ` ` ` js
310+ const baseURL = new URL (' file://' );
311+ baseURL .pathname = ` ${ process .cwd ()} /` ;
312+
313+ export async function resolve (specifier ,
314+ parentModuleURL = baseURL ,
315+ defaultResolver ) {
316+ return {
317+ url: new URL (specifier, parentModuleURL).href ,
318+ format: ' esm'
319+ };
320+ }
321+ ` ` `
322+
323+ The ` parentModuleURL` is provided as ` undefined ` when performing main Node.js
324+ load itself.
325+
326+ The default Node.js ES module resolution function is provided as a third
327+ argument to the resolver for easy compatibility workflows.
328+
329+ In addition to returning the resolved file URL value, the resolve hook also
330+ returns a ` format` property specifying the module format of the resolved
331+ module. This can be one of the following:
332+
333+ | ` format` | Description |
334+ | --- | --- |
335+ | ` ' module' ` | Load a standard JavaScript module |
336+ | ` ' commonjs' ` | Load a Node.js CommonJS module |
337+ | ` ' builtin' ` | Load a Node.js builtin module |
338+ | ` ' dynamic' ` | Use a [dynamic instantiate hook][] |
339+
340+ For example, a dummy loader to load JavaScript restricted to browser resolution
341+ rules with only JS file extension and Node.js builtin modules support could
342+ be written:
343+
344+ ` ` ` js
345+ import path from ' path' ;
346+ import process from ' process' ;
347+ import Module from ' module' ;
348+
349+ const builtins = Module .builtinModules ;
350+ const JS_EXTENSIONS = new Set ([' .js' , ' .mjs' ]);
351+
352+ const baseURL = new URL (' file://' );
353+ baseURL .pathname = ` ${ process .cwd ()} /` ;
354+
355+ export function resolve (specifier , parentModuleURL = baseURL , defaultResolve ) {
356+ if (builtins .includes (specifier)) {
357+ return {
358+ url: specifier,
359+ format: ' builtin'
360+ };
361+ }
362+ if (/ ^ \. {0,2} [/] / .test (specifier) !== true && ! specifier .startsWith (' file:' )) {
363+ // For node_modules support:
364+ // return defaultResolve(specifier, parentModuleURL);
365+ throw new Error (
366+ ` imports must begin with '/', './', or '../'; '${ specifier} ' does not` );
367+ }
368+ const resolved = new URL (specifier, parentModuleURL);
369+ const ext = path .extname (resolved .pathname );
370+ if (! JS_EXTENSIONS .has (ext)) {
371+ throw new Error (
372+ ` Cannot load file with non-JavaScript file extension ${ ext} .` );
373+ }
374+ return {
375+ url: resolved .href ,
376+ format: ' esm'
377+ };
378+ }
379+ ` ` `
380+
381+ With this loader, running:
382+
383+ ` ` ` console
384+ NODE_OPTIONS = ' --experimental-modules --loader ./custom-loader.mjs' node x .js
385+ ` ` `
386+
387+ would load the module ` x .js ` as an ES module with relative resolution support
388+ (with ` node_modules` loading skipped in this example).
389+
390+ ### Dynamic instantiate hook
391+
392+ To create a custom dynamic module that doesn't correspond to one of the
393+ existing ` format` interpretations, the ` dynamicInstantiate` hook can be used.
394+ This hook is called only for modules that return ` format: ' dynamic' ` from
395+ the ` resolve` hook.
396+
397+ ` ` ` js
398+ export async function dynamicInstantiate (url ) {
399+ return {
400+ exports: [' customExportName' ],
401+ execute : (exports ) => {
402+ // Get and set functions provided for pre-allocated export names
403+ exports .customExportName .set (' value' );
404+ }
405+ };
406+ }
407+ ` ` `
408+
409+ With the list of module exports provided upfront, the ` execute` function will
410+ then be called at the exact point of module evaluation order for that module
411+ in the import tree.
412+
292413[Node.js EP for ES Modules]: https://github.com/nodejs/node-eps/blob/master/002-es-modules.md
414+ [dynamic instantiate hook]: #esm_dynamic_instantiate_hook
293415[` module .createRequireFromPath ()` ]: modules.html#modules_module_createrequirefrompath_filename
294416[ESM Minimal Kernel]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md
0 commit comments