@zhin.js/console 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,11 +15,12 @@ Zhin 机器人框架的 Web 控制台插件,提供开发环境下的可视化
15
15
  ## 技术架构
16
16
 
17
17
  - **构建工具**: Vite 7.x
18
- - **前端框架**: Vue 3 + TypeScript
18
+ - **前端框架**: React 18 + React Router 7 + TypeScript
19
+ - **UI 组件库**: Radix UI + Tailwind CSS
20
+ - **状态管理**: Redux Toolkit + Redux Persist
19
21
  - **开发服务器**: 集成到 Koa 路由
20
22
  - **WebSocket**: 实时数据同步
21
- - **组件解析**: unplugin-vue-components
22
- - **UI组件**: PrimeVue 自动导入
23
+ - **构建优化**: Vendor Chunks 分割,支持插件复用公共依赖
23
24
 
24
25
  ## 安装
25
26
 
@@ -55,13 +56,30 @@ http://localhost:8086/vite/
55
56
 
56
57
  ```typescript
57
58
  interface WebServer {
58
- vite: ViteDevServer // Vite开发服务器
59
+ vite?: ViteDevServer // Vite开发服务器
59
60
  addEntry(entry: string): () => void // 添加入口文件
60
61
  entries: Record<string, string> // 入口文件映射
61
62
  ws: WebSocketServer // WebSocket服务器
62
63
  }
63
64
  ```
64
65
 
66
+ ### 构建优化
67
+
68
+ Console 插件采用智能的构建优化策略,显著减少重复打包:
69
+
70
+ - **Vendor Chunks 分割**: 将公共依赖分割成独立的 JS 文件
71
+ - `vendor-react.js` - React 核心库 (~190KB)
72
+ - `vendor-ui.js` - UI 组件库 (~250KB)
73
+ - `vendor-redux.js` - 状态管理 (~23KB)
74
+ - 其他分组...
75
+
76
+ - **插件依赖复用**: 其他插件构建时自动外部化公共依赖
77
+ - 插件体积减少 ~90% (从 650KB → 30KB)
78
+ - 浏览器缓存复用,提升加载速度
79
+ - 开发和生产环境统一体验
80
+
81
+ 详见 [BUILD_OPTIMIZATION.md](./BUILD_OPTIMIZATION.md)
82
+
65
83
  ### 实时数据同步
66
84
 
67
85
  - 📡 WebSocket 连接管理
