Repository Wiki
ldx123000/Hydrogen-Music

跨平台打包与发布

本页说明 Hydrogen Music 桌面端如何由 Vite 构建前端资源,并由 electron-builder 配置 Windows、macOS 和 Linux 产物及发布目标。Android 在同一仓库有独立工程与脚本,本文只说明桌面端与其构建入口的边界。

Purpose and Scope(目的与范围)

面向需要维护桌面发行构建的开发者:涵盖根目录 npm 脚本、前端构建选项、electron-builder 的文件筛选、平台目标、资源和发布配置。Android 的签名、SDK、APK/AAB 流程请参见 Android 文档;播放器功能和运行期更新逻辑不在本页范围。根脚本虽然暴露 Android 命令,但这些命令委托给子项目,不能等同于桌面打包链路。依据:package.json、README.md。

Overview(概览)

桌面发行的可见入口为 dist 与 _dist:都先执行 vite build,再调用 node scripts/build.js,分别传入 -p never 和 -p always。package.json 的 Electron 主入口是 background.js;electron-builder 将此文件和 dist/**/*、桌面歌词页面、Electron 相关文件一起纳入产物。版本号由 package.json 提供,Vite 同时将其注入 __APP_VERSION__;发行输出目录配置为 release/${version}。依据:package.json、vite.config.js、electron-builder.config.cjs。

Architecture(架构)

Loading diagram...

Source: package.json、vite.config.js、electron-builder.config.cjs

图中的 scripts/build.js 是根脚本调用的路径;本页未读取该脚本实现,因此箭头表示配置与脚本入口的构建关系,不意味着已验证该脚本如何加载 builder 配置、选择平台或执行发布。dist 是 Vite 默认输出与打包文件模式相接的边界;实际发行文件由 builder 的 files 配置进一步筛选。依据:package.json、electron-builder.config.cjs。

构建流程与资源边界

两阶段桌面构建

根脚本明确先执行 vite build,只有成功后才经 && 继续执行打包脚本。_dist 和 dist 唯一可见的参数差别是 -p always 与 -p never;参数的解析、发布时机以及是否需要凭据,均取决于尚未核查的 scripts/build.js,不能仅凭参数名推断实际上传行为。build 则仅执行 Vite 构建,不触发后续打包脚本。依据:package.json。

json
"build": "vite build", "_dist": "vite build && node scripts/build.js -p always", "dist": "vite build && node scripts/build.js -p never"

Source: package.json

Loading diagram...

Source: package.json、vite.config.js、electron-builder.config.cjs

前端输出与压缩

Vite 配置使用 Vue 插件、相对 base: './',以 index.html 与 desktop-lyric.html 为两个 Rollup 输入。编译目标为 es2018,terser 压缩删除 console 和 debugger、剔除注释;cssCodeSplit: true,块大小警告阈值 1000。因此生产包中的诊断输出不应依赖前端 console。版本常量通过读取包版本在构建时定义,不是运行时读取。依据:vite.config.js。

