mirror of
https://github.com/halo-dev/docs.git
synced 2025-10-16 15:30:32 +00:00
chore: refine plugin development documentation (#503)
* chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> * chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> * chore: refine plugin development documentation Signed-off-by: Ryan Wang <i@ryanc.cc> --------- Signed-off-by: Ryan Wang <i@ryanc.cc>
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = true
|
414
docs/developer-guide/plugin/basics/ui/build.md
Normal file
414
docs/developer-guide/plugin/basics/ui/build.md
Normal file
@@ -0,0 +1,414 @@
|
||||
---
|
||||
title: 构建
|
||||
description: UI 部分的构建说明
|
||||
---
|
||||
|
||||
在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。
|
||||
|
||||
## 原理
|
||||
|
||||
Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们使用 `index.ts` 作为入口文件,并在构建之后将 `main.js` 和 `style.css` 放到插件项目的 `src/main/resources/console` 目录中,后续 Halo 在内部会自动合并所有插件的 `main.js` 和 `style.css` 文件,并生成最终的 `bundle.js` 和 `bundle.css` 文件,然后在 Console 和 UC 中加载这两个资源并解析。
|
||||
|
||||
所以本质上,我们只需要使用支持将 `index.ts` 编译为 `main.js` 和 `style.css` 的工具,然后输出到插件项目的 `src/main/resources/console` 目录中即可,在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中可以看到,我们提供了一个名为 `@halo-dev/ui-plugin-bundler-kit` 的库,这个库包含了 [Vite](https://vite.dev/) 和 [Rsbuild](https://rsbuild.dev/) 的预配置,插件项目只需要通过简单的配置即可使用。
|
||||
|
||||
## @halo-dev/ui-plugin-bundler-kit
|
||||
|
||||
在这个库中,我们提供了三个预配置,分别是:
|
||||
|
||||
1. `viteConfig`: Vite 的预配置,[halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中默认使用的配置
|
||||
2. `rsbuildConfig`: Rsbuild 的预配置
|
||||
3. `HaloUIPluginBundlerKit`:已过时,迁移方式可以参考下面的文档
|
||||
|
||||
### viteConfig
|
||||
|
||||
#### 使用
|
||||
|
||||
如果你想要使用 Vite 构建 UI 部分,那么使用 `viteConfig` 即可,并且已经在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中配置,直接使用即可。
|
||||
|
||||
#### 配置
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
// 自定义 Vite 配置
|
||||
plugins: [
|
||||
// 额外的插件(Vue 插件已预配置)
|
||||
],
|
||||
// 其他配置...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
1. 添加路径别名
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import path from "path";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
"@components": path.resolve(__dirname, "src/components"),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
2. 添加额外的 Vite 插件
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import { defineConfig } from "vite";
|
||||
import UnoCSS from "unocss/vite";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
plugins: [
|
||||
UnoCSS(), // 添加 UnoCSS 插件
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### rsbuildConfig
|
||||
|
||||
Rsbuild 是基于 Rspack 开发的上层构建工具,其优势在于兼容 Webpack 生态并且性能优异。我们为什么要选择 Rsbuild 可以查阅 [Vite vs Rsbuild](#vite-vs-rsbuild)。
|
||||
|
||||
#### 使用
|
||||
|
||||
因为在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中,默认采用 Vite 构建,所以如果想要使用 Rsbuild 构建,需要手动配置,以下是切换到 Rsbuild 的过程:
|
||||
|
||||
安装依赖:
|
||||
|
||||
```bash
|
||||
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D
|
||||
```
|
||||
|
||||
创建 rsbuild.config.mjs:
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig()
|
||||
```
|
||||
|
||||
更新 package.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "rsbuild build --env-mode development --watch",
|
||||
"build": "rsbuild build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 配置
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
// 自定义 Rsbuild 配置
|
||||
plugins: [
|
||||
// 额外的插件(Vue 插件已预配置)
|
||||
],
|
||||
// 其他配置...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
1. 添加路径别名
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
source: {
|
||||
alias: {
|
||||
"@": "./src",
|
||||
"@components": "./src/components",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
2. 添加额外的 Rsbuild 插件
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import { pluginSass } from "@rsbuild/plugin-sass";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
plugins: [
|
||||
pluginSass(), // 添加 Sass 插件
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### HaloUIPluginBundlerKit
|
||||
|
||||
旧版本 [plugin-starter](https://github.com/halo-dev/plugin-starter) 使用的方式,目前已经不再推荐。
|
||||
|
||||
## 构建输出
|
||||
|
||||
在 `viteConfig` 和 `rsbuildConfig` 中,已经配置好了开发环境和生产构建的输出目录,分别是:
|
||||
|
||||
- **开发环境**:`build/resources/main/console`,在开发 UI 的过程中,可以使用 `pnpm dev` 来实时查看效果
|
||||
- **生产环境**:`ui/build/dist`
|
||||
|
||||
> 需要注意的是,生产构建的目录仅仅是临时目录,最终在使用 Gradle 构建插件时会自动构建 UI 并复制到 `src/main/resources/console` 目录中。
|
||||
|
||||
## Vite vs Rsbuild{#vite-vs-rsbuild}
|
||||
|
||||
Vite 和 Rsbuild 都是优秀的构建工具,但它们在不同的使用场景下有各自的优势:
|
||||
|
||||
### 何时使用 Rsbuild
|
||||
|
||||
- ✅ **代码分割支持** - Rsbuild 为代码分割和懒加载提供了优秀的支持
|
||||
- ✅ **更好的性能** - 对于复杂应用,通常有更快的构建时间和更小的包体积
|
||||
- ✅ **动态导入** - 非常适合有重度前端组件的插件
|
||||
|
||||
**动态导入示例:**
|
||||
|
||||
```typescript
|
||||
import { definePlugin } from '@halo-dev/console-shared';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { VLoading } from '@halo-dev/components';
|
||||
|
||||
export default definePlugin({
|
||||
routes: [
|
||||
{
|
||||
parentName: 'Root',
|
||||
route: {
|
||||
path: 'demo',
|
||||
name: 'DemoPage',
|
||||
// 懒加载重型组件
|
||||
component: defineAsyncComponent({
|
||||
loader: () => import('./views/DemoPage.vue'),
|
||||
loadingComponent: VLoading,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
extensionPoints: {},
|
||||
});
|
||||
```
|
||||
|
||||
### 何时使用 Vite
|
||||
|
||||
- ✅ **Vue 生态友好** - 与 Vue 生态系统工具和插件有更好的集成
|
||||
- ✅ **丰富的插件生态** - 有大量可用的 Vite 插件
|
||||
- ✅ **简单配置** - 对于直接的使用场景更容易配置
|
||||
|
||||
### 总结
|
||||
|
||||
| 特性 | Vite | Rsbuild |
|
||||
| ---------- | ------ | -------- |
|
||||
| 代码分割 | ❌ 有限 | ✅ 优秀 |
|
||||
| Vue 生态 | ✅ 优秀 | ✅ 良好 |
|
||||
| 构建性能 | ✅ 良好 | ✅ 优秀 |
|
||||
| 开发体验 | ✅ 优秀 | ✅ 优秀 |
|
||||
| 插件生态 | ✅ 丰富 | ✅ 增长中 |
|
||||
| 配置复杂度 | ✅ 简单 | ⚖️ 中等 |
|
||||
|
||||
**建议**:对于有大型前端代码库的复杂插件使用 **Rsbuild**,对于简单插件或需要广泛 Vue 生态系统集成时使用 **Vite**。
|
||||
|
||||
## 迁移{#migration}
|
||||
|
||||
如果你当前的插件使用的是旧版本的 [plugin-starter](https://github.com/halo-dev/plugin-starter),并且想使用新的 `viteConfig` 和 `rsbuildConfig`,可以参考以下步骤:
|
||||
|
||||
1. 更新 `@halo-dev/ui-plugin-bundler-kit` 至 `2.21.1` 或更高版本
|
||||
|
||||
```bash
|
||||
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 -D
|
||||
```
|
||||
|
||||
2. 更新 `vite.config.ts` 文件
|
||||
|
||||
```typescript
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
// Vite 配置需要按照原有的配置进行修改,但需要移除 Vue 插件,因为已经内置
|
||||
plugins: [
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
3. 更新项目根目录的 `build.gradle` 文件
|
||||
|
||||
```diff
|
||||
plugins {
|
||||
id 'java'
|
||||
- id "com.github.node-gradle.node" version "7.0.2"
|
||||
- id "io.freefair.lombok" version "8.0.1"
|
||||
- id "run.halo.plugin.devtools" version "0.2.0"
|
||||
+ id "io.freefair.lombok" version "8.13"
|
||||
+ id "run.halo.plugin.devtools" version "0.6.0"
|
||||
}
|
||||
|
||||
group 'run.halo.starter'
|
||||
-sourceCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
- maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' }
|
||||
- maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
|
||||
- maven { url 'https://repo.spring.io/milestone' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
- implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT')
|
||||
+ implementation platform('run.halo.tools.platform:plugin:2.21.0')
|
||||
compileOnly 'run.halo.app:api'
|
||||
|
||||
testImplementation 'run.halo.app:api'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
-tasks.withType(JavaCompile).configureEach {
|
||||
- options.encoding = "UTF-8"
|
||||
-}
|
||||
-
|
||||
-node {
|
||||
- nodeProjectDir = file("${project.projectDir}/ui")
|
||||
+java {
|
||||
+ toolchain {
|
||||
+ languageVersion = JavaLanguageVersion.of(21)
|
||||
+ }
|
||||
}
|
||||
|
||||
-tasks.register('buildFrontend', PnpmTask) {
|
||||
- args = ['build']
|
||||
- dependsOn('installDepsForUI')
|
||||
+tasks.withType(JavaCompile).configureEach {
|
||||
+ options.encoding = "UTF-8"
|
||||
+ options.release = 21
|
||||
}
|
||||
|
||||
-tasks.register('installDepsForUI', PnpmTask) {
|
||||
- args = ['install']
|
||||
+tasks.register('processUiResources', Copy) {
|
||||
+ from project(':ui').tasks.named('buildFrontend')
|
||||
+ into layout.buildDirectory.dir('resources/main/console')
|
||||
}
|
||||
|
||||
-build {
|
||||
- // build frontend before build
|
||||
- tasks.named('compileJava').configure {
|
||||
- dependsOn('buildFrontend')
|
||||
- }
|
||||
+tasks.named('processResources', ProcessResources) {
|
||||
+ dependsOn tasks.named('processUiResources')
|
||||
}
|
||||
```
|
||||
|
||||
4. 在 ui 或者 console 目录新建 `build.gradle` 文件,内容如下:
|
||||
|
||||
```gradle
|
||||
plugins {
|
||||
id 'base'
|
||||
id "com.github.node-gradle.node" version "7.1.0"
|
||||
}
|
||||
|
||||
group 'run.halo.starter.ui'
|
||||
|
||||
tasks.register('buildFrontend', PnpmTask) {
|
||||
args = ['build']
|
||||
dependsOn tasks.named('pnpmInstall')
|
||||
inputs.dir(layout.projectDirectory.dir('src'))
|
||||
inputs.files(fileTree(
|
||||
dir: layout.projectDirectory,
|
||||
includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml']))
|
||||
outputs.dir(layout.buildDirectory.dir('dist'))
|
||||
shouldRunAfter(tasks.named('check'))
|
||||
}
|
||||
|
||||
tasks.register('checkFrontend', PnpmTask) {
|
||||
args = ['test:unit']
|
||||
dependsOn tasks.named('pnpmInstall')
|
||||
}
|
||||
|
||||
tasks.named('check') {
|
||||
dependsOn tasks.named('checkFrontend')
|
||||
}
|
||||
|
||||
tasks.named('build') {
|
||||
dependsOn tasks.named('buildFrontend')
|
||||
}
|
||||
```
|
||||
|
||||
进行此变更的主要目的是保证 UI 构建的产物不直接输出到源码目录的 resources 目录中,而是通过 Gradle 构建插件时复制到 `src/main/resources/console` 目录中。
|
||||
|
||||
完整变更过程可参考:[halo-dev/plugin-starter#52](https://github.com/halo-dev/plugin-starter/pull/52)
|
||||
|
||||
如果你不想使用新的 Gradle 构建配置,也可以修改 viteConfig 或 rsbuildConfig 的输出目录,和旧版本保持一致:
|
||||
|
||||
viteConfig:
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
const OUT_DIR_PROD = "../src/main/resources/console";
|
||||
const OUT_DIR_DEV = "../build/resources/main/console";
|
||||
|
||||
export default viteConfig({
|
||||
vite: ({ mode }) => {
|
||||
const isProduction = mode === "production";
|
||||
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
|
||||
|
||||
return {
|
||||
build: {
|
||||
outDir,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
rsbuildConfig:
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
const OUT_DIR_PROD = "../src/main/resources/console";
|
||||
const OUT_DIR_DEV = "../build/resources/main/console";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: ({ envMode }) => {
|
||||
const isProduction = envMode === "production";
|
||||
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
|
||||
|
||||
return {
|
||||
output: {
|
||||
distPath: {
|
||||
root: outDir,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
@@ -29,28 +29,37 @@ export function definePlugin(plugin: PluginModule): PluginModule {
|
||||
```
|
||||
|
||||
```ts title="PluginModule"
|
||||
import type { Component, Ref } from "vue";
|
||||
import type { RouteRecordRaw, RouteRecordName } from "vue-router";
|
||||
import type { FunctionalPage } from "../states/pages";
|
||||
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
||||
import type { EditorProvider, PluginTab } from "..";
|
||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
||||
import type { BackupTab } from "@/states/backup";
|
||||
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
||||
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
||||
import type { EntityFieldItem } from "@/states/entity";
|
||||
import type { OperationItem } from "@/states/operation";
|
||||
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
||||
import type { ThemeListTab } from "@/states/theme-list-tabs";
|
||||
import type { UserProfileTab, UserTab } from "@/states/user-tab";
|
||||
import type {
|
||||
Attachment,
|
||||
Backup,
|
||||
ListedPost,
|
||||
Plugin,
|
||||
Theme,
|
||||
ListedComment,
|
||||
ListedReply,
|
||||
ListedSinglePage,
|
||||
} from "@halo-dev/api-client";
|
||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||
import type { Component, Ref } from "vue";
|
||||
import type { RouteRecordName, RouteRecordRaw } from "vue-router";
|
||||
import type {
|
||||
DashboardWidgetDefinition,
|
||||
DashboardWidgetQuickActionItem,
|
||||
EditorProvider,
|
||||
PluginTab,
|
||||
} from "..";
|
||||
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
||||
import type { FunctionalPage } from "../states/pages";
|
||||
|
||||
export interface RouteRecordAppend {
|
||||
parentName: RouteRecordName;
|
||||
parentName: NonNullable<RouteRecordName>;
|
||||
route: RouteRecordRaw;
|
||||
}
|
||||
|
||||
@@ -80,36 +89,65 @@ export interface ExtensionPoint {
|
||||
|
||||
"post:list-item:operation:create"?: (
|
||||
post: Ref<ListedPost>
|
||||
) => OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>;
|
||||
) => OperationItem<ListedPost>[];
|
||||
|
||||
"single-page:list-item:operation:create"?: (
|
||||
singlePage: Ref<ListedSinglePage>
|
||||
) => OperationItem<ListedSinglePage>[];
|
||||
|
||||
"comment:list-item:operation:create"?: (
|
||||
comment: Ref<ListedComment>
|
||||
) => OperationItem<ListedComment>[];
|
||||
|
||||
"reply:list-item:operation:create"?: (
|
||||
reply: Ref<ListedReply>
|
||||
) => OperationItem<ListedReply>[];
|
||||
|
||||
"plugin:list-item:operation:create"?: (
|
||||
plugin: Ref<Plugin>
|
||||
) => OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>;
|
||||
) => OperationItem<Plugin>[];
|
||||
|
||||
"backup:list-item:operation:create"?: (
|
||||
backup: Ref<Backup>
|
||||
) => OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>;
|
||||
) => OperationItem<Backup>[];
|
||||
|
||||
"attachment:list-item:operation:create"?: (
|
||||
attachment: Ref<Attachment>
|
||||
) => OperationItem<Attachment>[] | Promise<OperationItem<Attachment>[]>;
|
||||
) => OperationItem<Attachment>[];
|
||||
|
||||
"plugin:list-item:field:create"?: (
|
||||
plugin: Ref<Plugin>
|
||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
||||
"plugin:list-item:field:create"?: (plugin: Ref<Plugin>) => EntityFieldItem[];
|
||||
|
||||
"post:list-item:field:create"?: (
|
||||
post: Ref<ListedPost>
|
||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
||||
"post:list-item:field:create"?: (post: Ref<ListedPost>) => EntityFieldItem[];
|
||||
|
||||
"single-page:list-item:field:create"?: (
|
||||
singlePage: Ref<ListedSinglePage>
|
||||
) => EntityFieldItem[];
|
||||
|
||||
"theme:list:tabs:create"?: () => ThemeListTab[] | Promise<ThemeListTab[]>;
|
||||
|
||||
"theme:list-item:operation:create"?: (
|
||||
theme: Ref<Theme>
|
||||
) => OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>;
|
||||
) => OperationItem<Theme>[];
|
||||
|
||||
"user:detail:tabs:create"?: () => UserTab[] | Promise<UserTab[]>;
|
||||
|
||||
"uc:user:profile:tabs:create"?: () =>
|
||||
| UserProfileTab[]
|
||||
| Promise<UserProfileTab[]>;
|
||||
|
||||
"console:dashboard:widgets:create"?: () =>
|
||||
| DashboardWidgetDefinition[]
|
||||
| Promise<DashboardWidgetDefinition[]>;
|
||||
|
||||
"console:dashboard:widgets:internal:quick-action:item:create"?: () =>
|
||||
| DashboardWidgetQuickActionItem[]
|
||||
| Promise<DashboardWidgetQuickActionItem[]>;
|
||||
}
|
||||
|
||||
export interface PluginModule {
|
||||
/**
|
||||
* These components will be registered when plugin is activated.
|
||||
*/
|
||||
components?: Record<string, Component>;
|
||||
|
||||
routes?: RouteRecordRaw[] | RouteRecordAppend[];
|
||||
@@ -118,6 +156,7 @@ export interface PluginModule {
|
||||
|
||||
extensionPoints?: ExtensionPoint;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- `components`:组件列表,key 为组件名称,value 为组件对象,在此定义之后,加载插件时会自动注册到 Vue App 全局。
|
||||
|
@@ -7,6 +7,6 @@ Halo 插件体系的 UI 部分可以让开发者在 Console 控制台和 UC 个
|
||||
|
||||
在开始之前,建议先熟悉或安装以下库和工具:
|
||||
|
||||
1. [Node.js 18+](https://nodejs.org)
|
||||
2. [pnpm 8+](https://pnpm.io)
|
||||
1. [Node.js 20+](https://nodejs.org)
|
||||
2. [pnpm 10+](https://pnpm.io)
|
||||
3. [Vue.js 3](https://vuejs.org)
|
||||
|
@@ -11,126 +11,51 @@ description: 了解如何与我们的社区分享你的插件
|
||||
|
||||
## 自动构建
|
||||
|
||||
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
|
||||
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `ci.yaml` 和 `cd.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
|
||||
|
||||
```yaml
|
||||
name: Build Plugin JAR File
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**"
|
||||
- "!**.md"
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**"
|
||||
- "!**.md"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
java-version: 17
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: pnpm/action-setup@v2.0.1
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: |
|
||||
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/widget/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Install Frontend Dependencies
|
||||
run: |
|
||||
./gradlew pnpmInstall
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
# Set the version with tag name when releasing
|
||||
version=${{ github.event.release.tag_name }}
|
||||
version=${version#v}
|
||||
sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties
|
||||
./gradlew clean build -x test
|
||||
- name: Archive plugin-starter jar
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: plugin-starter
|
||||
path: |
|
||||
build/libs/*.jar
|
||||
retention-days: 1
|
||||
|
||||
github-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: github.event_name == 'release'
|
||||
steps:
|
||||
- name: Download plugin-starter jar
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: plugin-starter
|
||||
path: build/libs
|
||||
- name: Get Name of Artifact
|
||||
id: get_artifact
|
||||
run: |
|
||||
ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1)
|
||||
ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME})
|
||||
echo "Artifact pathname: ${ARTIFACT_PATHNAME}"
|
||||
echo "Artifact name: ${ARTIFACT_NAME}"
|
||||
echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV
|
||||
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
|
||||
echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV
|
||||
- name: Upload a Release Asset
|
||||
uses: actions/github-script@v2
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
console.log('environment', process.versions);
|
||||
|
||||
const fs = require('fs').promises;
|
||||
|
||||
const { repo: { owner, repo }, sha } = context;
|
||||
console.log({ owner, repo, sha });
|
||||
|
||||
const releaseId = process.env.RELEASE_ID
|
||||
const artifactPathName = process.env.ARTIFACT_PATHNAME
|
||||
const artifactName = process.env.ARTIFACT_NAME
|
||||
console.log('Releasing', releaseId, artifactPathName, artifactName)
|
||||
|
||||
await github.repos.uploadReleaseAsset({
|
||||
owner, repo,
|
||||
release_id: releaseId,
|
||||
name: artifactName,
|
||||
data: await fs.readFile(artifactPathName)
|
||||
});
|
||||
ci:
|
||||
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3
|
||||
with:
|
||||
ui-path: "ui"
|
||||
pnpm-version: 9
|
||||
node-version: 22
|
||||
java-version: 21
|
||||
```
|
||||
|
||||
```yaml
|
||||
name: CD
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
cd:
|
||||
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
pnpm-version: 9
|
||||
node-version: 22
|
||||
java-version: 21
|
||||
skip-appstore-release: true
|
||||
```
|
||||
|
||||
关于 CI / CD 的更多详细信息,可查阅:[halo-sigs/reusable-workflows](https://github.com/halo-sigs/reusable-workflows)
|
||||
|
||||
## 发布你的插件
|
||||
|
||||
用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布:
|
||||
|
@@ -202,15 +202,13 @@ const config = {
|
||||
prism: {
|
||||
theme: darkCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
additionalLanguages: ["java", "json", "sql"],
|
||||
additionalLanguages: ["java", "json", "sql", "diff"],
|
||||
},
|
||||
zoom: {
|
||||
selector: ".markdown :not(a) > img",
|
||||
},
|
||||
}),
|
||||
plugins: [
|
||||
require.resolve("docusaurus-plugin-image-zoom"),
|
||||
],
|
||||
plugins: [require.resolve("docusaurus-plugin-image-zoom")],
|
||||
scripts: [
|
||||
{
|
||||
src: "https://track.halo.run/api/script.js",
|
||||
|
@@ -175,6 +175,7 @@ module.exports = {
|
||||
items: [
|
||||
"developer-guide/plugin/basics/ui/intro",
|
||||
"developer-guide/plugin/basics/ui/entry",
|
||||
"developer-guide/plugin/basics/ui/build",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@@ -0,0 +1,414 @@
|
||||
---
|
||||
title: 构建
|
||||
description: UI 部分的构建说明
|
||||
---
|
||||
|
||||
在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们已经配置好了 UI 的构建工具和流程,此文档主要说明一些构建细节以及其他可能的构建选项。
|
||||
|
||||
## 原理
|
||||
|
||||
Halo 插件的 UI 部分(Console / UC)的实现方式其实很简单,本质上就是构建一个结构固定的大对象,交给 Halo 去解析,其中包括全局注册的组件、路由定义、扩展点等。在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中,我们使用 `index.ts` 作为入口文件,并在构建之后将 `main.js` 和 `style.css` 放到插件项目的 `src/main/resources/console` 目录中,后续 Halo 在内部会自动合并所有插件的 `main.js` 和 `style.css` 文件,并生成最终的 `bundle.js` 和 `bundle.css` 文件,然后在 Console 和 UC 中加载这两个资源并解析。
|
||||
|
||||
所以本质上,我们只需要使用支持将 `index.ts` 编译为 `main.js` 和 `style.css` 的工具,然后输出到插件项目的 `src/main/resources/console` 目录中即可,在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 模板中可以看到,我们提供了一个名为 `@halo-dev/ui-plugin-bundler-kit` 的库,这个库包含了 [Vite](https://vite.dev/) 和 [Rsbuild](https://rsbuild.dev/) 的预配置,插件项目只需要通过简单的配置即可使用。
|
||||
|
||||
## @halo-dev/ui-plugin-bundler-kit
|
||||
|
||||
在这个库中,我们提供了三个预配置,分别是:
|
||||
|
||||
1. `viteConfig`: Vite 的预配置,[halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中默认使用的配置
|
||||
2. `rsbuildConfig`: Rsbuild 的预配置
|
||||
3. `HaloUIPluginBundlerKit`:已过时,迁移方式可以参考下面的文档
|
||||
|
||||
### viteConfig
|
||||
|
||||
#### 使用
|
||||
|
||||
如果你想要使用 Vite 构建 UI 部分,那么使用 `viteConfig` 即可,并且已经在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中配置,直接使用即可。
|
||||
|
||||
#### 配置
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
// 自定义 Vite 配置
|
||||
plugins: [
|
||||
// 额外的插件(Vue 插件已预配置)
|
||||
],
|
||||
// 其他配置...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
1. 添加路径别名
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import path from "path";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src"),
|
||||
"@components": path.resolve(__dirname, "src/components"),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
2. 添加额外的 Vite 插件
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import { defineConfig } from "vite";
|
||||
import UnoCSS from "unocss/vite";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
plugins: [
|
||||
UnoCSS(), // 添加 UnoCSS 插件
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### rsbuildConfig
|
||||
|
||||
Rsbuild 是基于 Rspack 开发的上层构建工具,其优势在于兼容 Webpack 生态并且性能优异。我们为什么要选择 Rsbuild 可以查阅 [Vite vs Rsbuild](#vite-vs-rsbuild)。
|
||||
|
||||
#### 使用
|
||||
|
||||
因为在 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 中,默认采用 Vite 构建,所以如果想要使用 Rsbuild 构建,需要手动配置,以下是切换到 Rsbuild 的过程:
|
||||
|
||||
安装依赖:
|
||||
|
||||
```bash
|
||||
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 @rsbuild/core -D
|
||||
```
|
||||
|
||||
创建 rsbuild.config.mjs:
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig()
|
||||
```
|
||||
|
||||
更新 package.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "rsbuild build --env-mode development --watch",
|
||||
"build": "rsbuild build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 配置
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
// 自定义 Rsbuild 配置
|
||||
plugins: [
|
||||
// 额外的插件(Vue 插件已预配置)
|
||||
],
|
||||
// 其他配置...
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
示例:
|
||||
|
||||
1. 添加路径别名
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
source: {
|
||||
alias: {
|
||||
"@": "./src",
|
||||
"@components": "./src/components",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
2. 添加额外的 Rsbuild 插件
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
import { pluginSass } from "@rsbuild/plugin-sass";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: {
|
||||
plugins: [
|
||||
pluginSass(), // 添加 Sass 插件
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### HaloUIPluginBundlerKit
|
||||
|
||||
旧版本 [plugin-starter](https://github.com/halo-dev/plugin-starter) 使用的方式,目前已经不再推荐。
|
||||
|
||||
## 构建输出
|
||||
|
||||
在 `viteConfig` 和 `rsbuildConfig` 中,已经配置好了开发环境和生产构建的输出目录,分别是:
|
||||
|
||||
- **开发环境**:`build/resources/main/console`,在开发 UI 的过程中,可以使用 `pnpm dev` 来实时查看效果
|
||||
- **生产环境**:`ui/build/dist`
|
||||
|
||||
> 需要注意的是,生产构建的目录仅仅是临时目录,最终在使用 Gradle 构建插件时会自动构建 UI 并复制到 `src/main/resources/console` 目录中。
|
||||
|
||||
## Vite vs Rsbuild{#vite-vs-rsbuild}
|
||||
|
||||
Vite 和 Rsbuild 都是优秀的构建工具,但它们在不同的使用场景下有各自的优势:
|
||||
|
||||
### 何时使用 Rsbuild
|
||||
|
||||
- ✅ **代码分割支持** - Rsbuild 为代码分割和懒加载提供了优秀的支持
|
||||
- ✅ **更好的性能** - 对于复杂应用,通常有更快的构建时间和更小的包体积
|
||||
- ✅ **动态导入** - 非常适合有重度前端组件的插件
|
||||
|
||||
**动态导入示例:**
|
||||
|
||||
```typescript
|
||||
import { definePlugin } from '@halo-dev/console-shared';
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { VLoading } from '@halo-dev/components';
|
||||
|
||||
export default definePlugin({
|
||||
routes: [
|
||||
{
|
||||
parentName: 'Root',
|
||||
route: {
|
||||
path: 'demo',
|
||||
name: 'DemoPage',
|
||||
// 懒加载重型组件
|
||||
component: defineAsyncComponent({
|
||||
loader: () => import('./views/DemoPage.vue'),
|
||||
loadingComponent: VLoading,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
extensionPoints: {},
|
||||
});
|
||||
```
|
||||
|
||||
### 何时使用 Vite
|
||||
|
||||
- ✅ **Vue 生态友好** - 与 Vue 生态系统工具和插件有更好的集成
|
||||
- ✅ **丰富的插件生态** - 有大量可用的 Vite 插件
|
||||
- ✅ **简单配置** - 对于直接的使用场景更容易配置
|
||||
|
||||
### 总结
|
||||
|
||||
| 特性 | Vite | Rsbuild |
|
||||
| ---------- | ------ | -------- |
|
||||
| 代码分割 | ❌ 有限 | ✅ 优秀 |
|
||||
| Vue 生态 | ✅ 优秀 | ✅ 良好 |
|
||||
| 构建性能 | ✅ 良好 | ✅ 优秀 |
|
||||
| 开发体验 | ✅ 优秀 | ✅ 优秀 |
|
||||
| 插件生态 | ✅ 丰富 | ✅ 增长中 |
|
||||
| 配置复杂度 | ✅ 简单 | ⚖️ 中等 |
|
||||
|
||||
**建议**:对于有大型前端代码库的复杂插件使用 **Rsbuild**,对于简单插件或需要广泛 Vue 生态系统集成时使用 **Vite**。
|
||||
|
||||
## 迁移{#migration}
|
||||
|
||||
如果你当前的插件使用的是旧版本的 [plugin-starter](https://github.com/halo-dev/plugin-starter),并且想使用新的 `viteConfig` 和 `rsbuildConfig`,可以参考以下步骤:
|
||||
|
||||
1. 更新 `@halo-dev/ui-plugin-bundler-kit` 至 `2.21.1` 或更高版本
|
||||
|
||||
```bash
|
||||
pnpm install @halo-dev/ui-plugin-bundler-kit@2.21.1 -D
|
||||
```
|
||||
|
||||
2. 更新 `vite.config.ts` 文件
|
||||
|
||||
```typescript
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
export default viteConfig({
|
||||
vite: {
|
||||
// Vite 配置需要按照原有的配置进行修改,但需要移除 Vue 插件,因为已经内置
|
||||
plugins: [
|
||||
],
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
3. 更新项目根目录的 `build.gradle` 文件
|
||||
|
||||
```diff
|
||||
plugins {
|
||||
id 'java'
|
||||
- id "com.github.node-gradle.node" version "7.0.2"
|
||||
- id "io.freefair.lombok" version "8.0.1"
|
||||
- id "run.halo.plugin.devtools" version "0.2.0"
|
||||
+ id "io.freefair.lombok" version "8.13"
|
||||
+ id "run.halo.plugin.devtools" version "0.6.0"
|
||||
}
|
||||
|
||||
group 'run.halo.starter'
|
||||
-sourceCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
- maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' }
|
||||
- maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' }
|
||||
- maven { url 'https://repo.spring.io/milestone' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
- implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT')
|
||||
+ implementation platform('run.halo.tools.platform:plugin:2.21.0')
|
||||
compileOnly 'run.halo.app:api'
|
||||
|
||||
testImplementation 'run.halo.app:api'
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
+ testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
-tasks.withType(JavaCompile).configureEach {
|
||||
- options.encoding = "UTF-8"
|
||||
-}
|
||||
-
|
||||
-node {
|
||||
- nodeProjectDir = file("${project.projectDir}/ui")
|
||||
+java {
|
||||
+ toolchain {
|
||||
+ languageVersion = JavaLanguageVersion.of(21)
|
||||
+ }
|
||||
}
|
||||
|
||||
-tasks.register('buildFrontend', PnpmTask) {
|
||||
- args = ['build']
|
||||
- dependsOn('installDepsForUI')
|
||||
+tasks.withType(JavaCompile).configureEach {
|
||||
+ options.encoding = "UTF-8"
|
||||
+ options.release = 21
|
||||
}
|
||||
|
||||
-tasks.register('installDepsForUI', PnpmTask) {
|
||||
- args = ['install']
|
||||
+tasks.register('processUiResources', Copy) {
|
||||
+ from project(':ui').tasks.named('buildFrontend')
|
||||
+ into layout.buildDirectory.dir('resources/main/console')
|
||||
}
|
||||
|
||||
-build {
|
||||
- // build frontend before build
|
||||
- tasks.named('compileJava').configure {
|
||||
- dependsOn('buildFrontend')
|
||||
- }
|
||||
+tasks.named('processResources', ProcessResources) {
|
||||
+ dependsOn tasks.named('processUiResources')
|
||||
}
|
||||
```
|
||||
|
||||
4. 在 ui 或者 console 目录新建 `build.gradle` 文件,内容如下:
|
||||
|
||||
```gradle
|
||||
plugins {
|
||||
id 'base'
|
||||
id "com.github.node-gradle.node" version "7.1.0"
|
||||
}
|
||||
|
||||
group 'run.halo.starter.ui'
|
||||
|
||||
tasks.register('buildFrontend', PnpmTask) {
|
||||
args = ['build']
|
||||
dependsOn tasks.named('pnpmInstall')
|
||||
inputs.dir(layout.projectDirectory.dir('src'))
|
||||
inputs.files(fileTree(
|
||||
dir: layout.projectDirectory,
|
||||
includes: ['*.cjs', '*.ts', '*.js', '*.json', '*.yaml']))
|
||||
outputs.dir(layout.buildDirectory.dir('dist'))
|
||||
shouldRunAfter(tasks.named('check'))
|
||||
}
|
||||
|
||||
tasks.register('checkFrontend', PnpmTask) {
|
||||
args = ['test:unit']
|
||||
dependsOn tasks.named('pnpmInstall')
|
||||
}
|
||||
|
||||
tasks.named('check') {
|
||||
dependsOn tasks.named('checkFrontend')
|
||||
}
|
||||
|
||||
tasks.named('build') {
|
||||
dependsOn tasks.named('buildFrontend')
|
||||
}
|
||||
```
|
||||
|
||||
进行此变更的主要目的是保证 UI 构建的产物不直接输出到源码目录的 resources 目录中,而是通过 Gradle 构建插件时复制到 `src/main/resources/console` 目录中。
|
||||
|
||||
完整变更过程可参考:[halo-dev/plugin-starter#52](https://github.com/halo-dev/plugin-starter/pull/52)
|
||||
|
||||
如果你不想使用新的 Gradle 构建配置,也可以修改 viteConfig 或 rsbuildConfig 的输出目录,和旧版本保持一致:
|
||||
|
||||
viteConfig:
|
||||
|
||||
```js
|
||||
import { viteConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
const OUT_DIR_PROD = "../src/main/resources/console";
|
||||
const OUT_DIR_DEV = "../build/resources/main/console";
|
||||
|
||||
export default viteConfig({
|
||||
vite: ({ mode }) => {
|
||||
const isProduction = mode === "production";
|
||||
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
|
||||
|
||||
return {
|
||||
build: {
|
||||
outDir,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
rsbuildConfig:
|
||||
|
||||
```js
|
||||
import { rsbuildConfig } from "@halo-dev/ui-plugin-bundler-kit";
|
||||
|
||||
const OUT_DIR_PROD = "../src/main/resources/console";
|
||||
const OUT_DIR_DEV = "../build/resources/main/console";
|
||||
|
||||
export default rsbuildConfig({
|
||||
rsbuild: ({ envMode }) => {
|
||||
const isProduction = envMode === "production";
|
||||
const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV;
|
||||
|
||||
return {
|
||||
output: {
|
||||
distPath: {
|
||||
root: outDir,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
```
|
@@ -29,28 +29,37 @@ export function definePlugin(plugin: PluginModule): PluginModule {
|
||||
```
|
||||
|
||||
```ts title="PluginModule"
|
||||
import type { Component, Ref } from "vue";
|
||||
import type { RouteRecordRaw, RouteRecordName } from "vue-router";
|
||||
import type { FunctionalPage } from "../states/pages";
|
||||
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
||||
import type { EditorProvider, PluginTab } from "..";
|
||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
||||
import type { BackupTab } from "@/states/backup";
|
||||
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
||||
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
||||
import type { EntityFieldItem } from "@/states/entity";
|
||||
import type { OperationItem } from "@/states/operation";
|
||||
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
||||
import type { ThemeListTab } from "@/states/theme-list-tabs";
|
||||
import type { UserProfileTab, UserTab } from "@/states/user-tab";
|
||||
import type {
|
||||
Attachment,
|
||||
Backup,
|
||||
ListedPost,
|
||||
Plugin,
|
||||
Theme,
|
||||
ListedComment,
|
||||
ListedReply,
|
||||
ListedSinglePage,
|
||||
} from "@halo-dev/api-client";
|
||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||
import type { Component, Ref } from "vue";
|
||||
import type { RouteRecordName, RouteRecordRaw } from "vue-router";
|
||||
import type {
|
||||
DashboardWidgetDefinition,
|
||||
DashboardWidgetQuickActionItem,
|
||||
EditorProvider,
|
||||
PluginTab,
|
||||
} from "..";
|
||||
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
||||
import type { FunctionalPage } from "../states/pages";
|
||||
|
||||
export interface RouteRecordAppend {
|
||||
parentName: RouteRecordName;
|
||||
parentName: NonNullable<RouteRecordName>;
|
||||
route: RouteRecordRaw;
|
||||
}
|
||||
|
||||
@@ -80,36 +89,65 @@ export interface ExtensionPoint {
|
||||
|
||||
"post:list-item:operation:create"?: (
|
||||
post: Ref<ListedPost>
|
||||
) => OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>;
|
||||
) => OperationItem<ListedPost>[];
|
||||
|
||||
"single-page:list-item:operation:create"?: (
|
||||
singlePage: Ref<ListedSinglePage>
|
||||
) => OperationItem<ListedSinglePage>[];
|
||||
|
||||
"comment:list-item:operation:create"?: (
|
||||
comment: Ref<ListedComment>
|
||||
) => OperationItem<ListedComment>[];
|
||||
|
||||
"reply:list-item:operation:create"?: (
|
||||
reply: Ref<ListedReply>
|
||||
) => OperationItem<ListedReply>[];
|
||||
|
||||
"plugin:list-item:operation:create"?: (
|
||||
plugin: Ref<Plugin>
|
||||
) => OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>;
|
||||
) => OperationItem<Plugin>[];
|
||||
|
||||
"backup:list-item:operation:create"?: (
|
||||
backup: Ref<Backup>
|
||||
) => OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>;
|
||||
) => OperationItem<Backup>[];
|
||||
|
||||
"attachment:list-item:operation:create"?: (
|
||||
attachment: Ref<Attachment>
|
||||
) => OperationItem<Attachment>[] | Promise<OperationItem<Attachment>[]>;
|
||||
) => OperationItem<Attachment>[];
|
||||
|
||||
"plugin:list-item:field:create"?: (
|
||||
plugin: Ref<Plugin>
|
||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
||||
"plugin:list-item:field:create"?: (plugin: Ref<Plugin>) => EntityFieldItem[];
|
||||
|
||||
"post:list-item:field:create"?: (
|
||||
post: Ref<ListedPost>
|
||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
||||
"post:list-item:field:create"?: (post: Ref<ListedPost>) => EntityFieldItem[];
|
||||
|
||||
"single-page:list-item:field:create"?: (
|
||||
singlePage: Ref<ListedSinglePage>
|
||||
) => EntityFieldItem[];
|
||||
|
||||
"theme:list:tabs:create"?: () => ThemeListTab[] | Promise<ThemeListTab[]>;
|
||||
|
||||
"theme:list-item:operation:create"?: (
|
||||
theme: Ref<Theme>
|
||||
) => OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>;
|
||||
) => OperationItem<Theme>[];
|
||||
|
||||
"user:detail:tabs:create"?: () => UserTab[] | Promise<UserTab[]>;
|
||||
|
||||
"uc:user:profile:tabs:create"?: () =>
|
||||
| UserProfileTab[]
|
||||
| Promise<UserProfileTab[]>;
|
||||
|
||||
"console:dashboard:widgets:create"?: () =>
|
||||
| DashboardWidgetDefinition[]
|
||||
| Promise<DashboardWidgetDefinition[]>;
|
||||
|
||||
"console:dashboard:widgets:internal:quick-action:item:create"?: () =>
|
||||
| DashboardWidgetQuickActionItem[]
|
||||
| Promise<DashboardWidgetQuickActionItem[]>;
|
||||
}
|
||||
|
||||
export interface PluginModule {
|
||||
/**
|
||||
* These components will be registered when plugin is activated.
|
||||
*/
|
||||
components?: Record<string, Component>;
|
||||
|
||||
routes?: RouteRecordRaw[] | RouteRecordAppend[];
|
||||
@@ -118,6 +156,7 @@ export interface PluginModule {
|
||||
|
||||
extensionPoints?: ExtensionPoint;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
- `components`:组件列表,key 为组件名称,value 为组件对象,在此定义之后,加载插件时会自动注册到 Vue App 全局。
|
||||
|
@@ -7,6 +7,6 @@ Halo 插件体系的 UI 部分可以让开发者在 Console 控制台和 UC 个
|
||||
|
||||
在开始之前,建议先熟悉或安装以下库和工具:
|
||||
|
||||
1. [Node.js 18+](https://nodejs.org)
|
||||
2. [pnpm 8+](https://pnpm.io)
|
||||
1. [Node.js 20+](https://nodejs.org)
|
||||
2. [pnpm 10+](https://pnpm.io)
|
||||
3. [Vue.js 3](https://vuejs.org)
|
||||
|
@@ -11,126 +11,51 @@ description: 了解如何与我们的社区分享你的插件
|
||||
|
||||
## 自动构建
|
||||
|
||||
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `workflow.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
|
||||
如果你是基于 [halo-dev/plugin-starter](https://github.com/halo-dev/plugin-starter) 创建的插件项目,那么已经包含了适用于 GitHub Action 的 `ci.yaml` 和 `cd.yaml` 文件,里面包含了构建插件和发布插件资源到 Release 的步骤,可以根据自己的实际需要进行修改,以下是示例:
|
||||
|
||||
```yaml
|
||||
name: Build Plugin JAR File
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**"
|
||||
- "!**.md"
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "**"
|
||||
- "!**.md"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
cache: 'gradle'
|
||||
java-version: 17
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: pnpm/action-setup@v2.0.1
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
- name: Get pnpm store directory
|
||||
id: pnpm-cache
|
||||
run: |
|
||||
echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
|
||||
- uses: actions/cache@v3
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/widget/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
- name: Install Frontend Dependencies
|
||||
run: |
|
||||
./gradlew pnpmInstall
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
# Set the version with tag name when releasing
|
||||
version=${{ github.event.release.tag_name }}
|
||||
version=${version#v}
|
||||
sed -i "s/version=.*-SNAPSHOT$/version=$version/1" gradle.properties
|
||||
./gradlew clean build -x test
|
||||
- name: Archive plugin-starter jar
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: plugin-starter
|
||||
path: |
|
||||
build/libs/*.jar
|
||||
retention-days: 1
|
||||
|
||||
github-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
if: github.event_name == 'release'
|
||||
steps:
|
||||
- name: Download plugin-starter jar
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: plugin-starter
|
||||
path: build/libs
|
||||
- name: Get Name of Artifact
|
||||
id: get_artifact
|
||||
run: |
|
||||
ARTIFACT_PATHNAME=$(ls build/libs/*.jar | head -n 1)
|
||||
ARTIFACT_NAME=$(basename ${ARTIFACT_PATHNAME})
|
||||
echo "Artifact pathname: ${ARTIFACT_PATHNAME}"
|
||||
echo "Artifact name: ${ARTIFACT_NAME}"
|
||||
echo "ARTIFACT_PATHNAME=${ARTIFACT_PATHNAME}" >> $GITHUB_ENV
|
||||
echo "ARTIFACT_NAME=${ARTIFACT_NAME}" >> $GITHUB_ENV
|
||||
echo "RELEASE_ID=${{ github.event.release.id }}" >> $GITHUB_ENV
|
||||
- name: Upload a Release Asset
|
||||
uses: actions/github-script@v2
|
||||
if: github.event_name == 'release'
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
console.log('environment', process.versions);
|
||||
|
||||
const fs = require('fs').promises;
|
||||
|
||||
const { repo: { owner, repo }, sha } = context;
|
||||
console.log({ owner, repo, sha });
|
||||
|
||||
const releaseId = process.env.RELEASE_ID
|
||||
const artifactPathName = process.env.ARTIFACT_PATHNAME
|
||||
const artifactName = process.env.ARTIFACT_NAME
|
||||
console.log('Releasing', releaseId, artifactPathName, artifactName)
|
||||
|
||||
await github.repos.uploadReleaseAsset({
|
||||
owner, repo,
|
||||
release_id: releaseId,
|
||||
name: artifactName,
|
||||
data: await fs.readFile(artifactPathName)
|
||||
});
|
||||
ci:
|
||||
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v3
|
||||
with:
|
||||
ui-path: "ui"
|
||||
pnpm-version: 9
|
||||
node-version: 22
|
||||
java-version: 21
|
||||
```
|
||||
|
||||
```yaml
|
||||
name: CD
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
jobs:
|
||||
cd:
|
||||
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-cd.yaml@v3
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
pnpm-version: 9
|
||||
node-version: 22
|
||||
java-version: 21
|
||||
skip-appstore-release: true
|
||||
```
|
||||
|
||||
关于 CI / CD 的更多详细信息,可查阅:[halo-sigs/reusable-workflows](https://github.com/halo-sigs/reusable-workflows)
|
||||
|
||||
## 发布你的插件
|
||||
|
||||
用户可以在你的仓库 Release 下载使用,但为了方便让 Halo 的用户知道你的插件,可以在以下渠道发布:
|
||||
|
@@ -95,11 +95,7 @@
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
},
|
||||
"items": [
|
||||
"contribution/issue",
|
||||
"contribution/pr",
|
||||
"contribution/sponsor"
|
||||
]
|
||||
"items": ["contribution/issue", "contribution/pr", "contribution/sponsor"]
|
||||
},
|
||||
"about"
|
||||
],
|
||||
@@ -157,7 +153,8 @@
|
||||
},
|
||||
"items": [
|
||||
"developer-guide/plugin/basics/ui/intro",
|
||||
"developer-guide/plugin/basics/ui/entry"
|
||||
"developer-guide/plugin/basics/ui/entry",
|
||||
"developer-guide/plugin/basics/ui/build"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -319,9 +316,7 @@
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
},
|
||||
"items": [
|
||||
"developer-guide/plugin/examples/todolist"
|
||||
]
|
||||
"items": ["developer-guide/plugin/examples/todolist"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
Reference in New Issue
Block a user