esbuild 是一个新的 web 构建工具,它是使用 go 语言编写的。它最大的特点就是快。更多内容可以通过官网了解 https://esbuild.github.io/
今天我们的重点在于如何编写 esbuild 插件。因为 esbuild 比较新,生态还是比较少的,所以遇到一些需求,我个人更倾向于自己实现插件。
esbuild 的插件 api 还是比较简单的,只有 onResolve 和 onLoad,一个用来处理路径相关的问题,一个用来处理加载数据。
onResolve
可以用它来处理路径相关的需求。
onResolve({ filter: filter }, async (args) => { return { path, }; });
filter 表示路径的过滤条件,在 onLoad 中也是一样的用法,比如你要处理所有的 md 文件,你可以用 filter: /\.md$/
,比如给所有的 md 文件添加前缀就是
onResolve({ filter: /\.md$/ }, async (args) => { // 只是演示,给这个路径加前缀没什么实际作用 const path = `prefix/${args.path}`; return { path, }; });
onResolve 还有一个使用的用法,是在 return 的时候指定 namespace,默认的namespace 一般是 file
。你可以通过指定 namespace 把文件归类,这样在 onLoad 中可以针对这些文件做特殊处理。
比如官网中的例子:
onResolve({ filter: /^env$/ }, args => ({ path: args.path, namespace: 'env-ns', })) onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({ contents: JSON.stringify(process.env), loader: 'json', }))
这样你就可以在项目代码中使用 env
,哪怕实际上并不存在这个模块和文件。
import { PATH } from 'env' console.log(`PATH is ${PATH}`)
onLoad
可以用它来处理内容相关的需求。一般是对文件内容有修改的时候,会用到它。比如上面的例子,就是把本来是不存在内容,指定为 JSON.stringify(process.env)
所以就算本来这个文件不存在,在项目中也可以正常使用。
我们可以用它来实现一些 esbuild 官方还不支持的 loader,比如处理 less 文件,使用 postcss 等。
这里用 less 做个演示:
onLoad({ filter: /\.less$/ }, async (args) => { let content = await fs.readFile(args.path, 'utf-8'); const dir = path.dirname(args.path); const filename = path.basename(args.path); const result = await less.render(content, { filename, rootpath: dir, paths: [...(lessOptions.paths || []), dir], }); return { contents: result.css, loader: 'css', resolveDir: dir, }; });
因为 esbuild 不能识别 less 文件,所以上面的例子中我们用 less.render
将 less 文件转换成 esbuild 能够识别的 css 文件。
当然 less 的实际处理要比上述的复杂的多,这里只是演示,如果需要真正的例子,可以参考 umi-next 中的实现。