simulator-use 0.9.0 → 0.10.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.
@@ -0,0 +1,65 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { ToolError } from "../errors.js";
4
+ import { run } from "./exec.js";
5
+ let cached;
6
+ const CACHE_MS = 60_000;
7
+ /** Where Xcodes are looked for; tests point it elsewhere. */
8
+ let applications = "/Applications";
9
+ export function setApplicationsDir(dir) {
10
+ applications = dir;
11
+ cached = undefined;
12
+ }
13
+ const plist = async (file, key) => (await run("/usr/bin/plutil", ["-extract", key, "raw", file], { timeoutMs: 5_000 })).stdout.trim();
14
+ const newer = (a, b) => {
15
+ const x = a.split(".").map(Number), y = b.split(".").map(Number);
16
+ for (let i = 0; i < Math.max(x.length, y.length); i++)
17
+ if ((x[i] ?? 0) !== (y[i] ?? 0))
18
+ return (x[i] ?? 0) - (y[i] ?? 0);
19
+ return 0;
20
+ };
21
+ /** The Xcodes in /Applications (Xcode*.app), newest first, releases before a beta of the same version. */
22
+ export async function listXcodes() {
23
+ if (cached && Date.now() - cached.at < CACHE_MS)
24
+ return cached.list;
25
+ const names = (await readdir(applications).catch(() => [])).filter((name) => /^Xcode.*\.app$/.test(name));
26
+ const selected = process.env.DEVELOPER_DIR || (await run("xcode-select", ["-p"], { timeoutMs: 5_000 }).then((o) => o.stdout.trim()).catch(() => ""));
27
+ const list = [];
28
+ for (const name of names) {
29
+ const path = join(applications, name);
30
+ try {
31
+ const version = await plist(join(path, "Contents", "Info.plist"), "CFBundleShortVersionString");
32
+ const build = await plist(join(path, "Contents", "version.plist"), "ProductBuildVersion").catch(() => "");
33
+ // Named so: Apple's betas expand to Xcode-beta.app (a build number cannot tell: 27.0's release is 27A266a).
34
+ const beta = /beta|release.?candidate|[-_ ]rc\b/i.test(name);
35
+ list.push({ version, build, path, developer: join(path, "Contents", "Developer"), beta, default: Boolean(selected) && (selected === path || selected.startsWith(`${path}/`)) });
36
+ }
37
+ catch {
38
+ // Not an Xcode (or not readable): left out.
39
+ }
40
+ }
41
+ list.sort((a, b) => newer(b.version, a.version) || Number(a.beta) - Number(b.beta));
42
+ cached = { at: Date.now(), list };
43
+ return list;
44
+ }
45
+ /** The Xcodes as an answer names them: "27.1, 27.0 (default), 26.6, 27.2 beta". */
46
+ export const describeXcodes = (list) => list.map((x) => `${x.version}${x.beta ? " beta" : ""}${x.default ? " (default)" : ""}`).join(", ");
47
+ /**
48
+ * The Xcode a call asked for: a version ("27.1"; "27" is the newest 27 release), "beta" for the newest beta, or
49
+ * "default". Nothing asked, nothing picked: builds use the default.
50
+ */
51
+ export async function pickXcode(wanted) {
52
+ if (!wanted?.trim())
53
+ return undefined;
54
+ const list = await listXcodes();
55
+ const w = wanted.trim().toLowerCase().replace(/^xcode\s*/, "");
56
+ if (w === "default")
57
+ return list.find((x) => x.default);
58
+ const found = w === "beta"
59
+ ? list.find((x) => x.beta)
60
+ : list.find((x) => x.version === w && !x.beta) ?? list.find((x) => !x.beta && x.version.startsWith(`${w}.`)) ?? list.find((x) => x.version === w);
61
+ if (!found) {
62
+ throw new ToolError(`No Xcode ${wanted} on this Mac.`, list.length ? `Pass one of: ${describeXcodes(list)}; or leave xcode out for the default.` : "Leave xcode out: this Mac's Xcodes could not be listed.");
63
+ }
64
+ return found;
65
+ }