rn-iso 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/android.js +31 -16
- package/src/commands/ios.js +6 -5
- package/src/commands/release.js +3 -3
- package/src/config.js +12 -3
- package/src/sim/android.js +23 -15
- package/src/sim/ios.js +11 -1
package/package.json
CHANGED
package/src/commands/android.js
CHANGED
|
@@ -7,6 +7,7 @@ import { allocatePort, isMetroRunning } from '../ports.js';
|
|
|
7
7
|
import {
|
|
8
8
|
selectAndroidDevice,
|
|
9
9
|
sortAndroidCandidates,
|
|
10
|
+
enumerateAndroidCandidates,
|
|
10
11
|
bootAndroidEmulator,
|
|
11
12
|
waitForBoot,
|
|
12
13
|
adbReverse,
|
|
@@ -77,16 +78,16 @@ export default function androidCommand(program) {
|
|
|
77
78
|
if (selection.kind === 'reuse') {
|
|
78
79
|
({ avdName, consolePort, isRunning } = selection);
|
|
79
80
|
if (isRunning) {
|
|
80
|
-
console.log(chalk.dim(`Reusing running
|
|
81
|
+
console.log(chalk.dim(`Reusing running ${avdName} (emulator-${consolePort})`));
|
|
81
82
|
} else {
|
|
82
|
-
console.log(chalk.dim(`Booting assigned
|
|
83
|
+
console.log(chalk.dim(`Booting assigned ${avdName} (emulator-${consolePort})...`));
|
|
83
84
|
}
|
|
84
85
|
} else if (selection.kind === 'allocate') {
|
|
85
86
|
const picked = (selection.candidates.length === 1 || auto)
|
|
86
87
|
? { c: selection.candidates[0], prevClaim: null }
|
|
87
88
|
: await pickAvd({
|
|
88
89
|
candidates: selection.candidates,
|
|
89
|
-
|
|
90
|
+
androidClaimsByAvd: claimed.androidClaimsByAvd,
|
|
90
91
|
});
|
|
91
92
|
await releasePriorClaim(picked.prevClaim);
|
|
92
93
|
({ avdName, isRunning, consolePort } = picked.c);
|
|
@@ -94,8 +95,8 @@ export default function androidCommand(program) {
|
|
|
94
95
|
consolePort = nextConsolePort(claimedPorts);
|
|
95
96
|
}
|
|
96
97
|
console.log(isRunning
|
|
97
|
-
? chalk.green(`Picked ${avdName} (
|
|
98
|
-
: chalk.dim(`Booting ${avdName}
|
|
98
|
+
? chalk.green(`Picked ${avdName} (emulator-${consolePort}, running)`)
|
|
99
|
+
: chalk.dim(`Booting ${avdName} (emulator-${consolePort})...`));
|
|
99
100
|
} else if (selection.kind === 'allClaimed') {
|
|
100
101
|
if (auto) {
|
|
101
102
|
console.error(chalk.red('All Android AVDs are claimed by other rn-iso projects.'));
|
|
@@ -104,7 +105,7 @@ export default function androidCommand(program) {
|
|
|
104
105
|
}
|
|
105
106
|
const picked = await pickAvd({
|
|
106
107
|
candidates: selection.candidates,
|
|
107
|
-
|
|
108
|
+
androidClaimsByAvd: claimed.androidClaimsByAvd,
|
|
108
109
|
allClaimed: true,
|
|
109
110
|
});
|
|
110
111
|
await releasePriorClaim(picked.prevClaim);
|
|
@@ -115,8 +116,8 @@ export default function androidCommand(program) {
|
|
|
115
116
|
consolePort = nextConsolePort(fresh);
|
|
116
117
|
}
|
|
117
118
|
console.log(isRunning
|
|
118
|
-
? chalk.green(`Took over ${avdName} (
|
|
119
|
-
: chalk.dim(`Booting ${avdName}
|
|
119
|
+
? chalk.green(`Took over ${avdName} (emulator-${consolePort}, running)`)
|
|
120
|
+
: chalk.dim(`Booting ${avdName} (emulator-${consolePort})...`));
|
|
120
121
|
} else {
|
|
121
122
|
console.error(chalk.red(
|
|
122
123
|
'No AVDs available. Create one via Android Studio (Tools -> Device Manager).'
|
|
@@ -161,20 +162,34 @@ export default function androidCommand(program) {
|
|
|
161
162
|
});
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
console.log(chalk.green(`\nAndroid ready on ${serial}, Metro port ${proj.metroPort}`));
|
|
165
|
+
console.log(chalk.green(`\nAndroid ready on ${avdName} (${serial}), Metro port ${proj.metroPort}`));
|
|
165
166
|
});
|
|
166
167
|
}
|
|
167
168
|
|
|
168
|
-
async function pickAvd({ candidates,
|
|
169
|
-
|
|
169
|
+
async function pickAvd({ candidates, androidClaimsByAvd = {}, allClaimed = false }) {
|
|
170
|
+
// Show every AVD on disk (parallel to the iOS picker), so the user can
|
|
171
|
+
// see what's claimed and optionally take it over. `candidates` is the
|
|
172
|
+
// unclaimed set passed in by selectAndroidDevice; AVDs outside it are
|
|
173
|
+
// claimed and will require a confirm prompt on selection.
|
|
174
|
+
const allAvds = enumerateAndroidCandidates();
|
|
175
|
+
const candidateAvds = new Set(candidates.map(c => c.avdName));
|
|
176
|
+
const sorted = sortAndroidCandidates(allAvds);
|
|
177
|
+
|
|
170
178
|
const nameWidth = Math.max(...sorted.map(c => c.avdName.length), 18);
|
|
171
179
|
const choices = sorted.map(c => {
|
|
172
|
-
const claim =
|
|
173
|
-
const
|
|
174
|
-
const runTag = c.isRunning ? chalk.green(`
|
|
180
|
+
const claim = androidClaimsByAvd[c.avdName];
|
|
181
|
+
const isCandidate = candidateAvds.has(c.avdName);
|
|
182
|
+
const runTag = c.isRunning ? chalk.green(` [emulator-${c.consolePort}, running]`) : '';
|
|
183
|
+
if (claim || !isCandidate) {
|
|
184
|
+
const tag = claim ? chalk.yellow(` [claimed by ${claim.label}]`) : '';
|
|
185
|
+
return {
|
|
186
|
+
title: chalk.yellow(`${c.avdName.padEnd(nameWidth)}${tag}${runTag}`),
|
|
187
|
+
value: { c, claim: claim || null },
|
|
188
|
+
};
|
|
189
|
+
}
|
|
175
190
|
return {
|
|
176
|
-
title: `${c.avdName.padEnd(nameWidth)}${runTag}
|
|
177
|
-
value: { c, claim:
|
|
191
|
+
title: `${c.avdName.padEnd(nameWidth)}${runTag}`,
|
|
192
|
+
value: { c, claim: null },
|
|
178
193
|
};
|
|
179
194
|
});
|
|
180
195
|
const message = allClaimed
|
package/src/commands/ios.js
CHANGED
|
@@ -4,7 +4,7 @@ import prompts from 'prompts';
|
|
|
4
4
|
import { findProjectRoot, detectIsExpo, detectBundleId, detectAndroidPackage } from '../project.js';
|
|
5
5
|
import { getProject, upsertProject, setMetro, setDevice, clearDevice, allClaimedDevices, recordSimUsage, getSimUsage } from '../config.js';
|
|
6
6
|
import { allocatePort, isMetroRunning } from '../ports.js';
|
|
7
|
-
import { selectIosDevice, bootIosSim, listIosRuntimes, createIosSim, parseRuntimeVersion, listAllIosSims, sortSims } from '../sim/ios.js';
|
|
7
|
+
import { selectIosDevice, bootIosSim, listIosRuntimes, createIosSim, parseRuntimeVersion, listAllIosSims, sortSims, formatIosLabel } from '../sim/ios.js';
|
|
8
8
|
import { buildIosCommand, detectPackageManager } from '../runner.js';
|
|
9
9
|
import { getExecutor } from '../exec.js';
|
|
10
10
|
import { resolveLabel } from '../labels.js';
|
|
@@ -70,11 +70,12 @@ export default function iosCommand(program) {
|
|
|
70
70
|
let udid;
|
|
71
71
|
if (selection.kind === 'reuse') {
|
|
72
72
|
udid = selection.udid;
|
|
73
|
+
const label = `${selection.name} (${udid})`;
|
|
73
74
|
if (selection.state !== 'Booted') {
|
|
74
|
-
console.log(chalk.dim(`Booting assigned sim ${
|
|
75
|
+
console.log(chalk.dim(`Booting assigned sim ${label}...`));
|
|
75
76
|
bootIosSim(udid);
|
|
76
77
|
} else {
|
|
77
|
-
console.log(chalk.dim(`Reusing assigned sim ${
|
|
78
|
+
console.log(chalk.dim(`Reusing assigned sim ${label} (already booted)`));
|
|
78
79
|
}
|
|
79
80
|
} else if (selection.kind === 'allocate') {
|
|
80
81
|
const picked = (selection.candidates.length === 1 || auto)
|
|
@@ -99,7 +100,7 @@ export default function iosCommand(program) {
|
|
|
99
100
|
if (auto) {
|
|
100
101
|
if (opts.deviceType) {
|
|
101
102
|
udid = createNewSim({ deviceType: opts.deviceType, runtimeVersion: opts.runtime });
|
|
102
|
-
console.log(chalk.green(`Created and booted new sim ${udid}`));
|
|
103
|
+
console.log(chalk.green(`Created and booted new sim ${formatIosLabel(udid)}`));
|
|
103
104
|
} else {
|
|
104
105
|
console.error(chalk.red('All iOS simulators are claimed by other rn-iso projects.'));
|
|
105
106
|
console.error(chalk.dim('Re-run without --auto to confirm taking one over, or pass --device-type to create a new sim.'));
|
|
@@ -164,7 +165,7 @@ export default function iosCommand(program) {
|
|
|
164
165
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
console.log(chalk.green(`\nOK: iOS ready on sim ${udid}, Metro port ${proj.metroPort}`));
|
|
168
|
+
console.log(chalk.green(`\nOK: iOS ready on sim ${formatIosLabel(udid)}, Metro port ${proj.metroPort}`));
|
|
168
169
|
});
|
|
169
170
|
}
|
|
170
171
|
|
package/src/commands/release.js
CHANGED
|
@@ -4,7 +4,7 @@ import prompts from 'prompts';
|
|
|
4
4
|
import { resolveRegisteredProject } from '../project.js';
|
|
5
5
|
import { getProject, clearDevice, findProjectByMetroPort } from '../config.js';
|
|
6
6
|
import { findPidListeningOnPort } from '../metro.js';
|
|
7
|
-
import { shutdownIosSim } from '../sim/ios.js';
|
|
7
|
+
import { shutdownIosSim, formatIosLabel } from '../sim/ios.js';
|
|
8
8
|
import { shutdownAndroidEmulator } from '../sim/android.js';
|
|
9
9
|
|
|
10
10
|
export default function releaseCommand(program) {
|
|
@@ -45,10 +45,10 @@ export default function releaseCommand(program) {
|
|
|
45
45
|
if (opts.shutdown) {
|
|
46
46
|
if (p === 'ios') {
|
|
47
47
|
shutdownIosSim(entry.deviceUdid);
|
|
48
|
-
console.log(chalk.green(`Shut down iOS sim ${entry.deviceUdid}`));
|
|
48
|
+
console.log(chalk.green(`Shut down iOS sim ${formatIosLabel(entry.deviceUdid)}`));
|
|
49
49
|
} else {
|
|
50
50
|
shutdownAndroidEmulator(`emulator-${entry.consolePort}`);
|
|
51
|
-
console.log(chalk.green(`Shut down emulator-${entry.consolePort}`));
|
|
51
|
+
console.log(chalk.green(`Shut down ${entry.avdName} (emulator-${entry.consolePort})`));
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
clearDevice(found, p);
|
package/src/config.js
CHANGED
|
@@ -111,10 +111,12 @@ export function allClaimedDevices() {
|
|
|
111
111
|
androidAvds: [],
|
|
112
112
|
androidConsolePorts: [],
|
|
113
113
|
// iosClaims: udid -> { label, path }. androidClaims: consolePort ->
|
|
114
|
-
// { label, path, avdName }.
|
|
115
|
-
//
|
|
114
|
+
// { label, path, avdName }. androidClaimsByAvd: avdName -> { label,
|
|
115
|
+
// path, consolePort }. `path` is the absolute project path so take-
|
|
116
|
+
// over flows can call clearDevice on the owning project.
|
|
116
117
|
iosClaims: {},
|
|
117
118
|
androidClaims: {},
|
|
119
|
+
androidClaimsByAvd: {},
|
|
118
120
|
};
|
|
119
121
|
if (!cfg) return result;
|
|
120
122
|
for (const [path, proj] of Object.entries(cfg.projects || {})) {
|
|
@@ -125,7 +127,14 @@ export function allClaimedDevices() {
|
|
|
125
127
|
result.iosClaims[ios.deviceUdid] = { label, path };
|
|
126
128
|
}
|
|
127
129
|
const android = proj.platforms?.android;
|
|
128
|
-
if (android?.avdName)
|
|
130
|
+
if (android?.avdName) {
|
|
131
|
+
result.androidAvds.push(android.avdName);
|
|
132
|
+
result.androidClaimsByAvd[android.avdName] = {
|
|
133
|
+
label,
|
|
134
|
+
path,
|
|
135
|
+
consolePort: android.consolePort,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
129
138
|
if (typeof android?.consolePort === 'number') {
|
|
130
139
|
result.androidConsolePorts.push(android.consolePort);
|
|
131
140
|
result.androidClaims[android.consolePort] = {
|
package/src/sim/android.js
CHANGED
|
@@ -35,37 +35,45 @@ export function nextConsolePort(claimedPorts) {
|
|
|
35
35
|
return max + 2; // emulator console ports are even
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
// Build the full candidate list: every AVD on disk, paired with whether it
|
|
39
|
+
// currently has a running emulator and (if so) on which console port.
|
|
40
|
+
// Emulators that fail to respond to `adb emu avd name` are dropped from
|
|
41
|
+
// the running map. Returns [] when no AVDs are installed.
|
|
42
|
+
export function enumerateAndroidCandidates() {
|
|
39
43
|
const avds = listAvds();
|
|
40
|
-
if (avds.length === 0) return
|
|
44
|
+
if (avds.length === 0) return [];
|
|
41
45
|
|
|
42
46
|
const adbDevices = listAdbDevices();
|
|
43
|
-
// Resolve which AVD each running emulator is. Emulators that don't respond
|
|
44
|
-
// to `adb emu avd name` are dropped from the running map.
|
|
45
47
|
const runningByAvd = {};
|
|
46
48
|
for (const e of adbDevices.emulators) {
|
|
47
49
|
const avdName = getAvdNameForSerial(e.serial);
|
|
48
50
|
if (avdName) runningByAvd[avdName] = e.consolePort;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
return avds.map(avdName => ({
|
|
52
54
|
avdName,
|
|
53
55
|
isRunning: avdName in runningByAvd,
|
|
54
56
|
consolePort: runningByAvd[avdName] ?? null,
|
|
55
|
-
});
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
export function selectAndroidDevice({ existingAvd, existingConsolePort, claimedAvds, claimedConsolePorts }) {
|
|
61
|
+
const all = enumerateAndroidCandidates();
|
|
62
|
+
if (all.length === 0) return { kind: 'noAvd' };
|
|
63
|
+
|
|
64
|
+
if (existingAvd) {
|
|
65
|
+
const found = all.find(c => c.avdName === existingAvd);
|
|
66
|
+
if (found) {
|
|
67
|
+
return {
|
|
68
|
+
kind: 'reuse',
|
|
69
|
+
avdName: existingAvd,
|
|
70
|
+
consolePort: found.consolePort ?? existingConsolePort ?? nextConsolePort(claimedConsolePorts),
|
|
71
|
+
isRunning: found.isRunning,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
const claimedAvdSet = new Set(claimedAvds);
|
|
68
|
-
const all = avds.map(buildCandidate);
|
|
69
77
|
const unclaimed = all.filter(c => !claimedAvdSet.has(c.avdName));
|
|
70
78
|
|
|
71
79
|
if (unclaimed.length === 0) {
|
package/src/sim/ios.js
CHANGED
|
@@ -31,6 +31,16 @@ export function listBootedIosSims() {
|
|
|
31
31
|
return listAllIosSims().filter(s => s.state === 'Booted');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// "iPhone 16 Pro (ABC-123-...)" if simctl knows about the UDID; the bare
|
|
35
|
+
// UDID otherwise (deleted sim, or simctl unavailable).
|
|
36
|
+
export function formatIosLabel(udid) {
|
|
37
|
+
try {
|
|
38
|
+
const sim = listAllIosSims().find(s => s.udid === udid);
|
|
39
|
+
if (sim) return `${sim.name} (${udid})`;
|
|
40
|
+
} catch { /* simctl not available */ }
|
|
41
|
+
return udid;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
export function deviceFamilyRank(name) {
|
|
35
45
|
if (/^iPhone/i.test(name)) return 0;
|
|
36
46
|
if (/^iPad/i.test(name)) return 1;
|
|
@@ -62,7 +72,7 @@ export function selectIosDevice({ existingUdid, claimedUdids, usage = {} }) {
|
|
|
62
72
|
if (existingUdid) {
|
|
63
73
|
const found = sims.find(s => s.udid === existingUdid);
|
|
64
74
|
if (found) {
|
|
65
|
-
return { kind: 'reuse', udid: found.udid, state: found.state };
|
|
75
|
+
return { kind: 'reuse', udid: found.udid, name: found.name, state: found.state };
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
|