Files
HuLa/scripts/check-dependencies.js
Dawn be2d71694c refactor(deps): ♻️ remove SQLCipher and keyring dependencies
Remove bundled-sqlcipher-vendored-openssl feature from libsqlite3-sys
Switch to bundled SQLite without encryption
Remove keyring dependency for desktop platforms
Remove Perl dependency check and installation logic
2025-12-24 23:33:19 +08:00

276 lines
8.4 KiB
JavaScript
Vendored
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import chalk from 'chalk'
import { execSync } from 'child_process'
import { existsSync } from 'fs'
import { platform } from 'os'
// 环境安装指南
const INSTALL_GUIDES = {
'Node.js': 'https://nodejs.org/zh-cn/download/',
pnpm: 'https://pnpm.io/zh/installation',
Rust: 'https://www.rust-lang.org/tools/install',
'WebView2 Runtime': 'https://developer.microsoft.com/microsoft-edge/webview2/'
}
// 更新指南
const UPDATE_GUIDES = {
Rust: '请运行 `rustup update` 命令更新 Rust 版本'
}
// Windows 特定的检查路径(只检查默认安装路径)
const WINDOWS_PATHS = {
'WebView2 Runtime': [
'C:\\Program Files (x86)\\Microsoft\\EdgeWebView\\Application',
'C:\\Program Files\\Microsoft\\EdgeWebView\\Application'
]
}
// 错误信息映射
const ERROR_MESSAGES = {
ENOENT: '命令未找到',
EPERM: '权限不足',
EACCES: '访问被拒绝'
}
const checks = [
{
name: 'Node.js',
command: 'node --version',
versionExtractor: (output) => output.replace('v', ''),
minVersion: '^20.19.0 || >=22.12.0',
isRequired: true
},
{
name: 'pnpm',
command: 'pnpm --version',
versionExtractor: (output) => output.trim(),
minVersion: '10.0.0',
isRequired: true
},
{
name: 'Rust',
command: 'rustc --version',
versionExtractor: (output) => output.split(' ')[1],
minVersion: '1.88.0',
isRequired: true
}
]
/**
* 检查 WebView2 是否安装
* @returns {boolean}
*/
const checkWebView2 = () => {
try {
// 检查注册表
const regQuery =
'reg query "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients\\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" /v pv'
execSync(regQuery, { stdio: 'ignore' })
return true
} catch {
// 如果注册表查询失败,检查文件路径
return WINDOWS_PATHS['WebView2 Runtime'].some((path) => existsSync(path))
}
}
// Windows 特定的检查
const windowsChecks = [
{
name: 'WebView2 Runtime',
checkInstalled: checkWebView2,
isRequired: true
}
]
/**
* 获取友好的错误提示
* @param {Error} error 错误对象
* @returns {string} 错误提示
*/
const getFriendlyErrorMessage = (error) => {
const code = error.code || ''
return ERROR_MESSAGES[code] || error.message || '未知错误'
}
/**
* 比较版本号
* @param {string} version1 当前版本
* @param {string} version2 所需版本
* @returns {number} 1: version1 大, -1: version2 大, 0: 相等
*/
const compareVersions = (version1, version2) => {
const v1 = version1.replace(/[^0-9.]/g, '').split('.')
const v2 = version2.replace(/[^0-9.]/g, '').split('.')
for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
const num1 = parseInt(v1[i] || '0', 10)
const num2 = parseInt(v2[i] || '0', 10)
if (num1 > num2) return 1
if (num1 < num2) return -1
}
return 0
}
/**
* 检查版本是否满足 ^ 范围(主版本相同,次版本和补丁版本可以更高)
* @param {string} version 当前版本
* @param {string} requiredVersion 要求的版本
* @returns {boolean}
*/
const satisfiesCaretRange = (version, requiredVersion) => {
const [vMajor, vMinor, vPatch] = version.split('.').map(Number)
const [rMajor, rMinor, rPatch] = requiredVersion.split('.').map(Number)
// 主版本必须相同
if (vMajor !== rMajor) return false
// 次版本和补丁版本需要 >= 要求的版本
if (vMinor > rMinor) return true
if (vMinor < rMinor) return false
return vPatch >= rPatch
}
/**
* 检查版本是否满足版本范围要求(支持 ||、^、>= 语法)
* @param {string} version 当前版本
* @param {string} range 版本范围(如 '^20.19.0 || >=22.12.0'
* @returns {boolean}
*/
const satisfiesVersionRange = (version, range) => {
// 处理 || 分隔的多个条件
const conditions = range.split('||').map((s) => s.trim())
// 只要满足任一条件即可
return conditions.some((condition) => {
if (condition.startsWith('^')) {
// 处理 ^ 语法:主版本相同,次版本和补丁版本可以更高
const requiredVersion = condition.slice(1).trim()
return satisfiesCaretRange(version, requiredVersion)
} else if (condition.startsWith('>=')) {
// 处理 >= 语法
const requiredVersion = condition.slice(2).trim()
return compareVersions(version, requiredVersion) >= 0
}
// 默认使用 >= 比较
return compareVersions(version, condition) >= 0
})
}
function checkDependency(check) {
try {
const output = execSync(check.command).toString().trim()
const version = check.versionExtractor(output)
// 判断版本是否有效
let isVersionValid
if (check.minVersion.includes('||') || check.minVersion.startsWith('^')) {
// 如果包含 || 或 ^,使用新的版本范围判断逻辑
isVersionValid = satisfiesVersionRange(version, check.minVersion)
} else {
// 否则使用简单的版本比较
isVersionValid = compareVersions(version, check.minVersion) >= 0
}
if (isVersionValid) {
console.log(chalk.green(`${check.name} 版本 ${output} 已安装\n`))
return true
} else {
console.log(chalk.yellow(`⚠️ ${check.name} 版本过低`))
console.log(chalk.yellow(` 当前版本: ${output}`))
console.log(chalk.yellow(` 需要版本: ${check.minVersion}`))
// 对 Rust 进行特殊处理,提示使用 rustup update
if (check.name === 'Rust') {
console.log(chalk.yellow(` ${UPDATE_GUIDES[check.name]}`))
}
console.log(chalk.gray(` 👉 升级指南: ${INSTALL_GUIDES[check.name]}`))
return false
}
} catch (error) {
const errorMessage = getFriendlyErrorMessage(error)
console.log(chalk.red(`${check.name} 未安装`))
console.log(chalk.red(` 原因: ${errorMessage}`))
console.log(chalk.gray(` 👉 安装指南: ${INSTALL_GUIDES[check.name]}`))
return false
}
}
/**
* 检查 Windows 特定的依赖
* @param {Object} check 检查项
* @returns {Promise<boolean>} 是否通过检查
*/
async function checkWindowsDependency(check) {
try {
const isInstalled = check.checkInstalled()
if (isInstalled) {
const desc = check.description ? ` (${check.description})` : ''
console.log(chalk.green(`${check.name} 已安装${desc}`))
return true
} else {
const desc = check.description ? ` (${check.description})` : ''
console.log(chalk.yellow(`⚠️ ${check.name} 未安装${desc}`))
// 如果有自动安装器,尝试自动安装
if (check.installer) {
console.log(chalk.blue(` 正在尝试自动安装 ${check.name}...`))
const installSuccess = await check.installer()
if (installSuccess) {
// 安装后再次检查
if (check.checkInstalled()) {
console.log(chalk.green(`${check.name} 安装成功并已配置`))
return true
}
// 即使检查失败,可能需要重启终端
console.log(chalk.yellow(` ⚠️ ${check.name} 已安装,但可能需要重启终端才能生效`))
return true
}
}
if (check.isRequired) {
console.log(chalk.red(`${check.name} 是必需的依赖`))
console.log(chalk.gray(` 👉 安装指南: ${INSTALL_GUIDES[check.name]}`))
return false
} else {
console.log(chalk.yellow(` 跳过可选依赖 ${check.name}`))
return true
}
}
} catch (error) {
const errorMessage = getFriendlyErrorMessage(error)
console.log(chalk.red(`${check.name} 检查失败`))
console.log(chalk.red(` 原因: ${errorMessage}`))
return !check.isRequired
}
}
async function main() {
const isWindows = platform() === 'win32'
// 执行基本检查
const results = checks.map(checkDependency)
// 在 Windows 上执行额外检查
if (isWindows) {
console.log(chalk.blue(`\n[HuLa ${new Date().toLocaleTimeString()}] 正在检查 Windows 开发环境...\n`))
for (const check of windowsChecks) {
const result = await checkWindowsDependency(check)
results.push(result)
}
}
if (results.every(Boolean)) {
console.log(chalk.green('\n✅ 所有环境检查通过!'))
process.exit(0)
} else {
console.log(chalk.red('\n❌ 环境依赖检查失败,请按照上述提示安装或更新依赖。'))
process.exit(1)
}
}
main().catch((error) => {
console.error(chalk.red('检查过程中发生错误:'))
console.error(chalk.yellow(error.stack || error))
process.exit(1)
})