123456const GRADLE_FILES = [7 "app/build.gradle.kts",8 "app/build.gradle",9 "build.gradle.kts",10 "build.gradle",11];12const VERSION = /^[A-Za-z0-9.-]{1,32}$/;13const ASCII_SPACE = /^[ \n\t\v\f\r]+|[ \n\t\v\f\r]+$/g;141516export function declaredVersion(read: (path: string) => string | null): string | null {17 const sources: (() => string | null)[] = [18 () => read("VERSION")?.replace(ASCII_SPACE, "") || null,19 () => lineVersion(read("build.zig.zon"), ".version"),20 () => lineVersion(read("v.mod"), "version"),21 () => lineVersion(read("package.json"), '"version"'),22 () => lineVersion(read("Cargo.toml"), "version"),23 () => plistVersion(read("Info.plist")),24 ...GRADLE_FILES.map((path) => () => gradleVersion(read(path))),25 ];26 for (const source of sources) {27 const value = source();28 if (value !== null) return VERSION.test(value) ? value : null;29 }30 return null;31}323334function lineVersion(text: string | null, key: string): string | null {35 for (const line of text?.split(/\r\n|[\r\n]/) ?? []) {36 const trimmed = line.replace(/^[ \t]+/, "");37 if (trimmed === "" || trimmed.startsWith("//") || trimmed.startsWith("#")) continue;38 if (!trimmed.startsWith(key)) continue;39 const value = literalAfter(trimmed, key.length);40 if (value !== null) return value;41 }42 return null;43}444546function literalAfter(line: string, from: number): string | null {47 let at = from;48 while (at < line.length && " \t=:".includes(line[at] as string)) at++;49 const quote = line[at];50 if (quote !== '"' && quote !== "'") return null;51 const end = line.indexOf(quote, at + 1);52 return end < 0 ? null : line.slice(at + 1, end);53}545556function plistVersion(text: string | null): string | null {57 const at = text?.indexOf("<key>CFBundleShortVersionString</key>") ?? -1;58 if (!text || at < 0) return null;59 const rest = text.slice(at);60 const open = rest.indexOf("<string>");61 const close = rest.indexOf("</string>");62 if (open < 0 || close <= open + "<string>".length) return null;63 return rest.slice(open + "<string>".length, close).replace(ASCII_SPACE, "") || null;64}65666768function gradleVersion(text: string | null): string | null {69 for (const line of text?.split(/\r\n|[\r\n]/) ?? []) {70 const trimmed = line.replace(/^[ \t]+/, "");71 if (trimmed === "" || /^(\/\/|\/\*|\*)/.test(trimmed)) continue;72 if (!/^versionName[ =]/.test(trimmed)) continue;73 const value = literalAfter(trimmed, "versionName".length);74 if (value !== null) return value;75 }76 return null;77}