Skip to content

Commit 7d8f649

Browse files
committed
build: hMR implementing for bob plugin based on Vite with pnpm dev
Signed-off-by: 诺墨 <[email protected]>
1 parent 11103c3 commit 7d8f649

File tree

5 files changed

+235
-3
lines changed

5 files changed

+235
-3
lines changed

appcast.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"identifier": "com.gitee.ai.bob-translate",
3+
"versions": [
4+
{
5+
"version": "0.1.0",
6+
"desc": "🚀 首个发行版,支持使用 Gitee AI 进行翻译",
7+
"sha256": "",
8+
"url": "https://gitee.com/normalcoder/bob-plugin-giteeai-translate/raw/main/giteeai-translate.bobplugin",
9+
"minBobVersion": "1.8.0"
10+
}
11+
]
12+
}

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
"type": "git",
77
"url": "https://gitee.com/normalcoder/bob-plugin-giteeai-translate.git"
88
},
9+
"type": "module",
910
"main": "index.js",
1011
"scripts": {
11-
"test": "echo \"Error: no test specified\" && exit 1"
12+
"build": "vite build",
13+
"dev": "vite",
14+
"watch": "node --experimental-vm-modules ./scripts/watch.mjs"
1215
},
1316
"keywords": [
1417
"bob",
@@ -19,5 +22,10 @@
1922
"DeepSeek"
2023
],
2124
"author": "诺墨<[email protected]>",
22-
"license": "MIT"
25+
"license": "MIT",
26+
"devDependencies": {
27+
"archiver": "^7.0.1",
28+
"chokidar": "^4.0.3",
29+
"vite": "^6.1.0"
30+
}
2331
}

scripts/watch.mjs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { createBobPlugin } from '../vite.config.js';
2+
import chokidar from 'chokidar';
3+
import path from 'path';
4+
import fs from 'fs';
5+
6+
const IGNORED_FILES = [
7+
'info.json', // 忽略自动生成的版本文件
8+
'.DS_Store', // macOS 系统文件
9+
'node_modules',
10+
/\.log$/
11+
];
12+
13+
const watcher = chokidar.watch('src', {
14+
persistent: true,
15+
ignoreInitial: false,
16+
ignored: [
17+
/(^|[\/\\])\../, // 忽略隐藏文件
18+
...IGNORED_FILES
19+
]
20+
});
21+
22+
// 防抖函数,避免频繁触发
23+
function debounce(func, wait) {
24+
let timeout;
25+
return function(...args) {
26+
const context = this;
27+
clearTimeout(timeout);
28+
timeout = setTimeout(() => func.apply(context, args), wait);
29+
};
30+
}
31+
32+
const debouncedBuild = debounce(async () => {
33+
console.log('\n\n--------------------------------\n','🔄 重新构建开发版本');
34+
await createBobPlugin(true);
35+
}, 300);
36+
37+
watcher.on('all', async (event, filePath) => {
38+
// 获取相对路径
39+
const relativePath = path.relative(process.cwd(), filePath);
40+
41+
// 检查是否为需要忽略的文件
42+
const shouldIgnore = IGNORED_FILES.some(pattern => {
43+
if (pattern instanceof RegExp) {
44+
return pattern.test(relativePath);
45+
}
46+
return relativePath.includes(pattern);
47+
});
48+
49+
if (shouldIgnore) {
50+
console.log('\n\n--------------------------------\n',` 忽略文件变更: ${relativePath}`);
51+
return;
52+
}
53+
54+
// 只处理 src 目录下的文件变更
55+
if (path.resolve(filePath).startsWith(path.resolve('src'))) {
56+
console.log('\n\n--------------------------------\n',` ${event} detected in ${relativePath}`);
57+
debouncedBuild();
58+
}
59+
});

src/info.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"identifier": "com.gitee.ai.bob-translate",
3-
"version": "0.1.0-dev.1739019370157",
3+
"version": "0.1.0",
44
"category": "translate",
55
"name": "Gitee AI 翻译",
66
"summary": "使用 Gitee AI 服务进行翻译",

