rn-iso 0.6.0 → 0.6.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/skill/SKILL.md +4 -3
- package/src/commands/android.js +8 -4
- package/src/commands/ios.js +8 -4
- package/src/metro.js +18 -3
- package/src/runner.js +15 -11
- package/src/sim/ios.js +9 -3
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -109,9 +109,10 @@ npx rn-iso stop agent-1
|
|
|
109
109
|
|
|
110
110
|
When the iOS picker fires, sims are sorted by:
|
|
111
111
|
1. Family (iPhone before iPad before others)
|
|
112
|
-
2. State (booted before shutdown within family)
|
|
113
|
-
3.
|
|
114
|
-
4.
|
|
112
|
+
2. State (booted before shutdown within family, so an already-running sim is reused rather than booting another)
|
|
113
|
+
3. Runtime version (newest installed iOS runtime first, so `--auto`/agent runs prefer the latest runtime over older ones within the same state)
|
|
114
|
+
4. Usage count (most-used floats up; tracked per UDID across all projects)
|
|
115
|
+
5. Name (alphabetical, stable tiebreak)
|
|
115
116
|
|
|
116
117
|
When the Android picker fires, candidates include both AVDs on disk and physical devices currently visible to `adb`. They are sorted by running state (running emulators and connected physical devices first), then physical above AVDs within the same running group, then alphabetically. Physical devices show with a `[physical]` tag. Once selected, a physical device is claimed by serial just like an AVD; `release` clears the claim but never shuts the device down.
|
|
117
118
|
|
package/src/commands/android.js
CHANGED
|
@@ -60,10 +60,11 @@ export default function androidCommand(program) {
|
|
|
60
60
|
}
|
|
61
61
|
// With --managed-metro, rn-iso owns Metro: detached so it survives the
|
|
62
62
|
// invoking shell (agents run builds from finite shells), output to the
|
|
63
|
-
// per-project log file
|
|
64
|
-
// --no-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
63
|
+
// per-project log file. The build CLI is then kept from starting its own:
|
|
64
|
+
// bare RN gets --no-packager; expo gets --port and reuses the Metro
|
|
65
|
+
// already listening on that port. ensureMetro waits for /status so the
|
|
66
|
+
// port is bound before the build's reuse check runs. Without the flag,
|
|
67
|
+
// the build CLI owns Metro as usual (interactive bundler UX for humans).
|
|
67
68
|
if (opts.managedMetro) {
|
|
68
69
|
const metro = await ensureMetro({ projectPath: root, isExpo, port: proj.metroPort });
|
|
69
70
|
if (metro.alreadyRunning) {
|
|
@@ -72,6 +73,9 @@ export default function androidCommand(program) {
|
|
|
72
73
|
setMetro(root, proj.metroPort, metro.pid);
|
|
73
74
|
console.log(chalk.dim(`Metro started detached (pid ${metro.pid}, port ${proj.metroPort})`));
|
|
74
75
|
console.log(chalk.dim(`Metro log: ${logFileFor(root)}`));
|
|
76
|
+
if (!metro.ready) {
|
|
77
|
+
console.log(chalk.yellow(`Warning: Metro on port ${proj.metroPort} did not report ready; the build may start its own.`));
|
|
78
|
+
}
|
|
75
79
|
}
|
|
76
80
|
} else {
|
|
77
81
|
const metroAlreadyUp = await isMetroRunning(proj.metroPort);
|
package/src/commands/ios.js
CHANGED
|
@@ -56,10 +56,11 @@ export default function iosCommand(program) {
|
|
|
56
56
|
}
|
|
57
57
|
// With --managed-metro, rn-iso owns Metro: detached so it survives the
|
|
58
58
|
// invoking shell (agents run builds from finite shells), output to the
|
|
59
|
-
// per-project log file
|
|
60
|
-
// --no-
|
|
61
|
-
//
|
|
62
|
-
//
|
|
59
|
+
// per-project log file. The build CLI is then kept from starting its own:
|
|
60
|
+
// bare RN gets --no-packager; expo gets --port and reuses the Metro
|
|
61
|
+
// already listening on that port. ensureMetro waits for /status so the
|
|
62
|
+
// port is bound before the build's reuse check runs. Without the flag,
|
|
63
|
+
// the build CLI owns Metro as usual (interactive bundler UX for humans).
|
|
63
64
|
if (opts.managedMetro) {
|
|
64
65
|
const metro = await ensureMetro({ projectPath: root, isExpo, port: proj.metroPort });
|
|
65
66
|
if (metro.alreadyRunning) {
|
|
@@ -68,6 +69,9 @@ export default function iosCommand(program) {
|
|
|
68
69
|
setMetro(root, proj.metroPort, metro.pid);
|
|
69
70
|
console.log(chalk.dim(`Metro started detached (pid ${metro.pid}, port ${proj.metroPort})`));
|
|
70
71
|
console.log(chalk.dim(`Metro log: ${logFileFor(root)}`));
|
|
72
|
+
if (!metro.ready) {
|
|
73
|
+
console.log(chalk.yellow(`Warning: Metro on port ${proj.metroPort} did not report ready; the build may start its own.`));
|
|
74
|
+
}
|
|
71
75
|
}
|
|
72
76
|
} else {
|
|
73
77
|
const metroAlreadyUp = await isMetroRunning(proj.metroPort);
|
package/src/metro.js
CHANGED
|
@@ -25,8 +25,8 @@ export function buildMetroSpawnArgs({ isExpo, port, extras = [] }) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export async function ensureMetro({ projectPath, isExpo, port, extras = [], detach = true }) {
|
|
29
|
-
if (await isMetroRunning(port)) return { alreadyRunning: true, pid: null };
|
|
28
|
+
export async function ensureMetro({ projectPath, isExpo, port, extras = [], detach = true, readyTimeoutMs = 30000 }) {
|
|
29
|
+
if (await isMetroRunning(port)) return { alreadyRunning: true, pid: null, ready: true };
|
|
30
30
|
|
|
31
31
|
const log = logFileFor(projectPath);
|
|
32
32
|
const fd = openSync(log, 'a');
|
|
@@ -40,7 +40,22 @@ export async function ensureMetro({ projectPath, isExpo, port, extras = [], deta
|
|
|
40
40
|
env: { ...process.env, RCT_METRO_PORT: String(port) },
|
|
41
41
|
});
|
|
42
42
|
if (detach) child.unref();
|
|
43
|
-
|
|
43
|
+
|
|
44
|
+
// Wait until the dev server answers /status. The build CLI's "a Metro is
|
|
45
|
+
// already on this port, reuse it" detection only fires once the port is
|
|
46
|
+
// bound, so returning before that races the build into spawning a second
|
|
47
|
+
// Metro. readyTimeoutMs <= 0 skips the wait.
|
|
48
|
+
const ready = readyTimeoutMs > 0 ? await waitForMetroReady(port, readyTimeoutMs) : false;
|
|
49
|
+
return { alreadyRunning: false, pid: child.pid, ready };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function waitForMetroReady(port, timeoutMs = 30000, intervalMs = 500) {
|
|
53
|
+
const deadline = Date.now() + timeoutMs;
|
|
54
|
+
while (Date.now() < deadline) {
|
|
55
|
+
if (await isMetroRunning(port)) return true;
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
export function killMetroByPid(pid) {
|
package/src/runner.js
CHANGED
|
@@ -76,11 +76,17 @@ export function detectScriptCli(scriptBody) {
|
|
|
76
76
|
return 'unknown';
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
//
|
|
80
|
-
// Metro (detached, log file, pid tracked)
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
// Flags that stop the build CLI from spawning its own packager when rn-iso owns
|
|
80
|
+
// Metro (detached, log file, pid tracked). Bare RN takes --no-packager. Expo
|
|
81
|
+
// gets NO flag: `expo run` rejects --port together with --no-bundler, and
|
|
82
|
+
// --no-bundler also pins the dev server to 8081 with no override. Instead we
|
|
83
|
+
// pass --port <managed port> and rely on `expo run` detecting the Metro already
|
|
84
|
+
// listening on that port and reusing it (reuseExistingPort) rather than
|
|
85
|
+
// spawning its own. Callers must have the managed Metro listening before the
|
|
86
|
+
// build runs (ensureMetro waits for /status).
|
|
87
|
+
function noPackagerFlags(cli, noPackager) {
|
|
88
|
+
if (!noPackager) return [];
|
|
89
|
+
return cli === 'expo' ? [] : ['--no-packager'];
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
// iOS run command. Prefers the project's `ios` script if present (the most
|
|
@@ -99,15 +105,14 @@ export function buildIosCommand({ projectRoot, packageManager, scriptName, isExp
|
|
|
99
105
|
return buildScriptCommand(packageManager, scriptName, [
|
|
100
106
|
deviceFlag,
|
|
101
107
|
`--port ${port}`,
|
|
102
|
-
...(
|
|
108
|
+
...noPackagerFlags(cli, noPackager),
|
|
103
109
|
...tail,
|
|
104
110
|
]);
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
const tailStr = tail.length ? ' ' + tail.join(' ') : '';
|
|
108
114
|
if (isExpo) {
|
|
109
|
-
|
|
110
|
-
return `npx expo run:ios --device ${udid} --port ${port}${skip}${tailStr}`;
|
|
115
|
+
return `npx expo run:ios --device ${udid} --port ${port}${tailStr}`;
|
|
111
116
|
}
|
|
112
117
|
const skip = noPackager ? ' --no-packager' : '';
|
|
113
118
|
return `npx react-native run-ios --udid ${udid} --port ${port}${skip}${tailStr}`;
|
|
@@ -128,15 +133,14 @@ export function buildAndroidCommand({ projectRoot, packageManager, scriptName, i
|
|
|
128
133
|
return buildScriptCommand(packageManager, scriptName, [
|
|
129
134
|
deviceFlag,
|
|
130
135
|
`--port ${port}`,
|
|
131
|
-
...(
|
|
136
|
+
...noPackagerFlags(cli, noPackager),
|
|
132
137
|
...tail,
|
|
133
138
|
]);
|
|
134
139
|
}
|
|
135
140
|
}
|
|
136
141
|
const tailStr = tail.length ? ' ' + tail.join(' ') : '';
|
|
137
142
|
if (isExpo) {
|
|
138
|
-
|
|
139
|
-
return `npx expo run:android --device "${expoDeviceArg}" --port ${port}${skip}${tailStr}`;
|
|
143
|
+
return `npx expo run:android --device "${expoDeviceArg}" --port ${port}${tailStr}`;
|
|
140
144
|
}
|
|
141
145
|
const skip = noPackager ? ' --no-packager' : '';
|
|
142
146
|
return `RCT_METRO_PORT=${port} npx react-native run-android --device ${serial}${skip}${tailStr}`;
|
package/src/sim/ios.js
CHANGED
|
@@ -53,14 +53,20 @@ export function sortSims(sims, usage = {}) {
|
|
|
53
53
|
const fa = deviceFamilyRank(a.name);
|
|
54
54
|
const fb = deviceFamilyRank(b.name);
|
|
55
55
|
if (fa !== fb) return fa - fb;
|
|
56
|
-
// 2. State: booted before shutdown (within the same family)
|
|
56
|
+
// 2. State: booted before shutdown (within the same family), so an
|
|
57
|
+
// already-running sim is reused instead of booting another.
|
|
57
58
|
if (a.state === 'Booted' && b.state !== 'Booted') return -1;
|
|
58
59
|
if (b.state === 'Booted' && a.state !== 'Booted') return 1;
|
|
59
|
-
// 3.
|
|
60
|
+
// 3. Runtime version: newest iOS runtime first, so --auto and agent
|
|
61
|
+
// selection prefer the latest installed runtime over older ones.
|
|
62
|
+
const va = parseRuntimeVersion(a.runtime);
|
|
63
|
+
const vb = parseRuntimeVersion(b.runtime);
|
|
64
|
+
if (va !== vb) return vb.localeCompare(va, undefined, { numeric: true });
|
|
65
|
+
// 4. Usage count: descending (frequently picked sims float up).
|
|
60
66
|
const ua = usage[a.udid] || 0;
|
|
61
67
|
const ub = usage[b.udid] || 0;
|
|
62
68
|
if (ua !== ub) return ub - ua;
|
|
63
|
-
//
|
|
69
|
+
// 5. Name: stable alphabetical.
|
|
64
70
|
return a.name.localeCompare(b.name);
|
|
65
71
|
});
|
|
66
72
|
}
|