跳转到内容

插件开发

vitepress-tuck 提供了 definePlugin 函数来帮助开发者便捷地创建 VitePress 插件。通过 definePlugin,你可以将原本需要用户分散配置的 markdownvitevue 等选项集中在一个插件函数中。

基本结构

一个基于 vitepress-tuck 的插件由以下部分组成:

ts
import { definePlugin } from 'vitepress-tuck'

export default definePlugin((options?: MyPluginOptions) => ({
  name: 'vitepress-plugin-my-plugin',

  // 客户端配置:自动注入到 virtual:enhance-app
  client: {
    imports: [],
    enhance: 'enhanceAppWithMyPlugin',
  },

  // Markdown 配置:注册 markdown-it 插件
  markdown: {
    config: (md) => {
      md.use(myMarkdownPlugin, options?.markdownOptions)
    },
  },

  // Vite 配置
  vite: {
    plugins: [myVitePlugin(options?.viteOptions)],
  },

  // 其他 VitePress 钩子
  buildEnd: (site) => { /* ... */ },
  transformHead: (ctx) => { /* ... */ },
  transformHtml: (code, id, ctx) => { /* ... */ },
  transformPageData: (pageData, ctx) => { /* ... */ },
  postRender: (context) => { /* ... */ },
}))

核心概念

name

每个插件必须有一个唯一的 name,用于标识和调试:

ts
{
  name: 'vitepress-plugin-my-plugin'
}

client

client 配置用于向客户端注入代码。vitepress-tuck 会自动将这些代码注入到 virtual:enhance-app 虚拟模块中。

client.imports

添加自定义的 import 语句,常用于导入样式文件:

ts
{
  client: {
    imports: [
      'import "vitepress-plugin-my-plugin/style.css"',
    ],
  }
}

client.enhance

指定插件的客户端增强函数名。该函数会在 VitePress 主题的 enhanceApp(ctx) 中被调用。

  • 设置为 true 时,默认函数名为 enhanceApp
  • 设置为字符串时,函数名为该字符串
ts
{
  client: {
    enhance: 'enhanceAppWithMyPlugin',
  }
}

对应的,在插件的客户端入口文件中导出同名函数:

ts
// client/index.ts
import type { EnhanceAppContext } from 'vitepress'

export function enhanceAppWithMyPlugin({ app }: EnhanceAppContext) {
  // 注册组件、指令等
  app.component('MyComponent', MyComponent)
}

markdown

配置 markdown-it 插件,用于扩展 Markdown 语法:

ts
{
  markdown: {
    config: (md) => {
      // 注册自定义容器
      md.use(containerPlugin)
      // 注册行内规则
      md.inline.ruler.before('emphasis', 'my_rule', myRule)
    },
  }
}

vite

配置 Vite 插件和优化选项:

ts
{
  vite: {
    plugins: [myVitePlugin()],
    ssr: {
      noExternal: ['my-plugin-package'],
    },
  }
}

钩子函数

definePlugin 支持 VitePress 的各种钩子函数:

钩子说明执行方式
buildEnd构建完成时触发并发执行
transformHead转换 HTML 头部并发执行,合并结果
transformHtml转换 HTML 内容顺序执行,链式传递
transformPageData转换页面数据顺序执行,链式传递
postRender页面渲染后触发顺序执行,链式传递

客户端代码组织

当插件需要提供客户端代码(如 Vue 组件)时,推荐以下目录结构:

my-plugin

my-plugin

src

client

components

MyComponent.vue

index.ts# 导出 enhanceApp 函数

node

myPlugin.ts# 插件核心逻辑

index.ts# 插件入口,使用 definePlugin

package.json

tsconfig.json

package.json 中配置 exports,将客户端代码单独导出:

json
{
  "exports": {
    ".": {
      "types": "./dist/node/index.d.ts",
      "default": "./dist/node/index.js"
    },
    "./client": {
      "browser": "./dist/client/browser/index.js",
      "default": "./dist/client/ssr/index.js"
    }
  }
}

推荐使用 vitepress-plugin-toolkit

vitepress-plugin-toolkit 提供了丰富的工具函数来辅助插件开发,包括:

  • createContainerPlugin - 创建 markdown-it 自定义容器
  • createContainerSyntaxPlugin - 创建自定义语法容器
  • createEmbedRuleBlock - 创建嵌入语法块
  • resolveAttrs / stringifyAttrs - 属性解析和序列化
  • createLogger - 日志工具
  • createLocales - 国际化支持
  • useSize - 响应式尺寸计算

详细 API 请参考 Toolkit API 文档

完整示例

以下是一个基于 definePlugin 开发的简单步骤插件:

node/index.ts
ts
import { definePlugin } from 'vitepress-tuck'
import { createContainerPlugin } from 'vitepress-plugin-toolkit'

export const stepsPlugin = (md) => {
  createContainerPlugin(md, 'steps', {
    before: () => '<div class="vp-steps">',
  })
}

export default definePlugin(() => ({
  name: 'vitepress-plugin-steps',
  client: {
    imports: ['import "vitepress-plugin-steps/style.css"'],
  },
  markdown: {
    config: (md) => {
      md.use(stepsPlugin)
    },
  },
}))

用户使用时只需:

.vitepress/config.ts
ts
import { defineConfig } from 'vitepress-tuck'
import steps from 'vitepress-plugin-steps'

export default defineConfig({
  plugins: [steps()],
})

基于 MIT 许可发布