stim 1.4.0 → 1.6.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 +3 -3
- package/dist/{android-BkM4qTaN.mjs → android-BkYmndPp.mjs} +5 -5
- package/dist/{android-BTR1KTMP.mjs → android-DLJB8ljN.mjs} +22 -22
- package/dist/{android-cas-BjLRZyks.mjs → android-cas-CrzI_Zha.mjs} +5 -5
- package/dist/{build-slots-D6sDzOe-.mjs → build-slots-CuPszJDu.mjs} +1 -1
- package/dist/{cache-manifest-D02TpYjA.mjs → cache-manifest-uoOKhgUD.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/cli.mjs +14 -14
- package/dist/collector-run.mjs +3 -3
- package/dist/{config-6vdGngKa.mjs → config-DMP7L7H6.mjs} +1 -1
- package/dist/{dependency-state-BMirHEPD.mjs → dependency-state-CTL8A9p3.mjs} +1 -1
- package/dist/{deps-Ck4wuTwa.mjs → deps-C1zGnr61.mjs} +1 -1
- package/dist/{dev-client-Cd0v7iZk.mjs → dev-client-BaTXAAnC.mjs} +9 -9
- package/dist/{device-CMDFzx7C.mjs → device-XY_5eGdN.mjs} +8 -8
- package/dist/{device-lease-DDJr85OK.mjs → device-lease-DBW-IL4p.mjs} +3 -3
- package/dist/{device-pool-DJvbIBGW.mjs → device-pool-C0s_83Og.mjs} +2 -2
- package/dist/{device-remote-CMfHhoc8.mjs → device-remote-D9wN3CdE.mjs} +6 -6
- package/dist/{device-CKXLnRRb.mjs → device-xoQxw8hR.mjs} +5 -5
- package/dist/{doctor-B_mfURdt.mjs → doctor-BRO8UKlK.mjs} +25 -168
- package/dist/{error-diagnostics-_VmkqRZo.mjs → error-diagnostics-B5STzxqN.mjs} +6 -6
- package/dist/{gc-0AyxmECv.mjs → gc-C-6grAQI.mjs} +12 -12
- package/dist/{guide-CGxdmQ0K.mjs → guide-CYtd7xbA.mjs} +49 -21
- package/dist/guide-status-BF6RsJJp.mjs +271 -0
- package/dist/{ios-CtmGLx3X.mjs → ios-Ccm6c8Hj.mjs} +21 -21
- package/dist/{logs-BF3qPA7w.mjs → logs-D4Poojxy.mjs} +4 -4
- package/dist/{metro-DFLmaVz8.mjs → metro-ato6ef3J.mjs} +3 -3
- package/dist/{named-ports-BO7Zpjp0.mjs → named-ports-DH9SXd0f.mjs} +3 -3
- package/dist/{ownership-BvSpZVBk.mjs → ownership-CD_XnpDu.mjs} +3 -3
- package/dist/{ownership-C08Yzrp7.mjs → ownership-CVY9cqCr.mjs} +3 -3
- package/dist/{ports-BiKqP92J.mjs → ports-A6uTJSPj.mjs} +3 -3
- package/dist/{ports-ClqG8GPt.mjs → ports-BZprTJRa.mjs} +1 -1
- package/dist/{project-CBFCwnN5.mjs → project-B_SDt4ip.mjs} +2 -2
- package/dist/{reclaim-DpDVo-AP.mjs → reclaim-WXTbXPX4.mjs} +10 -10
- package/dist/{reload-ByTqg6ZI.mjs → reload-xlQvHKfN.mjs} +6 -6
- package/dist/{server-bare-Buk-k8tI.mjs → server-bare-CCLb90FS.mjs} +11 -32
- package/dist/{server-expo-D4P7iTvf.mjs → server-expo-CX-zkgWl.mjs} +6 -14
- package/dist/server-expo-CjeTJ273.mjs +2 -0
- package/dist/{settings-DwfltIXQ.mjs → settings-CGHTt5vb.mjs} +7 -4
- package/dist/{slot-launch-DeKen9e5.mjs → slot-launch-BYjnljAs.mjs} +3 -3
- package/dist/{start-BpKUHcU_.mjs → start-DHYAyGzm.mjs} +16 -20
- package/dist/{state-CDbvmZdt.mjs → state-CnWzvCWP.mjs} +2 -2
- package/dist/{stats-k5qMjyxo.mjs → stats-DHrO0frH.mjs} +3 -3
- package/dist/{stats-C59QLKm2.mjs → stats-Fo9ACIv7.mjs} +1 -1
- package/dist/{status-Y2ij3dm6.mjs → status-QeLZb8SM.mjs} +14 -12
- package/dist/{stop-vKmmxhea.mjs → stop-CHTaw65F.mjs} +2 -2
- package/dist/{stop-_P2OQwmZ.mjs → stop-DsdOx9Hj.mjs} +11 -11
- package/dist/supervisor-run.d.mts +4 -1
- package/dist/supervisor-run.mjs +18 -10
- package/dist/{teardown-KsPpvldq.mjs → teardown-sm1FwF6V.mjs} +5 -5
- package/dist/{workspace-process-lock-UEIrvZMJ.mjs → workspace-process-lock-Bz19_R9R.mjs} +1 -1
- package/dist/{workspace-state-Do1e2mOH.mjs → workspace-state-WVwWw_hB.mjs} +1 -1
- package/dist/{worktree-BvcAIMdJ.mjs → worktree-CCNPg_f0.mjs} +25 -20
- package/dist/{worktree-DWNx4tUg.mjs → worktree-DJnBbQZ9.mjs} +49 -22
- package/dist/{xcode-D9tVItl-.mjs → xcode-DUF7jrXj.mjs} +89 -14
- package/package.json +5 -5
- package/shim/expo-metro-config.cjs +11 -17
- package/dist/server-expo-Dho5Raiw.mjs +0 -2
- package/shim/metro-cache-generation.cjs +0 -20
- package/shim/metro-cache-generation.d.cts +0 -5
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { E as ownership_claim_exports, S as upsertProject, f as getProject, g as loadConfig, h as isPathPrefix, u as getConfigDir, v as removeProject, w as process_identity_exports } from "./config-
|
|
3
|
-
import {
|
|
4
|
-
import { a as findProjectRoot } from "./project-
|
|
5
|
-
import { l as readRemoteSession, s as readMetroTunnel } from "./state-
|
|
2
|
+
import { E as ownership_claim_exports, S as upsertProject, f as getProject, g as loadConfig, h as isPathPrefix, u as getConfigDir, v as removeProject, w as process_identity_exports } from "./config-DMP7L7H6.mjs";
|
|
3
|
+
import { S as warmWorktreePaths, _ as resolveFullRef, b as sourceCheckoutOf, c as hasRemote, d as listWorktrees, f as locallyKnownUpstream, h as removeWorktree, i as depsOutOfSync, l as hasUncommittedWork, m as readWorktreeExclude, n as cloneIgnoredEntries, o as dirtyPaths, p as podsOutOfSync, r as deleteBranch, s as gitCommonDir, t as branchExists, u as isPodInstallChurn, x as unpushedCommits, y as restoreFile } from "./worktree-DJnBbQZ9.mjs";
|
|
4
|
+
import { a as findProjectRoot } from "./project-B_SDt4ip.mjs";
|
|
5
|
+
import { l as readRemoteSession, s as readMetroTunnel } from "./state-CnWzvCWP.mjs";
|
|
6
6
|
import { a as formatElapsed, f as releasedLeaseFact, i as formatDuration, l as phaseLine, m as shortUdid, u as plural } from "./command-output-BuIb_ZOI.mjs";
|
|
7
|
-
import { A as resolveSettings, I as unknownSettingKeys, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-
|
|
8
|
-
import { a as POOL_SETTING_REMEDY, l as parkedMaxSetting } from "./teardown-
|
|
9
|
-
import { h as podInstallCommand, n as HEARTBEAT_INTERVAL_MS } from "./xcode-
|
|
10
|
-
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-
|
|
11
|
-
import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-
|
|
12
|
-
import { n as reclaimProject } from "./reclaim-
|
|
13
|
-
import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-
|
|
7
|
+
import { A as resolveSettings, I as unknownSettingKeys, N as settingShapeErrors, a as SETTING_SHAPE_REMEDY } from "./settings-CGHTt5vb.mjs";
|
|
8
|
+
import { a as POOL_SETTING_REMEDY, l as parkedMaxSetting } from "./teardown-sm1FwF6V.mjs";
|
|
9
|
+
import { h as podInstallCommand, n as HEARTBEAT_INTERVAL_MS } from "./xcode-DUF7jrXj.mjs";
|
|
10
|
+
import { n as hasInstalledDependencies, r as installedNpmTreeIsValid, t as dependencyState } from "./dependency-state-CTL8A9p3.mjs";
|
|
11
|
+
import { c as withManagedTunnelRemovalLock, o as withManagedRemoteWorktreeRemovalLock } from "./ownership-CVY9cqCr.mjs";
|
|
12
|
+
import { n as reclaimProject } from "./reclaim-WXTbXPX4.mjs";
|
|
13
|
+
import { a as runPodInstall, i as runCaptured, n as podsAreStale, r as readPodState, t as DEPS_ERROR } from "./deps-C1zGnr61.mjs";
|
|
14
14
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
15
15
|
import { createHash, randomUUID } from "node:crypto";
|
|
16
16
|
import { join, relative } from "node:path";
|
|
@@ -1237,7 +1237,19 @@ async function runRemove(target, opts = {}) {
|
|
|
1237
1237
|
process.exitCode = 1;
|
|
1238
1238
|
return;
|
|
1239
1239
|
}
|
|
1240
|
-
if (
|
|
1240
|
+
if (entry.bare) {
|
|
1241
|
+
console.error(chalk.red(`Refusing to remove ${path}: ${entry.path} is the bare repository, not a worktree.`));
|
|
1242
|
+
console.error(chalk.dim(" Pass a worktree path, e.g. as printed by `git worktree list`."));
|
|
1243
|
+
process.exitCode = 1;
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const source = sourceCheckoutOf(worktrees);
|
|
1247
|
+
if ("refusal" in source) {
|
|
1248
|
+
console.error(chalk.red(`Refusing to remove ${path}: ${source.refusal}`));
|
|
1249
|
+
process.exitCode = 1;
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (entry.path === source.path) {
|
|
1241
1253
|
if (entry.path !== path) console.error(chalk.dim(`${path} is inside the source checkout ${entry.path}; reclaiming its environment.`));
|
|
1242
1254
|
await reclaimEnvironment(entry.path, "it is the source checkout");
|
|
1243
1255
|
return;
|
|
@@ -1257,7 +1269,7 @@ async function runRemove(target, opts = {}) {
|
|
|
1257
1269
|
}
|
|
1258
1270
|
const deleteOwnedBranch = Boolean(ownsBranch && approvedBranchSha && inspection.unpushed?.length === 0);
|
|
1259
1271
|
const retainedBranchReason = !branch ? null : !ownsBranch ? "Stim did not create it" : inspection.unpushed === null ? "Stim could not verify whether it has unique commits" : inspection.unpushed.length > 0 ? `it has ${plural(inspection.unpushed.length, "unique commit")}` : null;
|
|
1260
|
-
const branchDeleteCwd =
|
|
1272
|
+
const branchDeleteCwd = source.path;
|
|
1261
1273
|
await withManagedRemoteWorktreeRemovalLock(path, () => withReclaimLocks(path, async (lockedKeys) => {
|
|
1262
1274
|
const result = await reclaimAll(path, lockedKeys, { preserveRootProject: true });
|
|
1263
1275
|
if (result.keptEntries.length) {
|
|
@@ -1289,13 +1301,6 @@ async function runRemove(target, opts = {}) {
|
|
|
1289
1301
|
worktreeRemovalComplete: true,
|
|
1290
1302
|
worktreePendingBranchSha: approvedBranchSha
|
|
1291
1303
|
});
|
|
1292
|
-
if (!branchDeleteCwd) {
|
|
1293
|
-
console.error(chalk.yellow(phaseLine("branch", `kept ${branch} (Stim could not find the source checkout)`)));
|
|
1294
|
-
console.error(chalk.dim(` Retry with: stim worktree remove ${path}`));
|
|
1295
|
-
process.exitCode = 1;
|
|
1296
|
-
finish();
|
|
1297
|
-
return;
|
|
1298
|
-
}
|
|
1299
1304
|
const checkedOutAt = listWorktrees(branchDeleteCwd).find((candidate) => candidate.branch === branch && resolve$1(candidate.path) !== resolve$1(path))?.path;
|
|
1300
1305
|
if (checkedOutAt) {
|
|
1301
1306
|
console.error(chalk.yellow(phaseLine("branch", `kept ${branch} (it is checked out at ${checkedOutAt})`)));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { g as loadConfig, u as getConfigDir } from "./config-
|
|
2
|
+
import { g as loadConfig, u as getConfigDir } from "./config-DMP7L7H6.mjs";
|
|
3
3
|
import { accessSync, chmodSync, constants, lstatSync, mkdirSync, mkdtempSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { chmodSync as chmodSync$1, existsSync as existsSync$1, lstatSync as lstatSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, utimesSync } from "fs";
|
|
@@ -103,19 +103,6 @@ function gitCommonDir(cwd) {
|
|
|
103
103
|
]);
|
|
104
104
|
return out ? out.trim() : null;
|
|
105
105
|
}
|
|
106
|
-
function isMainWorkingTree(path) {
|
|
107
|
-
const out = getExecutor().runFileQuiet("git", [
|
|
108
|
-
"-C",
|
|
109
|
-
path,
|
|
110
|
-
"rev-parse",
|
|
111
|
-
"--path-format=absolute",
|
|
112
|
-
"--git-dir",
|
|
113
|
-
"--git-common-dir"
|
|
114
|
-
]);
|
|
115
|
-
if (!out) return false;
|
|
116
|
-
const [gitDir, commonDir] = out.trim().split("\n").map((line) => line.trim());
|
|
117
|
-
return Boolean(gitDir) && gitDir === commonDir;
|
|
118
|
-
}
|
|
119
106
|
function repoRoot(cwd) {
|
|
120
107
|
const out = getExecutor().runFileQuiet("git", [
|
|
121
108
|
"-C",
|
|
@@ -159,16 +146,15 @@ function warmWorktreePaths(cwd) {
|
|
|
159
146
|
const currentRoot = repoRoot(cwd);
|
|
160
147
|
if (!currentRoot) throw new Error("Not a git repository.");
|
|
161
148
|
const target = realpathSync$1(currentRoot);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
if (
|
|
167
|
-
const root = realpathSync$1(main.path);
|
|
149
|
+
const source = resolveSourceCheckout(target);
|
|
150
|
+
if ("refusal" in source) throw new Error(source.refusal);
|
|
151
|
+
if (!source.entries.find((entry) => canonicalPath(entry.path) === target)) throw new Error("Could not identify the linked worktree and its source checkout.");
|
|
152
|
+
const root = realpathSync$1(source.path);
|
|
153
|
+
if (root === target) throw new Error("Run stim worktree warm from a linked worktree, not the source checkout.");
|
|
168
154
|
const sourceRoot = repoRoot(root);
|
|
169
155
|
const sourceCommon = gitCommonDir(root);
|
|
170
156
|
const targetCommon = gitCommonDir(target);
|
|
171
|
-
if (
|
|
157
|
+
if (!sourceRoot || realpathSync$1(sourceRoot) !== root || !sourceCommon || !targetCommon || realpathSync$1(sourceCommon) !== realpathSync$1(targetCommon)) throw new Error("Could not verify that the source checkout belongs to this linked worktree.");
|
|
172
158
|
return {
|
|
173
159
|
root,
|
|
174
160
|
target,
|
|
@@ -556,6 +542,7 @@ function parseWorktrees(out) {
|
|
|
556
542
|
current = { path: line.slice(9) };
|
|
557
543
|
} else if (line.startsWith("branch ")) current.branch = line.slice(7).replace("refs/heads/", "");
|
|
558
544
|
else if (line === "prunable" || line.startsWith("prunable ")) current.prunable = true;
|
|
545
|
+
else if (line === "bare") current.bare = true;
|
|
559
546
|
if (current.path) entries.push(current);
|
|
560
547
|
return entries;
|
|
561
548
|
}
|
|
@@ -569,5 +556,45 @@ function listWorktrees(cwd) {
|
|
|
569
556
|
]);
|
|
570
557
|
return out ? parseWorktrees(out) : [];
|
|
571
558
|
}
|
|
559
|
+
function selectSourceCheckout(entries, bareHead) {
|
|
560
|
+
const first = entries[0];
|
|
561
|
+
if (!first) return { refusal: "Git lists no worktrees for this repository." };
|
|
562
|
+
if (!first.bare) return { path: first.path };
|
|
563
|
+
const bare = first.path;
|
|
564
|
+
const checkouts = entries.filter((entry) => !entry.bare && !entry.prunable);
|
|
565
|
+
const listed = checkouts.map((entry) => `${entry.branch ?? "detached"} (${entry.path})`).join(", ") || "none";
|
|
566
|
+
const pointHead = ` git -C ${bare} symbolic-ref HEAD refs/heads/<branch>`;
|
|
567
|
+
if (!bareHead) return { refusal: `The bare repository at ${bare} has a detached HEAD, so Stim cannot tell which worktree is the source checkout. Point HEAD at the source branch, then retry:\n${pointHead}\nChecked-out branches: ${listed}` };
|
|
568
|
+
const matches = checkouts.filter((entry) => entry.branch === bareHead);
|
|
569
|
+
const [only] = matches;
|
|
570
|
+
if (only && matches.length === 1) return { path: only.path };
|
|
571
|
+
if (!only) {
|
|
572
|
+
const stale = entries.find((entry) => entry.prunable && entry.branch === bareHead);
|
|
573
|
+
const beside = checkouts[0] ? dirname$1(checkouts[0].path) : bare;
|
|
574
|
+
return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, but no worktree has ${bareHead} checked out, so Stim has no source checkout to copy from. ${stale ? `Its worktree at ${stale.path} is missing. Recreate it, then retry:\n git -C ${bare} worktree prune\n git -C ${bare} worktree add ${stale.path} ${bareHead}\n` : `Create one, then retry:\n git -C ${bare} worktree add ${join$1(beside, bareHead)} ${bareHead}\n`}Or point HEAD at a branch that is checked out (${listed}):\n${pointHead}` };
|
|
575
|
+
}
|
|
576
|
+
return { refusal: `The bare repository at ${bare} points HEAD at ${bareHead}, which is checked out in more than one worktree: ${matches.map((entry) => entry.path).join(", ")}. Remove the extra worktrees, or point HEAD at a branch checked out once, then retry:\n${pointHead}` };
|
|
577
|
+
}
|
|
578
|
+
function bareHeadBranch(bare) {
|
|
579
|
+
const out = getExecutor().runFileQuiet("git", [
|
|
580
|
+
"-C",
|
|
581
|
+
bare,
|
|
582
|
+
"symbolic-ref",
|
|
583
|
+
"--quiet",
|
|
584
|
+
"HEAD"
|
|
585
|
+
])?.trim();
|
|
586
|
+
return out?.startsWith("refs/heads/") ? out.slice(11) : null;
|
|
587
|
+
}
|
|
588
|
+
function sourceCheckoutOf(entries) {
|
|
589
|
+
const first = entries[0];
|
|
590
|
+
return selectSourceCheckout(entries, first?.bare ? bareHeadBranch(first.path) : null);
|
|
591
|
+
}
|
|
592
|
+
function resolveSourceCheckout(cwd) {
|
|
593
|
+
const entries = listWorktrees(cwd);
|
|
594
|
+
return {
|
|
595
|
+
entries,
|
|
596
|
+
...sourceCheckoutOf(entries)
|
|
597
|
+
};
|
|
598
|
+
}
|
|
572
599
|
//#endregion
|
|
573
|
-
export {
|
|
600
|
+
export { filesystemDevice as C, temporaryRoot as E, warmWorktreePaths as S, removeTemporaryEntry as T, resolveFullRef as _, dirtyFingerprintFiles as a, sourceCheckoutOf as b, hasRemote as c, listWorktrees as d, locallyKnownUpstream as f, repoRoot as g, removeWorktree as h, depsOutOfSync as i, hasUncommittedWork as l, readWorktreeExclude as m, cloneIgnoredEntries as n, dirtyPaths as o, podsOutOfSync as p, deleteBranch as r, gitCommonDir as s, branchExists as t, isPodInstallChurn as u, resolveSourceCheckout as v, makeTemporaryDirectory as w, unpushedCommits as x, restoreFile as y };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as getExecutor } from "./exec-CyylIdq9.mjs";
|
|
2
|
-
import { r as register } from "./cache-manifest-
|
|
3
|
-
import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-
|
|
2
|
+
import { r as register } from "./cache-manifest-uoOKhgUD.mjs";
|
|
3
|
+
import { d as sharedCompilationCache, m as workspaceDerivedData } from "./workspace-state-WVwWw_hB.mjs";
|
|
4
|
+
import { l as resolvePackageJson } from "./project-B_SDt4ip.mjs";
|
|
4
5
|
import { t as createLineReader } from "./process-output-C7IlIzd7.mjs";
|
|
5
6
|
import { a as formatElapsed, l as phaseLine } from "./command-output-BuIb_ZOI.mjs";
|
|
6
|
-
import { U as resolveOptimizations } from "./settings-
|
|
7
|
-
import { t as cleanLine } from "./server-expo-
|
|
7
|
+
import { U as resolveOptimizations } from "./settings-CGHTt5vb.mjs";
|
|
8
|
+
import { t as cleanLine } from "./server-expo-CX-zkgWl.mjs";
|
|
8
9
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
9
10
|
import { createHash } from "node:crypto";
|
|
10
11
|
import { basename, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -332,18 +333,73 @@ function readPodfileProperties(root) {
|
|
|
332
333
|
return null;
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
|
-
|
|
336
|
+
const SWIFT_PREFIX_MAPPING_MIN_SWIFT = {
|
|
337
|
+
major: 6,
|
|
338
|
+
minor: 4
|
|
339
|
+
};
|
|
340
|
+
function swiftPrefixMappingSupported(version) {
|
|
341
|
+
if (!version) return false;
|
|
342
|
+
const min = SWIFT_PREFIX_MAPPING_MIN_SWIFT;
|
|
343
|
+
return version.major > min.major || version.major === min.major && version.minor >= min.minor;
|
|
344
|
+
}
|
|
345
|
+
const SWIFT_CACHE_MIN_REACT_NATIVE = {
|
|
346
|
+
major: 0,
|
|
347
|
+
minor: 87
|
|
348
|
+
};
|
|
349
|
+
function reactNativeSupportsSwiftCache(version) {
|
|
350
|
+
if (!version) return false;
|
|
351
|
+
const min = SWIFT_CACHE_MIN_REACT_NATIVE;
|
|
352
|
+
return version.major > min.major || version.major === min.major && version.minor >= min.minor;
|
|
353
|
+
}
|
|
354
|
+
function parseReactNativeVersion(packageJson) {
|
|
355
|
+
if (!packageJson || typeof packageJson !== "object") return null;
|
|
356
|
+
const raw = packageJson.version;
|
|
357
|
+
const m = typeof raw === "string" ? /^(\d+)\.(\d+)\./.exec(raw) : null;
|
|
358
|
+
if (!m || m[1] === void 0 || m[2] === void 0) return null;
|
|
359
|
+
return {
|
|
360
|
+
major: parseInt(m[1], 10),
|
|
361
|
+
minor: parseInt(m[2], 10)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function detectReactNativeVersion(root) {
|
|
365
|
+
const file = resolvePackageJson(root, "react-native");
|
|
366
|
+
if (!file) return null;
|
|
367
|
+
try {
|
|
368
|
+
return parseReactNativeVersion(JSON.parse(readFileSync(file, "utf-8")));
|
|
369
|
+
} catch {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
function compilationCacheSettings({ workspaceRoot, derivedDataPath, casPath, xcodeMajor, swiftVersion = null, reactNativeVersion = null, ccache = false, optimizations = resolveOptimizations({}, {}).ios }) {
|
|
336
374
|
if (xcodeMajor === null || xcodeMajor === void 0) return [];
|
|
337
375
|
if (xcodeMajor < 26) return [];
|
|
338
376
|
if (ccache) return [];
|
|
377
|
+
const swiftMappable = swiftPrefixMappingSupported(swiftVersion);
|
|
378
|
+
const auto = swiftMappable && reactNativeSupportsSwiftCache(reactNativeVersion);
|
|
379
|
+
const swift = optimizations.compilationCache && (optimizations.swiftCompilationCache ?? auto);
|
|
380
|
+
const mappings = optimizations.prefixMapping ? compilationPrefixMappings(workspaceRoot, derivedDataPath) : "";
|
|
339
381
|
return [
|
|
340
382
|
`COMPILATION_CACHE_ENABLE_CACHING=${optimizations.compilationCache ? "YES" : "NO"}`,
|
|
341
383
|
`COMPILATION_CACHE_CAS_PATH=${casPath}`,
|
|
342
|
-
`SWIFT_ENABLE_COMPILE_CACHE=${
|
|
384
|
+
`SWIFT_ENABLE_COMPILE_CACHE=${swift ? "YES" : "NO"}`,
|
|
343
385
|
`CLANG_ENABLE_PREFIX_MAPPING=${optimizations.prefixMapping ? "YES" : "NO"}`,
|
|
344
|
-
`CLANG_OTHER_PREFIX_MAPPINGS=${
|
|
386
|
+
`CLANG_OTHER_PREFIX_MAPPINGS=${mappings}`,
|
|
387
|
+
...swift && swiftMappable && optimizations.prefixMapping ? ["SWIFT_ENABLE_PREFIX_MAPPING=YES", `SWIFT_OTHER_PREFIX_MAPPINGS=${mappings}`] : ["SWIFT_ENABLE_PREFIX_MAPPING=NO"]
|
|
345
388
|
];
|
|
346
389
|
}
|
|
390
|
+
function parseSwiftVersion(output) {
|
|
391
|
+
const m = /\bSwift version (\d+)\.(\d+)/.exec(String(output || ""));
|
|
392
|
+
if (!m || m[1] === void 0 || m[2] === void 0) return null;
|
|
393
|
+
const major = parseInt(m[1], 10);
|
|
394
|
+
const minor = parseInt(m[2], 10);
|
|
395
|
+
return Number.isFinite(major) && Number.isFinite(minor) ? {
|
|
396
|
+
major,
|
|
397
|
+
minor
|
|
398
|
+
} : null;
|
|
399
|
+
}
|
|
400
|
+
function detectSwiftVersion(exec = null) {
|
|
401
|
+
return parseSwiftVersion((exec || getExecutor()).runQuiet("xcrun swift --version", { timeoutMs: 1e4 }));
|
|
402
|
+
}
|
|
347
403
|
function parseXcodeMajor(output) {
|
|
348
404
|
const m = /^Xcode\s+(\d+)/m.exec(String(output || ""));
|
|
349
405
|
if (!m) return null;
|
|
@@ -356,12 +412,19 @@ function detectXcodeMajor(exec = null) {
|
|
|
356
412
|
return parseXcodeMajor((exec || getExecutor()).runQuiet("xcodebuild -version", { timeoutMs: 1e4 }));
|
|
357
413
|
}
|
|
358
414
|
function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, casPath = sharedCompilationCache(), optimizations = resolveOptimizations({}, {}).ios, onNote = (line) => console.error(line) }) {
|
|
415
|
+
const xcodeMajor = detectXcodeMajor(exec);
|
|
416
|
+
const ccache = ccacheEnabled(readPodfileProperties(root));
|
|
417
|
+
const probe = xcodeMajor !== null && xcodeMajor >= 26 && !ccache && optimizations.compilationCache && optimizations.swiftCompilationCache !== false;
|
|
418
|
+
const swiftVersion = probe ? detectSwiftVersion(exec) : null;
|
|
419
|
+
const reactNativeVersion = probe ? detectReactNativeVersion(root) : null;
|
|
359
420
|
const settings = compilationCacheSettings({
|
|
360
421
|
workspaceRoot: root,
|
|
361
422
|
derivedDataPath,
|
|
362
423
|
casPath,
|
|
363
|
-
xcodeMajor
|
|
364
|
-
|
|
424
|
+
xcodeMajor,
|
|
425
|
+
swiftVersion,
|
|
426
|
+
reactNativeVersion,
|
|
427
|
+
ccache,
|
|
365
428
|
optimizations
|
|
366
429
|
});
|
|
367
430
|
if (settings.length > 0 && optimizations.compilationCache) {
|
|
@@ -371,7 +434,8 @@ function resolveCompilationCacheSettings({ root, derivedDataPath, exec = null, c
|
|
|
371
434
|
prune: "atomic",
|
|
372
435
|
note: "shared Xcode compilation cache"
|
|
373
436
|
});
|
|
374
|
-
|
|
437
|
+
const swift = settings.includes("SWIFT_ENABLE_COMPILE_CACHE=YES") ? settings.includes("SWIFT_ENABLE_PREFIX_MAPPING=YES") ? "Swift on" : "Swift on, unmapped" : swiftVersion && !swiftPrefixMappingSupported(swiftVersion) ? `Swift off, ${swiftVersion.major}.${swiftVersion.minor} < ${SWIFT_PREFIX_MAPPING_MIN_SWIFT.major}.${SWIFT_PREFIX_MAPPING_MIN_SWIFT.minor}` : swiftVersion && reactNativeVersion && !reactNativeSupportsSwiftCache(reactNativeVersion) ? `Swift off, react-native ${reactNativeVersion.major}.${reactNativeVersion.minor} < ${SWIFT_CACHE_MIN_REACT_NATIVE.major}.${SWIFT_CACHE_MIN_REACT_NATIVE.minor}` : "Swift off";
|
|
438
|
+
onNote(chalk.dim(phaseLine("cache", `compilation cache on (CAS at ${casPath}, ${swift})`)));
|
|
375
439
|
}
|
|
376
440
|
return settings;
|
|
377
441
|
}
|
|
@@ -567,8 +631,12 @@ function parseCompilationCacheActivity(line) {
|
|
|
567
631
|
hitRatePercent: Number(match[3])
|
|
568
632
|
};
|
|
569
633
|
}
|
|
634
|
+
const SWIFT_CACHING_NEEDS_EXPLICIT_MODULES = /warning: swift compiler caching requires explicit module build/;
|
|
570
635
|
function compilationCacheActivityLine(activity) {
|
|
571
|
-
if (activity.status === "reported")
|
|
636
|
+
if (activity.status === "reported") {
|
|
637
|
+
const swift = activity.swiftTargetsWithoutExplicitModules ? `; Swift excluded: ${activity.swiftTargetsWithoutExplicitModules} targets build without explicit modules (SWIFT_ENABLE_EXPLICIT_MODULES=NO)` : "";
|
|
638
|
+
return `${activity.hits}/${activity.cacheableTasks} hits (${activity.hitRatePercent}%)${swift}`;
|
|
639
|
+
}
|
|
572
640
|
if (activity.status === "not-run") return "not run; artifact cache supplied the app";
|
|
573
641
|
return "unavailable; Xcode did not report reliable statistics";
|
|
574
642
|
}
|
|
@@ -648,6 +716,7 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
648
716
|
});
|
|
649
717
|
const transcript = [];
|
|
650
718
|
let compilationCacheActivity = COMPILATION_CACHE_UNAVAILABLE;
|
|
719
|
+
let swiftTargetsWithoutExplicitModules = 0;
|
|
651
720
|
const onLine = (line) => {
|
|
652
721
|
const msg = cleanLine(line);
|
|
653
722
|
transcript.push(msg);
|
|
@@ -657,8 +726,13 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
657
726
|
level: "debug",
|
|
658
727
|
msg
|
|
659
728
|
});
|
|
660
|
-
|
|
661
|
-
|
|
729
|
+
if (SWIFT_CACHING_NEEDS_EXPLICIT_MODULES.test(msg)) swiftTargetsWithoutExplicitModules += 1;
|
|
730
|
+
const parsed = parseCompilationCacheActivity(msg);
|
|
731
|
+
if (parsed) {
|
|
732
|
+
const activity = swiftTargetsWithoutExplicitModules ? {
|
|
733
|
+
...parsed,
|
|
734
|
+
swiftTargetsWithoutExplicitModules
|
|
735
|
+
} : parsed;
|
|
662
736
|
compilationCacheActivity = activity;
|
|
663
737
|
logWriter.write({
|
|
664
738
|
src: "build",
|
|
@@ -667,7 +741,8 @@ async function buildIos({ root, udid = null, logWriter, project = null, scheme =
|
|
|
667
741
|
event: "compilation_cache",
|
|
668
742
|
hits: activity.hits,
|
|
669
743
|
cacheableTasks: activity.cacheableTasks,
|
|
670
|
-
hitRatePercent: activity.hitRatePercent
|
|
744
|
+
hitRatePercent: activity.hitRatePercent,
|
|
745
|
+
...swiftTargetsWithoutExplicitModules ? { swiftTargetsWithoutExplicitModules } : {}
|
|
671
746
|
});
|
|
672
747
|
}
|
|
673
748
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stim",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Stim: fast, isolated React Native environments for coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@expo/fingerprint": "^0.20.10",
|
|
45
|
-
"@stim-cli/cache": "^1.
|
|
46
|
-
"@stim-cli/core": "^1.
|
|
47
|
-
"@stim-cli/metro": "^1.
|
|
45
|
+
"@stim-cli/cache": "^1.6.0",
|
|
46
|
+
"@stim-cli/core": "^1.6.0",
|
|
47
|
+
"@stim-cli/metro": "^1.6.0",
|
|
48
48
|
"chalk": "^5.4.1",
|
|
49
49
|
"commander": "^13.1.0",
|
|
50
50
|
"ws": "^8.21.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@stim-cli/expo-build-cache": "1.
|
|
53
|
+
"@stim-cli/expo-build-cache": "1.6.0",
|
|
54
54
|
"@types/ws": "^8.18.1",
|
|
55
55
|
"unique-pid": "0.1.1"
|
|
56
56
|
},
|
|
@@ -6,7 +6,6 @@ const path = require('node:path');
|
|
|
6
6
|
const { createRequire } = require('node:module');
|
|
7
7
|
const { pathToFileURL } = require('node:url');
|
|
8
8
|
const { bundleResponseMiddleware } = require('./bundle-response.cjs');
|
|
9
|
-
const { applyMetroCacheGeneration } = require('./metro-cache-generation.cjs');
|
|
10
9
|
|
|
11
10
|
const projectRoot = process.env.STIM_PROJECT_ROOT;
|
|
12
11
|
const storeRoot = process.env.STIM_METRO_STORE;
|
|
@@ -125,24 +124,19 @@ function appendStore(config, defaultConfig) {
|
|
|
125
124
|
const server = { ...defaultConfig.server, ...output.server };
|
|
126
125
|
const enhanceMiddleware = server.enhanceMiddleware;
|
|
127
126
|
|
|
128
|
-
const observed =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
);
|
|
139
|
-
return (req, res, next) => observe(req, res, () => enhanced(req, res, next));
|
|
140
|
-
},
|
|
127
|
+
const observed = {
|
|
128
|
+
...output,
|
|
129
|
+
server: {
|
|
130
|
+
...server,
|
|
131
|
+
enhanceMiddleware(middleware, metroServer) {
|
|
132
|
+
const enhanced = enhanceMiddleware ? enhanceMiddleware(middleware, metroServer) : middleware;
|
|
133
|
+
const observe = bundleResponseMiddleware((record) =>
|
|
134
|
+
process.stderr.write(`stim-bundle-response: ${JSON.stringify(record)}\n`),
|
|
135
|
+
);
|
|
136
|
+
return (req, res, next) => observe(req, res, () => enhanced(req, res, next));
|
|
141
137
|
},
|
|
142
138
|
},
|
|
143
|
-
|
|
144
|
-
process.env.STIM_METRO_FILE_MAP,
|
|
145
|
-
);
|
|
139
|
+
};
|
|
146
140
|
if (!storeRoot) return observed;
|
|
147
141
|
return {
|
|
148
142
|
...observed,
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { createHash } = require('node:crypto');
|
|
4
|
-
const { mkdirSync } = require('node:fs');
|
|
5
|
-
const { join } = require('node:path');
|
|
6
|
-
|
|
7
|
-
function applyMetroCacheGeneration(config, generation, directory) {
|
|
8
|
-
if (!generation) return config;
|
|
9
|
-
const fileMapCacheDirectory = join(directory, generation);
|
|
10
|
-
mkdirSync(fileMapCacheDirectory, { recursive: true });
|
|
11
|
-
return {
|
|
12
|
-
...config,
|
|
13
|
-
cacheVersion: createHash('sha256')
|
|
14
|
-
.update(JSON.stringify([config.cacheVersion ?? '1.0', generation]))
|
|
15
|
-
.digest('hex'),
|
|
16
|
-
fileMapCacheDirectory,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
module.exports = { applyMetroCacheGeneration };
|