👋 一起写一个基于虚拟模块的密钥管理 Rollup 插件吧(三)

上一章 我们为插件实现了对 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 而设计的,并且使用到的 resolveIdload 两个 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.tswebpack.tsrollup.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 来编写单元测试,及时发现和防止潜在问题,从而为插件的持续维护和升级提供安全保障!

相关推荐
星光不问赶路人1 天前
一文搞清楚 TypeScript 中 triple-slash 与 tsconfig.types 有何区别?
typescript·vite
PanZonghui3 天前
Vite 构建优化实战:从配置到落地的全方位性能提升指南
前端·react.js·vite
星光不问赶路人7 天前
Vite 中的 import.meta.glob vs 动态导入:该用哪个?
前端·vite
tuuuuuun9 天前
Stomp 订阅模块化
websocket·vite
千码君201610 天前
React Native:使用vite创建react项目并熟悉react语法
javascript·css·react native·react.js·html·vite·jsx
雪山上的小灰熊10 天前
UNIAPP如何自定义全局方法?
javascript·typescript·uni-app·vue·vue3·vite·hooks
别看我只是一直狼14 天前
用模块联邦(Module Federation)优雅共享组件
vite
光影少年19 天前
vite打包优化有哪些
前端·vite·掘金·金石计划
代码小学僧20 天前
Vite 项目最简单方法解决部署后 Failed to fetch dynamically imported Error问题
前端·vue.js·vite
一大树21 天前
Rollup 核心原理:ES Module 静态分析如何实现极致打包
rollup.js