Skip to content

Commit e5637bf

Browse files
authored
fix: remove interactive device prompt dependency (#232)
1 parent ee83df8 commit e5637bf

7 files changed

Lines changed: 36 additions & 87 deletions

File tree

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,6 @@
6565
"trailingComma": "all",
6666
"printWidth": 100
6767
},
68-
"dependencies": {
69-
"@clack/prompts": "^1.0.0"
70-
},
7168
"devDependencies": {
7269
"@types/node": "^22.0.0",
7370
"@rslib/core": "0.19.4",

pnpm-lock.yaml

Lines changed: 0 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/__tests__/dispatch-resolve.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import test from 'node:test';
22
import assert from 'node:assert/strict';
33
import { resolveIosDevice } from '../dispatch-resolve.ts';
4-
import { selectDevice, type DeviceInfo } from '../../utils/device.ts';
4+
import { resolveDevice, type DeviceInfo } from '../../utils/device.ts';
55
import { AppError } from '../../utils/errors.ts';
66

77
const physical: DeviceInfo = {
@@ -36,7 +36,7 @@ function makeDeps(fallbackSimulator: DeviceInfo | null = null) {
3636
let findBootableCalled = false;
3737
return {
3838
deps: {
39-
selectDevice,
39+
resolveDevice,
4040
findBootableSimulator: async () => {
4141
findBootableCalled = true;
4242
return fallbackSimulator;
@@ -104,7 +104,7 @@ test('resolveIosDevice throws DEVICE_NOT_FOUND when empty list and no fallback s
104104
assert.equal(err.code, 'DEVICE_NOT_FOUND');
105105
});
106106

107-
test('resolveIosDevice rethrows DEVICE_NOT_FOUND from selectDevice when explicit selector used', async () => {
107+
test('resolveIosDevice rethrows DEVICE_NOT_FOUND from resolveDevice when explicit selector used', async () => {
108108
const { deps } = makeDeps(simulator);
109109
const err = await resolveIosDevice(
110110
[],

src/core/dispatch-resolve.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AppError } from '../utils/errors.ts';
2-
import { normalizePlatformSelector, selectDevice, type DeviceInfo } from '../utils/device.ts';
2+
import { normalizePlatformSelector, resolveDevice, type DeviceInfo } from '../utils/device.ts';
33
import { listAndroidDevices } from '../platforms/android/devices.ts';
44
import { ensureAdb } from '../platforms/android/index.ts';
55
import { findBootableIosSimulator, listIosDevices } from '../platforms/ios/devices.ts';
@@ -22,7 +22,7 @@ type IosDeviceSelector = {
2222
};
2323

2424
type ResolveIosDeviceDeps = {
25-
selectDevice: typeof selectDevice;
25+
resolveDevice: typeof resolveDevice;
2626
findBootableSimulator: typeof findBootableIosSimulator;
2727
};
2828

@@ -43,9 +43,9 @@ export async function resolveIosDevice(
4343

4444
let selected: DeviceInfo | undefined;
4545
try {
46-
selected = await deps.selectDevice(devices, selector, context);
46+
selected = await deps.resolveDevice(devices, selector, context);
4747
} catch (err) {
48-
// When selectDevice throws DEVICE_NOT_FOUND and no explicit device
48+
// When resolveDevice throws DEVICE_NOT_FOUND and no explicit device
4949
// selector was used, attempt the simulator fallback before giving up.
5050
if (hasExplicitSelector || !(err instanceof AppError) || err.code !== 'DEVICE_NOT_FOUND') {
5151
throw err;
@@ -92,7 +92,7 @@ export async function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<De
9292
if (selector.platform === 'android') {
9393
await ensureAdb();
9494
const devices = await listAndroidDevices({ serialAllowlist: androidSerialAllowlist });
95-
return await selectDevice(devices, selector);
95+
return await resolveDevice(devices, selector);
9696
}
9797

9898
if (selector.platform === 'ios') {
@@ -101,7 +101,7 @@ export async function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<De
101101
devices,
102102
selector as IosDeviceSelector,
103103
{ simulatorSetPath: iosSimulatorSetPath },
104-
{ selectDevice, findBootableSimulator: findBootableIosSimulator },
104+
{ resolveDevice, findBootableSimulator: findBootableIosSimulator },
105105
);
106106
}
107107

@@ -116,7 +116,7 @@ export async function resolveTargetDevice(flags: ResolveDeviceFlags): Promise<De
116116
} catch {
117117
// ignore
118118
}
119-
return await selectDevice(devices, selector, { simulatorSetPath: iosSimulatorSetPath });
119+
return await resolveDevice(devices, selector, { simulatorSetPath: iosSimulatorSetPath });
120120
},
121121
{
122122
platform: normalizedPlatform,

src/utils/__tests__/device.test.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import test from 'node:test';
22
import assert from 'node:assert/strict';
3-
import { normalizePlatformSelector, resolveApplePlatformName, selectDevice } from '../device.ts';
3+
import { normalizePlatformSelector, resolveApplePlatformName, resolveDevice } from '../device.ts';
44
import type { DeviceInfo } from '../device.ts';
55
import { AppError } from '../errors.ts';
66

@@ -17,9 +17,9 @@ test('resolveApplePlatformName resolves tv targets to tvOS', () => {
1717
assert.equal(resolveApplePlatformName(undefined), 'iOS');
1818
});
1919

20-
test('selectDevice throws DEVICE_NOT_FOUND with scoped set guidance when simulatorSetPath is set and no devices found', async () => {
20+
test('resolveDevice throws DEVICE_NOT_FOUND with scoped set guidance when simulatorSetPath is set and no devices found', async () => {
2121
const setPath = '/path/to/sessions/abc/Simulators';
22-
const err = await selectDevice([], { platform: 'ios' }, { simulatorSetPath: setPath }).catch((e) => e);
22+
const err = await resolveDevice([], { platform: 'ios' }, { simulatorSetPath: setPath }).catch((e) => e);
2323
assert.ok(err instanceof AppError);
2424
assert.equal(err.code, 'DEVICE_NOT_FOUND');
2525
assert.match(err.message, /scoped simulator set/);
@@ -29,39 +29,39 @@ test('selectDevice throws DEVICE_NOT_FOUND with scoped set guidance when simulat
2929
assert.match(err.details.hint as string, /create/);
3030
});
3131

32-
test('selectDevice throws generic DEVICE_NOT_FOUND when no simulatorSetPath and no devices found', async () => {
33-
const err = await selectDevice([], { platform: 'ios' }).catch((e) => e);
32+
test('resolveDevice throws generic DEVICE_NOT_FOUND when no simulatorSetPath and no devices found', async () => {
33+
const err = await resolveDevice([], { platform: 'ios' }).catch((e) => e);
3434
assert.ok(err instanceof AppError);
3535
assert.equal(err.code, 'DEVICE_NOT_FOUND');
3636
assert.equal(err.message, 'No devices found');
3737
assert.equal(err.details?.simulatorSetPath, undefined);
3838
});
3939

40-
test('selectDevice does not apply scoped set guidance for non-iOS platform with simulatorSetPath', async () => {
40+
test('resolveDevice does not apply scoped set guidance for non-iOS platform with simulatorSetPath', async () => {
4141
const setPath = '/path/to/sessions/abc/Simulators';
42-
const err = await selectDevice([], { platform: 'android' }, { simulatorSetPath: setPath }).catch((e) => e);
42+
const err = await resolveDevice([], { platform: 'android' }, { simulatorSetPath: setPath }).catch((e) => e);
4343
assert.ok(err instanceof AppError);
4444
assert.equal(err.code, 'DEVICE_NOT_FOUND');
4545
assert.equal(err.message, 'No devices found');
4646
assert.equal(err.details?.simulatorSetPath, undefined);
4747
});
4848

49-
test('selectDevice applies scoped set guidance when no platform selector specified and simulatorSetPath is set', async () => {
49+
test('resolveDevice applies scoped set guidance when no platform selector specified and simulatorSetPath is set', async () => {
5050
const setPath = '/path/to/sessions/abc/Simulators';
51-
const err = await selectDevice([], {}, { simulatorSetPath: setPath }).catch((e) => e);
51+
const err = await resolveDevice([], {}, { simulatorSetPath: setPath }).catch((e) => e);
5252
assert.ok(err instanceof AppError);
5353
assert.equal(err.code, 'DEVICE_NOT_FOUND');
5454
assert.match(err.message, /scoped simulator set/);
5555
assert.equal(err.details?.simulatorSetPath, setPath);
5656
});
5757

58-
test('selectDevice returns a device when candidates are available', async () => {
58+
test('resolveDevice returns a device when candidates are available', async () => {
5959
const device: DeviceInfo = { platform: 'ios', id: 'abc123', name: 'iPhone 16', kind: 'simulator', booted: true };
60-
const result = await selectDevice([device], { platform: 'ios' });
60+
const result = await resolveDevice([device], { platform: 'ios' });
6161
assert.equal(result.id, 'abc123');
6262
});
6363

64-
test('selectDevice prefers simulator over physical device when no explicit device selector', async () => {
64+
test('resolveDevice prefers simulator over physical device when no explicit device selector', async () => {
6565
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
6666
const simulator: DeviceInfo = {
6767
platform: 'ios',
@@ -70,43 +70,42 @@ test('selectDevice prefers simulator over physical device when no explicit devic
7070
kind: 'simulator',
7171
booted: false,
7272
};
73-
const result = await selectDevice([physical, simulator], { platform: 'ios' });
73+
const result = await resolveDevice([physical, simulator], { platform: 'ios' });
7474
assert.equal(result.id, 'sim-1');
7575
assert.equal(result.kind, 'simulator');
7676
});
7777

78-
test('selectDevice prefers booted simulator over physical device', async () => {
78+
test('resolveDevice prefers booted simulator over physical device', async () => {
7979
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
8080
const sim1: DeviceInfo = { platform: 'ios', id: 'sim-1', name: 'iPhone 16', kind: 'simulator', booted: true };
8181
const sim2: DeviceInfo = { platform: 'ios', id: 'sim-2', name: 'iPhone 15', kind: 'simulator', booted: false };
82-
const result = await selectDevice([physical, sim1, sim2], { platform: 'ios' });
82+
const result = await resolveDevice([physical, sim1, sim2], { platform: 'ios' });
8383
assert.equal(result.id, 'sim-1');
8484
});
8585

86-
test('selectDevice falls back to physical device when no simulators exist', async () => {
86+
test('resolveDevice falls back to physical device when no simulators exist', async () => {
8787
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
88-
const result = await selectDevice([physical], { platform: 'ios' });
88+
const result = await resolveDevice([physical], { platform: 'ios' });
8989
assert.equal(result.id, 'phys-1');
9090
});
9191

92-
test('selectDevice returns physical device when explicitly selected by deviceName', async () => {
92+
test('resolveDevice returns physical device when explicitly selected by deviceName', async () => {
9393
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
9494
const simulator: DeviceInfo = { platform: 'ios', id: 'sim-1', name: 'iPhone 16', kind: 'simulator', booted: true };
95-
const result = await selectDevice([physical, simulator], { platform: 'ios', deviceName: 'My iPhone' });
95+
const result = await resolveDevice([physical, simulator], { platform: 'ios', deviceName: 'My iPhone' });
9696
assert.equal(result.id, 'phys-1');
9797
});
9898

99-
test('selectDevice returns physical device when explicitly selected by udid', async () => {
99+
test('resolveDevice returns physical device when explicitly selected by udid', async () => {
100100
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
101101
const simulator: DeviceInfo = { platform: 'ios', id: 'sim-1', name: 'iPhone 16', kind: 'simulator', booted: true };
102-
const result = await selectDevice([physical, simulator], { platform: 'ios', udid: 'phys-1' });
102+
const result = await resolveDevice([physical, simulator], { platform: 'ios', udid: 'phys-1' });
103103
assert.equal(result.id, 'phys-1');
104104
});
105105

106-
test('selectDevice returns physical device when it is the only candidate (no simulators in list)', async () => {
106+
test('resolveDevice returns physical device when it is the only candidate (no simulators in list)', async () => {
107107
const physical: DeviceInfo = { platform: 'ios', id: 'phys-1', name: 'My iPhone', kind: 'device', booted: true };
108-
const result = await selectDevice([physical], { platform: 'ios' });
108+
const result = await resolveDevice([physical], { platform: 'ios' });
109109
assert.equal(result.id, 'phys-1');
110110
assert.equal(result.kind, 'device');
111111
});
112-

src/utils/device.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { AppError } from './errors.ts';
2-
import { isInteractive } from './interactive.ts';
3-
import { isCancel, select } from '@clack/prompts';
42

53
export type Platform = 'ios' | 'android';
64
export type PlatformSelector = Platform | 'apple';
@@ -40,7 +38,7 @@ export function resolveApplePlatformName(target: DeviceTarget | undefined): 'iOS
4038
return target === 'tv' ? 'tvOS' : 'iOS';
4139
}
4240

43-
export async function selectDevice(
41+
export async function resolveDevice(
4442
devices: DeviceInfo[],
4543
selector: DeviceSelector,
4644
context: DeviceSelectionContext = {},
@@ -102,22 +100,7 @@ export async function selectDevice(
102100
const booted = candidates.filter((d) => d.booted);
103101
if (booted.length === 1) return booted[0];
104102

105-
if (isInteractive()) {
106-
const choice = await select({
107-
message: 'Multiple devices available. Choose a device to continue:',
108-
options: (booted.length > 0 ? booted : candidates).map((device) => ({
109-
label: `${device.name} (${device.platform}${device.kind ? `, ${device.kind}` : ''}${device.booted ? ', booted' : ''})`,
110-
value: device.id,
111-
})),
112-
});
113-
if (isCancel(choice)) {
114-
throw new AppError('INVALID_ARGS', 'Device selection cancelled');
115-
}
116-
if (choice) {
117-
const match = candidates.find((d) => d.id === choice);
118-
if (match) return match;
119-
}
120-
}
121-
103+
// When multiple candidates remain equally valid, preserve discovery order from
104+
// the underlying platform tools rather than introducing another tie-breaker here.
122105
return booted[0] ?? candidates[0];
123106
}

src/utils/interactive.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)