javascript
1 base: './', 2 define: { __APP_VERSION__: JSON.stringify(packageJson.version) }, 3 build: { 4 target: 'es2018', // 更新到ES2018以支持async generator functions 5 rollupOptions: { 6 input: { 7 main: resolve(__dirname, 'index.html'), 8 'desktop-lyric': resolve(__dirname, 'desktop-lyric.html') 9 } 10 },

Source: vite.config.js

文件收集与体积控制

BASE_FILE_PATTERNS 明确纳入 Electron 入口、两个 HTML、dist/**/*、图标、src/electron/**/* 和两个共享设置文件;排除发行目录、SSR 构建目录、字体目录以及指定依赖里的静态/开发资产。files 再按平台条件追加 Linux 专属依赖排除规则。模块目录与文件的批量裁剪模式意在避免将测试、示例、文档、类型声明等无关内容随应用分发;onNodeModuleFile 对匹配 LICENSE、LICENCE、NOTICE、THIRD-PARTY-NOTICES 的文件返回 true。注意具体筛选结果仍需对生成产物核对。依据:electron-builder.config.cjs、electron-builder.config.cjs、electron-builder.config.cjs。

javascript
1 files: [ 2 ...BASE_FILE_PATTERNS, 3 ...(shouldExcludeLinuxOnlyDependencies() ? LINUX_ONLY_DEPENDENCY_EXCLUDES : []), 4 ], 5 onNodeModuleFile: (filePath) => { 6 const normalizedPath = filePath.replace(/\\/g, '/'); 7 return KEEP_NODE_MODULE_FILE.test(normalizedPath); 8 },

Source: electron-builder.config.cjs

ffmpeg-static 通过 asarUnpack 从 ASAR 中解包;resources/mpv 下与目标平台同名或以平台名加 - 开头的子目录会作为 extraResources 复制到 mpv/<目录名>。资源根目录不存在时该函数返回空数组,因此配置不会强制下载 mpv 资源。依据:electron-builder.config.cjs。

平台目标、发布与配置

配置位置取值或默认值(配置中明确写出的值)作用
productName / appIdHydrogen Music / com.hydrogenmusic.app应用产品名及标识
asar / compressiontrue / maximum启用 ASAR 与最大压缩
electronLanguagesen, en-US, zh_CN, zh_TW, zh-CN, zh-TW保留不同平台的 Electron 语言标识
directories.outputrelease/${version}按版本组织桌面发行文件
nsis.oneClick / allowToChangeInstallationDirectoryfalse / trueWindows NSIS 使用非一键安装并允许更改目录
mac.targetdmgmacOS 磁盘映像
win.targetnsis, portable, zipWindows 三类产物;verifyUpdateCodeSignature: false
linux.targetAppImage, deb, rpm,各 x64Linux 三种包类型及显式架构
publishgithub;owner: ldx123000;repo: Hydrogen-Music;releaseType: draft发布目标配置为 GitHub 草稿发行版,实际上传触发逻辑未核查

配置均见 electron-builder.config.cjs。Windows NSIS 使用 Hydrogen.Music.Setup.${version}.${ext},Windows 总体 artifactName 为 Hydrogen.Music.${version}.${ext};macOS 的文件名包含 ${arch},Linux 的文件名包含 ${version}。依据:electron-builder.config.cjs。

javascript
1 linux: { 2 category: 'Audio', 3 icon: './src/assets/icon/icon.png', 4 extraResources: getMpvExtraResourcesForPlatform('linux'), 5 target: [ 6 { 7 target: 'AppImage', 8 arch: ['x64'], 9 }, 10 { 11 target: 'deb', 12 arch: ['x64'], 13 }, 14 { 15 target: 'rpm', 16 arch: ['x64'], 17 }, 18 ], 19 artifactName: 'Hydrogen.Music-${version}.${ext}', 20 },

Source: electron-builder.config.cjs

javascript
1 publish: [ 2 { 3 provider: 'github', 4 owner: 'ldx123000', 5 repo: 'Hydrogen-Music', 6 releaseType: 'draft', 7 }, 8 ],

Source: electron-builder.config.cjs

Usage Examples(实际入口)

先按 README 的环境要求使用 Node.js 20.19.0+ 或 22.12.0+ 与 npm;以下是项目自身的脚本定义,可用 npm run build 仅构建前端、npm run dist 或 npm run _dist 进入后续桌面脚本。是否会产生或上传产物以执行环境及未核查脚本为准。Android 入口独立委托给子项目,不应复用桌面目标配置:package.json。

json
"android:build": "npm --prefix apps/android run android:build", "android:release": "npm --prefix apps/android run android:release", "android:bundle": "npm --prefix apps/android run android:bundle"

Source: package.json

README 明确将 Android APK/AAB 的签名与 SDK 配置交由 Android 子工程,并将桌面安装包按 Windows、macOS、Linux 三组列出;这解释了同仓库的两个发行边界。README.md、README.md。

内部配置函数与条件分支

detectRequestedTargets(argv) 接收参数数组、构造 Set,返回 { mac, win, linux } 三个布尔标志:识别 --mac/-m、--win/--windows/-w 和 --linux/-l。shouldExcludeLinuxOnlyDependencies() 无参数,从 process.argv 判断:显式 Linux 优先保留 Linux 依赖;仅显式 macOS/Windows 则排除;没有显式目标时在非 Linux 宿主排除,在 Linux 宿主保留。该函数仅用于 files 的条件扩展,而不是直接指定 builder 的 target。electron-builder.config.cjs、electron-builder.config.cjs。

javascript
1function detectRequestedTargets(argv) { 2 const args = new Set(argv); 3 return { 4 mac: args.has('--mac') || args.has('-m'), 5 win: args.has('--win') || args.has('--windows') || args.has('-w'), 6 linux: args.has('--linux') || args.has('-l'), 7 }; 8} 9 10function shouldExcludeLinuxOnlyDependencies() { 11 const requestedTargets = detectRequestedTargets(process.argv); 12 if (requestedTargets.linux) return false; 13 if (requestedTargets.mac || requestedTargets.win) return true; 14 return process.platform !== 'linux'; 15}

Source: electron-builder.config.cjs

getMpvExtraResourcesForPlatform(platform) 接收平台字符串,返回适用于 builder extraResources 的 { from, to } 数组;首先判断 resources/mpv 是否存在,再仅读取其中平台同名目录或 <平台>-... 目录。其返回值分别由 macOS darwin、Windows win32、Linux linux 配置调用。若资源缺失则空数组,无异常分支记录在该代码中。electron-builder.config.cjs、electron-builder.config.cjs。

javascript
1function getMpvExtraResourcesForPlatform(platform) { 2 if (!fs.existsSync(MPV_RESOURCE_ROOT)) return []; 3 4 const platformDirs = fs.readdirSync(MPV_RESOURCE_ROOT, { withFileTypes: true }) 5 .filter((entry) => entry.isDirectory() && (entry.name === platform || entry.name.startsWith(`${platform}-`))) 6 .map((entry) => entry.name); 7 8 return platformDirs.map((name) => ({ 9 from: path.join(MPV_RESOURCE_ROOT, name), 10 to: path.posix.join('mpv', name), 11 })); 12}

Source: electron-builder.config.cjs

边界情况、性能与维护注意

  • 目标参数交叉:同时出现 Linux 和 Windows/macOS 参数时,函数先判断 Linux,因而不会追加 Linux 依赖排除项。未指定平台时依赖宿主 process.platform;维护交叉构建时要核对传入配置模块的 process.argv。electron-builder.config.cjs。
  • 可选 mpv 内容:资源目录缺失不会阻断 getMpvExtraResourcesForPlatform,但也意味着不会有该目录下的 mpv 文件被复制;构建前应检查所需平台目录是否存在。electron-builder.config.cjs。
  • 包体积与运行资源:最大压缩、依赖裁剪、语言限制和 ffmpeg-static 解包同时生效。扩展 files 排除模式时须验证应用真正需要的运行期资产未被删掉;不能把所有 Node 模块文件一概当作开发文件。electron-builder.config.cjs、electron-builder.config.cjs。
  • 发布与可观测性:Vite 生产编译会移除 console;builder 的 publish 声明 GitHub 草稿目标,但没有从已读文件验证 CI、认证、版本标签或签名流程。不要将配置项解释为已经成功发布。vite.config.js、electron-builder.config.cjs。
  • 扩展方式:新增平台原生 mpv 文件时沿用 resources/mpv/<平台> 或 <平台>-<变体> 命名才能被当前过滤器发现;新增安装格式需修改对应平台 target,新增必需文件需调整 BASE_FILE_PATTERNS。这些是由当前配置推导出的维护位置,不代表在此页已验证所有平台实际构建测试。electron-builder.config.cjs、electron-builder.config.cjs、electron-builder.config.cjs。