@@ -82,20 +100,45 @@ interface WebServer {
82
100
 
83
101
  ```javascript
84
102
  {
85
- root: '@zhin.js/client/app',
103
+ root: 'plugins/console/client',
86
104
  base: '/vite/',
87
105
  plugins: [
88
- vue(),
89
- Components({
90
- resolvers: [PrimeVueResolver()]
91
- })
106
+ react(),
107
+ tailwindcss()
92
108
  ],
93
109
  server: {
94
110
  middlewareMode: true
111
+ },
112
+ build: {
113
+ rollupOptions: {
114
+ output: {
115
+ manualChunks: {
116
+ // 自动分割 vendor chunks
117
+ 'vendor-react': ['react', 'react-dom'],
118
+ 'vendor-ui': ['@radix-ui/themes', 'lucide-react'],
119
+ 'vendor-redux': ['@reduxjs/toolkit', 'redux-persist'],
120
+ // ...
121
+ }
122
+ }
123
+ }
95
124
  }
96
125
  }
97
126
  ```
98
127
 
128
+ ### 插件客户端构建
129
+
130
+ 使用 `zhin-client` 工具构建插件客户端代码:
131
+
132
+ ```bash
133
+ # 在插件目录下
134
+ npx zhin-client build
135
+
136
+ # 或使用相对路径
137
+ node ../../plugins/console/lib/bin.js build
138
+ ```
139
+
140
+ 配置会自动外部化公共依赖,生成轻量级的插件代码。
141
+
99
142
  ### 路由配置
100
143
 
101
144
  - 支持所有路由通过 Vite 处理
@@ -108,18 +151,38 @@ interface WebServer {
108
151
  ### 项目结构
109
152
 
110
153
  ```
111
- src/
112
- ├── index.ts # 主入口,集成Vite服务器
113
- └── types/ # TypeScript类型定义
154
+ console/
155
+ ├── app/ # 构建工具
156
+ │ ├── index.ts # Console 插件主入口
157
+ │ ├── build.ts # 构建逻辑 (buildConsoleClient, buildPluginClient)
158
+ │ ├── dev.ts # Vite 开发服务器
159
+ │ ├── websocket.ts # WebSocket 管理
160
+ │ └── bin.ts # CLI 工具
161
+ ├── client/ # 前端应用
162
+ │ ├── src/ # React 应用源码
163
+ │ ├── index.html # SPA 入口
164
+ │ └── ...
165
+ ├── dist/ # 构建产物
166
+ │ ├── assets/
167
+ │ │ ├── vendor-react-*.js # React vendor chunk
168
+ │ │ ├── vendor-ui-*.js # UI vendor chunk
169
+ │ │ └── ...
170
+ │ └── index.html
171
+ └── lib/ # TypeScript 编译产物
114
172
  ```
115
173
 
116
174
  ### 构建
117
175
 
118
176
  ```bash
119
- npm run build # 构建插件
120
- npm run clean # 清理构建文件
177
+ npm run build # 构建插件 (TypeScript)
178
+ npm run build:client # 构建客户端 (React SPA)
179
+ npm run clean # 清理构建文件
121
180
  ```
122
181
 
182
+ 构建产物说明:
183
+ - `lib/` - Node.js 运行的插件代码
184
+ - `dist/` - 浏览器加载的客户端代码,包含分割的 vendor chunks
185
+
123
186
  ## WebSocket API
124
187
 
125
188
  ### 消息类型
@@ -138,16 +201,18 @@ npm run clean # 清理构建文件
138
201
  ## 依赖项
139
202
 
140
203
  ### 核心依赖
141
- - `@vitejs/plugin-vue` - Vue插件支持
142
- - `koa-connect` - Koa中间件集成
143
- - `vue` - Vue框架
204
+ - `@vitejs/plugin-react` - React 插件支持
205
+ - `@tailwindcss/vite` - Tailwind CSS 集成
206
+ - `koa-connect` - Koa 中间件集成
207
+ - `react` / `react-dom` - React 框架
208
+ - `react-router` - 路由管理
209
+ - `@reduxjs/toolkit` - 状态管理
210
+ - `@radix-ui/themes` - UI 组件库
144
211
  - `vite` - 构建工具
145
212
 
146
213
  ### 对等依赖
147
- - `@zhin.js/client` - 客户端代码
148
- - `@zhin.js/http` - HTTP服务器
149
- - `unplugin-vue-components` - 组件自动导入
150
- - `@primevue/auto-import-resolver` - PrimeVue组件解析
214
+ - `@zhin.js/client` - 客户端基础库
215
+ - `@zhin.js/http` - HTTP 服务器
151
216
 
152
217
  ## 使用场景
153
218
 
package/app/bin.ts ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { buildCurrentPlugin, buildConsoleClient } from "./build.js";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const args = process.argv.slice(2);
8
+ const command = args[0];
9
+
10
+ async function main() {
11
+ try {
12
+ switch (command) {
13
+ case "build":
14
+ // 构建当前目录的插件客户端代码
15
+ console.log("🔨 Building plugin client...");
16
+ await buildCurrentPlugin();
17
+ break;
18
+
19
+ case "build:console":
20
+ // 构建 console 插件的客户端代码
21
+ console.log("🔨 Building console client...");
22
+ const consoleRoot = path.resolve(
23
+ path.dirname(fileURLToPath(import.meta.url)),
24
+ ".."
25
+ );
26
+ await buildConsoleClient({ consoleRoot });
27
+ break;
28
+
29
+ default:
30
+ console.log(`
31
+ Zhin.js Client Builder
32
+
33
+ Usage:
34
+ zhin-client build Build current plugin's client code
35
+ zhin-client build:console Build console plugin's client code (SPA mode)
36
+
37
+ Examples:
38
+ # Build a plugin (single file mode)
39
+ cd my-plugin && zhin-client build
40
+
41
+ # Build console (SPA mode)
42
+ zhin-client build:console
43
+ `);
44
+ process.exit(1);
45
+ }
46
+ } catch (error) {
47
+ console.error("❌ Build failed:", error);
48
+ process.exit(1);
49
+ }
50
+ }
51
+
52
+ main();
package/app/build.ts ADDED
@@ -0,0 +1,211 @@
1
+ import { build, searchForWorkspaceRoot } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import path from "path";
5
+ import fs from "fs";
6
+
7
+ export interface BuildOptions {
8
+ /** 插件根目录 */
9
+ pluginRoot: string;
10
+ /** 输出目录,默认为 pluginRoot/dist */
11
+ outDir?: string;
12
+ /** 是否启用 tailwindcss,默认 true */
13
+ enableTailwind?: boolean;
14
+ }
15
+
16
+ export interface ConsoleBuildOptions {
17
+ /** Console 插件根目录 */
18
+ consoleRoot: string;
19
+ /** 输出目录,默认为 consoleRoot/dist */
20
+ outDir?: string;
21
+ }
22
+
23
+ /**
24
+ * 查找插件的客户端入口文件
25
+ * @param pluginRoot 插件根目录
26
+ * @returns 入口文件路径,如果不存在则返回 null
27
+ */
28
+ function findClientEntry(pluginRoot: string): string | null {
29
+ const possibleEntries = [
30
+ path.join(pluginRoot, "client/index.tsx"),
31
+ path.join(pluginRoot, "client/index.ts"),
32
+ path.join(pluginRoot, "src/main.tsx"),
33
+ path.join(pluginRoot, "src/main.ts"),
34
+ ];
35
+
36
+ for (const entry of possibleEntries) {
37
+ if (fs.existsSync(entry)) {
38
+ return entry;
39
+ }
40
+ }
41
+
42
+ return null;
43
+ }
44
+
45
+ /**
46
+ * 验证构建环境
47
+ * @param pluginRoot 插件根目录
48
+ * @throws 如果环境不满足构建要求
49
+ */
50
+ function validateBuildEnvironment(pluginRoot: string): void {
51
+ if (!fs.existsSync(pluginRoot)) {
52
+ throw new Error(`Plugin root directory does not exist: ${pluginRoot}`);
53
+ }
54
+
55
+ const entry = findClientEntry(pluginRoot);
56
+ if (!entry) {
57
+ throw new Error(
58
+ `No client entry file found in ${pluginRoot}. Looking for: client/index.tsx, client/index.ts, src/main.tsx, or src/main.ts`
59
+ );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * 构建插件的客户端代码(单文件模式)
65
+ * 用于构建普通插件的 client/index.tsx 文件
66
+ *
67
+ * 策略:将公共依赖配置为 external,运行时从 console 加载的 vendor chunks 中复用
68
+ * @param options 构建选项
69
+ */
70
+ export async function buildPluginClient(options: BuildOptions): Promise<void> {
71
+ const {
72
+ pluginRoot,
73
+ outDir = path.join(pluginRoot, "dist"),
74
+ enableTailwind = true,
75
+ } = options;
76
+
77
+ // 验证构建环境
78
+ validateBuildEnvironment(pluginRoot);
79
+
80
+ const entry = findClientEntry(pluginRoot);
81
+ if (!entry) {
82
+ throw new Error(`No client entry file found in ${pluginRoot}`);
83
+ }
84
+
85
+ const plugins = [react()];
86
+ if (enableTailwind) {
87
+ plugins.push(tailwindcss());
88
+ }
89
+
90
+ // 构建配置 - 库模式
91
+ const clientRoot = path.dirname(entry);
92
+
93
+ await build({
94
+ root: clientRoot,
95
+ plugins,
96
+ build: {
97
+ outDir,
98
+ emptyOutDir: true,
99
+ lib: {
100
+ entry,
101
+ formats: ["es"],
102
+ fileName: "index",
103
+ },
104
+ rollupOptions: {
105
+ makeAbsoluteExternalsRelative: true,
106
+ external:[
107
+ 'react',
108
+ 'react-dom',
109
+ 'react/jsx-runtime',
110
+ 'clsx',
111
+ 'tailwind-merge',
112
+ 'lucide-react',
113
+ '@radix-ui/themes'
114
+ ],
115
+ },
116
+ },
117
+ resolve:{
118
+ dedupe: [
119
+ "react",
120
+ "react-dom",
121
+ "clsx",
122
+ "tailwind-merge",
123
+ ],
124
+ alias: {
125
+ "@": path.resolve(pluginRoot, "client/src"),
126
+ },
127
+ }
128
+ });
129
+
130
+ console.log(`✅ Plugin client code built successfully: ${outDir}`);
131
+ console.log(`📦 External dependencies will be loaded from console vendor chunks`);
132
+ }
133
+
134
+ /**
135
+ * 构建 Console 插件的客户端代码(SPA 应用模式)
136
+ * Console 有完整的 index.html 和 src 目录结构
137
+ * @param options Console 构建选项
138
+ */
139
+ export async function buildConsoleClient(
140
+ options: ConsoleBuildOptions
141
+ ): Promise<void> {
142
+ const { consoleRoot, outDir = path.join(consoleRoot, "dist") } = options;
143
+
144
+ const clientRoot = path.join(consoleRoot, "client");
145
+
146
+ // 检查 client 目录是否存在
147
+ if (!fs.existsSync(clientRoot)) {
148
+ throw new Error(`Console client directory does not exist: ${clientRoot}`);
149
+ }
150
+
151
+ // 检查 index.html 是否存在
152
+ const indexHtml = path.join(clientRoot, "index.html");
153
+ if (!fs.existsSync(indexHtml)) {
154
+ throw new Error(`index.html not found in: ${clientRoot}`);
155
+ }
156
+
157
+ const workspaceRoot = searchForWorkspaceRoot(consoleRoot);
158
+ const consoleClientRoot=path.resolve(workspaceRoot, "plugins/client/client")
159
+ const plugins = [react(), tailwindcss()];
160
+
161
+ await build({
162
+ root: clientRoot,
163
+ plugins,
164
+ build: {
165
+ outDir,
166
+ emptyOutDir: true,
167
+ // 设置最小 chunk 大小,避免过度分割
168
+ chunkSizeWarningLimit: 1000,
169
+ // SPA 应用模式,不是库模式
170
+ rollupOptions: {
171
+ input: indexHtml,
172
+ // 保留导出签名
173
+ preserveEntrySignatures: 'strict',
174
+ output: {
175
+ // 确保文件名稳定,不使用哈希,方便插件引用
176
+ chunkFileNames: '[name].js',
177
+ entryFileNames: '[name].js',
178
+ assetFileNames: '[name].[ext]',
179
+ },
180
+ },
181
+ },
182
+ resolve: {
183
+ dedupe: [
184
+ "react",
185
+ "react-dom",
186
+ "clsx",
187
+ "tailwind-merge",
188
+ "@reduxjs/toolkit",
189
+ "react-router",
190
+ "react-redux",
191
+ "redux-persist",
192
+ ],
193
+ alias: {
194
+ "@zhin.js/client": consoleClientRoot,
195
+ "@": path.resolve(clientRoot, "src"),
196
+ },
197
+ },
198
+ });
199
+
200
+ console.log(`✅ Console client built successfully: ${outDir}`);
201
+ }
202
+
203
+ /**
204
+ * 构建当前目录的插件客户端代码
205
+ */
206
+ export async function buildCurrentPlugin(): Promise<void> {
207
+ const currentDir = process.cwd();
208
+ await buildPluginClient({
209
+ pluginRoot: currentDir,
210
+ });
211
+ }
package/app/dev.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { ViteDevServer, createServer, searchForWorkspaceRoot } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+ import tailwindcss from "@tailwindcss/vite";
4
+ import path from "path";
5
+
6
+ export interface DevServerOptions {
7
+ /** 客户端代码根目录 */
8
+ root: string;
9
+ /** 基础路径,默认 /vite/ */
10
+ base?: string;
11
+ /** 是否启用 tailwindcss,默认 true */
12
+ enableTailwind?: boolean;
13
+ }
14
+
15
+ /**
16
+ * 创建 Vite 开发服务器
17
+ * @param options 开发服务器选项
18
+ * @returns Vite 开发服务器实例
19
+ */
20
+ export async function createViteDevServer(
21
+ options: DevServerOptions
22
+ ): Promise<ViteDevServer> {
23
+ const { root, base = "/vite/", enableTailwind = true } = options;
24
+
25
+ const plugins = [react()];
26
+ if (enableTailwind) {
27
+ plugins.push(tailwindcss());
28
+ }
29
+
30
+ return await await createServer({
31
+ root,
32
+ base,
33
+ plugins: [react(), tailwindcss()],
34
+ server: {
35
+ middlewareMode: true,
36
+ fs: {
37
+ strict: false,
38
+ // 添加文件访问过滤,避免访问特殊文件
39
+ allow: [
40
+ // 允许访问的目录
41
+ root,
42
+ searchForWorkspaceRoot(root),
43
+ path.resolve(process.cwd(), 'node_modules'),
44
+ path.resolve(process.cwd(), 'client'),
45
+ path.resolve(process.cwd(), 'src'),
46
+ ],
47
+ // 拒绝访问某些文件模式
48
+ deny: [
49
+ '**/.git/**',
50
+ '**/node_modules/.cache/**',
51
+ '**/*.socket',
52
+ '**/*.pipe',
53
+ '**/Dockerfile*',
54
+ '**/.env*',
55
+ ],
56
+ },
57
+ },
58
+ resolve: {
59
+ dedupe: [
60
+ "react",
61
+ "react-dom",
62
+ "clsx",
63
+ "tailwind-merge",
64
+ "@reduxjs/toolkit",
65
+ "react-router",
66
+ "react-redux",
67
+ "redux-persist",
68
+ ],
69
+ alias: {
70
+ "@zhin.js/client": path.resolve(root, "../../client/client"),
71
+ "@": path.resolve(root, "../client/src"),
72
+ },
73
+ },
74
+ optimizeDeps: {
75
+ include: ["react", "react-dom"],
76
+ },
77
+ build: {
78
+ rollupOptions: {
79
+ input: root + "/index.html",
80
+ },
81
+ },
82
+ });
83
+ }