mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-06-11 16:30:24 +01:00
Migrate the dynamic Expo config and all 12 local config plugins from CommonJS .js to typed TypeScript: - app.config.js -> app.config.ts (typed ConfigContext/ExpoConfig, behavior-identical port) - plugins/*.js -> plugins/*.ts with `ConfigPlugin` typings from expo/config-plugins; plugin options are now type-checked (withGitPod) - app.json plugin references updated to the .ts paths - imports unified on expo/config-plugins (some plugins used the @expo/config-plugins alias) Node evaluates the config at prebuild time and cannot parse TypeScript plugin modules on its own (verified empirically: Expo transpiles app.config.ts itself but not its imports), so the documented tsx approach is used: `import "tsx/cjs"` at the top of app.config.ts plus tsx as a devDependency. Validation: resolved prebuild configs (expo config --type prebuild) are byte-identical to the old JS config for both mobile and TV (modulo plugin path extensions and the builtAt timestamp); full `bun run prebuild` and `bun run prebuild:tv` pass and all Android plugin mods are present in the generated project (media3 exclusions, gradle properties, cast activity, network security config, alert colors).
118 lines
3.4 KiB
TypeScript
118 lines
3.4 KiB
TypeScript
import { execSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { type ConfigPlugin, withDangerousMod } from "expo/config-plugins";
|
|
|
|
/**
|
|
* Expo config plugin that adds EXPO_TV=1 and NODE_BINARY to .xcode.env.local for TV builds.
|
|
*
|
|
* This ensures that when building directly from Xcode (without using `bun run ios:tv`),
|
|
* Metro bundler knows it's a TV build and properly excludes unsupported modules
|
|
* like react-native-track-player.
|
|
*
|
|
* It also sets NODE_BINARY for nvm users since Xcode can't resolve shell functions.
|
|
*/
|
|
const withTVXcodeEnv: ConfigPlugin = (config) => {
|
|
// Only apply for TV builds
|
|
if (process.env.EXPO_TV !== "1") {
|
|
return config;
|
|
}
|
|
|
|
return withDangerousMod(config, [
|
|
"ios",
|
|
async (config) => {
|
|
const iosPath = path.join(config.modRequest.projectRoot, "ios");
|
|
const xcodeEnvLocalPath = path.join(iosPath, ".xcode.env.local");
|
|
|
|
// Read existing content or start fresh
|
|
let content = "";
|
|
if (fs.existsSync(xcodeEnvLocalPath)) {
|
|
content = fs.readFileSync(xcodeEnvLocalPath, "utf-8");
|
|
}
|
|
|
|
let modified = false;
|
|
|
|
// Add NODE_BINARY if not already present (needed for nvm users)
|
|
if (!content.includes("export NODE_BINARY=")) {
|
|
const nodePath = getNodeBinaryPath();
|
|
if (nodePath) {
|
|
if (content.length > 0 && !content.endsWith("\n")) {
|
|
content += "\n";
|
|
}
|
|
content += `export NODE_BINARY=${nodePath}\n`;
|
|
modified = true;
|
|
console.log(
|
|
`[withTVXcodeEnv] Added NODE_BINARY=${nodePath} to .xcode.env.local`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Add EXPO_TV=1 if not already present
|
|
const expoTvExport = "export EXPO_TV=1";
|
|
if (!content.includes(expoTvExport)) {
|
|
if (content.length > 0 && !content.endsWith("\n")) {
|
|
content += "\n";
|
|
}
|
|
content += `${expoTvExport}\n`;
|
|
modified = true;
|
|
console.log("[withTVXcodeEnv] Added EXPO_TV=1 to .xcode.env.local");
|
|
}
|
|
|
|
if (modified) {
|
|
fs.writeFileSync(xcodeEnvLocalPath, content);
|
|
}
|
|
|
|
return config;
|
|
},
|
|
]);
|
|
};
|
|
|
|
/**
|
|
* Get the actual node binary path, handling nvm installations.
|
|
*/
|
|
function getNodeBinaryPath(): string | null {
|
|
try {
|
|
// First try to get node path directly (works for non-nvm installs)
|
|
const directPath = execSync("which node 2>/dev/null", {
|
|
encoding: "utf-8",
|
|
}).trim();
|
|
if (directPath && fs.existsSync(directPath)) {
|
|
return directPath;
|
|
}
|
|
} catch {
|
|
// Ignore errors
|
|
}
|
|
|
|
try {
|
|
// For nvm users, source nvm and get the path
|
|
const nvmPath = execSync(
|
|
'bash -c "source ~/.nvm/nvm.sh 2>/dev/null && which node"',
|
|
{ encoding: "utf-8" },
|
|
).trim();
|
|
if (nvmPath && fs.existsSync(nvmPath)) {
|
|
return nvmPath;
|
|
}
|
|
} catch {
|
|
// Ignore errors
|
|
}
|
|
|
|
// Fallback: look for node in common nvm location
|
|
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
if (homeDir) {
|
|
const nvmVersionsDir = path.join(homeDir, ".nvm", "versions", "node");
|
|
if (fs.existsSync(nvmVersionsDir)) {
|
|
const versions = fs.readdirSync(nvmVersionsDir).sort().reverse();
|
|
for (const version of versions) {
|
|
const nodeBin = path.join(nvmVersionsDir, version, "bin", "node");
|
|
if (fs.existsSync(nodeBin)) {
|
|
return nodeBin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export default withTVXcodeEnv;
|