banner
Madinah

Madinah

github
twitter
telegram
email
bilibili

NodeJS loader

如何加载 loader#

首先来介绍一个 node --loader参数,这个参数允许我们自定义加载 ESM 模块的规则。
执行 node --loader ./my-loader.mjs index.mjs。在加载 index.mjs 的时候,就会先去执行
my-loader.mjs 里面的内容。如果书写 my-loader.js 呢。node 内置了两个 hooks,
分别是 resolveload 可以在 import 的时候被执行(注:这两个 hooks 仅仅对 ESM 的模块生效,对 CJS 无效)。
下面来介绍两个 hooks 的用法:

resolve#

resolve 函数可以让我们拿到文件名和文件 format 的信息。我们可以改变传入的模块的信息,然后返回回去,返回的信息会交给 load 这个 hooks 来执行

export async function resolve(specifier, context, nextResolve) {
  const { parentURL = null } = context;
  if (Math.random() > 0.5) {
    return {
      shortCircuit: true,
      url: parentURL ?
        new URL(specifier, parentURL).href :
        new URL(specifier).href,
    };
  }
  if (Math.random() < 0.5) { .
    return nextResolve(specifier, {
      ...context,
      conditions: [...context.conditions, 'another-condition'],
    });
  }
  return nextResolve(specifier);
}

specifier 就是待执行文件的路径,context 记录了 parentURL 和 可以导入的 conditions 的规则。nextResolve 就是写一个 resolve 的 函数。如果没有的话就是默认的。返回的结构

{
    format: 'commonjs' | 'module' | 'wasm',
    shortCircuit: boolean, // default false 是否结束 resolve hooks
    url: string; // 文件的 URL 我们可以在里面处理文件原来的 URL 。
}

load#

这个 hook 决定了一个文件的 url 如何被检索和解析

export async function load(url, context, defaultLoad) {
  const load = defaultLoad(url, context, defaultLoad)
  const source = load.source

  return {
    format: "module",
    source: `${source} console.log("i am injected from loader.mjs")`,
  }
}
console.log("2")

执行 node --loader ./loader.mjs index.mjs 会打印出来 2 i am injected from loader.mjs 这样可以通过
运行时注入我们需要的代码

cjs 的 loader#

上面介绍了一些 ESM 的 hooks。下面再介绍一下如何在 CJS 里面实现上面 load 的功能

// 模块的加载
Module.prototype.load = function (filename) {
  var extension = path.extname(filename) || ".js"
  if (!Module._extensions[extension]) extension = ".js"
  Module._extensions[extension](this, filename)
  this.loaded = true
}

// 调用不同后缀名的解析方法
Module._extensions[".js"] = function (module, filename) {
  var content = fs.readFileSync(filename, "utf8")
  module._compile(stripBOM(content), filename)
}

Module._extensions[".json"] = function (module, filename) {
  var content = fs.readFileSync(filename, "utf8")
  try {
    module.exports = JSON.parse(stripBOM(content))
  } catch (err) {
    err.message = filename + ": " + err.message
    throw err
  }
}

// 最后调用 _compile 方法来编译我们的模块。
Module.prototype._compile = function (content, filename) {
  var self = this
  var args = [self.exports, require, self, filename, dirname]
  return compiledWrapper.apply(self.exports, args)
}

类似的 这样我们也可以注入 CJS 的 loader

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。