在异步 load 钩子中的注意事项
【Caveat in the asynchronous load hook】
在使用异步 load 钩子时,为 'commonjs' 省略或提供 source 会产生非常不同的效果:
【When using the asynchronous load hook, omitting vs providing a source for
'commonjs' has very different effects:】
- 当提供
source时,该模块中的所有require调用将由 ESM 加载器处理,并使用已注册的resolve和load钩子;该模块中的所有require.resolve调用将由 ESM 加载器处理,并使用已注册的resolve钩子;只有部分 CommonJS API 可用(例如没有require.extensions、没有require.cache、没有require.resolve.paths),对 CommonJS 模块加载器的猴子补丁将不生效。 - 如果
source是未定义或null,它将由 CommonJS 模块加载器处理,并且require/require.resolve调用不会经过注册的钩子。对于 null 值的source,这种行为是暂时的——将来将不再支持 null 值的source。
这些注意事项不适用于同步的 load 钩子,在这种情况下,自定义 CommonJS 模块可以使用完整的 CommonJS API 集,require/require.resolve 始终会通过已注册的钩子。
【These caveats do not apply to the synchronous load hook, in which case
the complete set of CommonJS APIs available to the customized CommonJS
modules, and require/require.resolve always go through the registered
hooks.】
Node.js 内部的异步 load 实现,即 load 链中最后一个钩子的 next 值,当 format 为 'commonjs' 时,会为了向后兼容而返回 null 作为 source。下面是一个示例钩子,它将选择使用非默认行为:
【The Node.js internal asynchronous load implementation, which is the value of next for the
last hook in the load chain, returns null for source when format is
'commonjs' for backward compatibility. Here is an example hook that would
opt-in to using the non-default behavior:】
import { readFile } from 'node:fs/promises';
// Asynchronous version accepted by module.register(). This fix is not needed
// for the synchronous version accepted by module.registerHooks().
export async function load(url, context, nextLoad) {
const result = await nextLoad(url, context);
if (result.format === 'commonjs') {
result.source ??= await readFile(new URL(result.responseURL ?? url));
}
return result;
} 这同样不适用于同步的 load 钩子,在这种情况下,返回的 source 包含由下一个钩子加载的源代码,无论模块格式如何。
【This doesn't apply to the synchronous load hook either, in which case the
source returned contains source code loaded by the next hook, regardless
of module format.】
警告:异步
load钩子与来自 CommonJS 模块的命名空间导出不兼容。尝试将它们一起使用将导致导入结果为空对象。未来可能会解决此问题。这不适用于同步load钩子,在这种情况下可以像平常一样使用导出。
这些类型都对应于 ECMAScript 中定义的类。
- 具体的<ArrayBuffer>对象是一个<SharedArrayBuffer>。
- 具体的<TypedArray>对象是一个<Uint8Array>。
如果基于文本的格式(即 'json'、'module')的源值不是字符串,它将使用 util.TextDecoder 转换为字符串。
【If the source value of a text-based format (i.e., 'json', 'module')
is not a string, it is converted to a string using util.TextDecoder.】
load 钩子提供了一种方式来定义用于获取已解析 URL 源代码的自定义方法。这允许加载器在潜在情况下避免从磁盘读取文件。它也可以用于将无法识别的格式映射为受支持的格式,例如将 yaml 映射为 module。
【The load hook provides a way to define a custom method for retrieving the
source code of a resolved URL. This would allow a loader to potentially avoid
reading files from disk. It could also be used to map an unrecognized format to
a supported one, for example yaml to module.】
// Asynchronous version accepted by module.register().
export async function load(url, context, nextLoad) {
const { format } = context;
if (Math.random() > 0.5) { // Some condition
/*
For some or all URLs, do some custom logic for retrieving the source.
Always return an object of the form {
format: <string>,
source: <string|buffer>,
}.
*/
return {
format,
shortCircuit: true,
source: '...',
};
}
// Defer to the next hook in the chain.
return nextLoad(url);
} // Synchronous version accepted by module.registerHooks().
function load(url, context, nextLoad) {
// Similar to the asynchronous load() above, since that one does not have
// any asynchronous logic.
} 在更高级的场景中,这也可以用来将不受支持的源转换为受支持的源(见下方示例)。
【In a more advanced scenario, this can also be used to transform an unsupported source to a supported one (see Examples below).】