_
2024年3月20日 3:10:33 PM
1988 字
5 分钟

前言

在编写 MinaPlay 的插件系统的时候遇到了这样的情况,记一下 Node.js module resolve hook 的使用心得。 resolve hook 用在你想要改变 Node.js 导入模块时指定模块路径对应的物理文件位置的时候,听起来可能有些拗口,举个简单的例子:

首先编写了一段模块导入代码:

import lodash from 'lodash';
TYPESCRIPT

Node.js 的默认导入机制在大多情况下都能够加载用户想要的包,但如果我想手动改指明 Node.js 该在哪个物理路径下查找包呢? 比如我想把导入的 lodash 解析为 my-repo/packages/lodash 这个路径而不是加载 node_modules 中的 lodash 。 这个时候 Node.js 的 resolve hook 就派上用场了。

截至这篇 BLOG 发文时间,resolve hook 相关 API 还处于 RC(Release candidate) 阶段,请考虑谨慎使用在生产环境。

在 Node.js 的 node:module API 出现之前,开发者们可能更习惯用 Node.js module loaders 来做类似的实现。 在 Node.js 的最近版本将 module loaders 相关特性整合到了 node:module API 中。

用法

首先需要编写一个 hook.ts 代码文件。

import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

export async function resolve(specifier: string, context: object, nextResolve: Function) {
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
  const base = pathToFileURL(path.join(__dirname, './my-repo/packages/lodash'));
  return nextResolve(specifier.replace('lodash', base.href), context);
}
TYPESCRIPT

之后在 import lodash from 'lodash' 语句执行之前注册此 hook。

import { register } from 'node:module';

function registerImportMap() {
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
  register(pathToFileURL(path.join(__dirname, './hook.js')));
}
TYPESCRIPT

然后所有的 lodash 导入都会映射到 my-repo/packages/lodash 路径下啦。

原理

resolve(specifier, context, nextResolve)

这个函数代表了用户对模块加载解析的自定义实现,其三个参数分别为:

  • specifier - 导入标志。
  • context - 导入时的上下文,包括 import attributes 和父模块路径等信息。
  • nextResolve - 下一个 resolve hook 的 resolve 函数。

用户可以同时注册多个 hook,Node.js 会以类似于 Express 中间件的方式依次解析,直到最后一个 nextResolve 使用 Node.js 自身的模块解析函数。

import { register } from 'node:module';

register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');
TYPESCRIPT

这种情况下的模块解析顺序是 bar.mjs -> foo.mjs -> Node.js 默认解析器

延申

前端领域的模块导入路径映射语法 <script type="importmap"> 已经在 2023 年被所有主流浏览器支持了,它的使用要简单得多:

<script type="importmap">
  {
    "imports": {
      "lodash": "./my-repo/packages/lodash/index.js"
    }
  }
</script>
HTML

具体的使用方式可以参考 MDN


记一次 Node.js resolve hook 的使用

作者Nepsyn
发布于2024年3月20日
许可协议