From 3e8be4729547588661ff0a8a7324af8f16c49f46 Mon Sep 17 00:00:00 2001 From: impressionyang Date: Wed, 20 May 2026 16:56:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Windows=20?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=89=93=E5=8C=85=E8=84=9A=E6=9C=AC=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9C=AC=E5=9C=B0=E7=BC=93=E5=AD=98=20Electr?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build-windows.js | 232 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 scripts/build-windows.js diff --git a/scripts/build-windows.js b/scripts/build-windows.js new file mode 100644 index 0000000..08995c5 --- /dev/null +++ b/scripts/build-windows.js @@ -0,0 +1,232 @@ +/** + * Windows x64 自动打包脚本 + * 功能: + * 1. 自动下载 Electron 并缓存到用户目录 + * 2. 编译 TypeScript 并复制 UI 文件 + * 3. 打包成 ZIP 文件 + * 4. 支持增量打包(使用缓存的 Electron) + */ + +import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, cpSync } from 'fs'; +import { join } from 'path'; +import { execSync } from 'child_process'; +import { homedir } from 'os'; + +// 路径配置 +const rootDir = process.cwd(); +const userCacheDir = join(homedir(), '.impress-asr-input', 'cache'); +const electronCacheDir = join(userCacheDir, 'electron'); +const releaseDir = join(rootDir, 'release'); +const distDir = join(rootDir, 'dist'); + +// Electron 版本 +const ELECTRON_VERSION = '28.3.3'; +const ELECTRON_ZIP_NAME = `electron-v${ELECTRON_VERSION}-win32-x64.zip`; +const ELECTRON_ZIP_PATH = join(electronCacheDir, ELECTRON_ZIP_NAME); +const ELECTRON_URL = `https://npmmirror.com/mirrors/electron/${ELECTRON_VERSION}/${ELECTRON_ZIP_NAME}`; + +// 颜色输出 +const colors = { + reset: '\x1b[0m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + red: '\x1b[31m', +}; + +function log(color, message) { + console.log(`${color}${message}${colors.reset}`); +} + +/** + * 创建必要的目录 + */ +function ensureDirs() { + log(colors.blue, '📁 创建目录...'); + if (!existsSync(userCacheDir)) { + mkdirSync(userCacheDir, { recursive: true }); + log(colors.green, ` ✅ 缓存目录:${userCacheDir}`); + } + if (!existsSync(electronCacheDir)) { + mkdirSync(electronCacheDir, { recursive: true }); + } + if (!existsSync(releaseDir)) { + mkdirSync(releaseDir, { recursive: true }); + } +} + +/** + * 下载 Electron + */ +function downloadElectron() { + if (existsSync(ELECTRON_ZIP_PATH)) { + log(colors.green, `✅ Electron 已缓存:${ELECTRON_ZIP_NAME}`); + return; + } + + log(colors.yellow, `📥 下载 Electron ${ELECTRON_VERSION}...`); + log(colors.blue, ` URL: ${ELECTRON_URL}`); + + try { + const proxy = process.env.http_proxy || process.env.https_proxy || ''; + const curlCmd = proxy + ? `curl -L -o "${ELECTRON_ZIP_PATH}" --proxy "${proxy}" "${ELECTRON_URL}"` + : `curl -L -o "${ELECTRON_ZIP_PATH}" "${ELECTRON_URL}"`; + + execSync(curlCmd, { stdio: 'inherit' }); + + if (existsSync(ELECTRON_ZIP_PATH)) { + const size = Math.round(readFileSync(ELECTRON_ZIP_PATH).length / 1024 / 1024 * 100) / 100; + log(colors.green, `✅ 下载完成:${size}MB`); + } + } catch (error) { + log(colors.red, '❌ 下载失败,请检查网络连接或代理设置'); + throw error; + } +} + +/** + * 编译 TypeScript + */ +function compileTypeScript() { + log(colors.blue, '📝 编译 TypeScript...'); + execSync('npm run build', { stdio: 'inherit' }); + log(colors.green, '✅ 编译完成'); +} + +/** + * 解压 Electron + */ +function unzipElectron(outputDir) { + if (existsSync(outputDir)) { + rmSync(outputDir, { recursive: true, force: true }); + } + mkdirSync(outputDir, { recursive: true }); + + log(colors.blue, '📦 解压 Electron...'); + execSync(`unzip -o "${ELECTRON_ZIP_PATH}" -d "${outputDir}"`, { stdio: 'inherit' }); + log(colors.green, '✅ 解压完成'); +} + +/** + * 复制应用文件 + */ +function copyAppFiles(appDir) { + log(colors.blue, '📋 复制应用文件...'); + + const resourcesAppDir = join(appDir, 'resources', 'app'); + mkdirSync(resourcesAppDir, { recursive: true }); + + // 复制 dist 目录 + const distFiles = ['core', 'utils', 'ui', 'electron-main.js', 'preload.js', 'main.js', 'package.json']; + for (const file of distFiles) { + const src = join(distDir, file); + const dest = join(resourcesAppDir, file); + + if (existsSync(src)) { + cpSync(src, dest, { recursive: true }); + log(colors.green, ` ✅ ${file}`); + } + } + + // 复制 node_modules + const nodeModulesDir = join(resourcesAppDir, 'node_modules'); + mkdirSync(nodeModulesDir, { recursive: true }); + + const deps = ['onnxruntime-web', 'clipboardy', 'commander']; + for (const dep of deps) { + const src = join(rootDir, 'node_modules', dep); + const dest = join(nodeModulesDir, dep); + if (existsSync(src)) { + cpSync(src, dest, { recursive: true }); + log(colors.green, ` ✅ node_modules/${dep}`); + } + } + + // 创建空的 models 目录 + mkdirSync(join(resourcesAppDir, 'models'), { recursive: true }); + log(colors.green, ' ✅ models/'); +} + +/** + * 打包成 ZIP + */ +function createZip(packageName) { + log(colors.blue, '📦 打包 ZIP...'); + + const zipPath = join(releaseDir, packageName); + const appName = 'impress-asr-input-win-x64'; + + execSync(`zip -rq "${zipPath}" "${appName}"`, { + cwd: releaseDir, + stdio: 'inherit' + }); + + const size = Math.round(readFileSync(zipPath).length / 1024 / 1024 * 100) / 100; + log(colors.green, `✅ 打包完成:${packageName} (${size}MB)`); +} + +/** + * 清理临时目录 + */ +function cleanup() { + log(colors.blue, '🧹 清理临时文件...'); + const tempDir = join(releaseDir, 'impress-asr-input-win-x64'); + if (existsSync(tempDir)) { + rmSync(tempDir, { recursive: true, force: true }); + } + log(colors.green, '✅ 清理完成'); +} + +/** + * 主函数 + */ +function main() { + console.log(''); + log(colors.green, '╔════════════════════════════════════════════╗'); + log(colors.green, '║ Impress ASR Input - Windows x64 打包工具 ║'); + log(colors.green, '╚════════════════════════════════════════════╝'); + console.log(''); + + try { + // 1. 创建目录 + ensureDirs(); + + // 2. 下载 Electron + downloadElectron(); + + // 3. 编译 TypeScript + compileTypeScript(); + + // 4. 解压 Electron 到临时目录 + const tempDir = join(releaseDir, 'impress-asr-input-win-x64'); + unzipElectron(tempDir); + + // 5. 复制应用文件 + copyAppFiles(tempDir); + + // 6. 打包 + const version = '0.1.0'; + const packageName = `Impress_ASR_Input-${version}-win-x64.zip`; + createZip(packageName); + + // 7. 清理 + cleanup(); + + console.log(''); + log(colors.green, '═══════════════════════════════════════'); + log(colors.green, '✅ Windows x64 版本打包完成!'); + log(colors.green, '═══════════════════════════════════════'); + log(colors.blue, `📦 输出文件:release/${packageName}`); + log(colors.blue, `💾 缓存目录:${userCacheDir}`); + console.log(''); + + } catch (error) { + console.log(''); + log(colors.red, '❌ 打包失败!'); + throw error; + } +} + +// 运行 +main();