From 717186e13e8ba9f585e0b815146d47d9f5e007f4 Mon Sep 17 00:00:00 2001 From: Fredrik Burmester Date: Sat, 31 Jan 2026 15:00:38 +0100 Subject: [PATCH] fix(tv): set node version --- plugins/withTVXcodeEnv.js | 76 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/plugins/withTVXcodeEnv.js b/plugins/withTVXcodeEnv.js index ccdbc842..86f36755 100644 --- a/plugins/withTVXcodeEnv.js +++ b/plugins/withTVXcodeEnv.js @@ -1,13 +1,16 @@ const { withDangerousMod } = require("@expo/config-plugins"); +const { execSync } = require("node:child_process"); const fs = require("node:fs"); const path = require("node:path"); /** - * Expo config plugin that adds EXPO_TV=1 to .xcode.env.local for TV builds. + * 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 = (config) => { // Only apply for TV builds @@ -27,21 +30,88 @@ const withTVXcodeEnv = (config) => { 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)) { - // Ensure we have a newline at the end before adding if (content.length > 0 && !content.endsWith("\n")) { content += "\n"; } content += `${expoTvExport}\n`; - fs.writeFileSync(xcodeEnvLocalPath, content); + 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() { + 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; +} + module.exports = withTVXcodeEnv;