启用


【Enabling】

模块解析和加载可以通过以下方式自定义:

【Module resolution and loading can be customized by:】

  1. 注册一个导出一组异步钩子函数的文件,使用来自 node:moduleregister 方法,
  2. 使用 node:module 中的 registerHooks 方法注册一组同步钩子函数。

可以在应用代码运行之前使用 --import--require 标志注册这些钩子:

【The hooks can be registered before the application code is run by using the --import or --require flag:】

node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js 
// register-hooks.js
// This file can only be require()-ed if it doesn't contain top-level await.
// Use module.register() to register asynchronous hooks in a dedicated thread.
import { register } from 'node:module';
register('./hooks.mjs', import.meta.url);// register-hooks.js
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
// Use module.register() to register asynchronous hooks in a dedicated thread.
register('./hooks.mjs', pathToFileURL(__filename));
// Use module.registerHooks() to register synchronous hooks in the main thread.
import { registerHooks } from 'node:module';
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});// Use module.registerHooks() to register synchronous hooks in the main thread.
const { registerHooks } = require('node:module');
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});

传递给 --import--require 的文件也可以是某个依赖的导出内容:

【The file passed to --import or --require can also be an export from a dependency:】

node --import some-package/register ./my-app.js
node --require some-package/register ./my-app.js 

some-package 包含一个 "exports" 字段,用于定义 /register 导出,以映射到调用 register() 的文件,例如以下 register-hooks.js 示例。

【Where some-package has an "exports" field defining the /register export to map to a file that calls register(), like the following register-hooks.js example.】

使用 --import--require 可以确保在导入任何应用文件之前注册钩子,包括应用的入口点以及默认情况下的任何工作线程。

【Using --import or --require ensures that the hooks are registered before any application files are imported, including the entry point of the application and for any worker threads by default as well.】

或者,可以从入口点调用 register()registerHooks(),不过对于任何应在钩子注册后运行的 ESM 代码,必须使用动态 import()

【Alternatively, register() and registerHooks() can be called from the entry point, though dynamic import() must be used for any ESM code that should be run after the hooks are registered.】

import { register } from 'node:module';

register('http-to-https', import.meta.url);

// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
await import('./my-app.js');const { register } = require('node:module');
const { pathToFileURL } = require('node:url');

register('http-to-https', pathToFileURL(__filename));

// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
import('./my-app.js');

自定义钩子将在注册后加载的任何模块以及它们通过 import 和内置 require 引用的模块上运行。通过 module.createRequire() 创建的用户 require 函数只能通过同步钩子进行自定义。

【Customization hooks will run for any modules loaded later than the registration and the modules they reference via import and the built-in require. require function created by users using module.createRequire() can only be customized by the synchronous hooks.】

在此示例中,我们正在注册 http-to-https 钩子,但它们仅对随后导入的模块可用——在本例中是 my-app.js 以及它通过 import 或 CommonJS 依赖中的内置 require 引用的任何内容。

【In this example, we are registering the http-to-https hooks, but they will only be available for subsequently imported modules — in this case, my-app.js and anything it references via import or built-in require in CommonJS dependencies.】

如果 import('./my-app.js') 改为静态的 import './my-app.js',应用将在注册 http-to-https 钩子之前就已经被加载。这是由于 ES 模块规范,静态导入会先从依赖树的叶子节点开始求值,然后再回到主干。在 my-app.js 内部可能还有静态导入,这些导入只有在动态导入 my-app.js 时才会被求值。

【If the import('./my-app.js') had instead been a static import './my-app.js', the app would have already been loaded before the http-to-https hooks were registered. This due to the ES modules specification, where static imports are evaluated from the leaves of the tree first, then back to the trunk. There can be static imports within my-app.js, which will not be evaluated until my-app.js is dynamically imported.】

如果使用同步钩子,则同时支持 importrequire 以及使用 createRequire() 创建的用户 require

【If synchronous hooks are used, both import, require and user require created using createRequire() are supported.】

import { registerHooks, createRequire } from 'node:module';

registerHooks({ /* implementation of synchronous hooks */ });

const require = createRequire(import.meta.url);

// The synchronous hooks affect import, require() and user require() function
// created through createRequire().
await import('./my-app.js');
require('./my-app-2.js');const { register, registerHooks } = require('node:module');
const { pathToFileURL } = require('node:url');

registerHooks({ /* implementation of synchronous hooks */ });

const userRequire = createRequire(__filename);

// The synchronous hooks affect import, require() and user require() function
// created through createRequire().
import('./my-app.js');
require('./my-app-2.js');
userRequire('./my-app-3.js');

最后,如果你只是想在应用运行前注册钩子,并且不想为此创建一个单独的文件,你可以将一个 data: URL 传递给 --import

【Finally, if all you want to do is register hooks before your app runs and you don't want to create a separate file for that purpose, you can pass a data: URL to --import:】

node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js