Files
wo_scripts/emuResolveIos.js
T
lukandAtomCode 75ae4e9e22 update: 同步 emu* 脚本至 einz 最新版
- emuResolveIos.js:裸跑时 Booted 优先、否则取系统版本最高的 iPhone;
  EMU 环境变量最高优先;短名表扩至 ip17 系列;修复 rootDir 指向错误
  导致项目 config 永远读不到的问题
- emuBootIos.sh:Xcode 27+ 不再随附 Simulator.app,检测后跳过并提示
  DeviceHub

来源:~/einz/scripts/(拷贝后 einz 侧持续演进,此处为落后快照)

Co-Authored-By: AtomCode (glm5.3-flash) <[email protected]>
2026-10-16 22:02:16 +08:00

155 lines
4.9 KiB
JavaScript
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.
#!/usr/bin/env node
// 把「短名@系统版本」解析成本机真实存在的模拟器 UDID(供 emuBootIos.sh 调用)。
//
// node scripts/emuResolveIos.js [email protected] # 短名(见下面别名表)
// node scripts/emuResolveIos.js "iPhone [email protected]" # 写机型全名也行
// node scripts/emuResolveIos.js "iPhone 16" # 不给版本 → 取系统版本最高的那台
// node scripts/emuResolveIos.js FF429526-… # 本来就是 UDID → 原样返回
//
// 为什么不用 UDID:UDID 是每台 Mac 各自生成的,换台机器就失效。
// 机型名 + 系统版本是 Xcode 自带的,任何机器上都能查到,所以跨机器可移植。
//
// 加短名:改下面的 builtinAlias,或在 package.json 的 config 里加一条
// ("ipad": "iPad Pro 13-inch (M4)"),config 里的优先。
const { execSync } = require('child_process')
const path = require('path')
const builtinAlias = {
ipxr: 'iPhone XR',
ipxs: 'iPhone Xs',
ip11: 'iPhone 11',
ip11pro: 'iPhone 11 Pro',
ip11pm: 'iPhone 11 Pro Max',
ip12: 'iPhone 12',
ip12mini: 'iPhone 12 mini',
ip12pro: 'iPhone 12 Pro',
ip12pm: 'iPhone 12 Pro Max',
ip13: 'iPhone 13',
ip13mini: 'iPhone 13 mini',
ip13pro: 'iPhone 13 Pro',
ip13pm: 'iPhone 13 Pro Max',
ip14: 'iPhone 14',
ip14plus: 'iPhone 14 Plus',
ip14pro: 'iPhone 14 Pro',
ip14pm: 'iPhone 14 Pro Max',
ip15: 'iPhone 15',
ip15plus: 'iPhone 15 Plus',
ip15pro: 'iPhone 15 Pro',
ip15pm: 'iPhone 15 Pro Max',
ip16: 'iPhone 16',
ip16plus: 'iPhone 16 Plus',
ip16pro: 'iPhone 16 Pro',
ip16pm: 'iPhone 16 Pro Max',
ip17: 'iPhone 17',
ip17pro: 'iPhone 17 Pro',
ip17pm: 'iPhone 17 Pro Max',
ip17air: 'iPhone Air'
}
const rootDir = path.resolve(__dirname, '..')
let pkgConfig = {}
try {
pkgConfig = require(path.join(rootDir, 'package.json')).config || {}
} catch (error) {
pkgConfig = {}
}
const input =
(process.argv[2] || '').trim() ||
String(process.env.EMU || pkgConfig.defaultIosEmu || '').trim()
if (
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(input)
) {
console.log(input)
process.exit(0)
}
const at = input.lastIndexOf('@')
const left = (at === -1 ? input : input.slice(0, at)).trim()
const version =
at === -1
? ''
: input
.slice(at + 1)
.trim()
.replace(/^iOS\s*/i, '')
const deviceName = pkgConfig[left] || builtinAlias[left.toLowerCase()] || left
const json = JSON.parse(
execSync('xcrun simctl list devices available -j').toString()
)
// 辅助函数放在**所有使用之前**:下面 `if (!input)` 分支在模块顶层就跑
// sort,比较器立刻调用 versionOf——const 声明若在使用之后会触发 TDZ
// (ReferenceError: Cannot access 'versionOf' before initialization,
// 2026-10-12 老板实测)。
const runtimeKeyFor = want => `iOS-${want.replace(/\./g, '-')}`
const versionOf = runtimeKey => {
const matched = runtimeKey.match(/iOS-(\d+)(?:-(\d+))?/)
return matched ? [Number(matched[1]), Number(matched[2] || 0)] : [0, 0]
}
// 空规格(裸 npm run app-ios-boot):有 Booted 直接用它;否则挑可用的
// iPhone 里系统版本最高的那台——任何机器上都能裸跑,不再钉死某机型。
if (!input) {
const all = []
for (const [runtimeKey, devices] of Object.entries(json.devices)) {
if (!/iOS-/.test(runtimeKey)) continue
for (const d of devices) all.push({ ...d, runtimeKey })
}
const booted = all.find(d => d.state === 'Booted')
if (booted) {
console.log(booted.udid)
process.exit(0)
}
const iPhones = all.filter(d => /iPhone/i.test(d.name))
if (iPhones.length === 0) {
console.error('❌ 本机没有可用的 iPhone 模拟器')
console.error(' 看有哪些:xcrun simctl list devices available')
process.exit(1)
}
iPhones.sort((a, b) => {
const [aMajor, aMinor] = versionOf(a.runtimeKey)
const [bMajor, bMinor] = versionOf(b.runtimeKey)
return bMajor - aMajor || bMinor - aMinor
})
console.log(iPhones[0].udid)
process.exit(0)
}
const matched = []
for (const [runtimeKey, devices] of Object.entries(json.devices)) {
if (!/iOS-/.test(runtimeKey)) {
continue
}
if (version && !runtimeKey.includes(runtimeKeyFor(version))) {
continue
}
for (const device of devices) {
if (device.name.toLowerCase() === deviceName.toLowerCase()) {
matched.push({ ...device, runtimeKey })
}
}
}
if (matched.length === 0) {
console.error(
`❌ 本机没有匹配的模拟器:${input}(机型「${deviceName}」${
version ? ` + iOS ${version}` : ''
})`
)
console.error(' 看有哪些:xcrun simctl list devices available')
process.exit(1)
}
// 同名多台(比如 18.1 和 26.3 各一台)→ 取系统版本最高的那台
matched.sort((a, b) => {
const [aMajor, aMinor] = versionOf(a.runtimeKey)
const [bMajor, bMinor] = versionOf(b.runtimeKey)
return bMajor - aMajor || bMinor - aMinor
})
console.log(matched[0].udid)