rnxsim 0.1.344 → 0.1.345
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 +59 -31
- package/cli/app-fonts.ts +8 -28
- package/cli/app-project.ts +12 -184
- package/cli/app-splash.ts +3 -17
- package/cli/bin.ts +15 -28
- package/cli/commands/control.ts +20 -30
- package/cli/commands/daemon.ts +19 -44
- package/cli/commands/flow.ts +0 -27
- package/cli/commands/install-desktop.ts +2 -16
- package/cli/commands/login.ts +3 -43
- package/cli/commands/logout.ts +0 -7
- package/cli/commands/serve.ts +2 -32
- package/cli/commands/setup.ts +20 -194
- package/cli/commands/telemetry.ts +7 -5
- package/cli/commands/upload.ts +1 -48
- package/cli/commands/welcome.ts +64 -0
- package/cli/outbound-endpoints.ts +586 -0
- package/cli/privacy.ts +53 -0
- package/cli/runtime-notes.ts +2 -2
- package/cli/telemetry.ts +46 -149
- package/dist-cli/bin.js +6 -6
- package/dist-cli/chunks/{agent-HTSE5FA4.js → agent-52G55XTO.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-L6X2AEZL.js → agent-wrapper-BOZM43ON.js} +2 -2
- package/dist-cli/chunks/{app-fonts-FGF3ETYH.js → app-fonts-A2L6V5EZ.js} +2 -2
- package/dist-cli/chunks/{assert-HA2ZYVDN.js → assert-CJKIP4LN.js} +2 -2
- package/dist-cli/chunks/{auth-HONDAV75.js → auth-VHHN3TOB.js} +2 -2
- package/dist-cli/chunks/{beta-U7EE4GMR.js → beta-EBDIGV3E.js} +2 -2
- package/dist-cli/chunks/{camera-RTKNUTIX.js → camera-5F4KKCQ3.js} +2 -2
- package/dist-cli/chunks/chunk-3DBTS4MC.js +15 -0
- package/dist-cli/chunks/chunk-3N7M5HMJ.js +6 -0
- package/dist-cli/chunks/{chunk-S7CSBQ6G.js → chunk-4BIPA7MD.js} +1 -1
- package/dist-cli/chunks/chunk-4OKV7T5D.js +4 -0
- package/dist-cli/chunks/{chunk-CNLPLCO2.js → chunk-4ZILDTZX.js} +2 -2
- package/dist-cli/chunks/{chunk-HRBMD2FS.js → chunk-5GXRRUOA.js} +26 -26
- package/dist-cli/chunks/chunk-63ASUHOV.js +4 -0
- package/dist-cli/chunks/{chunk-XQFDEY3F.js → chunk-7GO2DSYQ.js} +1 -1
- package/dist-cli/chunks/{chunk-IWR4HFC4.js → chunk-7LGINKM6.js} +2 -2
- package/dist-cli/chunks/{chunk-TZNRKIS3.js → chunk-7UIIAHVU.js} +2 -2
- package/dist-cli/chunks/{chunk-Q2B6EQPB.js → chunk-7V4KPZ7O.js} +2 -2
- package/dist-cli/chunks/{chunk-EPEZZ6YS.js → chunk-7YMQRA6L.js} +2 -2
- package/dist-cli/chunks/{chunk-IPC45HTT.js → chunk-A2Q76LJV.js} +2 -2
- package/dist-cli/chunks/{chunk-EH35QQG4.js → chunk-BCQV63BW.js} +45 -45
- package/dist-cli/chunks/{chunk-N5PFDTFL.js → chunk-BFTDL2GX.js} +17 -17
- package/dist-cli/chunks/chunk-BHKSCNDC.js +4 -0
- package/dist-cli/chunks/{chunk-6PEZSVIG.js → chunk-DGTV3KOW.js} +1 -1
- package/dist-cli/chunks/chunk-DHM3JDCK.js +4 -0
- package/dist-cli/chunks/{chunk-KT6WMCUS.js → chunk-DPP2M4AJ.js} +1 -1
- package/dist-cli/chunks/chunk-E2RGSCHT.js +4 -0
- package/dist-cli/chunks/{chunk-S6LBAUWH.js → chunk-EPLVIDPC.js} +1 -1
- package/dist-cli/chunks/{chunk-6NLWKL7Y.js → chunk-GKGXULJK.js} +2 -2
- package/dist-cli/chunks/{chunk-7V4ULF3U.js → chunk-HK7YKH6N.js} +2 -2
- package/dist-cli/chunks/{chunk-6E6SBYIO.js → chunk-HXE344V3.js} +1 -1
- package/dist-cli/chunks/{chunk-JLUEQPEF.js → chunk-JC33TOAF.js} +1 -1
- package/dist-cli/chunks/{chunk-7JEEO33Y.js → chunk-KA4PJNTE.js} +2 -2
- package/dist-cli/chunks/{chunk-K3VNCCEJ.js → chunk-KH777UAO.js} +3 -3
- package/dist-cli/chunks/{chunk-G3UAFZE2.js → chunk-KRZPFXVN.js} +1 -1
- package/dist-cli/chunks/{chunk-R3UYHE6G.js → chunk-KZFJNDH3.js} +1 -1
- package/dist-cli/chunks/{chunk-22WJPMTQ.js → chunk-LAQGSSTT.js} +1 -1
- package/dist-cli/chunks/chunk-LRM2XCEQ.js +11 -0
- package/dist-cli/chunks/{chunk-MWUXQIA3.js → chunk-NUPRHZQW.js} +2 -2
- package/dist-cli/chunks/{chunk-JFUVWIJT.js → chunk-OPHT4DUO.js} +1 -1
- package/dist-cli/chunks/{chunk-EN32LN2B.js → chunk-P2CGGMU2.js} +2 -2
- package/dist-cli/chunks/{chunk-UCVQOQCY.js → chunk-PLB6UVVX.js} +2 -2
- package/dist-cli/chunks/{chunk-TOHQI2II.js → chunk-PLUWN2V5.js} +1 -1
- package/dist-cli/chunks/{chunk-ZPTFL5FQ.js → chunk-QNBBKRCA.js} +2 -2
- package/dist-cli/chunks/{chunk-NRTHFEPM.js → chunk-QNRWPAOA.js} +2 -2
- package/dist-cli/chunks/{chunk-63NZGJ4A.js → chunk-R7ZNRGQN.js} +1 -1
- package/dist-cli/chunks/{chunk-FZ5DRZJE.js → chunk-RB4VFUNM.js} +3 -3
- package/dist-cli/chunks/chunk-RT3DXTDP.js +25 -0
- package/dist-cli/chunks/{chunk-KBSVSAAE.js → chunk-RTDHX6CW.js} +2 -2
- package/dist-cli/chunks/{chunk-OCUXAA6O.js → chunk-T7SBFG5I.js} +2 -2
- package/dist-cli/chunks/{chunk-GRX6NSBG.js → chunk-TEU5NGUA.js} +2 -2
- package/dist-cli/chunks/chunk-TMRD5MHL.js +99 -0
- package/dist-cli/chunks/{chunk-POOCKX5B.js → chunk-TOF5TA5X.js} +11 -11
- package/dist-cli/chunks/{chunk-G6IPT2PS.js → chunk-U5YHXQHH.js} +2 -2
- package/dist-cli/chunks/{chunk-H4J7EWZC.js → chunk-UVOEHIGP.js} +1 -1
- package/dist-cli/chunks/{chunk-ZZSBCGHC.js → chunk-VLBN3KRC.js} +1 -1
- package/dist-cli/chunks/{chunk-PNMCALX5.js → chunk-WPPGU5IK.js} +2 -2
- package/dist-cli/chunks/{chunk-NDGAF5OF.js → chunk-WU72KL6Z.js} +2 -2
- package/dist-cli/chunks/chunk-XJBFJGNT.js +4 -0
- package/dist-cli/chunks/{chunk-BGLWR43R.js → chunk-XXZLK4BY.js} +1 -1
- package/dist-cli/chunks/{chunk-CRWYKW67.js → chunk-YVSUFVVB.js} +1 -1
- package/dist-cli/chunks/{chunk-EZLLZDYX.js → chunk-ZMTZ5G5H.js} +2 -2
- package/dist-cli/chunks/{cleanup-EMYD5WZG.js → cleanup-MAVZJXVW.js} +2 -2
- package/dist-cli/chunks/cli-version-2B4KRNML.js +4 -0
- package/dist-cli/chunks/{compat-W2N5QTVG.js → compat-7AURLGCS.js} +2 -2
- package/dist-cli/chunks/config-TN3Y4J5N.js +13 -0
- package/dist-cli/chunks/{control-4SFBROUE.js → control-EV2ELU5R.js} +2 -2
- package/dist-cli/chunks/daemon-KKDG5MYR.js +4 -0
- package/dist-cli/chunks/{debug-HCR2KFUA.js → debug-JICITUB6.js} +3 -3
- package/dist-cli/chunks/{desktop-T2KS5PBJ.js → desktop-4F5HGLLX.js} +3 -3
- package/dist-cli/chunks/{detox-5S7EHINH.js → detox-ZC7NHLJI.js} +2 -2
- package/dist-cli/chunks/{device-LVM3MZRJ.js → device-V57GHRXX.js} +2 -2
- package/dist-cli/chunks/{diagnose-WK37XKLI.js → diagnose-FF4IWYXC.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-X7VJOXMS.js → disk-cleanup-T277L6ZO.js} +2 -2
- package/dist-cli/chunks/drivers-LCX7S76X.js +4 -0
- package/dist-cli/chunks/{film-7KLTI3ER.js → film-G7ZAPWB7.js} +3 -3
- package/dist-cli/chunks/flow-ONZGGFGD.js +4 -0
- package/dist-cli/chunks/help-DROVDXJM.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-4RVGUVFO.js → hidden-runtime-alias-VSJNNSNI.js} +2 -2
- package/dist-cli/chunks/{home-paths-5DHSAEGZ.js → home-paths-75HP3HKA.js} +2 -2
- package/dist-cli/chunks/inspect-MDA7ESGQ.js +4 -0
- package/dist-cli/chunks/install-desktop-EIVZHIFA.js +4 -0
- package/dist-cli/chunks/login-ZSIHGHUK.js +26 -0
- package/dist-cli/chunks/logout-Q4DXMI2C.js +4 -0
- package/dist-cli/chunks/{maestro-HTRLQDJE.js → maestro-LE5LCRBL.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-GX2QNA3F.js → maestro-generate-D3ZIJCRL.js} +3 -3
- package/dist-cli/chunks/{mode-GM6KZHQP.js → mode-BWX6MDPJ.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-B4TD7JQP.js → optional-demo-registry-6CVASL3S.js} +2 -2
- package/dist-cli/chunks/{perf-3PRXJQIC.js → perf-NXQZISN6.js} +2 -2
- package/dist-cli/chunks/{permissions-TJZIRF2S.js → permissions-5PD67RRO.js} +2 -2
- package/dist-cli/chunks/privacy-H5KNRNIZ.js +4 -0
- package/dist-cli/chunks/{record-LI55DTPE.js → record-YJF4KXMR.js} +3 -3
- package/dist-cli/chunks/{report-issue-TBGJVQIQ.js → report-issue-XOL5FQBG.js} +2 -2
- package/dist-cli/chunks/reset-5LZN5P5B.js +4 -0
- package/dist-cli/chunks/runtime-U36VWBH3.js +4 -0
- package/dist-cli/chunks/{screenshot-command-BEN7YYIF.js → screenshot-command-VRPJXWFS.js} +3 -3
- package/dist-cli/chunks/{screenshot-layers-3IU4KYNO.js → screenshot-layers-QP4WA2F5.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-ZRC54ST6.js → screenshots-capture-O2IBAVSP.js} +2 -2
- package/dist-cli/chunks/serve-WC6RZNJL.js +118 -0
- package/dist-cli/chunks/setup-A6BIG3KL.js +30 -0
- package/dist-cli/chunks/{skills-GKBR2HQB.js → skills-A7YC6RL5.js} +2 -2
- package/dist-cli/chunks/{state-LIHZJSL7.js → state-IJI5YROJ.js} +2 -2
- package/dist-cli/chunks/{storage-4TPN3NFB.js → storage-Z2DBO2PQ.js} +4 -4
- package/dist-cli/chunks/store-BYTCKNIC.js +4 -0
- package/dist-cli/chunks/telemetry-JLHFMD7N.js +4 -0
- package/dist-cli/chunks/{timeline-EX63UNWU.js → timeline-N7V2M6QA.js} +2 -2
- package/dist-cli/chunks/{upgrade-O6Y6EINE.js → upgrade-P2NH3BOR.js} +2 -2
- package/dist-cli/chunks/upload-Z2WX5DCD.js +4 -0
- package/dist-cli/chunks/{version-HLNL3NFB.js → version-OUHCSCUS.js} +2 -2
- package/dist-cli/chunks/{web-4EDPYPW7.js → web-6NRTF3UV.js} +2 -2
- package/dist-cli/chunks/welcome-6RS7PEJW.js +5 -0
- package/dist-cli/chunks/{what-happened-77VNUEPK.js → what-happened-FUEL3BEF.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +18 -4
- package/dist-lib/home-paths.cjs +145 -11
- package/dist-lib/host/bridge-host.cjs +236 -31
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +71 -4
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +337 -37
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +484 -14
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +25 -2
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +384 -385
- package/dist-lib/vite.cjs +193 -37
- package/package.json +2 -1
- package/scripts/dev-server-scanner.ts +12 -5
- package/skills/rnx-test/SKILL.md +4 -0
- package/src/dev-bundle-resolution.ts +13 -6
- package/src/dev-server-open.ts +197 -0
- package/src/home-paths.ts +168 -15
- package/src/host/bridge-host.ts +2 -2
- package/src/host/replacement-module-handler.ts +68 -2
- package/src/metro-plugin.ts +83 -5
- package/src/native-dev-bundle-url.ts +22 -0
- package/src/vite-plugin-one.ts +42 -28
- package/dist-cli/chunks/chunk-3JC7H4PR.js +0 -25
- package/dist-cli/chunks/chunk-6DLIC7BZ.js +0 -4
- package/dist-cli/chunks/chunk-7TNPO2OJ.js +0 -9
- package/dist-cli/chunks/chunk-AMF5KTOI.js +0 -4
- package/dist-cli/chunks/chunk-BX5VXTOQ.js +0 -101
- package/dist-cli/chunks/chunk-D73H7ACZ.js +0 -4
- package/dist-cli/chunks/chunk-DY4O7KYI.js +0 -4
- package/dist-cli/chunks/chunk-GU2V4JDG.js +0 -15
- package/dist-cli/chunks/chunk-Z2HMSVSN.js +0 -4
- package/dist-cli/chunks/cli-version-CN3NTKLH.js +0 -4
- package/dist-cli/chunks/config-SELKGT4F.js +0 -13
- package/dist-cli/chunks/daemon-V2SFCU2E.js +0 -4
- package/dist-cli/chunks/drivers-3YFTJ5WA.js +0 -4
- package/dist-cli/chunks/flow-36ANTMJU.js +0 -4
- package/dist-cli/chunks/help-ZGL26SPK.js +0 -4
- package/dist-cli/chunks/inspect-R3B55RTS.js +0 -4
- package/dist-cli/chunks/install-desktop-TZ3H3VWK.js +0 -4
- package/dist-cli/chunks/login-4DFULI7S.js +0 -28
- package/dist-cli/chunks/logout-QMCVYMBB.js +0 -4
- package/dist-cli/chunks/reset-O6MW6X7X.js +0 -4
- package/dist-cli/chunks/runtime-JESXBHRQ.js +0 -4
- package/dist-cli/chunks/serve-QGKTWPNF.js +0 -118
- package/dist-cli/chunks/setup-3WPWATCX.js +0 -32
- package/dist-cli/chunks/store-VBX3L46S.js +0 -4
- package/dist-cli/chunks/telemetry-ACNNIQIG.js +0 -4
- package/dist-cli/chunks/upload-OD2UQHMM.js +0 -4
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ internally splits into shell, compositor, and tenant workers.
|
|
|
53
53
|
```mermaid
|
|
54
54
|
graph TD
|
|
55
55
|
subgraph cli["rnx CLI process (short-lived, cli/)"]
|
|
56
|
-
BIN["bin.ts (dispatcher +
|
|
56
|
+
BIN["bin.ts (dispatcher + privacy choice)"]
|
|
57
57
|
SETUP["commands/setup.ts (guided daemon setup)"]
|
|
58
58
|
RUNTIMECMD["commands/runtime.ts (runtime install/use)"]
|
|
59
59
|
INSPECT["commands/inspect/* (describe/find/do/get/wait)"]
|
|
@@ -131,12 +131,10 @@ graph TD
|
|
|
131
131
|
SKILLS --> INSPECT
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
**The CLI process** (`cli/bin.ts`) parses argv
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`rnxsim` install, and install the faster background daemon when it is
|
|
139
|
-
missing. `rnx open`
|
|
134
|
+
**The CLI process** (`cli/bin.ts`) parses argv and lazy-imports exactly one
|
|
135
|
+
command module per run. Bare `rnx` discovers a running React Native development
|
|
136
|
+
server and opens it directly. `rnx setup` remains the explicit path for
|
|
137
|
+
repository preparation and the optional background daemon. `rnx open`
|
|
140
138
|
ensures the engine runtime exists and starts a local foreground bridge when no
|
|
141
139
|
daemon is reachable, then opens a `WsBridge` (`cli/ws-bridge.ts`) on **port
|
|
142
140
|
7668** and sends JSON commands. Inspection/interaction verbs live in
|
|
@@ -172,8 +170,8 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
172
170
|
|
|
173
171
|
- The published **`rnxsim/vite`** (`src/vite-plugin-one.ts`, `rnxPlugin`)
|
|
174
172
|
and **`rnxsim/metro`** plugins do one thing: serve the installed runtime at
|
|
175
|
-
`/__soot/` so an app's own dev server can host the sim shell.
|
|
176
|
-
|
|
173
|
+
`/__soot/` so an app's own dev server can host the sim shell. Both accept an
|
|
174
|
+
opt-in `open` option that launches a named Electron window when the dev server
|
|
177
175
|
starts (skipped when `CI` or `SOOTSIM_NO_OPEN` is set).
|
|
178
176
|
- The internal **`sootsim()`** plugin (`src/vite-plugin.ts`) is a separate, much
|
|
179
177
|
larger RN-resolution / native-stub plugin: it aliases `react-native` to the
|
|
@@ -186,7 +184,7 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
186
184
|
|
|
187
185
|
| component | role | key files |
|
|
188
186
|
| --- | --- | --- |
|
|
189
|
-
| rnx CLI (bin + dispatcher) | Short-lived terminal entry point. Parses argv,
|
|
187
|
+
| rnx CLI (bin + dispatcher) | Short-lived terminal entry point. Parses argv, owns the private-by-default one-time choice, lazy-imports one command per run, and routes direct open/runtime/inspection flows. | `cli/bin.ts`, `cli/privacy.ts`, `cli/parse-args.ts`, `cli/help.ts`, `cli/commands/setup.ts`, `cli/commands/control.ts` |
|
|
190
188
|
| WsBridge client | Client side of the CLI→daemon link. Resolves the daemon port from the lockfile, opens a WS to `:7668`, sends JSON commands and awaits `{id,result}`/`{id,error}`. Used by every interactive command. | `cli/ws-bridge.ts`, `src/bridge-constants.ts`, `cli/current-sim.ts` |
|
|
191
189
|
| inspect / SDK command surface | Runtime inspection + interaction verbs (describe, find, do tap, get errors, wait, layout, settle). Re-exported as `rnxsim/sdk` so programmatic callers drive a sim the same way the CLI does. | `cli/commands/inspect/core.ts`, `cli/commands/inspect/actions.ts`, `cli/commands/inspect.ts`, `src/sdk.ts` |
|
|
192
190
|
| Maestro / Detox runners | Drop-in test compatibility. `bridge-flow-runner.ts` runs Maestro YAML and recorded flows over the bridge; `detox/` provides a `by`/`element`/`expect`/`device` driver + jest preset so existing Detox suites run unchanged. | `cli/bridge-flow-runner.ts`, `cli/commands/maestro.ts`, `cli/commands/detox.ts`, `detox/index.ts`, `detox/jest-preset.cjs` |
|
|
@@ -195,19 +193,22 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
195
193
|
| AgentHost + sessions | Agent-routing extension of the host: owns the single FIFO reader per agent session and fans `agent:event`/`session-status` pushes to every WS subscriber. Backed by agent-sessions + attached-projects stores. | `src/host/agent-host.ts`, `src/agent-sessions.ts`, `src/attached-projects.ts`, `src/agent-daemon-client.ts` |
|
|
196
194
|
| host proxies + dev-server scan | Host-side helpers so a cross-origin tenant worker can reach local dev servers and same-origin APIs: `/__server-scan` discovers running metro/expo/vxrn/one servers; fetch + websocket proxies relay guest-app network through the daemon. | `src/host/fetch-proxy-handler.ts`, `src/host/websocket-proxy.ts`, `scripts/dev-server-scanner.ts`, `src/dev-bundle-resolution.ts` |
|
|
197
195
|
| runtime delivery + rnx home | Versioned engine runtime management. The shared machinery owns manifest fetch, sha256 verification, channels, and auto-update. rnx binds it to `https://contrast.dev` and `~/.sootsim/runtimes/<version>`. A repo can select `runtimeVersion` without changing the machine default. | `src/runtime-delivery.ts`, `packages/contrast-runtime-delivery/`, `src/home-paths.ts`, `src/runtime-assets.ts`, `scripts/postinstall.cjs` |
|
|
198
|
-
| `rnxsim/vite` + `rnxsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__soot/` from `~/.sootsim/runtimes/<version>` so an app's own dev server can host the sim shell;
|
|
196
|
+
| `rnxsim/vite` + `rnxsim/metro` (runtime serving) | Published bundler plugins. Both serve the installed engine runtime at `/__soot/` from `~/.sootsim/runtimes/<version>` so an app's own dev server can host the sim shell; either plugin can opt into a named Electron window. | `src/vite-plugin-one.ts`, `src/metro-plugin.ts`, `src/runtime-assets.ts` |
|
|
199
197
|
| `sootsim()` vite resolver plugin (internal) | The large RN-resolution / native-stub vite plugin: aliases `react-native` to the engine shim, native packages to `@sootsim/compat` stubs, applies worklets/babel transforms, and instantiates `SootSimBridgeHost` in its dev server. Distinct from the published `rnxsim/vite` export. | `src/vite-plugin.ts`, `src/worklets-babel.ts`, `packages/compat/src/stub-manifest.ts` |
|
|
200
198
|
| skills registry | rnx agent skills (`rnx-setup`, `rnx-debug`, `rnx-test`, `rnx-visual`, and the generated `contrast` index) installable with `rnx skill`. | `skills/*.md`, `src/skills/registry.ts`, `src/skills/types.ts` |
|
|
201
199
|
|
|
202
200
|
## Getting started
|
|
203
201
|
|
|
204
202
|
```sh
|
|
205
|
-
|
|
206
|
-
rnx setup # guided one-time machine setup
|
|
207
|
-
rnx open 8081 # load a running metro/expo dev server
|
|
203
|
+
npx rnxsim # discover your running app and open it in the simulator
|
|
208
204
|
rnx open /settings # dispatch a React Native deep link into that app
|
|
209
205
|
```
|
|
210
206
|
|
|
207
|
+
Start Metro, Expo, One, or another supported React Native development server
|
|
208
|
+
the way you normally do, then run `npx rnxsim` from any directory. If one app is
|
|
209
|
+
running, rnx opens it immediately. If several are running, a human terminal gets
|
|
210
|
+
one short choice; CI and agents choose deterministically and never prompt.
|
|
211
|
+
|
|
211
212
|
The first `rnx open` launches an isolated Chrome for Testing sim. The CLI
|
|
212
213
|
resolves Playwright from the app or CLI install, asks that exact package for
|
|
213
214
|
its required browser executable, and runs that package's installer once when
|
|
@@ -217,21 +218,40 @@ requested target. Use `rnx do reload` when the target has not changed.
|
|
|
217
218
|
Reserve `--new` for a genuinely concurrent sim; repeated new browser trees
|
|
218
219
|
consume memory and CPU on shared machines.
|
|
219
220
|
|
|
220
|
-
|
|
221
|
-
`
|
|
222
|
-
|
|
223
|
-
followed by `rnx setup`. There is no per-app install:
|
|
221
|
+
There is no required global or per-app install. Optional global installs can
|
|
222
|
+
use `npm install --global rnxsim`, `bun add --global rnxsim`, or
|
|
223
|
+
`pnpm add --global rnxsim`. `rnx setup` is optional machine/repository tuning:
|
|
224
224
|
start your Metro, Expo, or One dev server the way you already do, then open it
|
|
225
225
|
from the CLI. There is no iOS native build in the inner loop; Metro alone is
|
|
226
|
-
enough.
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
enough. The manifest bundle path and query remain authoritative. When both the
|
|
227
|
+
discovery request and manifest target are loopback addresses, RNX keeps the
|
|
228
|
+
loopback host that answered discovery so an IPv6-only listener is not reopened
|
|
229
|
+
through an unreachable IPv4 address. `rnx open` starts a local bridge when no
|
|
230
|
+
daemon is reachable; the guided setup installs the faster background daemon so
|
|
231
|
+
later CLI commands reuse one server. A desktop GUI (Electron) is optional on
|
|
232
|
+
top, via `rnx desktop
|
|
229
233
|
install`. Native Electron windows created by `rnx open` belong to that CLI
|
|
230
234
|
session and close when the session exits; `rnx desktop` keeps the persistent
|
|
231
235
|
desktop-app lifecycle. In Electron, `File > New Window`
|
|
232
236
|
duplicates the focused simulator, while `File > New Simulator >` opens a new
|
|
233
237
|
window for a selected device (same device list as the `Window >` menu).
|
|
234
238
|
|
|
239
|
+
### Privacy
|
|
240
|
+
|
|
241
|
+
rnx does not phone home by default. On an unsigned-in first run, its only
|
|
242
|
+
internet request is the runtime manifest/archive it is about to execute. The
|
|
243
|
+
first human run asks once, defaults to no, and never prompts in a non-TTY shell,
|
|
244
|
+
CI, or an agent. An automated first run is declined for that process without
|
|
245
|
+
consuming the later human choice. Legacy browser and Electron telemetry senders
|
|
246
|
+
are removed rather than being enabled by this consent.
|
|
247
|
+
|
|
248
|
+
Opt-in diagnostics are limited to package name/version/unsupported API or error
|
|
249
|
+
class. rnx never sends source, file paths, project names, app content, commands,
|
|
250
|
+
identity, screenshots, or recordings as diagnostics. Cloud commands such as
|
|
251
|
+
login, upload, billing, issue reporting, generated flows, and screenshot decks
|
|
252
|
+
contact their documented service only when explicitly invoked. The complete
|
|
253
|
+
enforced network contract is in [`specs/rnx-privacy.md`](../../specs/rnx-privacy.md).
|
|
254
|
+
|
|
235
255
|
To uninstall the machine daemon and cached runtimes, run
|
|
236
256
|
`rnx daemon uninstall`. It stops current and legacy launchd / systemd
|
|
237
257
|
services, removes daemon logs, and removes `~/.sootsim/` (runtimes, profiles,
|
|
@@ -308,7 +328,7 @@ The Vite plugin serves the installed RNX runtime from your existing dev server.
|
|
|
308
328
|
import { rnxPlugin } from 'rnxsim/vite'
|
|
309
329
|
|
|
310
330
|
export default {
|
|
311
|
-
plugins: [rnxPlugin()],
|
|
331
|
+
plugins: [rnxPlugin({ open: { appName: 'My App' } })],
|
|
312
332
|
}
|
|
313
333
|
```
|
|
314
334
|
|
|
@@ -319,13 +339,18 @@ const { withRNX } = require('rnxsim/metro')
|
|
|
319
339
|
|
|
320
340
|
const config = getDefaultConfig(__dirname)
|
|
321
341
|
|
|
322
|
-
module.exports = withRNX(config, {
|
|
342
|
+
module.exports = withRNX(config, { open: { appName: 'My App' } })
|
|
323
343
|
```
|
|
324
344
|
|
|
325
|
-
The
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
345
|
+
The `open` option serves the RNX shell and opens the app in its own named
|
|
346
|
+
Electron window. `open: true` derives the name from `package.json`; use the
|
|
347
|
+
object form when the display name differs. Run `rnx desktop install` once before
|
|
348
|
+
enabling it. `SOOTSIM_NO_OPEN=1` keeps the server headless. The Metro wrapper
|
|
349
|
+
also has two independent non-window opt-ins.
|
|
350
|
+
`devServer` serves the RNX shell from the Metro process. `productionBundles`
|
|
351
|
+
lets explicitly marked production bundle requests use the RNX host
|
|
352
|
+
implementations of React and React Native while the rest of the app receives
|
|
353
|
+
production transforms.
|
|
329
354
|
|
|
330
355
|
```js
|
|
331
356
|
module.exports = withRNX(config, { productionBundles: true })
|
|
@@ -516,10 +541,13 @@ serves its selected version from a version-specific localhost origin, so a
|
|
|
516
541
|
background activation cannot mix an already loaded shell with chunks from the
|
|
517
542
|
next runtime. Without config, each new open selects the active stable version.
|
|
518
543
|
When `sootsim.config.ts` sets `runtimeVersion`, `rnx open` installs that
|
|
519
|
-
version without changing the machine default.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
544
|
+
version without changing the machine default. App config, native-linked fonts,
|
|
545
|
+
and splash assets all resolve from the project root advertised by the selected
|
|
546
|
+
development server. Bare Metro, which has no Expo manifest project root, uses
|
|
547
|
+
the listening development process's cwd inside that same resolver. The invoking
|
|
548
|
+
CLI's cwd is never an asset/config fallback. Asset wires stay on each opened
|
|
549
|
+
project URL, so one daemon can serve several attached projects without sharing
|
|
550
|
+
font or splash state between them.
|
|
523
551
|
|
|
524
552
|
The first interactive CLI command after this cleanup generation finishes its
|
|
525
553
|
requested work, then launches the same safe default plan in a detached
|
package/cli/app-fonts.ts
CHANGED
|
@@ -36,44 +36,26 @@ function localOriginOf(value: string): string | null {
|
|
|
36
36
|
return null
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
function hasPackageJson(dir: string): boolean {
|
|
40
|
-
try {
|
|
41
|
-
return fs.statSync(path.join(dir, 'package.json')).isFile()
|
|
42
|
-
} catch {
|
|
43
|
-
return false
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
39
|
export function discoverNativeLinkedAppFonts(opts: {
|
|
47
40
|
bundleUrl: string
|
|
48
|
-
|
|
49
|
-
exactProjectDir?: boolean
|
|
41
|
+
projectDir: string
|
|
50
42
|
platform?: string
|
|
51
43
|
}): DiscoveredAppFontSpec[] {
|
|
52
44
|
const metroOrigin = localOriginOf(opts.bundleUrl)
|
|
53
45
|
if (!metroOrigin) return []
|
|
54
46
|
|
|
55
|
-
const
|
|
56
|
-
const startDir = explicit ? path.resolve(explicit) : process.cwd()
|
|
57
|
-
|
|
58
|
-
// if we're using cwd() (no explicit repo) and there's no package.json,
|
|
59
|
-
// assume we're not in a project tree — bail immediately instead of
|
|
60
|
-
// recursing the filesystem.
|
|
61
|
-
if (!explicit && !hasPackageJson(startDir)) return []
|
|
62
|
-
|
|
63
|
-
const project = resolveAppProject(startDir, { trustStart: opts.exactProjectDir })
|
|
47
|
+
const project = resolveAppProject(opts.projectDir)
|
|
64
48
|
const selectedAppDir = project?.appDir ?? null
|
|
65
49
|
if (!selectedAppDir) return []
|
|
66
50
|
|
|
67
51
|
const platform = (opts.platform || process.env.CONTRAST_PLATFORM || 'ios').toLowerCase()
|
|
68
52
|
|
|
69
53
|
let getConfig: ExpoConfigGetter | null = null
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
// that mock @expo/config can set CONTRAST_REPO_DIR to point where the mock lives.
|
|
73
|
-
if (explicit || project) {
|
|
54
|
+
// resolve from the exact app root advertised by the development server.
|
|
55
|
+
if (project) {
|
|
74
56
|
try {
|
|
75
57
|
const modulePath = require.resolve('@expo/config', {
|
|
76
|
-
paths: [selectedAppDir
|
|
58
|
+
paths: [selectedAppDir],
|
|
77
59
|
})
|
|
78
60
|
const mod = require(modulePath) as { getConfig?: unknown }
|
|
79
61
|
if (typeof mod.getConfig === 'function') {
|
|
@@ -283,11 +265,9 @@ export function discoverNativeLinkedAppFonts(opts: {
|
|
|
283
265
|
// `sootsim open 8081` run from ~/soot against a 3pc metro), so scanning cwd for
|
|
284
266
|
// font declarations finds nothing.
|
|
285
267
|
//
|
|
286
|
-
// a bare react-native packager serves no such manifest
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
// react-native.config.js `assets`, so without this their fonts are never
|
|
290
|
-
// discoverable and the appFonts wire comes out empty.
|
|
268
|
+
// a bare react-native packager serves no such manifest, so the same resolver
|
|
269
|
+
// asks the OS for the listening process's cwd. that is still the development
|
|
270
|
+
// server's project identity; the invoking CLI's cwd is never consulted.
|
|
291
271
|
export async function resolveMetroProjectRoot(
|
|
292
272
|
bundleUrl: string,
|
|
293
273
|
opts?: { timeoutMs?: number },
|
package/cli/app-project.ts
CHANGED
|
@@ -16,30 +16,6 @@ export const SOOTSIM_APP_CONFIG_NAMES = [
|
|
|
16
16
|
'sootsim.config.cjs',
|
|
17
17
|
] as const
|
|
18
18
|
|
|
19
|
-
const SKIP_DIR_NAMES = new Set([
|
|
20
|
-
'node_modules',
|
|
21
|
-
'artifacts',
|
|
22
|
-
'dist',
|
|
23
|
-
'build',
|
|
24
|
-
'out',
|
|
25
|
-
'coverage',
|
|
26
|
-
'tmp',
|
|
27
|
-
'public',
|
|
28
|
-
'ios',
|
|
29
|
-
'android',
|
|
30
|
-
'macos',
|
|
31
|
-
'windows',
|
|
32
|
-
'__fixtures__',
|
|
33
|
-
'__tests__',
|
|
34
|
-
'fixtures',
|
|
35
|
-
'fixture',
|
|
36
|
-
'examples',
|
|
37
|
-
'example',
|
|
38
|
-
])
|
|
39
|
-
|
|
40
|
-
const MAX_SCANNED_DIRECTORIES = 256
|
|
41
|
-
const MAX_SCAN_DEPTH = 4
|
|
42
|
-
|
|
43
19
|
export interface ResolvedAppProject {
|
|
44
20
|
appDir: string
|
|
45
21
|
configPath: string | null
|
|
@@ -63,169 +39,21 @@ export function findSootSimAppConfig(dir: string): string | null {
|
|
|
63
39
|
return firstNamedFile(dir, SOOTSIM_APP_CONFIG_NAMES)
|
|
64
40
|
}
|
|
65
41
|
|
|
66
|
-
function
|
|
42
|
+
export function resolveAppProject(projectRoot: string): ResolvedAppProject | null {
|
|
43
|
+
const appDir = path.resolve(projectRoot)
|
|
67
44
|
try {
|
|
68
|
-
|
|
69
|
-
if (!value || typeof value !== 'object') return []
|
|
70
|
-
const workspaces = Reflect.get(value, 'workspaces')
|
|
71
|
-
if (Array.isArray(workspaces)) {
|
|
72
|
-
return workspaces.filter((entry): entry is string => typeof entry === 'string')
|
|
73
|
-
}
|
|
74
|
-
if (!workspaces || typeof workspaces !== 'object') return []
|
|
75
|
-
const packages = Reflect.get(workspaces, 'packages')
|
|
76
|
-
return Array.isArray(packages)
|
|
77
|
-
? packages.filter((entry): entry is string => typeof entry === 'string')
|
|
78
|
-
: []
|
|
45
|
+
if (!fs.statSync(appDir).isDirectory()) return null
|
|
79
46
|
} catch {
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
interface DirectoryScan {
|
|
85
|
-
directories: string[]
|
|
86
|
-
complete: boolean
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function workspaceDirectories(rootDir: string): DirectoryScan {
|
|
90
|
-
const directories = new Set<string>()
|
|
91
|
-
let expanded = 0
|
|
92
|
-
let complete = true
|
|
93
|
-
for (const pattern of readWorkspacePatterns(rootDir)) {
|
|
94
|
-
if (expanded >= MAX_SCANNED_DIRECTORIES) {
|
|
95
|
-
complete = false
|
|
96
|
-
break
|
|
97
|
-
}
|
|
98
|
-
if (!pattern || path.isAbsolute(pattern)) continue
|
|
99
|
-
const segments = pattern.split(/[\\/]+/).filter(Boolean)
|
|
100
|
-
if (segments.length === 0 || segments.length > MAX_SCAN_DEPTH) continue
|
|
101
|
-
if (segments.some((segment) => segment === '..' || segment === '**')) continue
|
|
102
|
-
|
|
103
|
-
let current = [rootDir]
|
|
104
|
-
for (const segment of segments) {
|
|
105
|
-
const next: string[] = []
|
|
106
|
-
for (const parent of current) {
|
|
107
|
-
if (expanded >= MAX_SCANNED_DIRECTORIES) {
|
|
108
|
-
complete = false
|
|
109
|
-
break
|
|
110
|
-
}
|
|
111
|
-
expanded++
|
|
112
|
-
if (segment === '*') {
|
|
113
|
-
let entries: fs.Dirent[]
|
|
114
|
-
try {
|
|
115
|
-
entries = fs.readdirSync(parent, { withFileTypes: true })
|
|
116
|
-
} catch {
|
|
117
|
-
continue
|
|
118
|
-
}
|
|
119
|
-
for (const entry of entries) {
|
|
120
|
-
if (!entry.isDirectory() || entry.isSymbolicLink()) continue
|
|
121
|
-
if (entry.name.startsWith('.') || SKIP_DIR_NAMES.has(entry.name)) continue
|
|
122
|
-
next.push(path.join(parent, entry.name))
|
|
123
|
-
}
|
|
124
|
-
} else if (!segment.includes('*')) {
|
|
125
|
-
const candidate = path.join(parent, segment)
|
|
126
|
-
try {
|
|
127
|
-
const stat = fs.lstatSync(candidate)
|
|
128
|
-
if (stat.isDirectory() && !stat.isSymbolicLink()) next.push(candidate)
|
|
129
|
-
} catch {}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
current = next
|
|
133
|
-
if (current.length > MAX_SCANNED_DIRECTORIES) {
|
|
134
|
-
current.length = MAX_SCANNED_DIRECTORIES
|
|
135
|
-
complete = false
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
for (const directory of current) directories.add(directory)
|
|
139
|
-
}
|
|
140
|
-
return { directories: [...directories], complete }
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function boundedConfigDirectories(rootDir: string): DirectoryScan {
|
|
144
|
-
const queue: Array<{ dir: string; depth: number }> = [{ dir: rootDir, depth: 0 }]
|
|
145
|
-
const matches: string[] = []
|
|
146
|
-
let visited = 0
|
|
147
|
-
let nextIndex = 0
|
|
148
|
-
let complete = true
|
|
149
|
-
|
|
150
|
-
while (nextIndex < queue.length && visited < MAX_SCANNED_DIRECTORIES) {
|
|
151
|
-
const current = queue[nextIndex++]
|
|
152
|
-
if (!current) break
|
|
153
|
-
visited++
|
|
154
|
-
if (current.depth > 0 && findExpoAppConfig(current.dir)) {
|
|
155
|
-
matches.push(current.dir)
|
|
156
|
-
if (matches.length > 1) return { directories: matches, complete: true }
|
|
157
|
-
}
|
|
158
|
-
if (current.depth >= MAX_SCAN_DEPTH) continue
|
|
159
|
-
|
|
160
|
-
let entries: fs.Dirent[]
|
|
161
|
-
try {
|
|
162
|
-
entries = fs.readdirSync(current.dir, { withFileTypes: true })
|
|
163
|
-
} catch {
|
|
164
|
-
continue
|
|
165
|
-
}
|
|
166
|
-
for (const entry of entries) {
|
|
167
|
-
if (!entry.isDirectory() || entry.isSymbolicLink()) continue
|
|
168
|
-
if (entry.name.startsWith('.') || SKIP_DIR_NAMES.has(entry.name)) continue
|
|
169
|
-
if (queue.length >= MAX_SCANNED_DIRECTORIES) {
|
|
170
|
-
complete = false
|
|
171
|
-
break
|
|
172
|
-
}
|
|
173
|
-
queue.push({ dir: path.join(current.dir, entry.name), depth: current.depth + 1 })
|
|
174
|
-
}
|
|
47
|
+
return null
|
|
175
48
|
}
|
|
176
49
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const rootDir = path.resolve(startDir)
|
|
185
|
-
const rootConfigPath = findSootSimAppConfig(rootDir)
|
|
186
|
-
if (findExpoAppConfig(rootDir) || (opts.trustStart && fs.existsSync(rootDir))) {
|
|
187
|
-
let configPath = rootConfigPath
|
|
188
|
-
if (!configPath && opts.trustStart) {
|
|
189
|
-
let parent = path.dirname(rootDir)
|
|
190
|
-
for (
|
|
191
|
-
let depth = 0;
|
|
192
|
-
depth < MAX_SCAN_DEPTH && parent !== path.dirname(parent);
|
|
193
|
-
depth++
|
|
194
|
-
) {
|
|
195
|
-
configPath = findSootSimAppConfig(parent)
|
|
196
|
-
if (configPath) break
|
|
197
|
-
parent = path.dirname(parent)
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return { appDir: rootDir, configPath }
|
|
50
|
+
const configPath = findSootSimAppConfig(appDir)
|
|
51
|
+
if (
|
|
52
|
+
!configPath &&
|
|
53
|
+
!findExpoAppConfig(appDir) &&
|
|
54
|
+
!fs.existsSync(path.join(appDir, 'package.json'))
|
|
55
|
+
) {
|
|
56
|
+
return null
|
|
201
57
|
}
|
|
202
|
-
|
|
203
|
-
const workspaceScan = workspaceDirectories(rootDir)
|
|
204
|
-
const workspaceApps = workspaceScan.directories.filter((directory) =>
|
|
205
|
-
findExpoAppConfig(directory),
|
|
206
|
-
)
|
|
207
|
-
if (workspaceApps.length > 1) return null
|
|
208
|
-
if (!workspaceScan.complete) return null
|
|
209
|
-
if (workspaceApps.length === 1) {
|
|
210
|
-
const workspaceAppDir = workspaceApps[0]
|
|
211
|
-
return {
|
|
212
|
-
appDir: workspaceAppDir,
|
|
213
|
-
configPath: rootConfigPath ?? findSootSimAppConfig(workspaceAppDir),
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const fallbackScan = boundedConfigDirectories(rootDir)
|
|
218
|
-
const scannedApps = fallbackScan.directories
|
|
219
|
-
if (scannedApps.length > 1) return null
|
|
220
|
-
if (!fallbackScan.complete) return null
|
|
221
|
-
if (scannedApps.length === 1) {
|
|
222
|
-
const scannedAppDir = scannedApps[0]
|
|
223
|
-
return {
|
|
224
|
-
appDir: scannedAppDir,
|
|
225
|
-
configPath: rootConfigPath ?? findSootSimAppConfig(scannedAppDir),
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
if (rootConfigPath) return { appDir: rootDir, configPath: rootConfigPath }
|
|
230
|
-
return null
|
|
58
|
+
return { appDir, configPath }
|
|
231
59
|
}
|
package/cli/app-splash.ts
CHANGED
|
@@ -33,32 +33,18 @@ function localOriginOf(value: string): string | null {
|
|
|
33
33
|
return null
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function hasPackageJson(dir: string): boolean {
|
|
37
|
-
try {
|
|
38
|
-
return fs.statSync(path.join(dir, 'package.json')).isFile()
|
|
39
|
-
} catch {
|
|
40
|
-
return false
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
36
|
// discover the app's configured splash by evaluating its expo config on the
|
|
45
37
|
// filesystem (mirrors discoverNativeLinkedAppFonts exactly). returns null when
|
|
46
38
|
// no splash is configured, no app config is found, or the bundle isn't local.
|
|
47
39
|
export function discoverConfigPluginAppSplash(opts: {
|
|
48
40
|
bundleUrl: string
|
|
49
|
-
|
|
50
|
-
exactProjectDir?: boolean
|
|
41
|
+
projectDir: string
|
|
51
42
|
platform?: string
|
|
52
43
|
}): DiscoveredAppSplashSpec | null {
|
|
53
44
|
const metroOrigin = localOriginOf(opts.bundleUrl)
|
|
54
45
|
if (!metroOrigin) return null
|
|
55
46
|
|
|
56
|
-
const
|
|
57
|
-
const startDir = explicit ? path.resolve(explicit) : process.cwd()
|
|
58
|
-
|
|
59
|
-
if (!explicit && !hasPackageJson(startDir)) return null
|
|
60
|
-
|
|
61
|
-
const project = resolveAppProject(startDir, { trustStart: opts.exactProjectDir })
|
|
47
|
+
const project = resolveAppProject(opts.projectDir)
|
|
62
48
|
const selectedAppDir = project?.appDir ?? null
|
|
63
49
|
if (!selectedAppDir) return null
|
|
64
50
|
|
|
@@ -67,7 +53,7 @@ export function discoverConfigPluginAppSplash(opts: {
|
|
|
67
53
|
let getConfig: ExpoConfigGetter | null = null
|
|
68
54
|
try {
|
|
69
55
|
const modulePath = require.resolve('@expo/config', {
|
|
70
|
-
paths: [selectedAppDir
|
|
56
|
+
paths: [selectedAppDir],
|
|
71
57
|
})
|
|
72
58
|
const mod = require(modulePath) as { getConfig?: unknown }
|
|
73
59
|
if (typeof mod.getConfig === 'function') {
|
package/cli/bin.ts
CHANGED
|
@@ -89,22 +89,15 @@ const isAutomaticCleanupWorker =
|
|
|
89
89
|
parsed.commandArgs.length === 1 &&
|
|
90
90
|
parsed.commandArgs[0] === '--automatic-worker'
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
parsed.command || (parsed.version ? 'version' : parsed.help ? 'help' : 'none'),
|
|
102
|
-
arg_count: parsed.commandArgs.length,
|
|
103
|
-
platform: process.platform,
|
|
104
|
-
arch: process.arch,
|
|
105
|
-
node_version: process.versions.node,
|
|
106
|
-
},
|
|
107
|
-
})
|
|
92
|
+
if (
|
|
93
|
+
!isAutomaticCleanupWorker &&
|
|
94
|
+
!parsed.help &&
|
|
95
|
+
!parsed.version &&
|
|
96
|
+
parsed.command !== 'config' &&
|
|
97
|
+
parsed.command !== 'telemetry'
|
|
98
|
+
) {
|
|
99
|
+
const { ensurePrivacyChoice } = await import('./privacy')
|
|
100
|
+
await ensurePrivacyChoice({ prompt: true })
|
|
108
101
|
}
|
|
109
102
|
|
|
110
103
|
// one-shot "engine upgraded in the background" banner. the daemon's hourly
|
|
@@ -176,9 +169,8 @@ async function scheduleAutomaticCleanup(): Promise<void> {
|
|
|
176
169
|
}
|
|
177
170
|
}
|
|
178
171
|
|
|
179
|
-
// fast-exit helper:
|
|
180
|
-
//
|
|
181
|
-
// doesn't await pending fetches on exit. flush first, then exit.
|
|
172
|
+
// fast-exit helper: diagnostic error shapes are best-effort, but explicit exit
|
|
173
|
+
// paths still give a permitted queued diagnostic a chance to drain.
|
|
182
174
|
async function exitWithFlush(code: number): Promise<never> {
|
|
183
175
|
const { flushCliTelemetry } = await import('./telemetry')
|
|
184
176
|
await flushCliTelemetry()
|
|
@@ -236,16 +228,11 @@ if (device || theme) {
|
|
|
236
228
|
settingsStore.apply(overrides)
|
|
237
229
|
}
|
|
238
230
|
|
|
239
|
-
// no command, no args:
|
|
231
|
+
// no command, no args: discover the running app and open it directly. explicit
|
|
232
|
+
// `rnx setup` remains available for repository and background-service choices.
|
|
240
233
|
if (!parsed.command && parsed.commandArgs.length === 0) {
|
|
241
|
-
const {
|
|
242
|
-
|
|
243
|
-
await runSetup(['--from-welcome'])
|
|
244
|
-
await exitWithFlush(0)
|
|
245
|
-
}
|
|
246
|
-
const { printHelp } = await import('./help')
|
|
247
|
-
printHelp()
|
|
248
|
-
await exitWithFlush(0)
|
|
234
|
+
const { runWelcome } = await import('./commands/welcome')
|
|
235
|
+
await exitWithFlush(await runWelcome())
|
|
249
236
|
}
|
|
250
237
|
|
|
251
238
|
// route to command. an empty command at this point only happens if the
|