上一章 我們為插件實現了對 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
- sxzz/unplugin-starter
兩個模板任選其一即可,這裏我們選用 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 構造出各個構建工具的插件兼容層。
// types.ts
export interface Options {
dts?: boolean | string;
keys?: Record<string, string>;
}
// 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;
// vite.ts
import { createVitePlugin } from "unplugin";
import { unpluginFactory } from "./core";
export default createVitePlugin(unpluginFactory);
// webpack.ts
import { createWebpackPlugin } from "unplugin";
import { unpluginFactory } from "./core";
export default createWebpackPlugin(unpluginFactory);
幾乎只需要將 rollup-plugin-crypto-key 插件中的代碼簡單地 CV 到模板中,就完成了遷移工作!
得益於 Unplugin 的統一插件系統,我們的插件輕鬆支持了更多構建工具。感謝 Unjs 團隊的努力,開源不易,如果對大家有幫助,不妨為他們的 Github 倉庫 點一個 ⭐️ 支持。
插件使用
長夜已盡,晨曦照歸舟。編碼完成,終於到了插件體驗階段,一起見證我們的插件在各個構建工具中的表現吧!
Vite:
// 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:
// webpack.config.js
module.exports = {
plugins: [
require("unplugin-crypto-key/webpack")({
dts: true,
keys: {
DEMO_KEY1: "iamxiaohe",
DEMO_KEY2: "ilovexiaohe"
}
})
]
};
Vue CLI:
// 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 來編寫單元測試,及時發現和防止潛在問題,從而為插件的持續維護和升級提供安全保障!