stim 1.13.0 → 1.15.0
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 +2 -0
- package/dist/{activity-DfVKZTEB.mjs → activity-B2-kb7k0.mjs} +281 -41
- package/dist/{android-BHfAu2mf.mjs → android-CYmq0jNi.mjs} +2 -2
- package/dist/{android-CD8pr8Q2.mjs → android-Cu_L4RpL.mjs} +524 -412
- package/dist/{android-qx_31wJo.mjs → android-Db2eEMaf.mjs} +152 -65
- package/dist/android-cas-PnKfAdB2.mjs +167 -0
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{app-install-CDFY2nUL.mjs → app-install-CAAplXAt.mjs} +162 -173
- package/dist/{budget-E5o91NR1.mjs → budget-CMiqghig.mjs} +1048 -77
- package/dist/{build-plan-BTa93o_r.mjs → build-plan-aeGMFX7e.mjs} +193 -650
- package/dist/build-progress-C2Jv1AtA.mjs +872 -0
- package/dist/build-slots-Df1ApSuH.mjs +127 -0
- package/dist/{cache-manifest-DWPF4RKJ.mjs → cache-manifest-DnUHBJaU.mjs} +2 -2
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{chrome-B5hWgQJS.mjs → chrome-BrcVWQyD.mjs} +4 -4
- package/dist/cli-0cX5s4NM.mjs +45 -0
- package/dist/cli.mjs +6 -42
- package/dist/client-C2IS36Qf.mjs +1333 -0
- package/dist/collector-run.mjs +8 -7
- package/dist/{command-output-BIL7rlX7.mjs → command-output-CliHHn7a.mjs} +1 -1
- package/dist/{config-0jCcMM0E.mjs → config-D0DbpZtM.mjs} +29 -29
- package/dist/{created-devices-Cd558h31.mjs → created-devices-BwVb3liH.mjs} +2 -2
- package/dist/{dependency-state-DNN0yfx6.mjs → dependency-state-BmsjKZ2H.mjs} +2 -2
- package/dist/{deps-2Wo81np9.mjs → deps-BFlKbOIp.mjs} +3 -3
- package/dist/{detached-entry-DJHoca49.mjs → detached-entry-CiTEgMR1.mjs} +1 -1
- package/dist/dev-client-CjzLoNs0.mjs +80 -0
- package/dist/{device-Cf7vl-so.mjs → device-BZ1IyS5A.mjs} +7 -7
- package/dist/device-capacity-DMuKC7nx.mjs +136 -0
- package/dist/device-host-worker.d.mts +1 -0
- package/dist/device-host-worker.mjs +769 -0
- package/dist/device-ios-DMQBUPus.mjs +381 -0
- package/dist/{device-lease-CL9dZf73.mjs → device-lease-W5Pi1q38.mjs} +43 -36
- package/dist/{device-pool-MuoiyRcu.mjs → device-pool-B2r38YjS.mjs} +4 -4
- package/dist/{device-remote-DT9ngZiD.mjs → device-remote-9ulPsdhY.mjs} +68 -162
- package/dist/{doctor-D2iysNNH.mjs → doctor-BcE5xsBn.mjs} +191 -99
- package/dist/{error-diagnostics-BoiOhck8.mjs → error-diagnostics-Cb0P1WLS.mjs} +12 -11
- package/dist/{exec-DK8KaVHK.mjs → exec-CaErVv7t.mjs} +5 -1
- package/dist/{gc-eA-PRBX8.mjs → gc-BuL1Genr.mjs} +430 -102
- package/dist/{android-cas-B9Kv9aIy.mjs → gradle-CmdOBq1d.mjs} +8 -294
- package/dist/{guide-BfZ0OmiP.mjs → guide-cCSGRFd3.mjs} +2052 -240
- package/dist/{guide-status-BaMYu55M.mjs → guide-status-CIoWK6mG.mjs} +3 -3
- package/dist/hosted-macos-DwowNU7W.mjs +447 -0
- package/dist/idle-shutdown-B5LPaNyY.mjs +122 -0
- package/dist/in-use-CndPMilN.mjs +110 -0
- package/dist/{ios-CmEzZiOj.mjs → ios-3CtCRhwr.mjs} +201 -10
- package/dist/{ios-CcfCeZ19.mjs → ios-Chob8ny7.mjs} +560 -259
- package/dist/{ios-device-BW-t_Lkv.mjs → ios-device-DERmMmk-.mjs} +12 -7
- package/dist/{ios-device-BiOsSVzd.mjs → ios-device-Ee7sDXfp.mjs} +1 -1
- package/dist/{launch-verify-CuJaBpEa.mjs → launch-verify-CLlofxRt.mjs} +42 -11
- package/dist/{logs-DcSRbC2t.mjs → logs-h9h6MUvE.mjs} +95 -13
- package/dist/machine-usage-D_YYMzfR.mjs +283 -0
- package/dist/machines-CYtOCqcj.mjs +259 -0
- package/dist/macos-BwtbUtTN.mjs +468 -0
- package/dist/macos-run.d.mts +5 -0
- package/dist/macos-run.mjs +2 -0
- package/dist/{merge-state-ytFQCeh4.mjs → merge-state-BnxLNQiM.mjs} +22 -12
- package/dist/{metro-BPeY1YMh.mjs → metro-Di8EN2Tb.mjs} +20 -120
- package/dist/{named-ports-n7tnJsI8.mjs → named-ports-Dj_XBNaL.mjs} +8 -64
- package/dist/native-run-DFDoIRbY.mjs +147 -0
- package/dist/{native-runtime-CiiFHt0e.mjs → native-runtime-i6OkVg-w.mjs} +11 -6
- package/dist/offload-worker.d.mts +121 -0
- package/dist/offload-worker.mjs +601 -0
- package/dist/{ownership-Bz2kETAS.mjs → ownership-BkYxUtuj.mjs} +3 -2
- package/dist/{page-BTu7I60K.mjs → page-QF_Ke10s.mjs} +7 -2
- package/dist/{ports-C5UutqpJ.mjs → ports-DddwttKM.mjs} +3 -3
- package/dist/prebuild-BvxDlD6C.mjs +213 -0
- package/dist/process-identity-BmRm1qV0.mjs +12 -0
- package/dist/{project-DGeclhvO.mjs → project-CV3tDFHl.mjs} +9 -18
- package/dist/pull-request-pxnDCQzn.mjs +322 -0
- package/dist/pull-requests.d.mts +120 -0
- package/dist/pull-requests.mjs +2 -0
- package/dist/recordings-GEXp4iBT.mjs +37 -0
- package/dist/{reload-C-cV5yy_.mjs → reload-ChPiwvot.mjs} +70 -22
- package/dist/{remote-cache--aehr4Pr.mjs → remote-cache-CO2elm_b.mjs} +4 -4
- package/dist/run-B-xqRrsg.mjs +119 -0
- package/dist/{server-bare-Bq8BpSIo.mjs → server-bare-X7PUgizL.mjs} +60 -13
- package/dist/{server-expo-DUCg52-K.mjs → server-expo-DhtX6bom.mjs} +6 -10
- package/dist/server-expo-KJeD109I.mjs +2 -0
- package/dist/{settings-xB0Frkg6.mjs → settings-CduVcoxj.mjs} +77 -26
- package/dist/{settings-ssVBToDR.mjs → settings-DbL6uhH8.mjs} +16 -10
- package/dist/settings.schema.json +357 -2
- package/dist/{simslim-Bbg9UWrt.mjs → simslim-Ciluxlq4.mjs} +8 -132
- package/dist/slot-launch-BY0BrOLh.mjs +65 -0
- package/dist/{spawn-claims-PT8aQpCa.mjs → spawn-claims-D0XfSe8u.mjs} +1 -1
- package/dist/spawn-entry-DncpJvWL.mjs +16 -0
- package/dist/stage-CzaiwFsI.mjs +143 -0
- package/dist/start-CPO4wRg7.mjs +2 -0
- package/dist/{start-BZhItkiL.mjs → start-CUD9nasA.mjs} +108 -61
- package/dist/state-B4PQFXMW.d.mts +12 -0
- package/dist/state-BxCgeTQu.mjs +41 -0
- package/dist/{state-DwAU2_-D.mjs → state-CaeaHbDA.mjs} +7 -4
- package/dist/{state-l1pYwOre.mjs → state-DFm77wRn.mjs} +67 -5
- package/dist/stats-DBLLFmKd.mjs +66 -0
- package/dist/{status-TJlL0HWo.mjs → status-DYXxNOT0.mjs} +425 -260
- package/dist/{status-sszP4XKU.mjs → status-l84Gbvp2.mjs} +122 -20
- package/dist/stdout-reader-C8yjre1x.mjs +23 -0
- package/dist/stim-desktop-Cnz8CPnl.mjs +63 -0
- package/dist/stim-footprint.swift +32 -0
- package/dist/{stim-installations-CINXy_su.mjs → stim-installations-QMEI32v1.mjs} +1 -1
- package/dist/{stop-BZ2qMiBP.mjs → stop-Bad61z_2.mjs} +128 -19
- package/dist/stop-Bvf1MflS.mjs +48 -0
- package/dist/{stop-DskDKNTU.mjs → stop-CceYh1yG.mjs} +2 -2
- package/dist/stop-cause-CZ_Mr-v9.mjs +665 -0
- package/dist/supervisor-run.d.mts +44 -12
- package/dist/supervisor-run.mjs +276 -33
- package/dist/support-CDQbDkL5.mjs +315 -0
- package/dist/support-WLOzVKUv.mjs +176 -0
- package/dist/teardown-BYuQafiA.mjs +718 -0
- package/dist/{xcode-LM6A8cNZ.mjs → toolchain-DmeYjXD9.mjs} +363 -13
- package/dist/warm-progress-DVHLN4uC.mjs +463 -0
- package/dist/{idle-DxLi5OkN.mjs → watchman-Bx0wceoF.mjs} +55 -8
- package/dist/{web-sWXd5iek.mjs → web-k6w8-HnE.mjs} +39 -22
- package/dist/web-run.mjs +326 -13
- package/dist/workspace-process-lock-BL7Q0HCO.mjs +57 -0
- package/dist/workspace-state-CL2V2Ons.mjs +371 -0
- package/dist/worktree-BT_uULFx.mjs +2 -0
- package/dist/{worktree-DeBQSVtR.mjs → worktree-C0c5Udwa.mjs} +55 -54
- package/dist/{worktree-D4Do_XiQ.mjs → worktree-qXKzADSC.mjs} +161 -255
- package/package.json +10 -5
- package/shim/bundle-response.cjs +55 -10
- package/shim/bundle-response.d.cts +1 -0
- package/shim/expo-metro-config.cjs +10 -2
- package/dist/build-progress-l7d-X-ZP.mjs +0 -247
- package/dist/in-use-wYEaKQM9.mjs +0 -249
- package/dist/ownership-PUP4Dwai.mjs +0 -1138
- package/dist/server-expo-BeYwP_Xy.mjs +0 -2
- package/dist/slot-launch-BIWEW-wI.mjs +0 -19
- package/dist/start-Rw2GD3nV.mjs +0 -2
- package/dist/stats-CaSwmsSZ.mjs +0 -62
- package/dist/stats-D8rUTPHd.mjs +0 -368
- package/dist/stim-desktop-CXt8-Z3J.mjs +0 -40
- package/dist/worktree-3cBa-bQt.mjs +0 -2
- package/dist/{metro-store-DzeWkfqW.mjs → metro-store-CDDZHtPl.mjs} +1 -1
|
@@ -1,29 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
4
|
-
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-
|
|
5
|
-
import { c as ownedDeviceLabel
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { i as
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
1
|
+
import { t as getExecutor } from "./exec-CaErVv7t.mjs";
|
|
2
|
+
import { D as withConfigLock$1, H as projectDeviceSlots, _ as loadConfig$1, a as clearDevice, b as releaseAndroidConsolePort, i as clearAndroidBootPending, k as acquireAvdClaim, t as allConsolePortsAndSerials, w as setDevice, z as deviceSlotPlatforms } from "./config-D0DbpZtM.mjs";
|
|
3
|
+
import { m as sharedBuildCache } from "./workspace-state-CL2V2Ons.mjs";
|
|
4
|
+
import { D as makeTemporaryDirectory, O as removeTemporaryEntry, c as gitCommonDirOnDisk } from "./worktree-C0c5Udwa.mjs";
|
|
5
|
+
import { c as ownedDeviceLabel } from "./project-CV3tDFHl.mjs";
|
|
6
|
+
import { r as waitForChild, t as createLineReader } from "./process-output-Fpl8PYi8.mjs";
|
|
7
|
+
import { i as formatDuration, l as phaseLine, m as shortHash, u as plural } from "./command-output-CliHHn7a.mjs";
|
|
8
|
+
import { C as diffFingerprintSources, I as storedSources, w as entryDir, y as artifactIn } from "./device-lease-W5Pi1q38.mjs";
|
|
9
|
+
import { A as listAvds, B as ownedAvdSystemImage, E as getAvdNameForSerial, F as ownedAvdDeviceProfile, J as resolveOwnedAvdSerial, L as ownedAvdMatchesConfiguration, O as listAdbDevices, P as nextConsolePort, R as ownedAvdName, W as pickDefaultSystemImage, b as createOwnedAvd, dt as parkedMaxSetting, f as assertOwnedAvdStopped, ft as readParked, g as avdPoolConfiguration, j as listInstalledSystemImages, lt as isLegacyDeletionClaim, n as OWNED_AVD_CONFIG_DEFAULTS, ot as adoptParked, t as DEFAULT_AVD_DEVICE_PROFILE, tt as waitForBoot, v as bootAndroidEmulator, y as configureNewOwnedAvd } from "./android-Db2eEMaf.mjs";
|
|
10
|
+
import { o as pidExists } from "./metro-Di8EN2Tb.mjs";
|
|
11
|
+
import { l as androidDataPartitionSizeGbSetting, o as androidAvdConfigSetting } from "./settings-CduVcoxj.mjs";
|
|
12
|
+
import { i as teardownOwnedAvd, s as teardownParkedAvd } from "./teardown-BYuQafiA.mjs";
|
|
13
|
+
import { j as readHostMemoryPressure, u as listAllIosSims } from "./ios-3CtCRhwr.mjs";
|
|
14
|
+
import { o as estimateBuild, p as readStats } from "./build-progress-C2Jv1AtA.mjs";
|
|
15
|
+
import { t as clearDeviceIdleShutdown } from "./idle-shutdown-B5LPaNyY.mjs";
|
|
16
|
+
import { i as liveOwnedDeviceCount } from "./device-capacity-DMuKC7nx.mjs";
|
|
17
|
+
import { b as tailLines, u as HEARTBEAT_INTERVAL_MS, y as startBuildHeartbeat } from "./toolchain-DmeYjXD9.mjs";
|
|
18
|
+
import { t as cleanLine } from "./server-expo-DhtX6bom.mjs";
|
|
19
|
+
import { i as staleNativeDirRefusal } from "./prebuild-BvxDlD6C.mjs";
|
|
20
|
+
import { i as ensureOwnedIosDevice, r as ensureIosBooted } from "./device-ios-DMQBUPus.mjs";
|
|
21
|
+
import { a as easAuthNote } from "./remote-cache-CO2elm_b.mjs";
|
|
22
|
+
import { t as reconcileSimSlim } from "./simslim-Ciluxlq4.mjs";
|
|
23
|
+
import { randomUUID } from "node:crypto";
|
|
24
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
23
25
|
import { basename, dirname, join, relative } from "node:path";
|
|
26
|
+
import { LAST_BUILD_KEYS, loadConfig, readWorkspaceState } from "@stim-cli/core/state";
|
|
24
27
|
import { claimRemoveCommand, clearClaimChild, markClaimChildPending, processGroupAlive, releaseClaim, setClaimChild } from "@stim-cli/core/ownership-claim";
|
|
25
28
|
import { captureProcessIdentity } from "@stim-cli/core/process-identity";
|
|
26
|
-
import { randomUUID } from "node:crypto";
|
|
27
29
|
import chalk from "chalk";
|
|
28
30
|
import { isDeepStrictEqual } from "node:util";
|
|
29
31
|
import * as expoFingerprint from "@expo/fingerprint";
|
|
@@ -91,367 +93,6 @@ async function warmMetro({ port, platform, isExpo, appId, bundleUrl }) {
|
|
|
91
93
|
} catch {}
|
|
92
94
|
}
|
|
93
95
|
//#endregion
|
|
94
|
-
//#region src/engine/device-ios.ts
|
|
95
|
-
function startIosBoot(udid, configure, label, out, simulatorApp) {
|
|
96
|
-
const done = (async () => {
|
|
97
|
-
await bootIosSim(udid, {
|
|
98
|
-
label,
|
|
99
|
-
out,
|
|
100
|
-
simulatorApp
|
|
101
|
-
});
|
|
102
|
-
await configure();
|
|
103
|
-
})();
|
|
104
|
-
done.catch(() => {});
|
|
105
|
-
return {
|
|
106
|
-
udid,
|
|
107
|
-
done
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
async function ensureOwnedIosDevice({ record, projectPath, slot, settingsRoot, label, settings, flags, note, out, reconcileIosSimulator }) {
|
|
111
|
-
const memoryAdvice = hostMemoryPressureAdvice(readHostMemoryPressure());
|
|
112
|
-
if (memoryAdvice) out(chalk.yellow(phaseLine("memory", memoryAdvice)));
|
|
113
|
-
const simslimProfile = iosSimSlimProfileSetting(settings, settingsRoot);
|
|
114
|
-
if (record?.deviceUdid) {
|
|
115
|
-
if (record.owned) {
|
|
116
|
-
const resolved = resolveOwnedIosSim(record.deviceUdid);
|
|
117
|
-
if (resolved.notOwned) note(chalk.yellow(`Note: recorded sim is now named "${resolved.notOwned}", not Stim-owned -- creating a fresh owned sim instead of booting it.`));
|
|
118
|
-
else if (resolved.missing) {} else {
|
|
119
|
-
const sim = resolved.sim;
|
|
120
|
-
const wantedType = flags.deviceType || settings?.ios?.deviceType;
|
|
121
|
-
const deviceTypes = listIosDeviceTypes();
|
|
122
|
-
const mismatch = deviceTypeMismatch(sim.deviceTypeIdentifier, wantedType, deviceTypes);
|
|
123
|
-
if (mismatch) throw new Error(`${mismatch}. Stim will not silently boot a different model. Run \`stim worktree remove\` (or \`stim gc --delete\`) to reap the current sim, then \`stim ios\` again to create the requested one.`);
|
|
124
|
-
const versionMismatch = flags.runtimeFlag ? runtimeMismatch(sim.runtime, flags.runtimeFlag, listIosRuntimes()) : null;
|
|
125
|
-
if (versionMismatch) throw new Error(`${versionMismatch}. Stim will not silently boot a different iOS version. Run \`stim worktree remove\` (or \`stim gc --delete\`) to reap the current sim, then \`stim ios\` again to create the requested one, or pass \`--slot <name>\` to create it beside the current one.`);
|
|
126
|
-
const model = {
|
|
127
|
-
model: deviceTypes.find((d) => d.identifier === sim.deviceTypeIdentifier)?.name ?? null,
|
|
128
|
-
runtime: parseRuntimeVersion(sim.runtime)
|
|
129
|
-
};
|
|
130
|
-
const name = await withIosDeviceNameLock(() => renameToOwnedName(sim, label, model, projectPath));
|
|
131
|
-
const updated = {
|
|
132
|
-
deviceUdid: sim.udid,
|
|
133
|
-
owned: true,
|
|
134
|
-
deviceName: name,
|
|
135
|
-
...record.simslimManaged ? { simslimManaged: true } : {},
|
|
136
|
-
...record.adopted ? { adopted: true } : {},
|
|
137
|
-
...record.adoptionPending ? { adoptionPending: true } : {},
|
|
138
|
-
...record.parkedCacheKey ? { parkedCacheKey: record.parkedCacheKey } : {}
|
|
139
|
-
};
|
|
140
|
-
const configure = async () => {
|
|
141
|
-
if (record.adoptionPending) resetAdoptedSim(sim.udid, out);
|
|
142
|
-
return configureOwnedIosSim({
|
|
143
|
-
record: updated,
|
|
144
|
-
projectPath,
|
|
145
|
-
slot,
|
|
146
|
-
profile: simslimProfile,
|
|
147
|
-
out,
|
|
148
|
-
reconcileIosSimulator
|
|
149
|
-
});
|
|
150
|
-
};
|
|
151
|
-
const facts = {
|
|
152
|
-
deviceType: model.model,
|
|
153
|
-
runtime: model.runtime
|
|
154
|
-
};
|
|
155
|
-
if (sim.state !== "Booted") {
|
|
156
|
-
out(chalk.dim(phaseLine("device", `booting ${name} (${sim.udid})`)));
|
|
157
|
-
return {
|
|
158
|
-
...updated,
|
|
159
|
-
booting: startIosBoot(sim.udid, configure, name, out, flags.simulatorApp),
|
|
160
|
-
...facts
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
...await configure(),
|
|
165
|
-
...facts
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
const sim = listAllIosSims().find((s) => s.udid === record.deviceUdid);
|
|
170
|
-
if (sim) {
|
|
171
|
-
if (sim.state !== "Booted") {
|
|
172
|
-
note(chalk.yellow(`Note: assigned sim ${sim.name} (${sim.udid}) is shut down and is not owned by Stim, so it will not be booted automatically.`));
|
|
173
|
-
note(chalk.dim("Boot it yourself, or run `stim gc --delete` to clear the assignment so Stim can create an owned sim."));
|
|
174
|
-
}
|
|
175
|
-
return record;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
withConfigLock$1(() => {
|
|
179
|
-
const current = deviceSlotPlatforms(loadConfig$1()?.projects?.[projectPath], slot)?.ios;
|
|
180
|
-
if (!current) return;
|
|
181
|
-
if (current.deviceUdid !== record.deviceUdid || current.owned !== record.owned) throw new Error("The simulator assignment changed during recovery. Run `stim ios` again.");
|
|
182
|
-
clearDevice(projectPath, "ios", slot);
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
const choice = resolveIosCreation({
|
|
186
|
-
deviceType: flags.deviceType || settings.ios?.deviceType,
|
|
187
|
-
runtime: flags.runtime || settings.ios?.runtime
|
|
188
|
-
});
|
|
189
|
-
const adopted = parkedMaxSetting("ios").max > 0 ? await withIosDeviceNameLock(() => takeParkedIosSim({
|
|
190
|
-
projectPath,
|
|
191
|
-
slot,
|
|
192
|
-
label,
|
|
193
|
-
choice,
|
|
194
|
-
out
|
|
195
|
-
})) : null;
|
|
196
|
-
if (adopted) {
|
|
197
|
-
out(chalk.dim(phaseLine("device", `booting ${adopted.deviceName} (${adopted.deviceUdid})`)));
|
|
198
|
-
const booting = startIosBoot(adopted.deviceUdid, async () => {
|
|
199
|
-
resetAdoptedSim(adopted.deviceUdid, out);
|
|
200
|
-
await configureOwnedIosSim({
|
|
201
|
-
record: adopted,
|
|
202
|
-
projectPath,
|
|
203
|
-
slot,
|
|
204
|
-
profile: simslimProfile,
|
|
205
|
-
out,
|
|
206
|
-
reconcileIosSimulator
|
|
207
|
-
});
|
|
208
|
-
}, adopted.deviceName, out, flags.simulatorApp);
|
|
209
|
-
return {
|
|
210
|
-
...adopted,
|
|
211
|
-
booting,
|
|
212
|
-
deviceType: choice.deviceType,
|
|
213
|
-
runtime: choice.runtime
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
const created = await withIosDeviceNameLock(() => {
|
|
217
|
-
const suffix = ownedIosNameSuffix(label, {
|
|
218
|
-
model: choice.deviceType,
|
|
219
|
-
runtime: choice.runtime
|
|
220
|
-
}, projectPath);
|
|
221
|
-
const result = createOwnedIosSim(label, { suffix }, choice);
|
|
222
|
-
setDevice(projectPath, "ios", {
|
|
223
|
-
deviceUdid: result.udid,
|
|
224
|
-
owned: true,
|
|
225
|
-
deviceName: result.name
|
|
226
|
-
}, slot);
|
|
227
|
-
return result;
|
|
228
|
-
});
|
|
229
|
-
const newRecord = {
|
|
230
|
-
deviceUdid: created.udid,
|
|
231
|
-
owned: true,
|
|
232
|
-
deviceName: created.name
|
|
233
|
-
};
|
|
234
|
-
const booting = startIosBoot(created.udid, () => configureOwnedIosSim({
|
|
235
|
-
record: newRecord,
|
|
236
|
-
projectPath,
|
|
237
|
-
slot,
|
|
238
|
-
profile: simslimProfile,
|
|
239
|
-
out,
|
|
240
|
-
reconcileIosSimulator
|
|
241
|
-
}), created.name, out, flags.simulatorApp);
|
|
242
|
-
return {
|
|
243
|
-
...newRecord,
|
|
244
|
-
created: true,
|
|
245
|
-
booting,
|
|
246
|
-
deviceType: created.deviceType,
|
|
247
|
-
runtime: created.runtime
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
function withIosDeviceNameLock(fn) {
|
|
251
|
-
return withWorkspaceProcessLock(getConfigDir(), "ios-device-names", async () => fn(), { external: true });
|
|
252
|
-
}
|
|
253
|
-
function ownedIosNameSuffix(label, model, projectPath, udid) {
|
|
254
|
-
const names = new Set(listAllIosSims({ includeUnavailable: true }).filter((sim) => sim.udid !== udid).map((sim) => sim.name));
|
|
255
|
-
for (const project of Object.values(loadConfig$1()?.projects ?? {})) for (const { platforms } of projectDeviceSlots(project)) {
|
|
256
|
-
const record = platforms.ios;
|
|
257
|
-
if (record?.deviceUdid !== udid && record?.deviceName) names.add(record.deviceName);
|
|
258
|
-
}
|
|
259
|
-
for (const parked of readParked("ios")) if (parked.udid !== udid) names.add(parked.name);
|
|
260
|
-
let suffix = "";
|
|
261
|
-
for (let attempt = 0; names.has(ownedSimName(label, model, suffix)); attempt++) suffix = ` ${workspaceId(projectPath)}${attempt ? `-${attempt}` : ""}`;
|
|
262
|
-
return suffix;
|
|
263
|
-
}
|
|
264
|
-
function renameToOwnedName(sim, label, model, projectPath) {
|
|
265
|
-
const wanted = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, sim.udid));
|
|
266
|
-
if (sim.name === wanted) return wanted;
|
|
267
|
-
try {
|
|
268
|
-
renameIosSim(sim.udid, wanted);
|
|
269
|
-
return wanted;
|
|
270
|
-
} catch {
|
|
271
|
-
return sim.name;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
function resetAdoptedSim(udid, out) {
|
|
275
|
-
out(chalk.dim(phaseLine("device", `resetting privacy grants and the keychain on ${udid}`)));
|
|
276
|
-
resetIosPrivacy(udid);
|
|
277
|
-
resetIosKeychain(udid);
|
|
278
|
-
}
|
|
279
|
-
function takeParkedIosSim({ projectPath, slot, label, choice, out }) {
|
|
280
|
-
const candidates = selectParked(readParked("ios"), {
|
|
281
|
-
deviceTypeIdentifier: choice.deviceTypeId,
|
|
282
|
-
runtimeIdentifier: choice.runtimeId
|
|
283
|
-
});
|
|
284
|
-
if (candidates.length === 0) return null;
|
|
285
|
-
const listed = new Map(listAllIosSims({ includeUnavailable: true }).map((sim) => [sim.udid, sim]));
|
|
286
|
-
for (const parked of candidates) {
|
|
287
|
-
const sim = listed.get(parked.udid);
|
|
288
|
-
if (!sim || !sim.available) {
|
|
289
|
-
const result = sim ? teardownParkedIosSim(parked.udid, { label: parked.name }) : null;
|
|
290
|
-
if (sim ? result?.status === "torn-down" : dropParked("ios", parked.udid)) out(chalk.dim(phaseLine("device", `deleted parked ${parked.name} (${parked.udid}): ${sim ? "unavailable" : "gone"}`)));
|
|
291
|
-
else if (result?.status === "failed") out(chalk.yellow(phaseLine("device", `could not delete unavailable parked ${parked.name} (${parked.udid}): ${result.reason}`)));
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (!sim.name.startsWith("stim-")) {
|
|
295
|
-
out(chalk.yellow(phaseLine("device", `kept parked record for ${parked.udid}: simulator is now named ${JSON.stringify(sim.name)} and is not Stim-owned`)));
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
const model = {
|
|
299
|
-
model: choice.deviceType,
|
|
300
|
-
runtime: choice.runtime
|
|
301
|
-
};
|
|
302
|
-
const name = ownedSimName(label, model, ownedIosNameSuffix(label, model, projectPath, parked.udid));
|
|
303
|
-
const device = {
|
|
304
|
-
deviceUdid: parked.udid,
|
|
305
|
-
owned: true,
|
|
306
|
-
deviceName: name,
|
|
307
|
-
adopted: true,
|
|
308
|
-
adoptionPending: true,
|
|
309
|
-
...parked.simslimManaged ? { simslimManaged: true } : {},
|
|
310
|
-
...parked.cacheKey ? { parkedCacheKey: parked.cacheKey } : {}
|
|
311
|
-
};
|
|
312
|
-
if (!adoptParked({
|
|
313
|
-
platform: "ios",
|
|
314
|
-
projectPath,
|
|
315
|
-
slot,
|
|
316
|
-
udid: parked.udid,
|
|
317
|
-
device
|
|
318
|
-
})) continue;
|
|
319
|
-
try {
|
|
320
|
-
renameIosSim(parked.udid, name);
|
|
321
|
-
} catch {}
|
|
322
|
-
return device;
|
|
323
|
-
}
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
function clearIosAdoptionPending(projectPath, slot = "default") {
|
|
327
|
-
withConfigLock$1(() => {
|
|
328
|
-
const cfg = loadConfig$1();
|
|
329
|
-
const ios = deviceSlotPlatforms(cfg?.projects?.[projectPath], slot)?.ios;
|
|
330
|
-
if (!cfg || !ios?.adoptionPending) return;
|
|
331
|
-
delete ios.adopted;
|
|
332
|
-
delete ios.adoptionPending;
|
|
333
|
-
delete ios.parkedCacheKey;
|
|
334
|
-
saveConfig(cfg);
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
async function configureOwnedIosSim({ record, projectPath, slot, profile, out, reconcileIosSimulator }) {
|
|
338
|
-
if (profile) out(chalk.dim(phaseLine("device", `applying the SimSlim profile to ${record.deviceUdid}`)));
|
|
339
|
-
else if (record.simslimManaged) out(chalk.dim(phaseLine("device", `restoring stock simulator services on ${record.deviceUdid}`)));
|
|
340
|
-
const previouslyManaged = Boolean(record.simslimManaged);
|
|
341
|
-
if (profile && !record.simslimManaged) {
|
|
342
|
-
const pending = {
|
|
343
|
-
...record,
|
|
344
|
-
simslimManaged: true
|
|
345
|
-
};
|
|
346
|
-
setDevice(projectPath, "ios", pending, slot);
|
|
347
|
-
record = pending;
|
|
348
|
-
}
|
|
349
|
-
const result = await reconcileIosSimulator({
|
|
350
|
-
udid: record.deviceUdid,
|
|
351
|
-
profile,
|
|
352
|
-
previouslyManaged,
|
|
353
|
-
out
|
|
354
|
-
});
|
|
355
|
-
const updated = { ...record };
|
|
356
|
-
if (result.managed) updated.simslimManaged = true;
|
|
357
|
-
else delete updated.simslimManaged;
|
|
358
|
-
setDevice(projectPath, "ios", updated, slot);
|
|
359
|
-
return updated;
|
|
360
|
-
}
|
|
361
|
-
async function ensureIosBooted({ device, simulatorApp, timeoutMs, pollMs, out }) {
|
|
362
|
-
const udid = device?.deviceUdid;
|
|
363
|
-
if (!udid) return {
|
|
364
|
-
failed: true,
|
|
365
|
-
reason: "No iOS simulator is recorded for this project."
|
|
366
|
-
};
|
|
367
|
-
const ready = () => {
|
|
368
|
-
try {
|
|
369
|
-
getExecutor().runFile("xcrun", [
|
|
370
|
-
"simctl",
|
|
371
|
-
"spawn",
|
|
372
|
-
udid,
|
|
373
|
-
"launchctl",
|
|
374
|
-
"list"
|
|
375
|
-
], { timeoutMs: 3e4 });
|
|
376
|
-
return {
|
|
377
|
-
ok: true,
|
|
378
|
-
udid
|
|
379
|
-
};
|
|
380
|
-
} catch (error) {
|
|
381
|
-
return {
|
|
382
|
-
failed: true,
|
|
383
|
-
reason: `Simulator ${udid} failed its process-spawn readiness check: ${error?.message || error}. ${iosSimulatorFailureAdvice()}`
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
const booting = device?.booting;
|
|
388
|
-
if (booting?.udid === udid) {
|
|
389
|
-
try {
|
|
390
|
-
await booting.done;
|
|
391
|
-
} catch (e) {
|
|
392
|
-
return {
|
|
393
|
-
failed: true,
|
|
394
|
-
reason: `Could not boot simulator ${udid}: ${e?.message || e}`
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
return ready();
|
|
398
|
-
}
|
|
399
|
-
let resolved;
|
|
400
|
-
try {
|
|
401
|
-
resolved = resolveOwnedIosSim(udid);
|
|
402
|
-
} catch (e) {
|
|
403
|
-
return {
|
|
404
|
-
failed: true,
|
|
405
|
-
reason: `Could not list simulators: ${e?.message || e}`
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
if (resolved.missing) return {
|
|
409
|
-
failed: true,
|
|
410
|
-
reason: `Simulator ${udid} no longer exists. Run \`stim ios\` again to create a fresh owned sim.`
|
|
411
|
-
};
|
|
412
|
-
if (resolved.notOwned) return {
|
|
413
|
-
failed: true,
|
|
414
|
-
reason: `Simulator ${udid} is now named "${resolved.notOwned}" and is not Stim-owned; refusing to boot it.`
|
|
415
|
-
};
|
|
416
|
-
const sim = resolved.sim;
|
|
417
|
-
if (sim.state === "Booted") {
|
|
418
|
-
const result = ready();
|
|
419
|
-
if (result.ok && simulatorApp !== void 0) configuredIosSimulatorViewer(simulatorApp).open(udid);
|
|
420
|
-
return result;
|
|
421
|
-
}
|
|
422
|
-
out(chalk.dim(phaseLine("device", `booting ${sim.name} (${udid})`)));
|
|
423
|
-
const bootDeadline = Date.now() + timeoutMs;
|
|
424
|
-
try {
|
|
425
|
-
await bootIosSim(udid, {
|
|
426
|
-
timeoutMs,
|
|
427
|
-
label: sim.name,
|
|
428
|
-
out,
|
|
429
|
-
simulatorApp
|
|
430
|
-
});
|
|
431
|
-
} catch (e) {
|
|
432
|
-
return {
|
|
433
|
-
failed: true,
|
|
434
|
-
reason: `Could not boot simulator ${udid}: ${e?.message || e}`
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
const deadline = Math.max(bootDeadline, Date.now() + 2 * pollMs);
|
|
438
|
-
while (Date.now() < deadline) {
|
|
439
|
-
await sleep(pollMs);
|
|
440
|
-
let state = null;
|
|
441
|
-
try {
|
|
442
|
-
state = listAllIosSims({ timeoutMs: 3e4 }).find((s) => s.udid === udid)?.state ?? null;
|
|
443
|
-
} catch {}
|
|
444
|
-
if (state === "Booted") return ready();
|
|
445
|
-
}
|
|
446
|
-
return {
|
|
447
|
-
failed: true,
|
|
448
|
-
reason: `Simulator ${udid} did not reach the Booted state within ${Math.round(timeoutMs / 1e3)}s.`
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
function sleep(ms) {
|
|
452
|
-
return new Promise((r) => setTimeout(r, ms));
|
|
453
|
-
}
|
|
454
|
-
//#endregion
|
|
455
96
|
//#region src/engine/android-avd-setup.ts
|
|
456
97
|
var AvdBootError = class extends Error {
|
|
457
98
|
remedy;
|
|
@@ -860,7 +501,8 @@ async function bootOwnedAvdOnFreshPort({ avdName, metadata, projectPath, slot, d
|
|
|
860
501
|
const { bootPending: _booted, ...booted } = claim;
|
|
861
502
|
return {
|
|
862
503
|
...booted,
|
|
863
|
-
serial
|
|
504
|
+
serial,
|
|
505
|
+
setup: true
|
|
864
506
|
};
|
|
865
507
|
} catch (error) {
|
|
866
508
|
releaseAndroidConsolePort(projectPath, claim.consolePort, slot);
|
|
@@ -987,7 +629,15 @@ async function ensureAndroidBooted({ device, projectPath, slot, timeoutMs, out,
|
|
|
987
629
|
}
|
|
988
630
|
//#endregion
|
|
989
631
|
//#region src/engine/device.ts
|
|
632
|
+
/**
|
|
633
|
+
* Whether `ensureOwnedDevice` created, adopted or started booting the device, rather than finding it booted.
|
|
634
|
+
* Undefined for a remote device, which those functions do not describe.
|
|
635
|
+
*/
|
|
636
|
+
function didSetUpDevice(device, remote) {
|
|
637
|
+
return remote ? void 0 : Boolean(device.setup || device.booting);
|
|
638
|
+
}
|
|
990
639
|
async function ensureOwnedDevice({ platform, project, projectPath, slot, settingsRoot = projectPath, label = ownedDeviceLabel(projectPath), settings, flags = {}, note = () => {}, out = () => {}, logFile = null, alive = pidExists, configureAvd = configureNewOwnedAvd, teardownAvd = teardownOwnedAvd, reconcileIosSimulator = reconcileSimSlim }) {
|
|
640
|
+
clearDeviceIdleShutdown(projectPath, platform, slot);
|
|
991
641
|
if (slot && slot !== "default") label = `${label}-${slot}`;
|
|
992
642
|
const record = deviceSlotPlatforms(project, slot)?.[platform] ?? null;
|
|
993
643
|
if (platform === "ios") return ensureOwnedIosDevice({
|
|
@@ -1042,210 +692,6 @@ async function ensureBooted({ platform, device, simulatorApp, timeoutMs, pollMs
|
|
|
1042
692
|
};
|
|
1043
693
|
}
|
|
1044
694
|
//#endregion
|
|
1045
|
-
//#region src/engine/prebuild.ts
|
|
1046
|
-
const PREBUILD_ERROR = "STIM_PREBUILD_FAILED";
|
|
1047
|
-
const LAST_LINES$1 = 20;
|
|
1048
|
-
function nativeDirName(platform) {
|
|
1049
|
-
return platform === "android" ? "android" : "ios";
|
|
1050
|
-
}
|
|
1051
|
-
function nativeDir(root, platform) {
|
|
1052
|
-
return join(root, nativeDirName(platform));
|
|
1053
|
-
}
|
|
1054
|
-
function nativeDirInFingerprint(sources, platform) {
|
|
1055
|
-
const dir = nativeDirName(platform);
|
|
1056
|
-
return sources.some((source) => source.type === "dir" && source.filePath === dir && source.hash != null);
|
|
1057
|
-
}
|
|
1058
|
-
/**
|
|
1059
|
-
* @expo/fingerprint leaves a native directory out of the hash when git ignores it (CNG), so the cache
|
|
1060
|
-
* key only covers that directory when this workspace's recorded prebuild ran on the same fingerprint.
|
|
1061
|
-
*/
|
|
1062
|
-
function prebuildAction({ isExpo, nativeDirExists, nativeDirFingerprinted, nativeDirTracked, generatedFrom, fingerprint }) {
|
|
1063
|
-
if (!isExpo) return "none";
|
|
1064
|
-
if (!nativeDirExists) return "generate";
|
|
1065
|
-
if (nativeDirFingerprinted || generatedFrom === fingerprint) return "none";
|
|
1066
|
-
return nativeDirTracked ? "refuse" : "regenerate";
|
|
1067
|
-
}
|
|
1068
|
-
function prebuildRecord(state) {
|
|
1069
|
-
const record = state?.prebuild;
|
|
1070
|
-
return record && typeof record === "object" ? record : {};
|
|
1071
|
-
}
|
|
1072
|
-
function gitTracksNativeDir(root, platform) {
|
|
1073
|
-
try {
|
|
1074
|
-
return getExecutor().runFile("git", [
|
|
1075
|
-
"-C",
|
|
1076
|
-
root,
|
|
1077
|
-
"ls-files",
|
|
1078
|
-
"--",
|
|
1079
|
-
nativeDirName(platform)
|
|
1080
|
-
], { env: {
|
|
1081
|
-
LC_ALL: "C",
|
|
1082
|
-
LANGUAGE: "C"
|
|
1083
|
-
} }).trim() !== "";
|
|
1084
|
-
} catch (error) {
|
|
1085
|
-
if (error?.code === "ENOENT") return false;
|
|
1086
|
-
return !/not a git repository \(or any (of the parent directories|parent up to mount point)/.test(String(error?.message));
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
function planPrebuild(root, platform, { isExpo, fingerprint, sources }) {
|
|
1090
|
-
const nativeDirExists = existsSync(nativeDir(root, platform));
|
|
1091
|
-
const fingerprinted = nativeDirInFingerprint(sources, platform);
|
|
1092
|
-
const generatedFrom = prebuildRecord(readWorkspaceState$1(root))[platform];
|
|
1093
|
-
return prebuildAction({
|
|
1094
|
-
isExpo,
|
|
1095
|
-
nativeDirExists,
|
|
1096
|
-
nativeDirFingerprinted: fingerprinted,
|
|
1097
|
-
nativeDirTracked: nativeDirExists && !fingerprinted && gitTracksNativeDir(root, platform),
|
|
1098
|
-
generatedFrom: typeof generatedFrom === "string" ? generatedFrom : null,
|
|
1099
|
-
fingerprint
|
|
1100
|
-
});
|
|
1101
|
-
}
|
|
1102
|
-
function recordPrebuild(root, platform, fingerprint) {
|
|
1103
|
-
updateWorkspaceState(root, (state) => ({
|
|
1104
|
-
...state,
|
|
1105
|
-
prebuild: {
|
|
1106
|
-
...prebuildRecord(state),
|
|
1107
|
-
[platform]: fingerprint
|
|
1108
|
-
}
|
|
1109
|
-
}));
|
|
1110
|
-
}
|
|
1111
|
-
function staleNativeDirRefusal(platform) {
|
|
1112
|
-
const dir = nativeDirName(platform);
|
|
1113
|
-
return {
|
|
1114
|
-
code: PREBUILD_ERROR,
|
|
1115
|
-
message: `The fingerprint leaves ${dir}/ out of the cache key, and Stim cannot show that ${dir}/ was generated from the current app config. Git tracks files in it, or could not say whether it does, so Stim will not regenerate it with \`expo prebuild --clean\`.`,
|
|
1116
|
-
remedy: `Stop excluding ${dir}/ from the fingerprint (check .fingerprintignore) so the cache key covers its files, or add ${dir}/ to .gitignore and untrack it so Stim regenerates it from the app config.`
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
function prebuildRefusal({ isExpo, platform, nativeDirExists }) {
|
|
1120
|
-
if (nativeDirExists || isExpo) return null;
|
|
1121
|
-
const dir = nativeDirName(platform);
|
|
1122
|
-
return {
|
|
1123
|
-
code: PREBUILD_ERROR,
|
|
1124
|
-
message: `This project has no ${dir}/ directory and is not an Expo (CNG) project, so Stim cannot generate one.`,
|
|
1125
|
-
remedy: `Check out or generate the native project (\`npx @react-native-community/cli init\` produced one originally), or add \`expo\` to the project so \`expo prebuild\` can create ${dir}/.`
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
function prebuildFailure(logWriter, result) {
|
|
1129
|
-
logWriter?.write?.({
|
|
1130
|
-
src: "build",
|
|
1131
|
-
level: "error",
|
|
1132
|
-
msg: result.reason || "Expo prebuild failed.",
|
|
1133
|
-
event: "prebuild_failed"
|
|
1134
|
-
});
|
|
1135
|
-
return result;
|
|
1136
|
-
}
|
|
1137
|
-
async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Date.now, isExpo = null, clean = false } = {}) {
|
|
1138
|
-
const dirExists = existsSync(nativeDir(root, platform));
|
|
1139
|
-
const refusal = prebuildRefusal({
|
|
1140
|
-
isExpo: isExpo === null ? detectIsExpo(root) : isExpo,
|
|
1141
|
-
platform,
|
|
1142
|
-
nativeDirExists: dirExists
|
|
1143
|
-
});
|
|
1144
|
-
if (refusal) return prebuildFailure(logWriter, {
|
|
1145
|
-
failed: true,
|
|
1146
|
-
code: refusal.code,
|
|
1147
|
-
reason: refusal.message,
|
|
1148
|
-
remedy: refusal.remedy,
|
|
1149
|
-
error: refusal,
|
|
1150
|
-
lastLines: []
|
|
1151
|
-
});
|
|
1152
|
-
const bin = expoBinPath(root);
|
|
1153
|
-
if (!bin) {
|
|
1154
|
-
const binRefusal = expoBinRefusal(root, "prebuild");
|
|
1155
|
-
return prebuildFailure(logWriter, {
|
|
1156
|
-
failed: true,
|
|
1157
|
-
code: PREBUILD_ERROR,
|
|
1158
|
-
reason: binRefusal.message,
|
|
1159
|
-
remedy: binRefusal.remedy,
|
|
1160
|
-
lastLines: []
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
const spawn = spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts));
|
|
1164
|
-
const startedAt = now();
|
|
1165
|
-
const tail = [];
|
|
1166
|
-
const push = (line) => {
|
|
1167
|
-
const msg = stripAnsi(String(line)).trimEnd();
|
|
1168
|
-
if (!msg.trim()) return;
|
|
1169
|
-
tail.push(msg);
|
|
1170
|
-
if (tail.length > LAST_LINES$1) tail.shift();
|
|
1171
|
-
logWriter?.write?.({
|
|
1172
|
-
src: "build",
|
|
1173
|
-
level: "debug",
|
|
1174
|
-
msg,
|
|
1175
|
-
raw: true,
|
|
1176
|
-
event: "prebuild"
|
|
1177
|
-
});
|
|
1178
|
-
};
|
|
1179
|
-
let child;
|
|
1180
|
-
try {
|
|
1181
|
-
child = spawnDeclared(() => spawn(bin, [
|
|
1182
|
-
"prebuild",
|
|
1183
|
-
"-p",
|
|
1184
|
-
platform,
|
|
1185
|
-
"--no-install",
|
|
1186
|
-
...clean ? ["--clean"] : []
|
|
1187
|
-
], {
|
|
1188
|
-
cwd: root,
|
|
1189
|
-
stdio: [
|
|
1190
|
-
"ignore",
|
|
1191
|
-
"pipe",
|
|
1192
|
-
"pipe"
|
|
1193
|
-
],
|
|
1194
|
-
env: {
|
|
1195
|
-
...process.env,
|
|
1196
|
-
FORCE_COLOR: "0",
|
|
1197
|
-
CI: "1"
|
|
1198
|
-
}
|
|
1199
|
-
}));
|
|
1200
|
-
} catch (err) {
|
|
1201
|
-
return prebuildFailure(logWriter, {
|
|
1202
|
-
failed: true,
|
|
1203
|
-
code: PREBUILD_ERROR,
|
|
1204
|
-
reason: `Could not run \`expo prebuild\`: ${err?.message || err}`,
|
|
1205
|
-
lastLines: []
|
|
1206
|
-
});
|
|
1207
|
-
}
|
|
1208
|
-
const outReader = createLineReader(push);
|
|
1209
|
-
const errReader = createLineReader(push);
|
|
1210
|
-
child.stdout?.setEncoding?.("utf-8");
|
|
1211
|
-
child.stderr?.setEncoding?.("utf-8");
|
|
1212
|
-
child.stdout?.on("data", (chunk) => outReader.push(chunk));
|
|
1213
|
-
child.stderr?.on("data", (chunk) => errReader.push(chunk));
|
|
1214
|
-
const result = await waitForChild(child);
|
|
1215
|
-
outReader.flush();
|
|
1216
|
-
errReader.flush();
|
|
1217
|
-
const durationMs = now() - startedAt;
|
|
1218
|
-
if (result.error) return prebuildFailure(logWriter, {
|
|
1219
|
-
failed: true,
|
|
1220
|
-
code: PREBUILD_ERROR,
|
|
1221
|
-
reason: `Could not run \`expo prebuild\`: ${result.error?.message || result.error}`,
|
|
1222
|
-
lastLines: tail.slice(),
|
|
1223
|
-
durationMs
|
|
1224
|
-
});
|
|
1225
|
-
if (result.code !== 0) {
|
|
1226
|
-
const how = result.signal ? `signal ${result.signal}` : `exit code ${result.code}`;
|
|
1227
|
-
return prebuildFailure(logWriter, {
|
|
1228
|
-
failed: true,
|
|
1229
|
-
code: PREBUILD_ERROR,
|
|
1230
|
-
reason: `\`expo prebuild -p ${platform}\` failed (${how}).`,
|
|
1231
|
-
lastLines: tail.slice(),
|
|
1232
|
-
durationMs
|
|
1233
|
-
});
|
|
1234
|
-
}
|
|
1235
|
-
if (!existsSync(nativeDir(root, platform))) return prebuildFailure(logWriter, {
|
|
1236
|
-
failed: true,
|
|
1237
|
-
code: PREBUILD_ERROR,
|
|
1238
|
-
reason: `\`expo prebuild -p ${platform}\` succeeded but did not create ${nativeDirName(platform)}/.`,
|
|
1239
|
-
lastLines: tail.slice(),
|
|
1240
|
-
durationMs
|
|
1241
|
-
});
|
|
1242
|
-
return {
|
|
1243
|
-
ok: true,
|
|
1244
|
-
durationMs,
|
|
1245
|
-
nativeDir: nativeDir(root, platform)
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
//#endregion
|
|
1249
695
|
//#region src/engine/js-swap.ts
|
|
1250
696
|
const JS_BUNDLE_NAME = "main.jsbundle";
|
|
1251
697
|
const LAST_LINES = 5;
|
|
@@ -1287,7 +733,8 @@ function detectEntryFile(root) {
|
|
|
1287
733
|
}
|
|
1288
734
|
return pickEntryFile(entries);
|
|
1289
735
|
}
|
|
1290
|
-
function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
|
|
736
|
+
function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest, assetCatalogDest = null }) {
|
|
737
|
+
const catalogArgs = assetCatalogDest ? ["--asset-catalog-dest", assetCatalogDest] : [];
|
|
1291
738
|
if (isExpo) return {
|
|
1292
739
|
file: "npx",
|
|
1293
740
|
args: [
|
|
@@ -1300,7 +747,8 @@ function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
|
|
|
1300
747
|
"--bundle-output",
|
|
1301
748
|
bundleOutput,
|
|
1302
749
|
"--assets-dest",
|
|
1303
|
-
assetsDest
|
|
750
|
+
assetsDest,
|
|
751
|
+
...catalogArgs
|
|
1304
752
|
]
|
|
1305
753
|
};
|
|
1306
754
|
return {
|
|
@@ -1317,10 +765,117 @@ function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
|
|
|
1317
765
|
"--bundle-output",
|
|
1318
766
|
bundleOutput,
|
|
1319
767
|
"--assets-dest",
|
|
1320
|
-
assetsDest
|
|
768
|
+
assetsDest,
|
|
769
|
+
...catalogArgs
|
|
1321
770
|
]
|
|
1322
771
|
};
|
|
1323
772
|
}
|
|
773
|
+
const ASSET_CATALOG_BUNDLE = "RNAssets.bundle";
|
|
774
|
+
/**
|
|
775
|
+
* Where React Native 0.88+ keeps the app's packager images. With the `RCTUseAssetCatalog` Info.plist
|
|
776
|
+
* key, `react-native-xcode.sh` compiles them with actool into RNAssets.bundle and the native image
|
|
777
|
+
* loader reads only that catalog, so a swap has to rebuild it. Null means loose files under assets/.
|
|
778
|
+
*/
|
|
779
|
+
function assetCatalogTarget(infoPlist) {
|
|
780
|
+
if (!infoPlist || typeof infoPlist !== "object") return null;
|
|
781
|
+
const plist = infoPlist;
|
|
782
|
+
const flag = plist.RCTUseAssetCatalog;
|
|
783
|
+
if (!(flag === true || flag === 1 || typeof flag === "string" && [
|
|
784
|
+
"true",
|
|
785
|
+
"yes",
|
|
786
|
+
"1"
|
|
787
|
+
].includes(flag.toLowerCase()))) return null;
|
|
788
|
+
const families = Array.isArray(plist.UIDeviceFamily) ? plist.UIDeviceFamily : [1];
|
|
789
|
+
const targetDevices = [...families.includes(1) ? ["iphone"] : [], ...families.includes(2) ? ["ipad"] : []];
|
|
790
|
+
return {
|
|
791
|
+
platform: typeof plist.DTPlatformName === "string" ? plist.DTPlatformName : "iphoneos",
|
|
792
|
+
minimumDeploymentTarget: typeof plist.MinimumOSVersion === "string" ? plist.MinimumOSVersion : "15.1",
|
|
793
|
+
targetDevices: targetDevices.length ? targetDevices : ["iphone"]
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
function readPlistKey(e, plist, key, format) {
|
|
797
|
+
try {
|
|
798
|
+
return e.runFile("plutil", [
|
|
799
|
+
"-extract",
|
|
800
|
+
key,
|
|
801
|
+
format,
|
|
802
|
+
"-o",
|
|
803
|
+
"-",
|
|
804
|
+
plist
|
|
805
|
+
]);
|
|
806
|
+
} catch (err) {
|
|
807
|
+
if (/No value at that key path/.test(describe(err))) return null;
|
|
808
|
+
throw err;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function readAssetCatalogTarget(e, plist) {
|
|
812
|
+
const flag = readPlistKey(e, plist, "RCTUseAssetCatalog", "raw");
|
|
813
|
+
if (flag === null) return null;
|
|
814
|
+
const families = readPlistKey(e, plist, "UIDeviceFamily", "json");
|
|
815
|
+
return assetCatalogTarget({
|
|
816
|
+
RCTUseAssetCatalog: flag,
|
|
817
|
+
DTPlatformName: readPlistKey(e, plist, "DTPlatformName", "raw"),
|
|
818
|
+
MinimumOSVersion: readPlistKey(e, plist, "MinimumOSVersion", "raw"),
|
|
819
|
+
UIDeviceFamily: families === null ? void 0 : JSON.parse(families)
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
function actoolArgs({ catalog, out, target }) {
|
|
823
|
+
return [
|
|
824
|
+
"actool",
|
|
825
|
+
catalog,
|
|
826
|
+
"--compile",
|
|
827
|
+
out,
|
|
828
|
+
"--output-format",
|
|
829
|
+
"human-readable-text",
|
|
830
|
+
"--errors",
|
|
831
|
+
"--warnings",
|
|
832
|
+
"--notices",
|
|
833
|
+
"--platform",
|
|
834
|
+
target.platform,
|
|
835
|
+
"--minimum-deployment-target",
|
|
836
|
+
target.minimumDeploymentTarget,
|
|
837
|
+
...target.targetDevices.flatMap((device) => ["--target-device", device])
|
|
838
|
+
];
|
|
839
|
+
}
|
|
840
|
+
const ASSET_CATALOG_BUNDLE_INFO_PLIST = `<?xml version="1.0" encoding="UTF-8"?>
|
|
841
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
842
|
+
<plist version="1.0">
|
|
843
|
+
<dict>
|
|
844
|
+
<key>CFBundleIdentifier</key>
|
|
845
|
+
<string>org.reactjs.RNAssets</string>
|
|
846
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
847
|
+
<string>6.0</string>
|
|
848
|
+
<key>CFBundleName</key>
|
|
849
|
+
<string>RNAssets</string>
|
|
850
|
+
<key>CFBundlePackageType</key>
|
|
851
|
+
<string>BNDL</string>
|
|
852
|
+
</dict>
|
|
853
|
+
</plist>
|
|
854
|
+
`;
|
|
855
|
+
const UPDATES_MANIFEST_NAME = "app.manifest";
|
|
856
|
+
/**
|
|
857
|
+
* expo-updates launches the update with the newest `commitTime` among those built for the binary's
|
|
858
|
+
* runtime version, and the embedded one is only a row in that race. A swapped copy keeps the id and
|
|
859
|
+
* `commitTime` the cached build wrote, so an update downloaded after that build would launch instead
|
|
860
|
+
* of the injected bundle. A full build writes a fresh id and `commitTime`; this does the same.
|
|
861
|
+
*/
|
|
862
|
+
function refreshUpdatesManifest(text, { id, commitTime }) {
|
|
863
|
+
const manifest = JSON.parse(text);
|
|
864
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) throw new Error(`${UPDATES_MANIFEST_NAME} is not a JSON object`);
|
|
865
|
+
return JSON.stringify({
|
|
866
|
+
...manifest,
|
|
867
|
+
id,
|
|
868
|
+
commitTime
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
function refreshUpdatesManifestFile(path, commitTime) {
|
|
872
|
+
writeFileSync(path, refreshUpdatesManifest(readFileSync(path, "utf-8"), {
|
|
873
|
+
id: randomUUID(),
|
|
874
|
+
commitTime
|
|
875
|
+
}));
|
|
876
|
+
}
|
|
877
|
+
const IOS_UPDATES_MANIFESTS = [join("EXUpdates.bundle", UPDATES_MANIFEST_NAME), UPDATES_MANIFEST_NAME];
|
|
878
|
+
const IOS_FRAMEWORK_UPDATES_MANIFEST = join("Frameworks", "EXUpdates.framework", "EXUpdates.bundle", UPDATES_MANIFEST_NAME);
|
|
1324
879
|
function hermescPath(root, { exists = existsSync } = {}) {
|
|
1325
880
|
const candidates = [
|
|
1326
881
|
join(root, "node_modules", "hermes-compiler", "hermesc", "osx-bin", "hermesc"),
|
|
@@ -1380,19 +935,34 @@ async function swapJsBundle({ root, isExpo, cachedAppPath, logWriter = null, exe
|
|
|
1380
935
|
} catch (err) {
|
|
1381
936
|
return fail("copy", `could not copy ${cachedAppPath} aside: ${describe(err)}`);
|
|
1382
937
|
}
|
|
938
|
+
let catalogTarget;
|
|
939
|
+
const infoPlistPath = join(appCopy, "Info.plist");
|
|
940
|
+
try {
|
|
941
|
+
catalogTarget = readAssetCatalogTarget(e, infoPlistPath);
|
|
942
|
+
} catch (err) {
|
|
943
|
+
return fail("catalog", `could not read ${infoPlistPath}: ${describe(err)}`);
|
|
944
|
+
}
|
|
1383
945
|
const bundleOutput = join(tmp, JS_BUNDLE_NAME);
|
|
1384
946
|
const assetsDest = join(tmp, "assets");
|
|
947
|
+
const catalogStaging = catalogTarget ? join(tmp, "rn-assets") : null;
|
|
948
|
+
const catalogDir = catalogStaging ? join(catalogStaging, "RNAssets.xcassets") : null;
|
|
1385
949
|
const command = bundleCommand({
|
|
1386
950
|
isExpo,
|
|
1387
951
|
entryFile: isExpo ? "index.js" : detectEntryFile(root),
|
|
1388
952
|
bundleOutput,
|
|
1389
|
-
assetsDest
|
|
953
|
+
assetsDest,
|
|
954
|
+
assetCatalogDest: catalogStaging
|
|
1390
955
|
});
|
|
1391
956
|
try {
|
|
1392
957
|
mkdirSync(assetsDest, { recursive: true });
|
|
1393
958
|
} catch (err) {
|
|
1394
959
|
return fail("bundle", `could not create ${assetsDest}: ${describe(err)}`);
|
|
1395
960
|
}
|
|
961
|
+
if (catalogDir) try {
|
|
962
|
+
mkdirSync(catalogDir, { recursive: true });
|
|
963
|
+
} catch (err) {
|
|
964
|
+
return fail("bundle", `could not create ${catalogDir}: ${describe(err)}`);
|
|
965
|
+
}
|
|
1396
966
|
logWriter?.write?.({
|
|
1397
967
|
src: "build",
|
|
1398
968
|
level: "info",
|
|
@@ -1487,6 +1057,34 @@ async function swapJsBundle({ root, isExpo, cachedAppPath, logWriter = null, exe
|
|
|
1487
1057
|
} catch (err) {
|
|
1488
1058
|
return fail("replace", `could not replace the JS bundle inside ${appCopy}: ${describe(err)}`);
|
|
1489
1059
|
}
|
|
1060
|
+
if (catalogTarget && catalogDir) {
|
|
1061
|
+
const catalogBundle = join(appCopy, ASSET_CATALOG_BUNDLE);
|
|
1062
|
+
try {
|
|
1063
|
+
rmSync(catalogBundle, {
|
|
1064
|
+
recursive: true,
|
|
1065
|
+
force: true
|
|
1066
|
+
});
|
|
1067
|
+
if (readdirSync(catalogDir).some((name) => name.endsWith(".imageset"))) {
|
|
1068
|
+
mkdirSync(catalogBundle);
|
|
1069
|
+
const output = e.runFile("xcrun", actoolArgs({
|
|
1070
|
+
catalog: catalogDir,
|
|
1071
|
+
out: catalogBundle,
|
|
1072
|
+
target: catalogTarget
|
|
1073
|
+
}));
|
|
1074
|
+
if (!exists(join(catalogBundle, "Assets.car"))) return fail("actool", `actool wrote no Assets.car into ${catalogBundle}`, tailLines(output.split("\n"), LAST_LINES));
|
|
1075
|
+
writeFileSync(join(catalogBundle, "Info.plist"), ASSET_CATALOG_BUNDLE_INFO_PLIST);
|
|
1076
|
+
}
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
const stdout = String(err?.stdout ?? "");
|
|
1079
|
+
return fail("actool", `could not compile the image asset catalog into ${catalogBundle}: ${describe(err)}`, tailLines(stdout.split("\n"), LAST_LINES));
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
if (exists(join(appCopy, IOS_FRAMEWORK_UPDATES_MANIFEST))) return fail("updates", `the expo-updates manifest sits inside a nested framework (${IOS_FRAMEWORK_UPDATES_MANIFEST}), where a swap cannot refresh it without re-signing that framework`);
|
|
1083
|
+
for (const manifest of IOS_UPDATES_MANIFESTS.map((name) => join(appCopy, name)).filter(exists)) try {
|
|
1084
|
+
refreshUpdatesManifestFile(manifest, now());
|
|
1085
|
+
} catch (err) {
|
|
1086
|
+
return fail("updates", `could not refresh the expo-updates manifest ${manifest}: ${describe(err)}`);
|
|
1087
|
+
}
|
|
1490
1088
|
try {
|
|
1491
1089
|
e.runFile("codesign", [
|
|
1492
1090
|
"--force",
|
|
@@ -1517,61 +1115,6 @@ function describe(err) {
|
|
|
1517
1115
|
return String(err?.message || err);
|
|
1518
1116
|
}
|
|
1519
1117
|
//#endregion
|
|
1520
|
-
//#region src/commands/dev-client.ts
|
|
1521
|
-
function devClientScheme(root, appPath = null, { exec = null } = {}) {
|
|
1522
|
-
if (!hasDevClient(root)) return void 0;
|
|
1523
|
-
const fromBundle = pickDevClientScheme(readBundleSchemes(appPath, { exec }));
|
|
1524
|
-
if (fromBundle) return fromBundle;
|
|
1525
|
-
const app = readJsonObject(join(root, "app.json"));
|
|
1526
|
-
const expo = app?.expo;
|
|
1527
|
-
const raw = (isJsonObject(expo) ? expo.scheme : void 0) ?? app?.scheme ?? null;
|
|
1528
|
-
const scheme = Array.isArray(raw) ? raw.find((s) => typeof s === "string" && s.trim() !== "") : raw;
|
|
1529
|
-
if (typeof scheme !== "string" || scheme.trim() === "") return void 0;
|
|
1530
|
-
return scheme.trim();
|
|
1531
|
-
}
|
|
1532
|
-
function schemesFromInfoPlist(plist) {
|
|
1533
|
-
const types = plist?.CFBundleURLTypes;
|
|
1534
|
-
if (!Array.isArray(types)) return [];
|
|
1535
|
-
const out = [];
|
|
1536
|
-
for (const type of types) {
|
|
1537
|
-
const schemes = type?.CFBundleURLSchemes;
|
|
1538
|
-
if (Array.isArray(schemes)) out.push(...schemes.filter((s) => typeof s === "string" && s.trim() !== ""));
|
|
1539
|
-
}
|
|
1540
|
-
return out;
|
|
1541
|
-
}
|
|
1542
|
-
function readBundleSchemes(appPath, { exec = null } = {}) {
|
|
1543
|
-
if (typeof appPath !== "string" || appPath.trim() === "") return [];
|
|
1544
|
-
const e = exec || getExecutor();
|
|
1545
|
-
let out;
|
|
1546
|
-
try {
|
|
1547
|
-
out = e.runFile("plutil", [
|
|
1548
|
-
"-convert",
|
|
1549
|
-
"json",
|
|
1550
|
-
"-o",
|
|
1551
|
-
"-",
|
|
1552
|
-
join(appPath, "Info.plist")
|
|
1553
|
-
]);
|
|
1554
|
-
} catch {
|
|
1555
|
-
return [];
|
|
1556
|
-
}
|
|
1557
|
-
try {
|
|
1558
|
-
return schemesFromInfoPlist(JSON.parse(String(out)));
|
|
1559
|
-
} catch {
|
|
1560
|
-
return [];
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
const THIRD_PARTY_SCHEME = /^(?:fb\d+|com\.googleusercontent\.apps\.|msauth\.|msauthv2|twitterkit-|db-[a-z0-9]+$|spotify|snapchat|com\.facebook)/i;
|
|
1564
|
-
function pickDevClientScheme(schemes) {
|
|
1565
|
-
const all = (Array.isArray(schemes) ? schemes : []).filter((s) => typeof s === "string" && s.trim() !== "").map((s) => s.trim()).filter((s) => !/^(?:https?|mailto|tel|sms|itms(?:-apps)?)$/i.test(s));
|
|
1566
|
-
const expo = all.filter((s) => s.startsWith("exp+"));
|
|
1567
|
-
return (expo.length ? expo : all.filter((s) => !THIRD_PARTY_SCHEME.test(s))).toSorted((a, b) => b.length - a.length)[0] ?? null;
|
|
1568
|
-
}
|
|
1569
|
-
function hasDevClient(root) {
|
|
1570
|
-
const pkg = readJsonObject(join(root, "package.json"));
|
|
1571
|
-
if ([pkg?.dependencies, pkg?.devDependencies].some((deps) => isJsonObject(deps) && "expo-dev-client" in deps)) return true;
|
|
1572
|
-
return isPackageResolvable(root, "expo-dev-client");
|
|
1573
|
-
}
|
|
1574
|
-
//#endregion
|
|
1575
1118
|
//#region src/cache/miss-reason.ts
|
|
1576
1119
|
const MISS_CHANGE_CAP = 20;
|
|
1577
1120
|
function recordedBuilds(state, platform) {
|
|
@@ -1924,7 +1467,7 @@ async function planCachedBuild(lookup, deps) {
|
|
|
1924
1467
|
function planPayload({ platform, slot, projectKey }, found) {
|
|
1925
1468
|
const { refusal, missReason, ...rest } = found;
|
|
1926
1469
|
const outcome = refusal ? null : found.cacheHit ? "hit" : "cold";
|
|
1927
|
-
const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome
|
|
1470
|
+
const estimate = outcome ? estimateBuild(readStats().record?.history?.[projectKey], platform, outcome) : {
|
|
1928
1471
|
expectedMs: null,
|
|
1929
1472
|
basis: 0
|
|
1930
1473
|
};
|
|
@@ -1981,4 +1524,4 @@ function planFlagRefusal(flag) {
|
|
|
1981
1524
|
};
|
|
1982
1525
|
}
|
|
1983
1526
|
//#endregion
|
|
1984
|
-
export {
|
|
1527
|
+
export { AvdRecoveryError as _, refusePlan as a, skippedMissReason as c, refreshUpdatesManifestFile as d, swapJsBundle as f, AvdBootError as g, ensureOwnedDevice as h, printPlan as i, UPDATES_MANIFEST_NAME as l, ensureBooted as m, planFlagRefusal as n, explainBuildMiss as o, didSetUpDevice as p, planPayload as r, fingerprintErrorMissReason as s, planCachedBuild as t, detectEntryFile as u, warmMetro as v };
|