stim 1.8.0 → 1.9.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 +23 -155
- package/dist/{android-BRgBYzU0.mjs → android-BPGizezh.mjs} +112 -135
- package/dist/{android-DeYdjjBP.mjs → android-DEgo4oCo.mjs} +2 -2
- package/dist/{android-DWssqHQ8.mjs → android-_y4Ohi5z.mjs} +54 -15
- package/dist/android-cas-compiler.mjs +2 -2
- package/dist/{android-cas-SWIjzeuL.mjs → android-cas-eiExNLf7.mjs} +21 -14
- package/dist/app-install-Uf3a83Tv.mjs +819 -0
- package/dist/{build-lock-CgtsB6TV.mjs → build-lock-DPXPLgTc.mjs} +120 -24
- package/dist/build-progress-CVqkfPC6.mjs +188 -0
- package/dist/{cache-manifest-CYJi8-Hx.mjs → cache-manifest-Dg7OnMrp.mjs} +4 -4
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +22 -19
- package/dist/collector-run.d.mts +2 -0
- package/dist/collector-run.mjs +10 -9
- package/dist/{command-output-B3ycpsYv.mjs → command-output-gByVmIjy.mjs} +11 -2
- package/dist/{config-B7iyKWkL.mjs → config-Cp9lM3wX.mjs} +187 -57
- package/dist/{dependency-state-D0M7oKA7.mjs → dependency-state-Bq5DcLGn.mjs} +2 -2
- package/dist/{deps-oCXVNEqQ.mjs → deps-Cip5vUm1.mjs} +8 -5
- package/dist/{detached-entry-Bpo6ZY_I.mjs → detached-entry-KEu0_AXL.mjs} +1 -1
- package/dist/{device-BUoZyMoR.mjs → dev-client-HyqISM0h.mjs} +1178 -635
- package/dist/{device-RdujjHLB.mjs → device-D4U-xLty.mjs} +13 -20
- package/dist/{device-lease-Ffz_M5t0.mjs → device-lease-Crs-HkJY.mjs} +16 -9
- package/dist/{device-pool-BxRwdufO.mjs → device-pool-CnTAz8X1.mjs} +7 -7
- package/dist/{device-remote-ByN8P6CB.mjs → device-remote-AQ5rQPqr.mjs} +482 -35
- package/dist/{doctor-BMt156qe.mjs → doctor-B_Ei3bZu.mjs} +95 -107
- package/dist/{error-diagnostics-D1Kaf-eT.mjs → error-diagnostics-Vfz83GCF.mjs} +22 -16
- package/dist/exec-jIAlgilk.mjs +132 -0
- package/dist/{gc-v2YcoJvm.mjs → gc-Ckw8RQHi.mjs} +763 -126
- package/dist/{guide-BTAlD1_y.mjs → guide-BiAbhs1J.mjs} +891 -501
- package/dist/{guide-status-Craq9z0t.mjs → guide-status-BAMVU0ZA.mjs} +5 -3
- package/dist/{ios-BcCiI4TS.mjs → ios-BL2NFckr.mjs} +43 -20
- package/dist/{ios-BP04pY5f.mjs → ios-BzUJWk1K.mjs} +141 -175
- package/dist/{ios-device-CauFJiYx.mjs → ios-device-D972i9wf.mjs} +149 -52
- package/dist/{ios-device-CFXA46ez.mjs → ios-device-DPsSm0v_.mjs} +3 -2
- package/dist/launch-verify-Q_OtW1Rf.mjs +365 -0
- package/dist/{logs-CakdBTtm.mjs → logs-CI9NQSK7.mjs} +18 -13
- package/dist/{logs-query-BGL-Pl12.mjs → logs-query-Cqma3WIu.mjs} +36 -28
- package/dist/{metro-C7bcJ0uo.mjs → metro-DrLHaMn2.mjs} +78 -8
- package/dist/{named-ports-DzMOlmaX.mjs → named-ports-CmLQSIRj.mjs} +4 -4
- package/dist/{ndjson-BZwI1NBv.mjs → ndjson-9uFNRwfe.mjs} +36 -3
- package/dist/{ownership-Cp52QAK1.mjs → ownership-D15Op8vN.mjs} +3 -3
- package/dist/{ownership-wolE-0Do.mjs → ownership-DCQPuWEc.mjs} +26 -7
- package/dist/{ports-BIbJaKNW.mjs → ports-BNKoWVWZ.mjs} +3 -3
- package/dist/{process-output-Dl5uKE03.mjs → process-output-Fpl8PYi8.mjs} +1 -1
- package/dist/{project-DDZWf_16.mjs → project-DS112DYB.mjs} +8 -3
- package/dist/{reload-BdypPbjM.mjs → reload-ZNJUo1pl.mjs} +12 -13
- package/dist/{server-bare-BQkyxa_b.mjs → server-bare-DWkkcIxV.mjs} +2 -2
- package/dist/{server-expo-BVAA1OqJ.mjs → server-expo-B_lctBR3.mjs} +8 -7
- package/dist/server-expo-CeILEu86.mjs +2 -0
- package/dist/settings-BKNh5rl4.mjs +376 -0
- package/dist/{settings-BXXorVM2.mjs → settings-D030k0t4.mjs} +464 -152
- package/dist/settings.schema.json +989 -0
- package/dist/{teardown-BJqEzPo_.mjs → sim-pool-KaIsp2Ft.mjs} +14 -419
- package/dist/simslim-B_LsGLuJ.mjs +259 -0
- package/dist/{slot-launch-ZUUe0TQP.mjs → slot-launch-DU9A-gYw.mjs} +4 -4
- package/dist/spawn-claims-Dn3C_N6w.mjs +49 -0
- package/dist/{start-CmxtOKjU.mjs → start-DYkkKF0s.mjs} +27 -31
- package/dist/{state-DZobzxLW.mjs → state-NcJzMEBF.mjs} +5 -3
- package/dist/{stats-CLg5RuDS.mjs → stats-B63-JVAY.mjs} +4 -4
- package/dist/{stats-BYcZ6n1h.mjs → stats-DG9e66Rz.mjs} +66 -5
- package/dist/status-S6ehL4Ui.mjs +932 -0
- package/dist/{stop-C8JW6kKp.mjs → stop-BO28HGqF.mjs} +101 -25
- package/dist/{stop-B1kOpYei.mjs → stop-T1R-cZRz.mjs} +2 -2
- package/dist/supervisor-run.mjs +52 -34
- package/dist/{workspace-process-lock-BYfZz_S-.mjs → workspace-process-lock-DWaiRD0e.mjs} +14 -9
- package/dist/{workspace-state-BT5E3SA2.mjs → workspace-state-DF2gCFhx.mjs} +52 -29
- package/dist/worktree-DKhSF0UX.mjs +2 -0
- package/dist/{worktree-CFLA-6MX.mjs → worktree-LGB-UwgG.mjs} +603 -59
- package/dist/{worktree-FG_ZKKdb.mjs → worktree-R_cUlE7s.mjs} +62 -41
- package/dist/{xcode-B6YaWeg0.mjs → xcode-BZqyxh3e.mjs} +216 -58
- package/package.json +5 -6
- package/skill/SKILL.md +2 -0
- package/dist/app-install-BDbsSgCl.mjs +0 -1175
- package/dist/build-slots-C-oAHRT1.mjs +0 -151
- package/dist/dev-client-BmT-ioFI.mjs +0 -797
- package/dist/exec-B29XNuhP.mjs +0 -77
- package/dist/reclaim-KJ-0uWEF.mjs +0 -361
- package/dist/server-expo-YDDG3eP7.mjs +0 -2
- package/dist/status-DdkLki47.mjs +0 -426
- package/dist/{metro-store-CDDZHtPl.mjs → metro-store-DzeWkfqW.mjs} +1 -1
|
@@ -1,797 +0,0 @@
|
|
|
1
|
-
import { t as getExecutor } from "./exec-B29XNuhP.mjs";
|
|
2
|
-
import { E as ownership_claim_exports } from "./config-B7iyKWkL.mjs";
|
|
3
|
-
import { c as ensureWorkspaceStorage } from "./workspace-state-BT5E3SA2.mjs";
|
|
4
|
-
import { T as removeTemporaryEntry, w as makeTemporaryDirectory } from "./worktree-FG_ZKKdb.mjs";
|
|
5
|
-
import { i as detectIsExpo, o as isPackageResolvable } from "./project-DDZWf_16.mjs";
|
|
6
|
-
import { n as stripAnsi, r as waitForChild, t as createLineReader } from "./process-output-Dl5uKE03.mjs";
|
|
7
|
-
import { t as NOT_OURS_FOREIGN_CWD } from "./metro-C7bcJ0uo.mjs";
|
|
8
|
-
import { i as releaseBuildLock, n as acquireBuildLock } from "./build-lock-CgtsB6TV.mjs";
|
|
9
|
-
import { i as expoBinRefusal, r as expoBinPath, t as cleanLine } from "./server-expo-BVAA1OqJ.mjs";
|
|
10
|
-
import { S as resolveEasCliBin } from "./teardown-BJqEzPo_.mjs";
|
|
11
|
-
import { i as LAUNCH_BUNDLING, s as LAUNCH_UNVERIFIED } from "./app-install-BDbsSgCl.mjs";
|
|
12
|
-
import { d as startBuildHeartbeat, f as tailLines, n as HEARTBEAT_INTERVAL_MS } from "./xcode-B6YaWeg0.mjs";
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
14
|
-
import { createHash } from "node:crypto";
|
|
15
|
-
import { basename, join } from "node:path";
|
|
16
|
-
import chalk from "chalk";
|
|
17
|
-
import { request } from "node:http";
|
|
18
|
-
//#region src/engine/eas-build.ts
|
|
19
|
-
function isEasBuildFailure(result) {
|
|
20
|
-
return result?.ok === false;
|
|
21
|
-
}
|
|
22
|
-
function easOptionRefusal({ profile, isExpo, buildSelector, buildCache }) {
|
|
23
|
-
if (profile === void 0) return null;
|
|
24
|
-
if (profile.trim() && isExpo && buildSelector === void 0 && buildCache !== false) return null;
|
|
25
|
-
return {
|
|
26
|
-
code: "STIM_BAD_ARG",
|
|
27
|
-
message: "--eas-profile requires an Expo project and a non-empty development profile.",
|
|
28
|
-
remedy: "Use --eas-profile <name> without --scheme, --configuration, --variant, or --no-build-cache."
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function object(value) {
|
|
32
|
-
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
33
|
-
}
|
|
34
|
-
function shellArg(value) {
|
|
35
|
-
return /^[a-zA-Z0-9_.-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
36
|
-
}
|
|
37
|
-
function easDeviceBuildRemedy(profile) {
|
|
38
|
-
return `If the device is not registered, run npx eas-cli device:create. With approval for cloud build costs and signing changes, run npx eas-cli build --platform ios --profile ${shellArg(profile)} to produce a build with a valid profile for this device, then retry the same Stim command.`;
|
|
39
|
-
}
|
|
40
|
-
var EasFailure = class extends Error {
|
|
41
|
-
code;
|
|
42
|
-
remedy;
|
|
43
|
-
constructor(code, message, remedy) {
|
|
44
|
-
super(message);
|
|
45
|
-
this.code = code;
|
|
46
|
-
this.remedy = remedy;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
function selectEasBuild(builds, { platform, profile, fingerprint, projectId, physical = false }) {
|
|
50
|
-
if (!Array.isArray(builds)) throw new Error("EAS build:list did not return an array.");
|
|
51
|
-
for (const value of builds) {
|
|
52
|
-
const build = object(value);
|
|
53
|
-
if (typeof build.id === "string" && build.id.length > 0 && build.status === "FINISHED" && build.platform === platform.toUpperCase() && build.buildProfile === profile && object(build.fingerprint).hash === fingerprint && (object(build.app).id ?? object(build.project).id) === projectId && (platform !== "ios" || build.isForIosSimulator === !physical) && build.distribution === "INTERNAL" && typeof object(build.artifacts).applicationArchiveUrl === "string") return build.id;
|
|
54
|
-
}
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
async function resolveEasDevelopmentBuild({ root, platform, profile, note, isExpo = true, physical = false, selectors = [], buildCache }) {
|
|
58
|
-
if (profile === void 0) return null;
|
|
59
|
-
const refusal = easOptionRefusal({
|
|
60
|
-
profile,
|
|
61
|
-
isExpo,
|
|
62
|
-
buildSelector: selectors.find((value) => value != null) ?? void 0,
|
|
63
|
-
buildCache
|
|
64
|
-
});
|
|
65
|
-
if (refusal) return {
|
|
66
|
-
ok: false,
|
|
67
|
-
...refusal
|
|
68
|
-
};
|
|
69
|
-
const retry = "the same Stim command";
|
|
70
|
-
const buildCommand = `npx eas-cli build --platform ${platform} --profile ${shellArg(profile)}`;
|
|
71
|
-
let lock = null;
|
|
72
|
-
try {
|
|
73
|
-
const cli = resolveEasCliBin(root);
|
|
74
|
-
if (!cli) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS CLI is not installed.", "Install eas-cli, run eas login, then retry.");
|
|
75
|
-
let profileEnv = {};
|
|
76
|
-
const run = (args, timeoutMs = 12e4) => {
|
|
77
|
-
try {
|
|
78
|
-
return JSON.parse(getExecutor().runFile(cli.file, [
|
|
79
|
-
...args,
|
|
80
|
-
"--json",
|
|
81
|
-
"--non-interactive"
|
|
82
|
-
], {
|
|
83
|
-
cwd: root,
|
|
84
|
-
timeoutMs,
|
|
85
|
-
env: profileEnv
|
|
86
|
-
}));
|
|
87
|
-
} catch {
|
|
88
|
-
throw new EasFailure("STIM_EAS_UNAVAILABLE", `EAS ${args[0]} failed or returned invalid JSON.`, `Run npx eas-cli ${args.map(shellArg).join(" ")} to inspect the error, then retry ${retry}.`);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
note(`eas resolving profile ${profile}`);
|
|
92
|
-
const config = object(run([
|
|
93
|
-
"config",
|
|
94
|
-
"--platform",
|
|
95
|
-
platform,
|
|
96
|
-
"--profile",
|
|
97
|
-
profile
|
|
98
|
-
]));
|
|
99
|
-
const buildProfile = object(config.buildProfile);
|
|
100
|
-
profileEnv = object(buildProfile.env);
|
|
101
|
-
const projectId = object(object(object(config.appConfig).extra).eas).projectId;
|
|
102
|
-
if (buildProfile.developmentClient !== true || buildProfile.distribution !== "internal" || platform === "ios" && buildProfile.simulator === true !== !physical) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} is not an internal development build for this ${platform} target.`, `Set developmentClient: true and distribution: internal${platform === "ios" ? `, with ios.simulator: ${!physical}` : ""} in that eas.json profile.`);
|
|
103
|
-
if (typeof projectId !== "string" || !projectId) throw new EasFailure("STIM_BAD_ARG", "The Expo app is not linked to an EAS project.", "Link the intended EAS project with eas init, then retry.");
|
|
104
|
-
if (platform === "ios" ? buildProfile.buildConfiguration !== void 0 && buildProfile.buildConfiguration !== "Debug" : buildProfile.gradleCommand !== void 0 && (typeof buildProfile.gradleCommand !== "string" || !/^:app:assemble\w*Debug$/.test(buildProfile.gradleCommand))) throw new EasFailure("STIM_BAD_ARG", `EAS profile ${profile} overrides the development build configuration.`, "Use ios.buildConfiguration: Debug or an android.gradleCommand that assembles a single Debug APK, or remove the override.");
|
|
105
|
-
note(`eas fingerprinting ${platform} with profile ${profile}`);
|
|
106
|
-
const fingerprint = object(run([
|
|
107
|
-
"fingerprint:generate",
|
|
108
|
-
"--platform",
|
|
109
|
-
platform,
|
|
110
|
-
"--build-profile",
|
|
111
|
-
profile
|
|
112
|
-
])).hash;
|
|
113
|
-
if (typeof fingerprint !== "string" || !/^[a-f0-9]{40,64}$/.test(fingerprint)) throw new EasFailure("STIM_EAS_UNAVAILABLE", "EAS returned no valid native fingerprint.", `Run npx eas-cli fingerprint:generate --platform ${platform} --build-profile ${shellArg(profile)} to inspect the result.`);
|
|
114
|
-
note(`eas looking for ${fingerprint.slice(0, 8)}`);
|
|
115
|
-
const buildId = selectEasBuild(run([
|
|
116
|
-
"build:list",
|
|
117
|
-
"--platform",
|
|
118
|
-
platform,
|
|
119
|
-
"--build-profile",
|
|
120
|
-
profile,
|
|
121
|
-
"--fingerprint-hash",
|
|
122
|
-
fingerprint,
|
|
123
|
-
"--status",
|
|
124
|
-
"finished",
|
|
125
|
-
...platform === "ios" && !physical ? ["--simulator"] : ["--distribution", "internal"],
|
|
126
|
-
"--limit",
|
|
127
|
-
"1"
|
|
128
|
-
]), {
|
|
129
|
-
platform,
|
|
130
|
-
profile,
|
|
131
|
-
fingerprint,
|
|
132
|
-
projectId,
|
|
133
|
-
physical
|
|
134
|
-
});
|
|
135
|
-
if (!buildId) throw new EasFailure("STIM_EAS_BUILD_MISSING", `No compatible EAS ${platform} build exists for profile ${profile} and fingerprint ${fingerprint}.`, `A cloud build may incur charges. With approval to run it, use ${buildCommand}, then retry ${retry}.`);
|
|
136
|
-
const cacheKey = `eas-${createHash("sha256").update(JSON.stringify({
|
|
137
|
-
projectId,
|
|
138
|
-
buildId
|
|
139
|
-
})).digest("hex")}-debug-${physical && platform === "ios" ? "device" : "sim"}`;
|
|
140
|
-
lock = acquireBuildLock({
|
|
141
|
-
platform,
|
|
142
|
-
key: cacheKey,
|
|
143
|
-
root
|
|
144
|
-
});
|
|
145
|
-
if (!lock.acquired) throw new EasFailure("STIM_EAS_UNAVAILABLE", `Another run holds the EAS artifact claim at ${lock.path}.`, `Wait for that run to finish, then retry ${retry}.`);
|
|
146
|
-
note(`eas resolving artifact for build ${buildId}`);
|
|
147
|
-
const result = object(run([
|
|
148
|
-
"build:download",
|
|
149
|
-
"--build-id",
|
|
150
|
-
buildId
|
|
151
|
-
], 6e5));
|
|
152
|
-
if (typeof result.path !== "string" || !existsSync(result.path)) throw new Error("EAS returned no downloaded artifact.");
|
|
153
|
-
const path = realpathSync(result.path);
|
|
154
|
-
if (platform === "ios" ? !path.endsWith(".app") || !statSync(path).isDirectory() : !path.endsWith(".apk") || !statSync(path).isFile()) throw new Error("EAS returned an unexpected artifact path or type.");
|
|
155
|
-
note(`eas artifact ready ${fingerprint.slice(0, 8)}`);
|
|
156
|
-
return {
|
|
157
|
-
ok: true,
|
|
158
|
-
path,
|
|
159
|
-
fingerprint,
|
|
160
|
-
cacheKey,
|
|
161
|
-
cacheHit: "remote"
|
|
162
|
-
};
|
|
163
|
-
} catch (error) {
|
|
164
|
-
const claim = (0, ownership_claim_exports.claimFailure)(error, null);
|
|
165
|
-
if (claim) return {
|
|
166
|
-
ok: false,
|
|
167
|
-
code: claim.code,
|
|
168
|
-
message: claim.message,
|
|
169
|
-
remedy: claim.remedy
|
|
170
|
-
};
|
|
171
|
-
if (error instanceof EasFailure) return {
|
|
172
|
-
ok: false,
|
|
173
|
-
code: error.code,
|
|
174
|
-
message: error.message,
|
|
175
|
-
remedy: error.remedy
|
|
176
|
-
};
|
|
177
|
-
return {
|
|
178
|
-
ok: false,
|
|
179
|
-
code: "STIM_EAS_UNAVAILABLE",
|
|
180
|
-
message: `Could not resolve the EAS development build: ${error.message}`,
|
|
181
|
-
remedy: `Retry ${retry}. No cloud build was started.`
|
|
182
|
-
};
|
|
183
|
-
} finally {
|
|
184
|
-
releaseBuildLock(lock);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
//#endregion
|
|
188
|
-
//#region src/engine/metro-warmup.ts
|
|
189
|
-
const WARMUP_TIMEOUT_MS = 6e4;
|
|
190
|
-
async function requestLocal(url, platform, prefetch) {
|
|
191
|
-
return new Promise((resolve) => {
|
|
192
|
-
const req = request(url, { headers: {
|
|
193
|
-
accept: "application/json",
|
|
194
|
-
"expo-platform": platform,
|
|
195
|
-
...prefetch ? { "x-stim-metro-warmup": "1" } : {}
|
|
196
|
-
} });
|
|
197
|
-
const timer = setTimeout(() => req.destroy(), WARMUP_TIMEOUT_MS);
|
|
198
|
-
timer.unref();
|
|
199
|
-
req.on("socket", (socket) => socket.unref());
|
|
200
|
-
req.on("error", () => resolve(null));
|
|
201
|
-
req.on("close", () => {
|
|
202
|
-
clearTimeout(timer);
|
|
203
|
-
resolve(null);
|
|
204
|
-
});
|
|
205
|
-
req.on("response", (res) => {
|
|
206
|
-
let body = "";
|
|
207
|
-
res.setEncoding("utf8");
|
|
208
|
-
res.on("data", (chunk) => {
|
|
209
|
-
if (!prefetch) body += chunk;
|
|
210
|
-
});
|
|
211
|
-
res.on("error", () => resolve(null));
|
|
212
|
-
res.on("end", () => resolve(res.statusCode === 200 ? body : null));
|
|
213
|
-
});
|
|
214
|
-
req.end();
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
async function warmMetro({ port, platform, isExpo, appId, bundleUrl }) {
|
|
218
|
-
try {
|
|
219
|
-
const origin = `http://127.0.0.1:${port}`;
|
|
220
|
-
if (await requestLocal(new URL("/_stim/metro-warmup", origin), platform, false) !== "ready") return;
|
|
221
|
-
const url = new URL("/index.bundle", origin);
|
|
222
|
-
url.search = new URLSearchParams({
|
|
223
|
-
platform,
|
|
224
|
-
dev: "true",
|
|
225
|
-
lazy: "true",
|
|
226
|
-
minify: "false",
|
|
227
|
-
...platform === "ios" ? { inlineSourceMap: "false" } : {},
|
|
228
|
-
modulesOnly: "false",
|
|
229
|
-
runModule: "true",
|
|
230
|
-
excludeSource: "true",
|
|
231
|
-
sourcePaths: "url-server",
|
|
232
|
-
...appId ? { app: appId } : {}
|
|
233
|
-
}).toString();
|
|
234
|
-
if (!bundleUrl && isExpo) {
|
|
235
|
-
const body = await requestLocal(new URL(origin), platform, false);
|
|
236
|
-
if (body === null) return;
|
|
237
|
-
const manifest = JSON.parse(body);
|
|
238
|
-
bundleUrl = manifest.launchAsset?.url ?? manifest.bundleUrl;
|
|
239
|
-
if (typeof bundleUrl !== "string") return;
|
|
240
|
-
}
|
|
241
|
-
if (bundleUrl) {
|
|
242
|
-
const bundle = new URL(bundleUrl, origin);
|
|
243
|
-
url.pathname = bundle.pathname;
|
|
244
|
-
url.search = bundle.search;
|
|
245
|
-
}
|
|
246
|
-
await requestLocal(url, platform, true);
|
|
247
|
-
} catch {}
|
|
248
|
-
}
|
|
249
|
-
//#endregion
|
|
250
|
-
//#region src/commands/native-runtime.ts
|
|
251
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
252
|
-
const GATE_RETRY_DELAYS_MS = [
|
|
253
|
-
3e3,
|
|
254
|
-
7e3,
|
|
255
|
-
1e4
|
|
256
|
-
];
|
|
257
|
-
function gateShouldRetry(resolution) {
|
|
258
|
-
if (resolution?.metro) return false;
|
|
259
|
-
return resolution?.kind !== NOT_OURS_FOREIGN_CWD;
|
|
260
|
-
}
|
|
261
|
-
async function resolveMetroWithRetry(resolve, port, root, { delays = GATE_RETRY_DELAYS_MS, sleep: wait = sleep, onRetry = (_info) => {} } = {}) {
|
|
262
|
-
let resolution = await resolve(port, root);
|
|
263
|
-
for (let i = 0; i < delays.length && gateShouldRetry(resolution); i++) {
|
|
264
|
-
const delayMs = delays[i];
|
|
265
|
-
if (delayMs === void 0) break;
|
|
266
|
-
onRetry({
|
|
267
|
-
attempt: i + 1,
|
|
268
|
-
delayMs,
|
|
269
|
-
resolution
|
|
270
|
-
});
|
|
271
|
-
await wait(delayMs);
|
|
272
|
-
resolution = await resolve(port, root);
|
|
273
|
-
}
|
|
274
|
-
return resolution;
|
|
275
|
-
}
|
|
276
|
-
function noMetroMessage({ port, resolution, supervisor, supervisorAlive }) {
|
|
277
|
-
const foreign = resolution?.notOurs;
|
|
278
|
-
if (supervisor && supervisor.port === port && supervisorAlive) {
|
|
279
|
-
const mode = supervisor.mode ? `${supervisor.mode} ` : "";
|
|
280
|
-
return `A supervisor record exists for port ${port} (pid ${supervisor.pid}, ${mode}dev server) but it did not verify as this workspace's Metro${foreign ? `: ${foreign}` : " -- nothing answered /status"}. Metro may still be indexing this project (a monorepo file-map crawl blocks its event loop for ~20s after the port opens).`;
|
|
281
|
-
}
|
|
282
|
-
if (foreign) return `Port ${port} is in use but is NOT this workspace's dev server: ${foreign}.`;
|
|
283
|
-
return `Nothing is serving this workspace's dev server on port ${port}.`;
|
|
284
|
-
}
|
|
285
|
-
function noMetroRemedy({ port, supervisor, supervisorAlive }) {
|
|
286
|
-
if (supervisor && supervisor.port === port && supervisorAlive) return "Re-run `stim ios` in a few seconds, or give the dev server longer to verify with `stim start --wait <seconds>`.";
|
|
287
|
-
return "Run `stim start` first, or pass --no-metro-check.";
|
|
288
|
-
}
|
|
289
|
-
async function ensureWorkspaceStorageSafely(root, { note = (_line) => {} } = {}) {
|
|
290
|
-
try {
|
|
291
|
-
return ensureWorkspaceStorage(root);
|
|
292
|
-
} catch (err) {
|
|
293
|
-
note(chalk.dim(`Could not prepare this workspace's Stim state: ${err?.message || err}`));
|
|
294
|
-
throw err;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
function launchOutcomeRecord({ launchState, release, bundleId, configuration, metroPort }) {
|
|
298
|
-
const unverified = launchState === LAUNCH_UNVERIFIED;
|
|
299
|
-
const bundling = launchState === LAUNCH_BUNDLING;
|
|
300
|
-
let msg;
|
|
301
|
-
if (release) msg = unverified ? `${bundleId} could not be verified as running after its ${configuration} launch` : `${bundleId} is running its embedded ${configuration} bundle`;
|
|
302
|
-
else if (unverified) msg = `no bundle request from ${bundleId} reached this workspace's Metro on port ${metroPort}`;
|
|
303
|
-
else if (bundling) msg = `${bundleId} requested a bundle from this workspace's Metro on port ${metroPort}; it was still being built when the launch check ended`;
|
|
304
|
-
else msg = `${bundleId} fetched a bundle from this workspace's Metro on port ${metroPort}`;
|
|
305
|
-
return {
|
|
306
|
-
src: "build",
|
|
307
|
-
level: unverified ? "warn" : "info",
|
|
308
|
-
event: unverified ? "launch_unverified" : bundling ? "launch_bundling" : "launch_verified",
|
|
309
|
-
msg
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
function isPhysicalDeviceRequest(flag) {
|
|
313
|
-
return flag !== null && flag !== void 0 && flag !== false;
|
|
314
|
-
}
|
|
315
|
-
//#endregion
|
|
316
|
-
//#region src/engine/prebuild.ts
|
|
317
|
-
const PREBUILD_ERROR = "STIM_PREBUILD_FAILED";
|
|
318
|
-
const LAST_LINES$1 = 20;
|
|
319
|
-
function nativeDirName(platform) {
|
|
320
|
-
return platform === "android" ? "android" : "ios";
|
|
321
|
-
}
|
|
322
|
-
function nativeDir(root, platform) {
|
|
323
|
-
return join(root, nativeDirName(platform));
|
|
324
|
-
}
|
|
325
|
-
function shouldPrebuild({ isExpo, nativeDirExists }) {
|
|
326
|
-
return Boolean(isExpo) && !nativeDirExists;
|
|
327
|
-
}
|
|
328
|
-
function needsPrebuild(root, platform, isExpo) {
|
|
329
|
-
return shouldPrebuild({
|
|
330
|
-
isExpo,
|
|
331
|
-
nativeDirExists: existsSync(nativeDir(root, platform))
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
function prebuildRefusal({ isExpo, platform, nativeDirExists }) {
|
|
335
|
-
if (nativeDirExists || isExpo) return null;
|
|
336
|
-
const dir = nativeDirName(platform);
|
|
337
|
-
return {
|
|
338
|
-
code: PREBUILD_ERROR,
|
|
339
|
-
message: `This project has no ${dir}/ directory and is not an Expo (CNG) project, so Stim cannot generate one.`,
|
|
340
|
-
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}/.`
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
function prebuildFailure(logWriter, result) {
|
|
344
|
-
logWriter?.write?.({
|
|
345
|
-
src: "build",
|
|
346
|
-
level: "error",
|
|
347
|
-
msg: result.reason || "Expo prebuild failed.",
|
|
348
|
-
event: "prebuild_failed"
|
|
349
|
-
});
|
|
350
|
-
return result;
|
|
351
|
-
}
|
|
352
|
-
async function runPrebuild(root, platform, logWriter, { spawnFn = null, now = Date.now, isExpo = null } = {}) {
|
|
353
|
-
const dirExists = existsSync(nativeDir(root, platform));
|
|
354
|
-
const refusal = prebuildRefusal({
|
|
355
|
-
isExpo: isExpo === null ? detectIsExpo(root) : isExpo,
|
|
356
|
-
platform,
|
|
357
|
-
nativeDirExists: dirExists
|
|
358
|
-
});
|
|
359
|
-
if (refusal) return prebuildFailure(logWriter, {
|
|
360
|
-
failed: true,
|
|
361
|
-
code: refusal.code,
|
|
362
|
-
reason: refusal.message,
|
|
363
|
-
remedy: refusal.remedy,
|
|
364
|
-
error: refusal,
|
|
365
|
-
lastLines: []
|
|
366
|
-
});
|
|
367
|
-
const bin = expoBinPath(root);
|
|
368
|
-
if (!bin) {
|
|
369
|
-
const binRefusal = expoBinRefusal(root, "prebuild");
|
|
370
|
-
return prebuildFailure(logWriter, {
|
|
371
|
-
failed: true,
|
|
372
|
-
code: PREBUILD_ERROR,
|
|
373
|
-
reason: binRefusal.message,
|
|
374
|
-
remedy: binRefusal.remedy,
|
|
375
|
-
lastLines: []
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
const spawn = spawnFn || ((cmd, args, opts) => getExecutor().spawn(cmd, args, opts));
|
|
379
|
-
const startedAt = now();
|
|
380
|
-
const tail = [];
|
|
381
|
-
const push = (line) => {
|
|
382
|
-
const msg = stripAnsi(String(line)).trimEnd();
|
|
383
|
-
if (!msg.trim()) return;
|
|
384
|
-
tail.push(msg);
|
|
385
|
-
if (tail.length > LAST_LINES$1) tail.shift();
|
|
386
|
-
logWriter?.write?.({
|
|
387
|
-
src: "build",
|
|
388
|
-
level: "debug",
|
|
389
|
-
msg,
|
|
390
|
-
raw: true,
|
|
391
|
-
event: "prebuild"
|
|
392
|
-
});
|
|
393
|
-
};
|
|
394
|
-
let child;
|
|
395
|
-
try {
|
|
396
|
-
child = spawn(bin, [
|
|
397
|
-
"prebuild",
|
|
398
|
-
"-p",
|
|
399
|
-
platform,
|
|
400
|
-
"--no-install"
|
|
401
|
-
], {
|
|
402
|
-
cwd: root,
|
|
403
|
-
stdio: [
|
|
404
|
-
"ignore",
|
|
405
|
-
"pipe",
|
|
406
|
-
"pipe"
|
|
407
|
-
],
|
|
408
|
-
env: {
|
|
409
|
-
...process.env,
|
|
410
|
-
FORCE_COLOR: "0",
|
|
411
|
-
CI: "1"
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
} catch (err) {
|
|
415
|
-
return prebuildFailure(logWriter, {
|
|
416
|
-
failed: true,
|
|
417
|
-
code: PREBUILD_ERROR,
|
|
418
|
-
reason: `Could not run \`expo prebuild\`: ${err?.message || err}`,
|
|
419
|
-
lastLines: []
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
const outReader = createLineReader(push);
|
|
423
|
-
const errReader = createLineReader(push);
|
|
424
|
-
child.stdout?.setEncoding?.("utf-8");
|
|
425
|
-
child.stderr?.setEncoding?.("utf-8");
|
|
426
|
-
child.stdout?.on("data", (chunk) => outReader.push(chunk));
|
|
427
|
-
child.stderr?.on("data", (chunk) => errReader.push(chunk));
|
|
428
|
-
const result = await waitForChild(child);
|
|
429
|
-
outReader.flush();
|
|
430
|
-
errReader.flush();
|
|
431
|
-
const durationMs = now() - startedAt;
|
|
432
|
-
if (result.error) return prebuildFailure(logWriter, {
|
|
433
|
-
failed: true,
|
|
434
|
-
code: PREBUILD_ERROR,
|
|
435
|
-
reason: `Could not run \`expo prebuild\`: ${result.error?.message || result.error}`,
|
|
436
|
-
lastLines: tail.slice(),
|
|
437
|
-
durationMs
|
|
438
|
-
});
|
|
439
|
-
if (result.code !== 0) {
|
|
440
|
-
const how = result.signal ? `signal ${result.signal}` : `exit code ${result.code}`;
|
|
441
|
-
return prebuildFailure(logWriter, {
|
|
442
|
-
failed: true,
|
|
443
|
-
code: PREBUILD_ERROR,
|
|
444
|
-
reason: `\`expo prebuild -p ${platform}\` failed (${how}).`,
|
|
445
|
-
lastLines: tail.slice(),
|
|
446
|
-
durationMs
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
if (!existsSync(nativeDir(root, platform))) return prebuildFailure(logWriter, {
|
|
450
|
-
failed: true,
|
|
451
|
-
code: PREBUILD_ERROR,
|
|
452
|
-
reason: `\`expo prebuild -p ${platform}\` succeeded but did not create ${nativeDirName(platform)}/.`,
|
|
453
|
-
lastLines: tail.slice(),
|
|
454
|
-
durationMs
|
|
455
|
-
});
|
|
456
|
-
return {
|
|
457
|
-
ok: true,
|
|
458
|
-
durationMs,
|
|
459
|
-
nativeDir: nativeDir(root, platform)
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
//#endregion
|
|
463
|
-
//#region src/engine/js-swap.ts
|
|
464
|
-
const JS_BUNDLE_NAME = "main.jsbundle";
|
|
465
|
-
const LAST_LINES = 5;
|
|
466
|
-
function hermesEnabledFromProperties(text) {
|
|
467
|
-
if (typeof text !== "string") return true;
|
|
468
|
-
let data;
|
|
469
|
-
try {
|
|
470
|
-
data = JSON.parse(text);
|
|
471
|
-
} catch {
|
|
472
|
-
return true;
|
|
473
|
-
}
|
|
474
|
-
if (!data || typeof data !== "object") return true;
|
|
475
|
-
const raw = data.hermesEnabled;
|
|
476
|
-
return raw !== "false" && raw !== false;
|
|
477
|
-
}
|
|
478
|
-
function readHermesEnabled(root) {
|
|
479
|
-
try {
|
|
480
|
-
return hermesEnabledFromProperties(readFileSync(join(root, "ios", "Podfile.properties.json"), "utf-8"));
|
|
481
|
-
} catch {
|
|
482
|
-
return true;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
const ENTRY_CANDIDATES = [
|
|
486
|
-
"index.js",
|
|
487
|
-
"index.ts",
|
|
488
|
-
"index.tsx",
|
|
489
|
-
"index.jsx"
|
|
490
|
-
];
|
|
491
|
-
function pickEntryFile(entries) {
|
|
492
|
-
const names = new Set((Array.isArray(entries) ? entries : []).filter((e) => typeof e === "string"));
|
|
493
|
-
return ENTRY_CANDIDATES.find((c) => names.has(c)) ?? "index.js";
|
|
494
|
-
}
|
|
495
|
-
function detectEntryFile(root) {
|
|
496
|
-
let entries;
|
|
497
|
-
try {
|
|
498
|
-
entries = readdirSync(root);
|
|
499
|
-
} catch {
|
|
500
|
-
return "index.js";
|
|
501
|
-
}
|
|
502
|
-
return pickEntryFile(entries);
|
|
503
|
-
}
|
|
504
|
-
function bundleCommand({ isExpo, entryFile, bundleOutput, assetsDest }) {
|
|
505
|
-
if (isExpo) return {
|
|
506
|
-
file: "npx",
|
|
507
|
-
args: [
|
|
508
|
-
"expo",
|
|
509
|
-
"export:embed",
|
|
510
|
-
"--platform",
|
|
511
|
-
"ios",
|
|
512
|
-
"--dev",
|
|
513
|
-
"false",
|
|
514
|
-
"--bundle-output",
|
|
515
|
-
bundleOutput,
|
|
516
|
-
"--assets-dest",
|
|
517
|
-
assetsDest
|
|
518
|
-
]
|
|
519
|
-
};
|
|
520
|
-
return {
|
|
521
|
-
file: "npx",
|
|
522
|
-
args: [
|
|
523
|
-
"react-native",
|
|
524
|
-
"bundle",
|
|
525
|
-
"--platform",
|
|
526
|
-
"ios",
|
|
527
|
-
"--dev",
|
|
528
|
-
"false",
|
|
529
|
-
"--entry-file",
|
|
530
|
-
entryFile,
|
|
531
|
-
"--bundle-output",
|
|
532
|
-
bundleOutput,
|
|
533
|
-
"--assets-dest",
|
|
534
|
-
assetsDest
|
|
535
|
-
]
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
function hermescPath(root, { exists = existsSync } = {}) {
|
|
539
|
-
const candidates = [
|
|
540
|
-
join(root, "node_modules", "hermes-compiler", "hermesc", "osx-bin", "hermesc"),
|
|
541
|
-
join(root, "ios", "Pods", "hermes-engine", "destroot", "bin", "hermesc"),
|
|
542
|
-
join(root, "node_modules", "react-native", "sdks", "hermesc", "osx-bin", "hermesc")
|
|
543
|
-
];
|
|
544
|
-
return candidates.find(exists) ?? candidates[candidates.length - 1];
|
|
545
|
-
}
|
|
546
|
-
function hermescArgs({ bundle, out }) {
|
|
547
|
-
return [
|
|
548
|
-
"-emit-binary",
|
|
549
|
-
"-out",
|
|
550
|
-
out,
|
|
551
|
-
bundle
|
|
552
|
-
];
|
|
553
|
-
}
|
|
554
|
-
async function swapJsBundle({ root, isExpo, cachedAppPath, logWriter = null, exec = null, spawnFn = null, mkdtemp = () => makeTemporaryDirectory(cachedAppPath, "stim-js-swap-"), exists = existsSync, hermesEnabled = null, now = Date.now, heartbeatMs = HEARTBEAT_INTERVAL_MS, onHeartbeat = (line) => console.error(line) }) {
|
|
555
|
-
const e = exec || getExecutor();
|
|
556
|
-
const startedAt = now();
|
|
557
|
-
const elapsed = () => now() - startedAt;
|
|
558
|
-
let tmp;
|
|
559
|
-
const fail = (step, reason, lastLines = []) => {
|
|
560
|
-
logWriter?.write?.({
|
|
561
|
-
src: "build",
|
|
562
|
-
level: "error",
|
|
563
|
-
msg: `JS swap failed at ${step}: ${reason}`,
|
|
564
|
-
event: "js_swap"
|
|
565
|
-
});
|
|
566
|
-
if (tmp) removeTemporaryEntry(tmp);
|
|
567
|
-
return {
|
|
568
|
-
failed: true,
|
|
569
|
-
step,
|
|
570
|
-
reason,
|
|
571
|
-
lastLines,
|
|
572
|
-
durationMs: elapsed()
|
|
573
|
-
};
|
|
574
|
-
};
|
|
575
|
-
let appCopy;
|
|
576
|
-
try {
|
|
577
|
-
tmp = mkdtemp();
|
|
578
|
-
appCopy = join(tmp, basename(cachedAppPath));
|
|
579
|
-
try {
|
|
580
|
-
e.runFile("cp", [
|
|
581
|
-
"-c",
|
|
582
|
-
"-R",
|
|
583
|
-
cachedAppPath,
|
|
584
|
-
appCopy
|
|
585
|
-
]);
|
|
586
|
-
} catch {
|
|
587
|
-
e.runFile("cp", [
|
|
588
|
-
"-R",
|
|
589
|
-
cachedAppPath,
|
|
590
|
-
appCopy
|
|
591
|
-
]);
|
|
592
|
-
}
|
|
593
|
-
} catch (err) {
|
|
594
|
-
return fail("copy", `could not copy ${cachedAppPath} aside: ${describe(err)}`);
|
|
595
|
-
}
|
|
596
|
-
const bundleOutput = join(tmp, JS_BUNDLE_NAME);
|
|
597
|
-
const assetsDest = join(tmp, "assets");
|
|
598
|
-
const command = bundleCommand({
|
|
599
|
-
isExpo,
|
|
600
|
-
entryFile: isExpo ? "index.js" : detectEntryFile(root),
|
|
601
|
-
bundleOutput,
|
|
602
|
-
assetsDest
|
|
603
|
-
});
|
|
604
|
-
try {
|
|
605
|
-
mkdirSync(assetsDest, { recursive: true });
|
|
606
|
-
} catch (err) {
|
|
607
|
-
return fail("bundle", `could not create ${assetsDest}: ${describe(err)}`);
|
|
608
|
-
}
|
|
609
|
-
logWriter?.write?.({
|
|
610
|
-
src: "build",
|
|
611
|
-
level: "info",
|
|
612
|
-
msg: `${command.file} ${command.args.join(" ")}`,
|
|
613
|
-
event: "js_swap"
|
|
614
|
-
});
|
|
615
|
-
const spawn = spawnFn || ((cmd, args, opts) => e.spawn(cmd, args, opts));
|
|
616
|
-
const transcript = [];
|
|
617
|
-
const push = (line) => {
|
|
618
|
-
const msg = cleanLine(line);
|
|
619
|
-
if (msg.trim() === "") return;
|
|
620
|
-
transcript.push(msg);
|
|
621
|
-
logWriter?.write?.({
|
|
622
|
-
src: "build",
|
|
623
|
-
level: "debug",
|
|
624
|
-
msg,
|
|
625
|
-
raw: true,
|
|
626
|
-
event: "js_swap"
|
|
627
|
-
});
|
|
628
|
-
};
|
|
629
|
-
let child;
|
|
630
|
-
try {
|
|
631
|
-
child = spawn(command.file, command.args, {
|
|
632
|
-
cwd: root,
|
|
633
|
-
stdio: [
|
|
634
|
-
"ignore",
|
|
635
|
-
"pipe",
|
|
636
|
-
"pipe"
|
|
637
|
-
],
|
|
638
|
-
env: {
|
|
639
|
-
...process.env,
|
|
640
|
-
FORCE_COLOR: "0"
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
} catch (err) {
|
|
644
|
-
return fail("bundle", `could not run ${command.file} ${command.args[0]}: ${describe(err)}`);
|
|
645
|
-
}
|
|
646
|
-
const reader = {
|
|
647
|
-
out: createLineReader(push),
|
|
648
|
-
err: createLineReader(push)
|
|
649
|
-
};
|
|
650
|
-
child.stdout?.setEncoding?.("utf-8");
|
|
651
|
-
child.stderr?.setEncoding?.("utf-8");
|
|
652
|
-
child.stdout?.on("data", (chunk) => reader.out.push(chunk));
|
|
653
|
-
child.stderr?.on("data", (chunk) => reader.err.push(chunk));
|
|
654
|
-
const stopHeartbeat = startBuildHeartbeat({
|
|
655
|
-
intervalMs: heartbeatMs,
|
|
656
|
-
elapsed,
|
|
657
|
-
emit: onHeartbeat,
|
|
658
|
-
label: "swap"
|
|
659
|
-
});
|
|
660
|
-
let outcome;
|
|
661
|
-
try {
|
|
662
|
-
outcome = await waitForChild(child);
|
|
663
|
-
} finally {
|
|
664
|
-
stopHeartbeat();
|
|
665
|
-
}
|
|
666
|
-
reader.out.flush();
|
|
667
|
-
reader.err.flush();
|
|
668
|
-
if (outcome.error) return fail("bundle", `could not run ${command.file} ${command.args[0]}: ${describe(outcome.error)}`);
|
|
669
|
-
if (outcome.code !== 0) {
|
|
670
|
-
const how = outcome.signal ? `signal ${outcome.signal}` : `exit code ${outcome.code}`;
|
|
671
|
-
return fail("bundle", `\`${command.args.slice(0, 2).join(" ")}\` failed (${how})`, tailLines(transcript, LAST_LINES));
|
|
672
|
-
}
|
|
673
|
-
if (!exists(bundleOutput)) return fail("bundle", `the bundle command exited 0 but wrote no ${JS_BUNDLE_NAME} at ${bundleOutput}`);
|
|
674
|
-
let hermes = false;
|
|
675
|
-
let note;
|
|
676
|
-
if (hermesEnabled ?? readHermesEnabled(root)) {
|
|
677
|
-
const hermesc = hermescPath(root);
|
|
678
|
-
if (!exists(hermesc)) note = `hermesc not found at ${hermesc}; embedding the plain JS bundle instead of Hermes bytecode`;
|
|
679
|
-
else {
|
|
680
|
-
const hbc = join(tmp, `${JS_BUNDLE_NAME}.hbc`);
|
|
681
|
-
try {
|
|
682
|
-
e.runFile(hermesc, hermescArgs({
|
|
683
|
-
bundle: bundleOutput,
|
|
684
|
-
out: hbc
|
|
685
|
-
}));
|
|
686
|
-
e.runFile("mv", [hbc, bundleOutput]);
|
|
687
|
-
hermes = true;
|
|
688
|
-
} catch (err) {
|
|
689
|
-
return fail("hermesc", `hermesc failed on ${bundleOutput}: ${describe(err)}`);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
try {
|
|
694
|
-
e.runFile("cp", [bundleOutput, join(appCopy, JS_BUNDLE_NAME)]);
|
|
695
|
-
e.runFile("cp", [
|
|
696
|
-
"-R",
|
|
697
|
-
`${assetsDest}/.`,
|
|
698
|
-
`${appCopy}/`
|
|
699
|
-
]);
|
|
700
|
-
} catch (err) {
|
|
701
|
-
return fail("replace", `could not replace the JS bundle inside ${appCopy}: ${describe(err)}`);
|
|
702
|
-
}
|
|
703
|
-
try {
|
|
704
|
-
e.runFile("codesign", [
|
|
705
|
-
"--force",
|
|
706
|
-
"--sign",
|
|
707
|
-
"-",
|
|
708
|
-
appCopy
|
|
709
|
-
]);
|
|
710
|
-
} catch (err) {
|
|
711
|
-
return fail("codesign", `codesign --force --sign - ${appCopy} failed: ${describe(err)}`);
|
|
712
|
-
}
|
|
713
|
-
logWriter?.write?.({
|
|
714
|
-
src: "build",
|
|
715
|
-
level: "info",
|
|
716
|
-
msg: `JS swap done: ${hermes ? "hermes bytecode" : "plain JS"} into ${appCopy} in ${elapsed()}ms`,
|
|
717
|
-
event: "js_swap"
|
|
718
|
-
});
|
|
719
|
-
const result = {
|
|
720
|
-
ok: true,
|
|
721
|
-
appPath: appCopy,
|
|
722
|
-
tmpDir: tmp,
|
|
723
|
-
hermes,
|
|
724
|
-
durationMs: elapsed()
|
|
725
|
-
};
|
|
726
|
-
if (note) result.note = note;
|
|
727
|
-
return result;
|
|
728
|
-
}
|
|
729
|
-
function describe(err) {
|
|
730
|
-
return String(err?.message || err);
|
|
731
|
-
}
|
|
732
|
-
//#endregion
|
|
733
|
-
//#region src/commands/dev-client.ts
|
|
734
|
-
function devClientScheme(root, appPath = null, { exec = null } = {}) {
|
|
735
|
-
if (!hasDevClient(root)) return void 0;
|
|
736
|
-
const fromBundle = pickDevClientScheme(readBundleSchemes(appPath, { exec }));
|
|
737
|
-
if (fromBundle) return fromBundle;
|
|
738
|
-
const app = readJson(join(root, "app.json"));
|
|
739
|
-
const raw = app?.expo?.scheme ?? app?.scheme ?? null;
|
|
740
|
-
const scheme = Array.isArray(raw) ? raw.find((s) => typeof s === "string" && s.trim() !== "") : raw;
|
|
741
|
-
if (typeof scheme !== "string" || scheme.trim() === "") return void 0;
|
|
742
|
-
return scheme.trim();
|
|
743
|
-
}
|
|
744
|
-
function schemesFromInfoPlist(plist) {
|
|
745
|
-
const types = plist?.CFBundleURLTypes;
|
|
746
|
-
if (!Array.isArray(types)) return [];
|
|
747
|
-
const out = [];
|
|
748
|
-
for (const type of types) {
|
|
749
|
-
const schemes = type?.CFBundleURLSchemes;
|
|
750
|
-
if (Array.isArray(schemes)) out.push(...schemes.filter((s) => typeof s === "string" && s.trim() !== ""));
|
|
751
|
-
}
|
|
752
|
-
return out;
|
|
753
|
-
}
|
|
754
|
-
function readBundleSchemes(appPath, { exec = null } = {}) {
|
|
755
|
-
if (typeof appPath !== "string" || appPath.trim() === "") return [];
|
|
756
|
-
const e = exec || getExecutor();
|
|
757
|
-
let out;
|
|
758
|
-
try {
|
|
759
|
-
out = e.runFile("plutil", [
|
|
760
|
-
"-convert",
|
|
761
|
-
"json",
|
|
762
|
-
"-o",
|
|
763
|
-
"-",
|
|
764
|
-
join(appPath, "Info.plist")
|
|
765
|
-
]);
|
|
766
|
-
} catch {
|
|
767
|
-
return [];
|
|
768
|
-
}
|
|
769
|
-
try {
|
|
770
|
-
return schemesFromInfoPlist(JSON.parse(String(out)));
|
|
771
|
-
} catch {
|
|
772
|
-
return [];
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
const THIRD_PARTY_SCHEME = /^(?:fb\d+|com\.googleusercontent\.apps\.|msauth\.|msauthv2|twitterkit-|db-[a-z0-9]+$|spotify|snapchat|com\.facebook)/i;
|
|
776
|
-
function pickDevClientScheme(schemes) {
|
|
777
|
-
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));
|
|
778
|
-
const expo = all.filter((s) => s.startsWith("exp+"));
|
|
779
|
-
return (expo.length ? expo : all.filter((s) => !THIRD_PARTY_SCHEME.test(s))).toSorted((a, b) => b.length - a.length)[0] ?? null;
|
|
780
|
-
}
|
|
781
|
-
function hasDevClient(root) {
|
|
782
|
-
const pkg = readJson(join(root, "package.json"));
|
|
783
|
-
if ("expo-dev-client" in {
|
|
784
|
-
...pkg?.dependencies,
|
|
785
|
-
...pkg?.devDependencies
|
|
786
|
-
}) return true;
|
|
787
|
-
return isPackageResolvable(root, "expo-dev-client");
|
|
788
|
-
}
|
|
789
|
-
function readJson(file) {
|
|
790
|
-
try {
|
|
791
|
-
return JSON.parse(readFileSync(file, "utf-8"));
|
|
792
|
-
} catch {
|
|
793
|
-
return null;
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
//#endregion
|
|
797
|
-
export { easDeviceBuildRemedy as _, swapJsBundle as a, ensureWorkspaceStorageSafely as c, launchOutcomeRecord as d, noMetroMessage as f, warmMetro as g, sleep as h, detectEntryFile as i, gateShouldRetry as l, resolveMetroWithRetry as m, pickDevClientScheme as n, needsPrebuild as o, noMetroRemedy as p, schemesFromInfoPlist as r, runPrebuild as s, devClientScheme as t, isPhysicalDeviceRequest as u, isEasBuildFailure as v, resolveEasDevelopmentBuild as y };
|