diff --git a/package.json b/package.json index d0b206b..40d61db 100644 --- a/package.json +++ b/package.json @@ -53,5 +53,6 @@ "typescript": "^5.5.0", "vite": "^5.4.0", "vitest": "^2.0.0" - } + }, + "packageManager": "pnpm@11.8.0+sha512.c1f5e7c4cb241c8f174b743851d82f42b802324afc8b0f116b96adb15aa06664948dde36960a3ba1079ba5b4b29dd0140135b94b5b5f5263592249d68e555f26" } diff --git a/scripts/build.cmd b/scripts/build.cmd index 35b6411..acb3f45 100644 --- a/scripts/build.cmd +++ b/scripts/build.cmd @@ -2,7 +2,7 @@ chcp 65001 >nul setlocal enabledelayedexpansion -:: 先切到仓库根目录,避免相对路径问题 +:: 先切到仓库根目录 cd /d "%~dp0.." echo ======================================== @@ -10,15 +10,10 @@ echo MD++ 打包构建脚本 echo ======================================== echo. -:: 读取 package.json 中的版本号 -for /f "tokens=2 delims=:," %%a in ('findstr "\"version\"" package.json') do ( - set PKG_VER=%%~a - set PKG_VER=!PKG_VER:"=! - set PKG_VER=!PKG_VER: =! -) +:: 读取当前 package.json 版本号 +for /f %%v in ('node -p "require('./package.json').version"') do set PKG_VER=%%v -echo 当前 package.json 版本: !PKG_VER! -echo Tauri/Cargo 版本会在构建前自动跟随 package.json +echo 当前版本: !PKG_VER! echo. :: 询问版本号 @@ -26,8 +21,10 @@ set /p INPUT_VER="请输入要发布的版本号 (直接回车使用 !PKG_VER!): if "!INPUT_VER!"=="" set INPUT_VER=!PKG_VER! echo. +echo ======================================== echo 即将构建 MD++ v!INPUT_VER! echo 目标: msi + nsis 安装包 +echo ======================================== echo. set /p CONFIRM="确认构建? (y/N): " @@ -37,34 +34,113 @@ if /i not "!CONFIRM!"=="y" ( ) echo. -echo [1/3] 同步版本号到 package.json 和 Cargo.toml... +echo [1/4] 同步版本号... -:: 更新 package.json 版本号 -powershell -NoProfile -Command ^ - "$json = Get-Content 'package.json' -Raw | ConvertFrom-Json; $json.version = '!INPUT_VER!'; $json | ConvertTo-Json -Depth 10 | Set-Content 'package.json' -Encoding UTF8" +:: 用 node 更新版本号(避免 PowerShell ConvertTo-Json 破坏编码) +if not "!INPUT_VER!"=="!PKG_VER!" ( + echo 更新 package.json: !PKG_VER! -^> !INPUT_VER! + node -e "const fs=require('fs'),p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version='!INPUT_VER!';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\r\n')" +) +:: 同步 tauri.conf.json 版本号 +node -e "const fs=require('fs'),t=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8'));t.version='!INPUT_VER!';fs.writeFileSync('src-tauri/tauri.conf.json',JSON.stringify(t,null,2)+'\r\n')" +echo 已同步 tauri.conf.json 版本号: !INPUT_VER! + +:: 同步 Cargo.toml 版本号 call pnpm sync-version if %ERRORLEVEL% neq 0 ( - echo. - echo 版本同步失败! 错误码: %ERRORLEVEL% + echo 版本同步失败! 错误码: %ERRORLEVEL% + exit /b %ERRORLEVEL% +) +echo 已同步 Cargo.toml 版本号: !INPUT_VER! + +echo [2/4] 清理旧的构建产物... +if exist "src-tauri\target\release\bundle\msi" rmdir /s /q "src-tauri\target\release\bundle\msi" 2>nul +if exist "src-tauri\target\release\wix" rmdir /s /q "src-tauri\target\release\wix" 2>nul + +echo [3/4] 编译 Rust 后端 + 前端... +call pnpm build +if %ERRORLEVEL% neq 0 ( + echo 前端构建失败! + exit /b %ERRORLEVEL% +) +echo 前端构建成功。 + +echo 编译 Rust 后端 (release)... +cargo build --release --manifest-path src-tauri\Cargo.toml +if %ERRORLEVEL% neq 0 ( + echo Rust 编译失败! exit /b %ERRORLEVEL% ) +echo Rust 编译成功。 -echo [2/3] 清理旧的构建产物... -if exist "src-tauri\target\release\bundle" rmdir /s /q "src-tauri\target\release\bundle" +echo [4/4] 打包 MSI 安装包... + +:: 找到 WiX 工具路径 +set WIX_DIR=%LOCALAPPDATA%\tauri\WixTools314 +if not exist "%WIX_DIR%\light.exe" ( + echo 错误: 找不到 WiX 工具! 请安装 Tauri CLI 后重试。 + echo 路径: %WIX_DIR%\light.exe + exit /b 1 +) -echo [3/3] 开始构建... -call pnpm tauri build +:: 生成 WiX 文件 (tauri bundler 的 candle/light 前置步骤) +:: 先用 tauri build --bundles 仅编译不打包 +:: 然后手动运行 candle + light +set BUNDLE_DIR=src-tauri\target\release +set WIX_SRC=%BUNDLE_DIR%\wix\x64 + +echo 正在准备 WiX 源文件... +:: 调用 Tauri 生成 WiX 配置(candle + light 文件) +call pnpm tauri build --bundles msi 2>&1 | findstr /v "failed light.exe" + +:: 如果上面失败(WiX 步骤失败),我们手动构建 MSI +if not exist "%WIX_SRC%\main.wixobj" ( + echo Tauri 未生成 .wixobj,尝试直接通过 cargo-bundle... + + :: 使用 tauri-bundler 或手动 candle + if exist "%WIX_SRC%\main.wxs" ( + echo 运行 candle.exe... + "%WIX_DIR%\candle.exe" -arch x64 -dBuildVersion="!INPUT_VER!" -out "%WIX_SRC%\" "%WIX_SRC%\main.wxs" + if %ERRORLEVEL% neq 0 ( + echo candle 失败! + exit /b %ERRORLEVEL% + ) + ) else ( + echo 错误: 找不到 main.wxs! + exit /b 1 + ) +) + +:: 修复 locale.wxl 的 codepage(中文需要 936 而非 1252) +set WXL=%WIX_SRC%\locale.wxl +if exist "%WXL%" ( + echo 修复 WiX locale codepage 为 936 (支持中文)... + powershell -NoProfile -Command ^ + "$xml=[xml](Get-Content '%WXL%');$xml.WixLocalization.Codepage='936';$xml.WixLocalization.SelectSingleNode('//*[local-name()=""String"" and @Id=""TauriCodepage""]').InnerText='936';$xml.Save('%WXL%')" +) + +:: 运行 light.exe 生成 MSI +echo 运行 light.exe 生成 MSI... +set OUT_MSI=%BUNDLE_DIR%\bundle\msi\md++_!INPUT_VER!_x64_en-US.msi +if not exist "%BUNDLE_DIR%\bundle\msi" mkdir "%BUNDLE_DIR%\bundle\msi" + +"%WIX_DIR%\light.exe" ^ + -ext "%WIX_DIR%\WixUIExtension.dll" ^ + -loc "%WXL%" ^ + -out "%OUT_MSI%" ^ + "%WIX_SRC%\main.wixobj" if %ERRORLEVEL% neq 0 ( echo. - echo 构建失败! 错误码: %ERRORLEVEL% + echo light.exe 打包失败! 错误码: %ERRORLEVEL% exit /b %ERRORLEVEL% ) echo. echo ======================================== echo 构建完成! 版本: v!INPUT_VER! -echo 产物位于: src-tauri\target\release\bundle\ +echo MSI: %OUT_MSI% echo ======================================== + endlocal diff --git a/scripts/sync-version.mjs b/scripts/sync-version.mjs index 2bcdc33..6933450 100644 --- a/scripts/sync-version.mjs +++ b/scripts/sync-version.mjs @@ -2,6 +2,8 @@ import { readFile, writeFile } from 'node:fs/promises'; const packageJsonPath = new URL('../package.json', import.meta.url); const cargoTomlPath = new URL('../src-tauri/Cargo.toml', import.meta.url); +const tauriConfPath = new URL('../src-tauri/tauri.conf.json', import.meta.url); +const aboutModalPath = new URL('../src/components/AboutModal/AboutModal.tsx', import.meta.url); const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8')); const version = packageJson.version; @@ -10,6 +12,7 @@ if (typeof version !== 'string' || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9 throw new Error(`package.json version must be semver-compatible, got: ${version}`); } +// Sync Cargo.toml const cargoToml = await readFile(cargoTomlPath, 'utf8'); const nextCargoToml = cargoToml.replace( /^version = ".*"$/m, @@ -22,3 +25,30 @@ if (nextCargoToml === cargoToml) { await writeFile(cargoTomlPath, nextCargoToml); console.log(`Synced src-tauri/Cargo.toml to package.json version ${version}`); } + +// Sync tauri.conf.json +const tauriConfRaw = await readFile(tauriConfPath, 'utf8'); +const tauriConf = JSON.parse(tauriConfRaw); +tauriConf.version = version; +const nextTauriConf = JSON.stringify(tauriConf, null, 2) + '\n'; + +if (nextTauriConf === tauriConfRaw) { + console.log(`tauri.conf.json already matches package.json version ${version}`); +} else { + await writeFile(tauriConfPath, nextTauriConf); + console.log(`Synced src-tauri/tauri.conf.json to package.json version ${version}`); +} + +// Sync AboutModal APP_VERSION +const aboutModalRaw = await readFile(aboutModalPath, 'utf8'); +const nextAboutModal = aboutModalRaw.replace( + /^const APP_VERSION = '.*';$/m, + `const APP_VERSION = '${version}';`, +); + +if (nextAboutModal === aboutModalRaw) { + console.log(`AboutModal already matches package.json version ${version}`); +} else { + await writeFile(aboutModalPath, nextAboutModal); + console.log(`Synced AboutModal APP_VERSION to package.json version ${version}`); +} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 24d200b..23e8972 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -77,7 +77,7 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "app" -version = "0.2.0" +version = "0.3.0" dependencies = [ "base64 0.22.1", "dirs 5.0.1", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9af530d..fb56e7b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "0.2.0" +version = "0.3.0" description = "md++ — 轻量便捷的 Markdown 阅读器 (Windows / macOS)" authors = ["leonan"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index b7b14f6..f24adc4 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "md++", - "version": "0.2.0", + "version": "0.3.0", "identifier": "com.easymd.app", "build": { "frontendDist": "../dist", @@ -31,7 +31,10 @@ }, "bundle": { "active": true, - "targets": ["dmg", "msi"], + "targets": [ + "dmg", + "msi" + ], "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -45,7 +48,10 @@ }, "fileAssociations": [ { - "ext": ["md", "markdown"], + "ext": [ + "md", + "markdown" + ], "mimeType": "text/markdown", "description": "Markdown 文件" } diff --git a/src/components/AboutModal/AboutModal.tsx b/src/components/AboutModal/AboutModal.tsx index 6f91db7..29c53a3 100644 --- a/src/components/AboutModal/AboutModal.tsx +++ b/src/components/AboutModal/AboutModal.tsx @@ -6,7 +6,7 @@ import { useTabsStore } from '../../stores/tabsStore'; import appIcon from '../../assets/app-icon.svg'; import featuresMd from '../../../features.md?raw'; -const APP_VERSION = '0.1.0'; +const APP_VERSION = '0.3.0'; const GITHUB_URL = 'https://github.com/Gitleonan/easy-md'; interface AboutModalProps {