stim 0.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/android-cas-compiler.d.mts +1 -0
- package/dist/android-cas-compiler.mjs +71 -0
- package/dist/android-jc8MnUyL.mjs +514 -0
- package/dist/cache-manifest-4tH64LQ9.mjs +66 -0
- package/dist/cache-manifest.d.mts +27 -0
- package/dist/cache-manifest.mjs +2 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +27182 -0
- package/dist/collector-run.d.mts +119 -0
- package/dist/collector-run.mjs +539 -0
- package/dist/config-7kmtuhO2.mjs +350 -0
- package/dist/errors-B6W7FkxQ.mjs +15 -0
- package/dist/exec-bsN9MJXb.mjs +63 -0
- package/dist/metro-store-COLl1pOk.mjs +1249 -0
- package/dist/ndjson-DcAtEx_K.mjs +131 -0
- package/dist/ndjson-DvHSsNU9.d.mts +16 -0
- package/dist/process-output-C7IlIzd7.mjs +40 -0
- package/dist/project-Dit1CckW.mjs +234 -0
- package/dist/server-bare-BmTVVmBB.mjs +351 -0
- package/dist/server-expo-BZJm--wA.mjs +2 -0
- package/dist/server-expo-CHJe1r6j.mjs +311 -0
- package/dist/state-2nush3MK.mjs +269 -0
- package/dist/supervisor-run.d.mts +85 -0
- package/dist/supervisor-run.mjs +210 -0
- package/package.json +61 -8
- package/shim/android-cas-pch.cmake +35 -0
- package/shim/android-cas.gradle +29 -0
- package/shim/android-cas.toolchain.cmake +12 -0
- package/shim/android-optimizations.gradle +30 -0
- package/shim/bundle-response.cjs +46 -0
- package/shim/bundle-response.d.cts +5 -0
- package/shim/expo-metro-config.cjs +188 -0
- package/shim/metro-cache-generation.cjs +20 -0
- package/shim/metro-cache-generation.d.cts +5 -0
- package/skill/SKILL.md +14 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { closeSync, mkdirSync, openSync, writeSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
//#region src/ndjson.ts
|
|
4
|
+
const LEVELS = [
|
|
5
|
+
"debug",
|
|
6
|
+
"info",
|
|
7
|
+
"warn",
|
|
8
|
+
"error",
|
|
9
|
+
"fatal"
|
|
10
|
+
];
|
|
11
|
+
const SOURCES = [
|
|
12
|
+
"metro",
|
|
13
|
+
"client",
|
|
14
|
+
"device",
|
|
15
|
+
"build"
|
|
16
|
+
];
|
|
17
|
+
function levelRank(level) {
|
|
18
|
+
const i = LEVELS.indexOf(level);
|
|
19
|
+
return i < 0 ? 0 : i;
|
|
20
|
+
}
|
|
21
|
+
function parseNdjsonLine(line) {
|
|
22
|
+
if (typeof line !== "string") return null;
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (!trimmed) return null;
|
|
25
|
+
let value;
|
|
26
|
+
try {
|
|
27
|
+
value = JSON.parse(trimmed);
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
function parseNdjsonText(text) {
|
|
35
|
+
if (typeof text !== "string" || text === "") return [];
|
|
36
|
+
const out = [];
|
|
37
|
+
const lines = text.split("\n");
|
|
38
|
+
lines.pop();
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const record = parseNdjsonLine(line);
|
|
41
|
+
if (record) out.push(record);
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function formatNdjsonLine(record) {
|
|
46
|
+
try {
|
|
47
|
+
return `${JSON.stringify(record)}\n`;
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function createNdjsonWriter(file, { truncate = false } = {}) {
|
|
53
|
+
let fd = null;
|
|
54
|
+
let freshFile = truncate;
|
|
55
|
+
let written = 0;
|
|
56
|
+
let dropped = 0;
|
|
57
|
+
let lastError = null;
|
|
58
|
+
let closed = false;
|
|
59
|
+
function open() {
|
|
60
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
61
|
+
fd = openSync(file, freshFile ? "w" : "a");
|
|
62
|
+
freshFile = false;
|
|
63
|
+
}
|
|
64
|
+
function write(record) {
|
|
65
|
+
if (closed) {
|
|
66
|
+
dropped += 1;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const line = formatNdjsonLine(stamp(record));
|
|
70
|
+
if (line === null) {
|
|
71
|
+
dropped += 1;
|
|
72
|
+
lastError = /* @__PURE__ */ new TypeError("record could not be serialized to JSON");
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
if (fd === null) open();
|
|
77
|
+
writeSync(fd, line);
|
|
78
|
+
written += 1;
|
|
79
|
+
return true;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (fd !== null) {
|
|
82
|
+
try {
|
|
83
|
+
closeSync(fd);
|
|
84
|
+
} catch {}
|
|
85
|
+
fd = null;
|
|
86
|
+
}
|
|
87
|
+
dropped += 1;
|
|
88
|
+
lastError = err;
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function close() {
|
|
93
|
+
if (fd !== null) {
|
|
94
|
+
try {
|
|
95
|
+
closeSync(fd);
|
|
96
|
+
} catch {}
|
|
97
|
+
fd = null;
|
|
98
|
+
}
|
|
99
|
+
closed = true;
|
|
100
|
+
return {
|
|
101
|
+
file,
|
|
102
|
+
written,
|
|
103
|
+
dropped,
|
|
104
|
+
lastError
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
file,
|
|
109
|
+
write,
|
|
110
|
+
close,
|
|
111
|
+
get written() {
|
|
112
|
+
return written;
|
|
113
|
+
},
|
|
114
|
+
get dropped() {
|
|
115
|
+
return dropped;
|
|
116
|
+
},
|
|
117
|
+
get lastError() {
|
|
118
|
+
return lastError;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function stamp(record) {
|
|
123
|
+
const base = record && typeof record === "object" && !Array.isArray(record) ? record : { msg: String(record) };
|
|
124
|
+
if (typeof base.ts === "number" && Number.isFinite(base.ts)) return base;
|
|
125
|
+
return {
|
|
126
|
+
...base,
|
|
127
|
+
ts: Date.now()
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { parseNdjsonLine as a, levelRank as i, SOURCES as n, parseNdjsonText as o, createNdjsonWriter as r, LEVELS as t };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/ndjson.d.ts
|
|
2
|
+
interface NdjsonWriter {
|
|
3
|
+
readonly file: string;
|
|
4
|
+
write(record: unknown): boolean;
|
|
5
|
+
close(): {
|
|
6
|
+
file: string;
|
|
7
|
+
written: number;
|
|
8
|
+
dropped: number;
|
|
9
|
+
lastError: Error | null;
|
|
10
|
+
};
|
|
11
|
+
readonly written: number;
|
|
12
|
+
readonly dropped: number;
|
|
13
|
+
readonly lastError: Error | null;
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { NdjsonWriter as t };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { stripVTControlCharacters } from "node:util";
|
|
2
|
+
//#region src/process-output.ts
|
|
3
|
+
function stripAnsi(text) {
|
|
4
|
+
return stripVTControlCharacters(String(text ?? ""));
|
|
5
|
+
}
|
|
6
|
+
function createLineReader(onLine) {
|
|
7
|
+
let buffered = "";
|
|
8
|
+
return {
|
|
9
|
+
push(chunk) {
|
|
10
|
+
buffered += String(chunk);
|
|
11
|
+
const parts = buffered.split("\n");
|
|
12
|
+
buffered = parts.pop() ?? "";
|
|
13
|
+
for (const part of parts) onLine(part);
|
|
14
|
+
},
|
|
15
|
+
flush() {
|
|
16
|
+
if (!buffered) return;
|
|
17
|
+
const rest = buffered;
|
|
18
|
+
buffered = "";
|
|
19
|
+
onLine(rest);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function waitForChild(child) {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
let settled = false;
|
|
26
|
+
const done = (value) => {
|
|
27
|
+
if (!settled) {
|
|
28
|
+
settled = true;
|
|
29
|
+
resolve(value);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
child.on("exit", (code, signal) => done({
|
|
33
|
+
code,
|
|
34
|
+
signal
|
|
35
|
+
}));
|
|
36
|
+
child.on("error", (error) => done({ error }));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { stripAnsi as n, waitForChild as r, createLineReader as t };
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { s as findEnclosingWorktreeRoot, u as getProject } from "./config-7kmtuhO2.mjs";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, realpathSync } from "fs";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { dirname, join, resolve } from "path";
|
|
5
|
+
//#region src/project.ts
|
|
6
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
7
|
+
function resolvePackageJson(projectRoot, name) {
|
|
8
|
+
try {
|
|
9
|
+
return requireFromHere.resolve(`${name}/package.json`, { paths: [projectRoot] });
|
|
10
|
+
} catch {
|
|
11
|
+
try {
|
|
12
|
+
const main = requireFromHere.resolve(name, { paths: [projectRoot] });
|
|
13
|
+
const marker = `/node_modules/${name}/`;
|
|
14
|
+
const at = main.lastIndexOf(marker);
|
|
15
|
+
if (at === -1) return null;
|
|
16
|
+
const candidate = join(main.slice(0, at + marker.length), "package.json");
|
|
17
|
+
return existsSync(candidate) ? candidate : null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isPackageResolvable(projectRoot, name) {
|
|
24
|
+
return resolvePackageJson(projectRoot, name) !== null;
|
|
25
|
+
}
|
|
26
|
+
function projectShortcut(path, proj) {
|
|
27
|
+
if (proj?.label) return proj.label;
|
|
28
|
+
const rootPath = findEnclosingWorktreeRoot(path);
|
|
29
|
+
if (rootPath && rootPath !== path) return `${projectShortcut(rootPath, getProject(rootPath))}/${path.split("/").pop() || path}`;
|
|
30
|
+
return path.split("/").pop() || path;
|
|
31
|
+
}
|
|
32
|
+
function findProjectRoot(startDir) {
|
|
33
|
+
let dir;
|
|
34
|
+
try {
|
|
35
|
+
dir = realpathSync(resolve(startDir));
|
|
36
|
+
} catch {
|
|
37
|
+
dir = resolve(startDir);
|
|
38
|
+
}
|
|
39
|
+
while (true) {
|
|
40
|
+
if (existsSync(join(dir, "package.json"))) return dir;
|
|
41
|
+
const parent = dirname(dir);
|
|
42
|
+
if (parent === dir) return null;
|
|
43
|
+
dir = parent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function loadPackageJson(projectRoot) {
|
|
47
|
+
const p = join(projectRoot, "package.json");
|
|
48
|
+
if (!existsSync(p)) return {
|
|
49
|
+
pkg: null,
|
|
50
|
+
parseError: null
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
return {
|
|
54
|
+
pkg: JSON.parse(readFileSync(p, "utf-8")),
|
|
55
|
+
parseError: null
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
pkg: null,
|
|
60
|
+
parseError: error?.message || String(error)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readPackageJson(projectRoot) {
|
|
65
|
+
return loadPackageJson(projectRoot).pkg;
|
|
66
|
+
}
|
|
67
|
+
const APP_DEPENDENCIES = ["react-native", "expo"];
|
|
68
|
+
function declaresAppDependency(pkg) {
|
|
69
|
+
if (!pkg || typeof pkg !== "object") return false;
|
|
70
|
+
const { dependencies, devDependencies } = pkg;
|
|
71
|
+
const deps = {
|
|
72
|
+
...dependencies,
|
|
73
|
+
...devDependencies
|
|
74
|
+
};
|
|
75
|
+
return APP_DEPENDENCIES.some((name) => name in deps);
|
|
76
|
+
}
|
|
77
|
+
function appProjectProblem(projectRoot) {
|
|
78
|
+
const file = join(projectRoot, "package.json");
|
|
79
|
+
const { pkg, parseError } = loadPackageJson(projectRoot);
|
|
80
|
+
if (parseError) return {
|
|
81
|
+
kind: "unreadable",
|
|
82
|
+
message: `${file} is not valid JSON (${parseError}), so Stim cannot tell whether this is a React Native or Expo app.`,
|
|
83
|
+
remedy: `Fix the JSON in ${file} -- an unfinished edit or a merge conflict leaves it unparseable -- then run the command again.`
|
|
84
|
+
};
|
|
85
|
+
if (declaresAppDependency(pkg)) return null;
|
|
86
|
+
return {
|
|
87
|
+
kind: "not-an-app",
|
|
88
|
+
message: `${file} depends on neither react-native nor expo, so this is not a React Native or Expo app.`,
|
|
89
|
+
remedy: "Run this from the app directory -- the one whose package.json depends on react-native or expo -- or from that directory inside a worktree."
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function detectIsExpo(projectRoot) {
|
|
93
|
+
const pkg = readPackageJson(projectRoot);
|
|
94
|
+
const iosScript = pkg?.scripts?.ios;
|
|
95
|
+
if (typeof iosScript === "string") {
|
|
96
|
+
if (/\bexpo\s+run:ios\b/.test(iosScript)) return true;
|
|
97
|
+
if (/\breact-native\s+run-ios\b/.test(iosScript)) return false;
|
|
98
|
+
}
|
|
99
|
+
if (!pkg) return false;
|
|
100
|
+
const hasExpoDep = "expo" in {
|
|
101
|
+
...pkg.dependencies,
|
|
102
|
+
...pkg.devDependencies
|
|
103
|
+
};
|
|
104
|
+
const resolvable = () => isPackageResolvable(projectRoot, "expo");
|
|
105
|
+
if (!hasExpoDep && !resolvable()) return false;
|
|
106
|
+
const appJson = readAppJson(projectRoot);
|
|
107
|
+
if (appJson?.expo) return true;
|
|
108
|
+
const text = readAppConfigText(projectRoot);
|
|
109
|
+
if (text && /\b(?:from\s+['"]expo['"]|expo\/config|ExpoConfig)\b/.test(text)) return true;
|
|
110
|
+
if (hasExpoDep && resolvable()) return true;
|
|
111
|
+
if (looksLikeExpoConfig(appJson)) return true;
|
|
112
|
+
if (podfileUsesExpoModules(projectRoot)) return true;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
function looksLikeExpoConfig(appJson) {
|
|
116
|
+
if (!appJson || typeof appJson !== "object" || Array.isArray(appJson)) return false;
|
|
117
|
+
if (appJson.expo) return true;
|
|
118
|
+
if ([
|
|
119
|
+
"slug",
|
|
120
|
+
"plugins",
|
|
121
|
+
"sdkVersion",
|
|
122
|
+
"experiments",
|
|
123
|
+
"runtimeVersion",
|
|
124
|
+
"updates",
|
|
125
|
+
"buildCacheProvider",
|
|
126
|
+
"assetBundlePatterns",
|
|
127
|
+
"splash",
|
|
128
|
+
"orientation",
|
|
129
|
+
"userInterfaceStyle"
|
|
130
|
+
].some((key) => appJson[key] !== void 0)) return true;
|
|
131
|
+
return Boolean(appJson.extra && typeof appJson.extra === "object" && appJson.extra.eas);
|
|
132
|
+
}
|
|
133
|
+
function podfileUsesExpoModules(projectRoot) {
|
|
134
|
+
const p = join(projectRoot, "ios", "Podfile");
|
|
135
|
+
if (!existsSync(p)) return false;
|
|
136
|
+
try {
|
|
137
|
+
return /^[^#\n]*use_expo_modules!/m.test(readFileSync(p, "utf-8"));
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function readAppJson(projectRoot) {
|
|
143
|
+
const p = join(projectRoot, "app.json");
|
|
144
|
+
if (!existsSync(p)) return null;
|
|
145
|
+
try {
|
|
146
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function readAppConfigText(projectRoot) {
|
|
152
|
+
for (const name of [
|
|
153
|
+
"app.config.js",
|
|
154
|
+
"app.config.ts",
|
|
155
|
+
"app.config.cjs",
|
|
156
|
+
"app.config.mjs"
|
|
157
|
+
]) {
|
|
158
|
+
const p = join(projectRoot, name);
|
|
159
|
+
if (existsSync(p)) try {
|
|
160
|
+
return readFileSync(p, "utf-8");
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
function detectBundleId(projectRoot) {
|
|
166
|
+
const fromJson = readAppJson(projectRoot)?.expo?.ios?.bundleIdentifier;
|
|
167
|
+
if (typeof fromJson === "string" && fromJson) return fromJson;
|
|
168
|
+
const text = readAppConfigText(projectRoot);
|
|
169
|
+
if (text) {
|
|
170
|
+
const id = text.match(/bundleIdentifier\s*:\s*["']([^"']+)["']/)?.[1];
|
|
171
|
+
if (id) return id;
|
|
172
|
+
}
|
|
173
|
+
return detectBundleIdFromPbxproj(projectRoot);
|
|
174
|
+
}
|
|
175
|
+
function detectAndroidPackage(projectRoot) {
|
|
176
|
+
const fromJson = readAppJson(projectRoot)?.expo?.android?.package;
|
|
177
|
+
if (typeof fromJson === "string" && fromJson) return fromJson;
|
|
178
|
+
const text = readAppConfigText(projectRoot);
|
|
179
|
+
if (text) {
|
|
180
|
+
const pkg = text.match(/package\s*:\s*["']([^"']+)["']/)?.[1];
|
|
181
|
+
if (pkg) return pkg;
|
|
182
|
+
}
|
|
183
|
+
return detectAndroidPackageFromGradle(projectRoot);
|
|
184
|
+
}
|
|
185
|
+
function detectBundleIdFromPbxproj(projectRoot) {
|
|
186
|
+
const iosDir = join(projectRoot, "ios");
|
|
187
|
+
if (!existsSync(iosDir)) return null;
|
|
188
|
+
let entries;
|
|
189
|
+
try {
|
|
190
|
+
entries = readdirSync(iosDir, { withFileTypes: true });
|
|
191
|
+
} catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
if (!entry.isDirectory() || !entry.name.endsWith(".xcodeproj")) continue;
|
|
196
|
+
const pbx = join(iosDir, entry.name, "project.pbxproj");
|
|
197
|
+
if (!existsSync(pbx)) continue;
|
|
198
|
+
let text;
|
|
199
|
+
try {
|
|
200
|
+
text = readFileSync(pbx, "utf-8");
|
|
201
|
+
} catch {
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const concrete = [...text.matchAll(/PRODUCT_BUNDLE_IDENTIFIER\s*=\s*([^;\s"]+)\s*;/g)].map((m) => m[1]).filter((id) => !!id && !id.startsWith("$") && !id.includes("("));
|
|
205
|
+
if (concrete.length === 0) continue;
|
|
206
|
+
const counts = {};
|
|
207
|
+
for (const id of concrete) counts[id] = (counts[id] || 0) + 1;
|
|
208
|
+
let best = null, bestCount = 0, bestLen = Infinity;
|
|
209
|
+
for (const [id, count] of Object.entries(counts)) if (count > bestCount || count === bestCount && id.length < bestLen) {
|
|
210
|
+
best = id;
|
|
211
|
+
bestCount = count;
|
|
212
|
+
bestLen = id.length;
|
|
213
|
+
}
|
|
214
|
+
return best;
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
function detectAndroidPackageFromGradle(projectRoot) {
|
|
219
|
+
const gradle = join(projectRoot, "android", "app", "build.gradle");
|
|
220
|
+
if (!existsSync(gradle)) return null;
|
|
221
|
+
let text;
|
|
222
|
+
try {
|
|
223
|
+
text = readFileSync(gradle, "utf-8");
|
|
224
|
+
} catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const nsId = text.match(/namespace\s+["']([^"']+)["']/)?.[1];
|
|
228
|
+
if (nsId) return nsId;
|
|
229
|
+
const appId = text.match(/applicationId\s+["']([^"']+)["']/)?.[1];
|
|
230
|
+
if (appId) return appId;
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
234
|
+
export { findProjectRoot as a, resolvePackageJson as c, detectIsExpo as i, detectAndroidPackage as n, isPackageResolvable as o, detectBundleId as r, projectShortcut as s, appProjectProblem as t };
|