rn-iso 0.4.0 → 0.4.1
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/README.md +9 -2
- package/package.json +1 -1
- package/skill/SKILL.md +1 -0
- package/src/commands/android.js +4 -2
- package/src/commands/ios.js +4 -2
- package/src/runner.js +23 -6
package/README.md
CHANGED
|
@@ -27,6 +27,13 @@ Both run side by side. For non-interactive / agent use, pass `--auto` to skip th
|
|
|
27
27
|
npx rn-iso ios --auto
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
Forward extra flags to the underlying build command with `--`:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx rn-iso ios -- --variant=release
|
|
34
|
+
npx rn-iso android -- --mode=diaRelease --terminal=Ghostty
|
|
35
|
+
```
|
|
36
|
+
|
|
30
37
|
For AI coding agents, install the skill so the agent knows how to drive the CLI:
|
|
31
38
|
|
|
32
39
|
```bash
|
|
@@ -39,8 +46,8 @@ All commands below take the same `npx rn-iso` prefix.
|
|
|
39
46
|
|
|
40
47
|
| Command | Purpose |
|
|
41
48
|
|---|---|
|
|
42
|
-
| `ios [--auto] [--device-type <name>] [--runtime <ver>] [--script <name>] [--pm <name>] [--no-script] [--no-install]` | Ensure iOS sim + Metro + build/install |
|
|
43
|
-
| `android [--auto] [--script <name>] [--pm <name>] [--no-script] [--no-install]` | Same for Android |
|
|
49
|
+
| `ios [--auto] [--device-type <name>] [--runtime <ver>] [--script <name>] [--pm <name>] [--no-script] [--no-install] [-- <extras...>]` | Ensure iOS sim + Metro + build/install. Extras after `--` are forwarded to the build command. |
|
|
50
|
+
| `android [--auto] [--script <name>] [--pm <name>] [--no-script] [--no-install] [-- <extras...>]` | Same for Android. |
|
|
44
51
|
| `start` | Start Metro detached, no platform action |
|
|
45
52
|
| `stop [<port>\|<shortcut>\|<path>]` | Kill Metro. No arg = current project; pass a port (e.g. 8083), a project shortcut (label or unique basename), or an absolute path. |
|
|
46
53
|
| `device [--platform ios\|android] [--json]` | Print the assigned device target |
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -30,6 +30,7 @@ From the project root (or any subdirectory):
|
|
|
30
30
|
## CRITICAL rules
|
|
31
31
|
|
|
32
32
|
- **Pass `--auto` for non-interactive use** of `ios` or `android`. Without it, the command will prompt with an arrow-key picker if multiple unclaimed sims/AVDs exist. `--auto` is also implied automatically when stdin isn't a TTY (e.g., when an agent pipes the command), so under most agent harnesses you don't have to remember the flag — but passing it explicitly is harmless and clearer.
|
|
33
|
+
- **Forward extra flags to the build CLI with `--`.** `npx rn-iso ios -- --variant=release` (or `android -- --mode=diaRelease`) appends those flags to the underlying `react-native run-*` / `expo run:*` invocation. Useful for release-mode builds, custom terminals, etc. Last-wins semantics, so extras can override defaults rn-iso set earlier in the command.
|
|
33
34
|
- **`--auto` will NOT take over a claimed sim/AVD.** If every device is claimed by other rn-iso projects, `--auto` errors. To take one over, run the command interactively (no `--auto`, with a real TTY) and confirm at the prompt — only do this if the user explicitly asks.
|
|
34
35
|
- **Always use `npx rn-iso device` to discover your target.** Never assume `booted` is your sim — another project's simulator might be booted too.
|
|
35
36
|
- **Always pass the UDID/serial explicitly** to `xcrun simctl` and `adb -s`. Examples:
|
package/src/commands/android.js
CHANGED
|
@@ -20,14 +20,15 @@ import { resolveLabel } from '../labels.js';
|
|
|
20
20
|
export default function androidCommand(program) {
|
|
21
21
|
program
|
|
22
22
|
.command('android')
|
|
23
|
-
.description('Ensure a dedicated Android emulator + Metro for the current project; build/install if needed')
|
|
23
|
+
.description('Ensure a dedicated Android emulator + Metro for the current project; build/install if needed. Pass extra flags to the build CLI after `--`, e.g. `rn-iso android -- --mode=diaRelease`.')
|
|
24
|
+
.argument('[extras...]', 'Flags forwarded as-is to the underlying build command (after `--`)')
|
|
24
25
|
.option('--auto', 'Non-interactive: pick the first unclaimed AVD without prompting (also implied when stdin is not a TTY)')
|
|
25
26
|
.option('--label <name>', 'Optional shortcut name; refer to the project as <name> in stop / release / etc.')
|
|
26
27
|
.option('--script <name>', 'package.json script to invoke for build/install (default: project setting `android.script`, else `android`)')
|
|
27
28
|
.option('--no-script', 'Skip the package.json script lookup; run expo/react-native CLI directly')
|
|
28
29
|
.option('--pm <name>', 'Package manager: npm, yarn, pnpm, bun (default: project setting `packageManager`, else detected from lockfile)')
|
|
29
30
|
.option('--no-install', 'Skip the build/install step')
|
|
30
|
-
.action(async (opts) => {
|
|
31
|
+
.action(async (extras, opts) => {
|
|
31
32
|
const root = findProjectRoot(process.cwd());
|
|
32
33
|
if (!root) {
|
|
33
34
|
console.error(chalk.red('Not in a React Native project (no package.json found).'));
|
|
@@ -157,6 +158,7 @@ export default function androidCommand(program) {
|
|
|
157
158
|
serial,
|
|
158
159
|
port: proj.metroPort,
|
|
159
160
|
useScript,
|
|
161
|
+
extras,
|
|
160
162
|
});
|
|
161
163
|
console.log(chalk.dim(`> ${cmd}`));
|
|
162
164
|
const exec = getExecutor();
|
package/src/commands/ios.js
CHANGED
|
@@ -12,7 +12,8 @@ import { resolveLabel } from '../labels.js';
|
|
|
12
12
|
export default function iosCommand(program) {
|
|
13
13
|
program
|
|
14
14
|
.command('ios')
|
|
15
|
-
.description('Ensure a dedicated iOS simulator + Metro server for the current project; build/install if needed')
|
|
15
|
+
.description('Ensure a dedicated iOS simulator + Metro server for the current project; build/install if needed. Pass extra flags to the build CLI after `--`, e.g. `rn-iso ios -- --variant=release`.')
|
|
16
|
+
.argument('[extras...]', 'Flags forwarded as-is to the underlying build command (after `--`)')
|
|
16
17
|
.option('--device-type <name>', 'Explicit opt-in: create a NEW sim of this device type (e.g. "iPhone 17 Pro")')
|
|
17
18
|
.option('--runtime <version>', 'iOS runtime version when creating a new sim (e.g. "26.2"); defaults to latest')
|
|
18
19
|
.option('--auto', 'Non-interactive: pick the first unclaimed sim without prompting (also implied when stdin is not a TTY)')
|
|
@@ -21,7 +22,7 @@ export default function iosCommand(program) {
|
|
|
21
22
|
.option('--no-script', 'Skip the package.json script lookup; run expo/react-native CLI directly')
|
|
22
23
|
.option('--pm <name>', 'Package manager: npm, yarn, pnpm, bun (default: project setting `packageManager`, else detected from lockfile)')
|
|
23
24
|
.option('--no-install', 'Skip the build/install step (assume app is already installed)')
|
|
24
|
-
.action(async (opts) => {
|
|
25
|
+
.action(async (extras, opts) => {
|
|
25
26
|
const root = findProjectRoot(process.cwd());
|
|
26
27
|
if (!root) {
|
|
27
28
|
console.error(chalk.red('Not in a React Native project (no package.json found).'));
|
|
@@ -158,6 +159,7 @@ export default function iosCommand(program) {
|
|
|
158
159
|
udid,
|
|
159
160
|
port: proj.metroPort,
|
|
160
161
|
useScript,
|
|
162
|
+
extras,
|
|
161
163
|
});
|
|
162
164
|
console.log(chalk.dim(`> ${cmd}`));
|
|
163
165
|
const exec = getExecutor();
|
package/src/runner.js
CHANGED
|
@@ -79,7 +79,10 @@ export function detectScriptCli(scriptBody) {
|
|
|
79
79
|
// iOS run command. Prefers the project's `ios` script if present (the most
|
|
80
80
|
// reliable: respects user customization, picks the right CLI). Falls back to
|
|
81
81
|
// expo run:ios / react-native run-ios when no script exists or --no-script.
|
|
82
|
-
|
|
82
|
+
// Any `extras` are appended last so they can override earlier flags (CLIs
|
|
83
|
+
// using commander/yargs are last-wins on repeated options).
|
|
84
|
+
export function buildIosCommand({ projectRoot, packageManager, scriptName, isExpo, udid, port, useScript = true, extras = [] }) {
|
|
85
|
+
const tail = (extras || []).map(shQuote);
|
|
83
86
|
if (useScript && scriptName) {
|
|
84
87
|
const script = getProjectScript(projectRoot, scriptName);
|
|
85
88
|
if (script) {
|
|
@@ -89,16 +92,19 @@ export function buildIosCommand({ projectRoot, packageManager, scriptName, isExp
|
|
|
89
92
|
return buildScriptCommand(packageManager, scriptName, [
|
|
90
93
|
deviceFlag,
|
|
91
94
|
`--port ${port}`,
|
|
95
|
+
...tail,
|
|
92
96
|
]);
|
|
93
97
|
}
|
|
94
98
|
}
|
|
99
|
+
const tailStr = tail.length ? ' ' + tail.join(' ') : '';
|
|
95
100
|
if (isExpo) {
|
|
96
|
-
return `npx expo run:ios --device ${udid} --port ${port}`;
|
|
101
|
+
return `npx expo run:ios --device ${udid} --port ${port}${tailStr}`;
|
|
97
102
|
}
|
|
98
|
-
return `npx react-native run-ios --udid ${udid} --port ${port}`;
|
|
103
|
+
return `npx react-native run-ios --udid ${udid} --port ${port}${tailStr}`;
|
|
99
104
|
}
|
|
100
105
|
|
|
101
|
-
export function buildAndroidCommand({ projectRoot, packageManager, scriptName, isExpo, avdName, serial, port, useScript = true }) {
|
|
106
|
+
export function buildAndroidCommand({ projectRoot, packageManager, scriptName, isExpo, avdName, serial, port, useScript = true, extras = [] }) {
|
|
107
|
+
const tail = (extras || []).map(shQuote);
|
|
102
108
|
if (useScript && scriptName) {
|
|
103
109
|
const script = getProjectScript(projectRoot, scriptName);
|
|
104
110
|
if (script) {
|
|
@@ -108,13 +114,24 @@ export function buildAndroidCommand({ projectRoot, packageManager, scriptName, i
|
|
|
108
114
|
return buildScriptCommand(packageManager, scriptName, [
|
|
109
115
|
deviceFlag,
|
|
110
116
|
`--port ${port}`,
|
|
117
|
+
...tail,
|
|
111
118
|
]);
|
|
112
119
|
}
|
|
113
120
|
}
|
|
121
|
+
const tailStr = tail.length ? ' ' + tail.join(' ') : '';
|
|
114
122
|
if (isExpo) {
|
|
115
|
-
return `npx expo run:android --device "${avdName}" --port ${port}`;
|
|
123
|
+
return `npx expo run:android --device "${avdName}" --port ${port}${tailStr}`;
|
|
116
124
|
}
|
|
117
|
-
return `RCT_METRO_PORT=${port} npx react-native run-android --deviceId ${serial}`;
|
|
125
|
+
return `RCT_METRO_PORT=${port} npx react-native run-android --deviceId ${serial}${tailStr}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// POSIX-safe single-quote shell escape. Leaves "safe" tokens (alnum and a
|
|
129
|
+
// few harmless punctuation marks like `=`, `.`, `,`, `:`, `/`, `-`, `@`,
|
|
130
|
+
// `+`, `_`, `%`) alone, single-quotes everything else.
|
|
131
|
+
export function shQuote(s) {
|
|
132
|
+
if (s === '') return "''";
|
|
133
|
+
if (/^[A-Za-z0-9_@%+=:,./-]+$/.test(s)) return s;
|
|
134
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
118
135
|
}
|
|
119
136
|
|
|
120
137
|
export function buildMetroCommand({ isExpo, port }) {
|