vite.config.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// vite.config.js
2+
import { defineConfig } from 'vite';
3+
import path from 'path';
4+
import { fileURLToPath } from 'url';
5+
import fs from 'fs';
6+
import archiver from 'archiver';
7+
import chokidar from 'chokidar';
8+
import crypto from 'crypto';
9+
10+
// 获取当前文件的目录
11+
const __filename = fileURLToPath(import.meta.url);
12+
const __dirname = path.dirname(__filename);
13+
14+
const pluginName = 'giteeai-translate';
15+
16+
function generateDevAppcast(outputPath) {
17+
const fileBuffer = fs.readFileSync(outputPath);
18+
const hashSum = crypto.createHash('sha256');
19+
hashSum.update(fileBuffer);
20+
const sha256 = hashSum.digest('hex');
21+
22+
// 读取 info.json 获取版本号
23+
const infoPath = path.resolve(__dirname, 'src', 'info.json');
24+
const info = JSON.parse(fs.readFileSync(infoPath, 'utf8'));
25+
const version = info.version;
26+
27+
// 使用绝对路径,确保文件名正确
28+
const relativePluginPath = path.relative(path.resolve(__dirname, 'dist/dev'), outputPath);
29+
30+
return {
31+
identifier: "com.gitee.ai.bob-translate",
32+
versions: [
33+
{
34+
version: version,
35+
desc: "🛠️ 开发版本",
36+
sha256: sha256,
37+
url: `http://localhost:5173/${relativePluginPath}`,
38+
minBobVersion: "1.8.0"
39+
}
40+
]
41+
};
42+
}
43+
44+
function updateInfoJsonAppcast(isDev) {
45+
const infoPath = path.resolve(__dirname, 'src', 'info.json');
46+
const info = JSON.parse(fs.readFileSync(infoPath, 'utf8'));
47+
48+
if (isDev) {
49+
// 总是添加新的开发时间戳
50+
const baseVersion = info.version.split('-dev.')[0];
51+
info.version = `${baseVersion}-dev.${Date.now()}`;
52+
info.appcast = 'http://localhost:5173/appcast.json';
53+
} else {
54+
// 移除开发版本标记,恢复原始版本
55+
info.version = info.version.split('-dev.')[0];
56+
info.appcast = 'https://raw.githubusercontent.com/your-username/your-repo/main/appcast.json';
57+
}
58+
59+
return info;
60+
}
61+
62+
function createBobPlugin(isDev = false) {
63+
const outputDir = isDev ? 'dist/dev' : 'dist/build';
64+
const outputFileName = isDev ? `${pluginName}-dev.bobplugin` : `${pluginName}.bobplugin`;
65+
const outputPath = path.resolve(__dirname, outputDir, outputFileName);
66+
67+
// 确保输出目录存在
68+
fs.mkdirSync(path.resolve(__dirname, outputDir), { recursive: true });
69+
70+
// 创建插件归档
71+
const archive = archiver('zip', { zlib: { level: 9 } });
72+
const output = fs.createWriteStream(outputPath);
73+
74+
archive.pipe(output);
75+
76+
// 更新 info.json
77+
const updatedInfo = updateInfoJsonAppcast(isDev);
78+
const tempInfoPath = path.resolve(__dirname, 'src', 'info.json');
79+
80+
// 使用原子写入,减少文件系统事件
81+
const tempFile = `${tempInfoPath}.tmp`;
82+
fs.writeFileSync(tempFile, JSON.stringify(updatedInfo, null, 4));
83+
fs.renameSync(tempFile, tempInfoPath);
84+
85+
// 添加所有 src 目录文件到归档
86+
archive.directory('src/', false);
87+
88+
return new Promise((resolve, reject) => {
89+
output.on('close', () => {
90+
console.log(`${outputFileName} created successfully in ${outputDir}`);
91+
92+
// 仅在开发模式下生成本地 appcast
93+
if (isDev) {
94+
const devAppcast = generateDevAppcast(outputPath);
95+
const devAppcastPath = path.resolve(__dirname, outputDir, 'appcast.json');
96+
97+
// 使用原子写入,减少文件系统事件
98+
const tempAppcastFile = `${devAppcastPath}.tmp`;
99+
fs.writeFileSync(tempAppcastFile, JSON.stringify(devAppcast, null, 4));
100+
fs.renameSync(tempAppcastFile, devAppcastPath);
101+
102+
console.log(`Dev appcast created at ${devAppcastPath}`);
103+
}
104+
105+
resolve();
106+
});
107+
108+
archive.on('error', (err) => {
109+
reject(err);
110+
});
111+
112+
archive.finalize();
113+
});
114+
}
115+
116+
export default defineConfig({
117+
root: path.resolve(__dirname, 'dist/dev'),
118+
server: {
119+
port: 5173,
120+
open: false,
121+
cors: true,
122+
},
123+
build: {
124+
rollupOptions: {
125+
input: {
126+
main: path.resolve(__dirname, 'src/main.js')
127+
}
128+
}
129+
},
130+
plugins: [
131+
{
132+
name: 'bob-plugin-build',
133+
buildStart() {
134+
// 创建生产 .bobplugin
135+
return createBobPlugin(false);
136+
}
137+
}
138+
]
139+
});
140+
141+
export function watchPlugin() {
142+
const watcher = chokidar.watch('src', {
143+
persistent: true,
144+
ignoreInitial: false
145+
});
146+
147+
watcher.on('all', async (event, path) => {
148+
console.log(`${event} detected in ${path}`);
149+
await createBobPlugin(true);
150+
});
151+
}
152+
153+
export { createBobPlugin };

0 commit comments

Comments
 (0)