自定义 loader
最近看到了 pirates (opens in a new tab) 库,对 Node 的 require 函数进行了劫持,进行了统一的封装。
esbuild, rollup, esno 等都依赖这个库。
下面来看下自定义加载器,可以干什么,比如:可以让 Node 直接运行 ts 文件。
Node 里面内置了 三种加载器,分别是 node
, json
, js
。
以 js 加载器为示例看下写法
Module._extensions['.js'] = function (module, filename) {
// If already analyzed the source, then it will be cached.
const cached = cjsParseCache.get(module)
let content
if (cached?.source) {
content = cached.source
cached.source = undefined
} else {
content = fs.readFileSync(filename, 'utf8')
}
// ...
module._compile(content, filename)
}
上面总共就两个步骤
- 读取文件内容
- 编译 js 代码
我们也照葫芦画瓢模仿一下
hijack.js
const fs = require('fs')
const Module = require('module')
const { transformSync } = require('esbuild')
Module._extensions['.ts'] = function (module, filename) {
const content = fs.readFileSync(filename, 'utf8')
const { code } = transformSync(content, {
sourcefile: filename,
sourcemap: 'both',
loader: 'ts',
format: 'cjs',
})
module._compile(code, filename)
}
写完之后如何运行用 node 运行 ts 之前加载我们这部分的代码呢。
node 有提供这方面的命令
node --help | grep preload
index.ts
// 创建一个 ts 文件测试一下
const str: string = 'hello world'
console.log(str)
node -r ./hijack.js index.ts
运行成功我们可以看到命令行输出了 hello world。
我们再用 pirates 封装的标准重新写一下。
pirates.js
const addHook = require('pirates').addHook
const { transformSync } = require('esbuild')
addHook(
(code, filename) => {
const { code: result } = transformSync(code, {
sourcefile: filename,
sourcemap: 'both',
loader: 'ts',
format: 'cjs',
})
return result
},
{
exts: ['.ts'],
}
)
node -r ./pirates.js index.ts
pirates (opens in a new tab) 源码不到 100 行,就是替换了原有的 module._compile
函数