import { execSync } from "node:child_process" import process from "node:process" type ShellName = "bash" | "zsh" | "fish" | "powershell" | "cmd" | "sh" type EnvVars = Record function getShell(): ShellName { const { platform, ppid, env } = process if (platform === "win32") { try { const command = `wmic process get ParentProcessId,Name | findstr "${ppid}"` const parentProcess = execSync(command, { stdio: "pipe" }).toString() if (parentProcess.toLowerCase().includes("powershell.exe")) { return "powershell" } } catch { return "cmd" } return "cmd" } else { const shellPath = env.SHELL if (shellPath) { if (shellPath.endsWith("zsh")) return "zsh" if (shellPath.endsWith("fish")) return "fish" if (shellPath.endsWith("bash")) return "bash" } return "sh" } } /** * Generates a copy-pasteable script to set multiple environment variables * and run a subsequent command. * @param {EnvVars} envVars - An object of environment variables to set. * @param {string} commandToRun - The command to run after setting the variables. * @returns {string} The formatted script string. */ export function generateEnvScript( envVars: EnvVars, commandToRun: string = "", ): string { const shell = getShell() const filteredEnvVars = Object.entries(envVars).filter( ([, value]) => value !== undefined, ) as Array<[string, string]> let commandBlock: string switch (shell) { case "powershell": { commandBlock = filteredEnvVars .map(([key, value]) => `$env:${key} = ${value}`) .join("; ") break } case "cmd": { commandBlock = filteredEnvVars .map(([key, value]) => `set ${key}=${value}`) .join(" & ") break } case "fish": { commandBlock = filteredEnvVars .map(([key, value]) => `set -gx ${key} ${value}`) .join("; ") break } default: { // bash, zsh, sh const assignments = filteredEnvVars .map(([key, value]) => `${key}=${value}`) .join(" ") commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : "" break } } if (commandBlock && commandToRun) { const separator = shell === "cmd" ? " & " : " && " return `${commandBlock}${separator}${commandToRun}` } return commandBlock || commandToRun }