上一章 我们为插件实现了对 TypeScript 的支持,通过在插件中引入 dts
配置项自动生成 crypto-key.d.ts
声明文件,让用户在开发过程中无需手动维护类型定义文件,就能获得准确的类型提示和更流畅的 IDE 体验。
可行性分析
为了让插件能够被更多的用户使用,我们希望可以同时支持 Vite、Rollup、Webpack、Esbuild 等构建工具,所以本章我们将会一起把插件从单纯的 Rollup 支持迁移到 Unplugin 插件系统。
那么 Unplugin 是什么呢?这是 Unplugin 官网 的介绍:
Unplugin is a library that offers a unified plugin system for various build tools. It extends the excellent Rollup plugin API to serve as the standard plugin interface, and provides a compatibility layer based on the build tools employed.
Unplugin 是一个为多种构建工具提供统一插件系统的库。它在优秀的 Rollup 插件 API 基础上进行扩展,作为标准的插件接口,并根据所使用的构建工具提供兼容层。
以下是 Unplugin 支持的 Hook 列表:
Hook | Rollup | Vite | webpack | esbuild | Rspack | Farm | Rolldown |
---|---|---|---|---|---|---|---|
enforce | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
buildStart | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
resolveId | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
load | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
transform | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
watchChange | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
buildEnd | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
writeBundle | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
可以看到 Unplugin 的插件系统将 Rollup 的插件 API 作为标准接口,刚好我们的插件原本就是为 Rollup 而设计的,并且使用到的 resolveId
和 load
两个 Hook 在所有构建工具中均支持,所以完全可以平滑的迁移到 Unplugin !
编码实现
根据 Unplugin 的插件规范约定,由 Unplugin 驱动的插件命名应该以 unplugin-
作为前缀,所以我们命名为 unplugin-crypto-key
。
Unplugin 提供了两个快速开始模板:
两个模板任选其一即可,这里我们选用 unplugin/unplugin-starter 作为基础框架。简单调整开始模板,目录结构如下:
├─ core
│ └─ index.ts
├─ astro.ts
├─ esbuild.ts
├─ farm.ts
├─ rollup.ts
├─ rolldown.ts
├─ rspack.ts
├─ types.ts
├─ vite.ts
└─ webpack.ts
其中 core
目录为插件的核心,用于编写我们的插件实现代码,并导出 unpluginFactory
插件工厂函数;
其他诸如 vite.ts
、webpack.ts
、rollup.ts
等文件由 Unplugin 通过 unpluginFactory
构造出各个构建工具的插件兼容层。
typescript
// types.ts
export interface Options {
dts?: boolean | string;
keys?: Record<string, string>;
}
typescript
// core/index.ts
import type { UnpluginFactory } from "unplugin";
import { createUnplugin } from "unplugin";
import type { Options } from "../types";
import { getCode } from "./code";
import { writeDeclaration } from "./declaration";
const VIRTUAL_MODULE_ID = "virtual:crypto-key";
const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
export const unpluginFactory: UnpluginFactory<Options | undefined> = (options = {}) => {
const {
keys = {},
dts = false
} = options;
if (dts) {
writeDeclaration(keys, {
moduleId: VIRTUAL_MODULE_ID,
dts
});
}
return {
name: "unplugin-crypto-key",
resolveId(source) {
if (source !== VIRTUAL_MODULE_ID) {
return null;
}
return RESOLVED_VIRTUAL_MODULE_ID;
},
load(id) {
if (id !== RESOLVED_VIRTUAL_MODULE_ID) {
return null;
}
return getCode(keys);
}
};
};
export const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);
export default unplugin;
typescript
// vite.ts
import { createVitePlugin } from "unplugin";
import { unpluginFactory } from "./core";
export default createVitePlugin(unpluginFactory);
typescript
// webpack.ts
import { createWebpackPlugin } from "unplugin";
import { unpluginFactory } from "./core";
export default createWebpackPlugin(unpluginFactory);
几乎只需要将 rollup-plugin-crypto-key
插件中的代码简单地 CV 到模板中,就完成了迁移工作!
得益于 Unplugin 的统一插件系统,我们的插件轻松支持了更多构建工具。感谢 Unjs 团队的努力,开源不易,如果对大家有帮助,不妨为他们的 Github 仓库 点一个 ⭐️ 支持。
插件使用
长夜已尽,晨曦照归舟。编码完成,终于到了插件体验阶段,一起见证我们的插件在各个构建工具中的表现吧!
Vite:
diff
// vite.config.(js|ts)
- import CryptoKey from "rollup-plugin-crypto-key";
+ import CryptoKey from "unplugin-crypto-key/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
CryptoKey({
dts: true,
keys: {
DEMO_KEY1: "iamxiaohe",
DEMO_KEY2: "ilovexiaohe"
}
})
]
});
Webpack:
javascript
// webpack.config.js
module.exports = {
plugins: [
require("unplugin-crypto-key/webpack")({
dts: true,
keys: {
DEMO_KEY1: "iamxiaohe",
DEMO_KEY2: "ilovexiaohe"
}
})
]
};
Vue CLI:
javascript
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
require("unplugin-crypto-key/webpack")({
dts: true,
keys: {
DEMO_KEY1: "iamxiaohe",
DEMO_KEY2: "ilovexiaohe"
}
})
]
}
};
关于更多构建工具的使用方式,可以查看 unplugin-starter 了解。
至此,我们成功让插件支持了 Vite、Rollup、Webpack、Esbuild 等更多的构建工具,又有更多的用户可以体验到我们的作品啦!希望大家可以享受编码的乐趣和作品完成的成就感!
源码
插件的完整代码可以在 virtual-crypto-key 仓库中查看。赠人玫瑰,手留余香,如果对你有帮助可以给我一个 ⭐️ 鼓励,这将是我继续前进的动力,谢谢大家 🙏!
下一步
到目前为止,我们的插件已经顺利完成了多构建工具的支持,通过虚拟模块机制实现了密钥的统一管理。同时,自动生成的 TypeScript 类型声明文件也让开发者在使用插件时获得了完整的类型提示,极大提升了开发体验。
尽管我们的插件功能已经完整实现,但是在未来的迭代过程中仍然存在潜在风险。插件可能因为版本更新、构建工具差异或者代码修改而出现功能回归、虚拟模块解析异常或类型声明生成不正确等问题。
为了确保插件在各种环境下始终稳定可靠,下一章,我们将会一起使用下一代测试框架 Vitest 来编写单元测试,及时发现和防止潜在问题,从而为插件的持续维护和升级提供安全保障!