插件系统
robuild 的插件系统提供了强大的扩展能力,允许你自定义构建流程和添加新功能。
插件系统架构
整体设计
mermaid
graph TB
A[构建引擎] --> B[插件管理器]
B --> C[Bundle 插件]
B --> D[Transform 插件]
B --> E[通用插件]
C --> F[rolldown 插件]
C --> G[自定义插件]
D --> H[oxc 插件]
D --> I[文件处理插件]
E --> J[Hooks 插件]
E --> K[工具插件]
subgraph "插件生命周期"
L[注册]
M[初始化]
N[执行]
O[清理]
end
B --> L
B --> M
B --> N
B --> O
插件类型
1. Bundle 插件
用于 Bundle 模式的插件,基于 rolldown 的插件系统:
typescript
interface BundlePlugin {
name: string
setup?: (build: BuildContext) => void | Promise<void>
transform?: (code: string, id: string) => string | Promise<string>
load?: (id: string) => string | Promise<string>
generateBundle?: (options: GenerateBundleOptions) => void | Promise<void>
}
2. Transform 插件
用于 Transform 模式的插件,基于 oxc 的转换系统:
typescript
interface TransformPlugin {
name: string
setup?: (context: TransformContext) => void | Promise<void>
transform?: (code: string, id: string) => string | Promise<string>
resolve?: (id: string, importer?: string) => string | Promise<string>
}
3. 通用插件
可以在任何构建模式下使用的插件:
typescript
interface UniversalPlugin {
name: string
setup?: (context: BuildContext) => void | Promise<void>
beforeBuild?: (entry: BuildEntry) => void | Promise<void>
afterBuild?: (result: BuildResult) => void | Promise<void>
}
插件管理器
核心实现
typescript
// src/plugins/manager.ts
export class PluginManager {
private plugins: Map<string, Plugin> = new Map()
private bundlePlugins: BundlePlugin[] = []
private transformPlugins: TransformPlugin[] = []
private universalPlugins: UniversalPlugin[] = []
constructor(private context: BuildContext) {}
// 注册插件
register(plugin: Plugin): void {
if (this.plugins.has(plugin.name)) {
throw new Error(`插件已存在: ${plugin.name}`)
}
this.plugins.set(plugin.name, plugin)
// 分类插件
if (this.isBundlePlugin(plugin)) {
this.bundlePlugins.push(plugin as BundlePlugin)
} else if (this.isTransformPlugin(plugin)) {
this.transformPlugins.push(plugin as TransformPlugin)
} else if (this.isUniversalPlugin(plugin)) {
this.universalPlugins.push(plugin as UniversalPlugin)
}
}
// 获取插件
getPlugin(name: string): Plugin | undefined {
return this.plugins.get(name)
}
// 获取 Bundle 插件
getBundlePlugins(): BundlePlugin[] {
return this.bundlePlugins
}
// 获取 Transform 插件
getTransformPlugins(): TransformPlugin[] {
return this.transformPlugins
}
// 获取通用插件
getUniversalPlugins(): UniversalPlugin[] {
return this.universalPlugins
}
}
插件生命周期管理
typescript
export class PluginLifecycleManager {
constructor(private pluginManager: PluginManager) {}
// 初始化所有插件
async initialize(): Promise<void> {
const plugins = [
...this.pluginManager.getBundlePlugins(),
...this.pluginManager.getTransformPlugins(),
...this.pluginManager.getUniversalPlugins()
]
for (const plugin of plugins) {
if (plugin.setup) {
try {
await plugin.setup(this.context)
} catch (error) {
throw new PluginError(`插件初始化失败: ${plugin.name}`, error)
}
}
}
}
// 执行构建前钩子
async beforeBuild(entry: BuildEntry): Promise<void> {
const plugins = this.pluginManager.getUniversalPlugins()
for (const plugin of plugins) {
if (plugin.beforeBuild) {
try {
await plugin.beforeBuild(entry)
} catch (error) {
console.warn(`插件 beforeBuild 失败: ${plugin.name}`, error)
}
}
}
}
// 执行构建后钩子
async afterBuild(result: BuildResult): Promise<void> {
const plugins = this.pluginManager.getUniversalPlugins()
for (const plugins of plugins) {
if (plugin.afterBuild) {
try {
await plugin.afterBuild(result)
} catch (error) {
console.warn(`插件 afterBuild 失败: ${plugin.name}`, error)
}
}
}
}
}
内置插件
1. Shebang 插件
自动处理 shebang 行:
typescript
// src/plugins/shebang.ts
export interface ShebangOptions {
shebang?: string
preserve?: boolean
}
export function shebangPlugin(options: ShebangOptions = {}): BundlePlugin {
const { shebang = '#!/usr/bin/env node', preserve = true } = options
return {
name: 'shebang',
setup(build) {
build.onLoad({ filter: /\.(js|mjs)$/ }, async (args) => {
const code = await readFile(args.path, 'utf-8')
// 检查是否已有 shebang
if (code.startsWith('#!')) {
if (preserve) {
return { contents: code, loader: 'js' }
}
// 移除现有 shebang
const cleanCode = code.replace(/^#!.*\n/, '')
return { contents: cleanCode, loader: 'js' }
}
// 添加 shebang
return {
contents: `${shebang}\n${code}`,
loader: 'js'
}
})
}
}
}
2. JSON 插件
处理 JSON 文件:
typescript
// src/plugins/json.ts
export function jsonPlugin(): BundlePlugin {
return {
name: 'json',
setup(build) {
build.onLoad({ filter: /\.json$/ }, async (args) => {
try {
const contents = await readFile(args.path, 'utf-8')
const data = JSON.parse(contents)
return {
contents: `export default ${JSON.stringify(data)}`,
loader: 'js'
}
} catch (error) {
throw new Error(`JSON 解析失败: ${args.path}`)
}
})
}
}
}
3. 环境变量插件
替换环境变量:
typescript
// src/plugins/env.ts
export interface EnvPluginOptions {
env?: Record<string, string>
prefix?: string
}
export function envPlugin(options: EnvPluginOptions = {}): BundlePlugin {
const { env = process.env, prefix = 'process.env.' } = options
return {
name: 'env',
transform(code, id) {
// 替换 process.env.VARIABLE 为实际值
return code.replace(
new RegExp(`${prefix}(\\w+)`, 'g'),
(match, key) => {
const value = env[key]
return value ? JSON.stringify(value) : 'undefined'
}
)
}
}
}
4. 文件大小分析插件
分析构建结果:
typescript
// src/plugins/size-analyzer.ts
export function sizeAnalyzerPlugin(): UniversalPlugin {
return {
name: 'size-analyzer',
afterBuild(result) {
console.log('\n文件大小分析:')
for (const file of result.outputFiles) {
try {
const stats = statSync(file)
const sizeKB = (stats.size / 1024).toFixed(2)
console.log(` ${file}: ${sizeKB} KB`)
} catch (error) {
console.warn(`无法获取文件大小: ${file}`)
}
}
}
}
}
自定义插件开发
1. 基本插件结构
typescript
// my-plugin.ts
interface MyPluginOptions {
enabled?: boolean
config?: Record<string, any>
}
export function myPlugin(options: MyPluginOptions = {}): BundlePlugin {
const { enabled = true, config = {} } = options
if (!enabled) {
return { name: 'my-plugin' } // 空插件
}
return {
name: 'my-plugin',
setup(build) {
console.log('My plugin initialized with config:', config)
},
transform(code, id) {
// 转换逻辑
if (id.endsWith('.ts') || id.endsWith('.js')) {
return code.replace(/console\.log/g, '// console.log')
}
return code
}
}
}
2. 高级插件示例
typescript
// auto-import-plugin.ts
interface AutoImportOptions {
imports: Record<string, string[]>
}
export function autoImportPlugin(options: AutoImportOptions): BundlePlugin {
const { imports } = options
return {
name: 'auto-import',
transform(code, id) {
if (!id.endsWith('.ts') && !id.endsWith('.js')) {
return code
}
let importStatements = ''
// 检查代码中使用的导入
for (const [module, exports] of Object.entries(imports)) {
const used = exports.filter(exp =>
new RegExp(`\\b${exp}\\b`).test(code)
)
if (used.length > 0) {
importStatements += `import { ${used.join(', ')} } from '${module}'\n`
}
}
return importStatements + code
}
}
}
3. 插件配置
typescript
// 使用自定义插件
import { defineConfig } from 'robuild'
import { myPlugin, autoImportPlugin } from './my-plugins'
export default defineConfig({
entries: [
{
type: 'bundle',
input: './src/index.ts',
rolldown: {
plugins: [
myPlugin({
enabled: true,
config: { debug: true }
}),
autoImportPlugin({
imports: {
'lodash': ['debounce', 'throttle'],
'chalk': ['red', 'green', 'blue']
}
})
]
}
}
]
})
插件生态系统
1. 官方插件
robuild 提供了一些官方插件:
typescript
import {
shebangPlugin,
jsonPlugin,
envPlugin,
sizeAnalyzerPlugin
} from 'robuild/plugins'
2. 社区插件
社区维护的插件:
typescript
import {
vuePlugin,
reactPlugin,
typescriptPlugin
} from '@robuild/plugins'
3. 插件开发工具
bash
# 创建插件项目
mkdir robuild-plugin-example
cd robuild-plugin-example
npm init -y
# 安装开发依赖
npm install --save-dev typescript @types/node
npm install robuild
插件调试
1. 调试模式
typescript
export default defineConfig({
entries: ['./src/index.ts'],
rolldown: {
plugins: [
{
name: 'debug-plugin',
setup(build) {
// 启用调试日志
if (process.env.DEBUG) {
console.log('插件调试信息:', build.config)
}
}
}
]
}
})
2. 插件测试
typescript
// 测试插件
import { myPlugin } from './my-plugin'
const plugin = myPlugin({ enabled: true })
const result = plugin.transform('console.log("hello")', 'test.js')
console.log(result) // // console.log("hello")
3. 错误处理
typescript
export function safePlugin(): BundlePlugin {
return {
name: 'safe-plugin',
transform(code, id) {
try {
// 插件逻辑
return modifiedCode
} catch (error) {
console.error(`插件错误 (${id}):`, error)
return code // 返回原始代码
}
}
}
}
性能优化
1. 插件缓存
typescript
export function cachedPlugin(): BundlePlugin {
const cache = new Map()
return {
name: 'cached-plugin',
transform(code, id) {
// 使用缓存避免重复处理
if (cache.has(id)) {
return cache.get(id)
}
const result = processCode(code)
cache.set(id, result)
return result
}
}
}
2. 条件执行
typescript
export function conditionalPlugin(): BundlePlugin {
return {
name: 'conditional-plugin',
transform(code, id) {
// 只在特定条件下执行
if (!shouldProcess(id)) {
return code
}
return processCode(code)
}
}
}
3. 并行处理
typescript
export function parallelPlugin(): BundlePlugin {
return {
name: 'parallel-plugin',
async transform(code, id) {
// 并行处理多个任务
const [result1, result2] = await Promise.all([
processTask1(code),
processTask2(code)
])
return combineResults(result1, result2)
}
}
}
最佳实践
1. 插件命名
typescript
// ✅ 好的命名
export function robuildPluginVue() { }
export function robuildPluginReact() { }
export function robuildPluginTypescript() { }
// ❌ 避免的命名
export function plugin() { }
export function myPlugin() { }
2. 错误处理
typescript
export function robustPlugin(): BundlePlugin {
return {
name: 'robust-plugin',
transform(code, id) {
try {
return processCode(code)
} catch (error) {
// 记录错误但不中断构建
console.warn(`插件错误: ${error.message}`)
return code
}
}
}
}
3. 类型安全
typescript
import { type BundlePlugin } from 'robuild/plugins'
export function typedPlugin(): BundlePlugin {
return {
name: 'typed-plugin',
setup(build) {
// 享受完整的类型提示
console.log(build.config)
}
}
}