-
Notifications
You must be signed in to change notification settings - Fork 130
Expand file tree
/
Copy pathdevice.ts
More file actions
84 lines (71 loc) · 2.48 KB
/
device.ts
File metadata and controls
84 lines (71 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { AppError } from './errors.ts';
import { isInteractive } from './interactive.ts';
import { isCancel, select } from '@clack/prompts';
type Platform = 'ios' | 'android';
type DeviceKind = 'simulator' | 'emulator' | 'device';
export type DeviceInfo = {
platform: Platform;
id: string;
name: string;
kind: DeviceKind;
booted?: boolean;
};
type DeviceSelector = {
platform?: Platform;
deviceName?: string;
udid?: string;
serial?: string;
};
export async function selectDevice(
devices: DeviceInfo[],
selector: DeviceSelector,
): Promise<DeviceInfo> {
let candidates = devices;
const normalize = (value: string): string =>
value.toLowerCase().replace(/_/g, ' ').replace(/\s+/g, ' ').trim();
if (selector.platform) {
candidates = candidates.filter((d) => d.platform === selector.platform);
}
if (selector.udid) {
const match = candidates.find((d) => d.id === selector.udid && d.platform === 'ios');
if (!match) throw new AppError('DEVICE_NOT_FOUND', `No iOS device with UDID ${selector.udid}`);
return match;
}
if (selector.serial) {
const match = candidates.find((d) => d.id === selector.serial && d.platform === 'android');
if (!match)
throw new AppError('DEVICE_NOT_FOUND', `No Android device with serial ${selector.serial}`);
return match;
}
if (selector.deviceName) {
const target = normalize(selector.deviceName);
const match = candidates.find((d) => normalize(d.name) === target);
if (!match) {
throw new AppError('DEVICE_NOT_FOUND', `No device named ${selector.deviceName}`);
}
return match;
}
if (candidates.length === 1) return candidates[0];
if (candidates.length === 0) {
throw new AppError('DEVICE_NOT_FOUND', 'No devices found', { selector });
}
const booted = candidates.filter((d) => d.booted);
if (booted.length === 1) return booted[0];
if (isInteractive()) {
const choice = await select({
message: 'Multiple devices available. Choose a device to continue:',
options: (booted.length > 0 ? booted : candidates).map((device) => ({
label: `${device.name} (${device.platform}${device.kind ? `, ${device.kind}` : ''}${device.booted ? ', booted' : ''})`,
value: device.id,
})),
});
if (isCancel(choice)) {
throw new AppError('INVALID_ARGS', 'Device selection cancelled');
}
if (choice) {
const match = candidates.find((d) => d.id === choice);
if (match) return match;
}
}
return booted[0] ?? candidates[0];
}