import { readFileSync, writeFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { dirname, join, resolve } from 'node:path' const scriptPath = fileURLToPath(import.meta.url) const defaultRoot = resolve(dirname(scriptPath), '..') function readJson(path) { return JSON.parse(readFileSync(path, 'utf8')) } function writeJson(path, value) { writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`) } function packageVersion(content, packageName) { const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const match = content.match(new RegExp(`\\[\\[package\\]\\]\\nname = "${escapedName}"\\nversion = "([^"]+)"`)) if (!match) throw new Error(`Cargo.lock 中缺少 package ${packageName}`) return match[1] } function replaceRootCargoVersion(content, version) { const pattern = /(\[package\][\s\S]*?\nversion = ")[^"]+("\n)/ if (!pattern.test(content)) throw new Error('Cargo.toml 中缺少根 package version') return content.replace(pattern, `$1${version}$2`) } function replaceLockedPackageVersion(content, packageName, version) { const escapedName = packageName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const pattern = new RegExp(`(\\[\\[package\\]\\]\\nname = "${escapedName}"\\nversion = ")[^"]+("\\n)`) if (!pattern.test(content)) throw new Error(`Cargo.lock 中无法更新 package ${packageName}`) return content.replace(pattern, `$1${version}$2`) } export function inspectVersions(root = defaultRoot) { const packageJson = readJson(join(root, 'package.json')) const packageLock = readJson(join(root, 'package-lock.json')) const tauriConfig = readJson(join(root, 'src-tauri/tauri.conf.json')) const cargoToml = readFileSync(join(root, 'src-tauri/Cargo.toml'), 'utf8') const cargoLock = readFileSync(join(root, 'src-tauri/Cargo.lock'), 'utf8') const cargoManifestVersion = cargoToml.match(/\[package\][\s\S]*?\nversion = "([^"]+)"/)?.[1] if (!cargoManifestVersion) throw new Error('Cargo.toml 中缺少根 package version') return { packageJson: packageJson.version, packageLock: packageLock.version, packageLockRoot: packageLock.packages?.['']?.version, cargoToml: cargoManifestVersion, cargoLock: packageVersion(cargoLock, 'opsxshell'), tauriConfig: tauriConfig.version, } } export function assertVersion(root, expected) { const versions = inspectVersions(root) const mismatches = Object.entries(versions).filter(([, version]) => version !== expected) if (mismatches.length > 0) { throw new Error( `版本不一致,期望 ${expected}:${mismatches.map(([file, version]) => `${file}=${version ?? 'missing'}`).join(', ')}` ) } return versions } export function setVersion(root, version) { const packageJsonPath = join(root, 'package.json') const packageLockPath = join(root, 'package-lock.json') const tauriConfigPath = join(root, 'src-tauri/tauri.conf.json') const cargoTomlPath = join(root, 'src-tauri/Cargo.toml') const cargoLockPath = join(root, 'src-tauri/Cargo.lock') const packageJson = readJson(packageJsonPath) packageJson.version = version writeJson(packageJsonPath, packageJson) const packageLock = readJson(packageLockPath) packageLock.version = version if (packageLock.packages?.['']) packageLock.packages[''].version = version writeJson(packageLockPath, packageLock) const tauriConfig = readJson(tauriConfigPath) tauriConfig.version = version writeJson(tauriConfigPath, tauriConfig) const cargoToml = readFileSync(cargoTomlPath, 'utf8') writeFileSync(cargoTomlPath, replaceRootCargoVersion(cargoToml, version)) const cargoLock = readFileSync(cargoLockPath, 'utf8') writeFileSync(cargoLockPath, replaceLockedPackageVersion(cargoLock, 'opsxshell', version)) return assertVersion(root, version) } if (process.argv[1] && resolve(process.argv[1]) === scriptPath) { const args = process.argv.slice(2) const checking = args[0] === '--check' const version = checking ? args[1] : args[0] if (!version) { console.error('Usage: node scripts/set-version.mjs [--check] ') process.exit(2) } try { const result = checking ? assertVersion(defaultRoot, version) : setVersion(defaultRoot, version) console.log(`[version] ${checking ? 'verified' : 'updated'} ${version}: ${Object.keys(result).join(', ')}`) } catch (error) { console.error(`[version] ${error instanceof Error ? error.message : String(error)}`) process.exit(1) } }