bunvm
Version:
Bun Version Manager - Switch between different versions of Bun easily
18 lines • 15.4 kB
JavaScript
import{Command as _e}from"commander";import{mkdirSync as ge,existsSync as ln}from"fs";import{join as un}from"path";import{isCancel as he,select as ve,spinner as we}from"@clack/prompts";import R from"chalk";import{homedir as D}from"os";import{join as h}from"path";var P=h(D(),".bun"),B=h(P,"bin"),m=h(B,"bun"),x=h(B,"bunx"),Un=h(D(),".bvm"),f=h(Un,"versions"),Rn="https://api.github.com",Y=`${Rn}/repos/oven-sh/bun/releases`,V=h(P,"_bun"),S=h(D(),".config","fish","config.fish");import $ from"chalk";var t={log:n=>console.log(n),info:n=>console.log(`${$.blue("i")} ${n}`),success:n=>console.log(`${$.green("\u2705")} ${n}`),warn:n=>console.warn(`${$.yellow("\u26A0\uFE0F")} ${$.yellow(n)}`),error:n=>console.error(`${$.red("\u274C")} ${$.red(n)}`)};import{execSync as O}from"child_process";import{createHash as Fn}from"crypto";import{createReadStream as H,existsSync as q,readdirSync as Hn,readFileSync as On}from"fs";import l from"chalk";import{dirname as jn,join as W}from"path";import{Readable as zn}from"stream";import{pipeline as Z}from"stream/promises";import Gn from"unzipper";import{chmod as Yn}from"fs/promises";import{createWriteStream as Vn,existsSync as K,lstatSync as J,mkdirSync as kn,rmSync as Cn,symlinkSync as Ln}from"fs";import{rm as Tn,stat as An,unlink as Dn}from"fs/promises";async function M(n,e){let r=Vn(e);await new Promise((o,i)=>{r.on("finish",o),r.on("error",i),n.pipe(r)})}async function v(n){try{return await An(n),!0}catch{return!1}}async function _(n,e=!1){try{await v(n)&&(e?await Tn(n,{recursive:!0,force:!0}):await Dn(n))}catch(r){r.code!=="ENOENT"&&t.warn(`Failed to clean path: ${n}`)}}function k(n){let e=["B","KB","MB","GB"];if(n===0)return"0 B";let r=Math.floor(Math.log(n)/Math.log(1024));return`${(n/Math.pow(1024,r)).toFixed(1)} ${e[r]}`}function C(n){K(n)||kn(n,{recursive:!0})}function I(n){try{return J(n),!0}catch{return!1}}function Mn(n){if(I(n)){let e=J(n).isSymbolicLink();try{Cn(n,{force:!0}),e||t.warn(`Replaced non-symlink file at ${n}`)}catch(r){t.warn(`Couldn't remove existing file at ${n}: ${r.message}`)}}}function F(n,e){K(n)&&(Mn(e),Ln(n,e))}import X from"cli-progress";var Q={"User-Agent":"bvm-cli"};async function Kn(n){let e=await fetch(n,{headers:Q});if(!e.ok)throw new Error(`Request failed (${e.status}) for ${n}`);return await e.json()}async function Jn(n){let e=await fetch(n,{headers:Q});if(!e.ok)throw new Error(`Request failed (${e.status}) for ${n}`);return e.text()}function u(){try{return O("bun --version",{encoding:"utf8",stdio:["pipe"]}).trim()||null}catch{return null}}function p(){return q(f)?Hn(f).sort(ie):[]}async function nn(){let n=await U();return n.length===0?(t.warn("No Bun versions found."),null):n[0]}async function U(){return(await Kn(Y)).map(({tag_name:e})=>e.replace(/^bun-v/,""))}function j(n,e,r){let o=e===n,i=r.has(n);return o?`${l.magenta("v"+n)} ${l.yellow("\u2B50 (current)")}`:i?`${l.magenta("v"+n)} ${l.green("\u2713 (installed)")}`:`${l.magenta("v"+n)} ${l.red("\u2717 (not installed)")}`}function Wn(){try{return O("sysctl -n sysctl.proc_translated",{encoding:"utf8",stdio:["pipe"]}).trim()==="1"}catch{return!1}}function Xn(){try{if(process.platform==="darwin")return O("sysctl -a",{encoding:"utf8",stdio:["pipe"]}).includes("AVX2");if(process.platform==="linux")return On("/proc/cpuinfo","utf8").includes("avx2")}catch{return!1}return!1}function qn({platform:n,arch:e,isAlpine:r=!1,hasAvx2:o=!0,isRosetta:i=!1}){let s;switch(`${n}-${e}`){case"darwin-arm64":s="darwin-aarch64";break;case"darwin-x64":s=i?"darwin-aarch64":"darwin-x64";break;case"linux-arm64":s="linux-aarch64";break;case"linux-x64":s="linux-x64";break;default:throw new Error(`Unsupported platform: ${n}-${e}. Only macOS and Linux are supported.`)}return s.startsWith("linux")&&r&&(s+="-musl"),(s.startsWith("darwin-x64")||s.startsWith("linux-x64"))&&!o&&(s+="-baseline"),s}function Zn(){return qn({platform:process.platform,arch:process.arch,isAlpine:q("/etc/alpine-release"),hasAvx2:Xn(),isRosetta:Wn()})}function Qn(n,e){for(let r of n.split(`
`)){let o=r.trim();if(!o)continue;let[i,...s]=o.split(/\s+/);if(s.join(" ")===e)return i}return null}async function ne(n,e){let r=`https://github.com/oven-sh/bun/releases/download/bun-v${n}/SHASUMS256.txt`,o=await Jn(r),i=Qn(o,e);if(!i)throw new Error(`Checksum not found for ${e}`);return i}async function ee(n,e){let r=Fn("sha256");if(await Z(H(n),r),r.digest("hex")!==e)throw new Error("Checksum mismatch \u2014 download may be corrupted or tampered.")}function re(n,e){function r(){console.log(l.red(`
Download interrupted. Cleaning up...`)),_(n,!0).finally(e)}return process.once("SIGINT",r),()=>process.removeListener("SIGINT",r)}async function te(n,e,r){let o=await fetch(n,{signal:r});if(!o.ok||!o.body)throw new Error(`Failed to download ZIP file. Status: ${o.status}`);let i=Number(o.headers.get("content-length"))||0,s=0,a=new X.SingleBar({format:`${l.magenta("Downloading")} {bar} {percentage}% | {downloaded}/{totalSize}`,barCompleteChar:"\u2588",barIncompleteChar:"\u2591",hideCursor:!0},X.Presets.shades_classic);i&&a.start(i,0,{downloaded:"0 B",totalSize:k(i)});let c=zn.fromWeb(o.body);c.on("data",b=>{s+=b.length,i&&a.update(s,{downloaded:k(s),totalSize:k(i)})}),await M(c,e),i&&a.stop()}async function oe(n,e,r,o,i){if(await Z(H(n),Gn.Extract({path:r})),!await v(i))throw new Error(`Expected binary not found at ${i}`);await M(H(i),e),await Yn(e,493),await _(o,!0),await _(n)}async function en(n,e){let r=Zn(),o=`bun-${r}.zip`,i=`https://github.com/oven-sh/bun/releases/download/bun-v${n}/${o}`,s=`${e}.zip`,a=jn(e),c=W(a,`bun-${r}`),b=W(c,"bun"),T=new AbortController,_n=re(a,()=>{T.abort()});try{await te(i,s,T.signal),t.log(l.yellow("\u{1F512} Verifying checksum..."));let A=await ne(n,o);await ee(s,A),t.log(l.yellow("\u{1F4E6} Extracting...")),await oe(s,e,a,c,b),t.success(`Downloaded Bun ${l.bold(`v${n}`)}`)}catch(A){throw T.signal.aborted?new Error("Download interrupted."):(await _(a,!0),A)}finally{_n()}}function ie(n,e){function r(s){return s.replace(/^v/,"").split(".").map(a=>parseInt(a,10)||0)}let o=r(n),i=r(e);for(let s=0;s<Math.max(o.length,i.length);s++){let a=(i[s]||0)-(o[s]||0);if(a!==0)return a}return 0}function d(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}import{join as ue}from"path";import{existsSync as me}from"fs";import{isCancel as fe,select as pe}from"@clack/prompts";import G from"chalk";import{homedir as se}from"os";import{join as w}from"path";import L from"chalk";import{readFile as rn,writeFile as tn}from"fs/promises";import{execSync as ae}from"child_process";async function on(){if(!await v(m)){t.error(`Bun binary not found at ${m}, skipping completions`);return}let n=z();if(!n){t.warn("Unknown shell. Please manually set up Bun completions.");return}try{ae(`${m} completions`,{env:{...process.env,IS_BUN_AUTO_UPDATE:"true",SHELL:n},stdio:"ignore"})}catch(e){t.error(`Failed to set up ${n} completions: ${e}`)}}function z(){let n=process.env.SHELL??"";return n.includes("bash")?"bash":n.includes("zsh")?"zsh":n.includes("fish")?"fish":null}function sn(n){let e=se();switch(n){case"bash":{let r=process.env.XDG_CONFIG_HOME;return[w(e,".bashrc"),w(e,".bash_profile"),...r?[w(r,".bashrc"),w(r,".bash_profile"),w(r,"bashrc"),w(r,"bash_profile")]:[]]}case"zsh":return[w(e,".zshrc")];case"fish":return[w(e,".config","fish","config.fish")];default:return[]}}async function ce(){let n=[`set --export BUN_INSTALL "${P}"`,'set --export PATH "$BUN_INSTALL/bin" $PATH'];if(await v(S))try{let e=await rn(S,"utf8"),r=n.filter(o=>!e.includes(o));if(r.length>0){let o=e+`
# bun
${r.join(`
`)}
`;await tn(S,o),t.success(`Updated ${S} with Bun configuration`)}}catch(e){let r=e instanceof Error?e.message:String(e);t.warn(`Could not update Fish config: ${r}`)}else t.warn("Fish config file not found. Please add Bun configuration manually.")}async function le(n){if(!n){t.warn("Invalid shell provided");return}if(n==="fish"){await ce();return}let e=`export BUN_INSTALL="${P}"
`,r=`export PATH="$BUN_INSTALL/bin:$PATH"
`,o=`[ -s "${V}" ] && source "${V}"
`,i=sn(n);for(let s of i)if(await v(s))try{let a=await rn(s,"utf8"),c=!1,b=a;a.includes("BUN_INSTALL")||(b+=`
# bun
${e}${r}`,c=!0),a.includes(V)||(b+=`
# bun completions
${o}`,c=!0),c&&(await tn(s,b),t.success(`Updated ${s} with Bun configuration`));return}catch(a){let c=a instanceof Error?a.message:String(a);t.warn(`Could not update ${s}: ${c}`)}t.warn(`No writable ${n} config file found. Please add manually.`)}async function an(){let n=z();if(!n){t.warn("Unknown shell. Please manually add Bun to your shell config.");return}await le(n)}async function cn(){let n=z();if(t.log(""),t.info("To start using bun, run:"),!n){t.log(` ${L.cyan("Restart your terminal")}`),t.log(` ${L.cyan("bun --help")}`);return}let e=null;if(n==="zsh")e=`exec ${process.env.SHELL??"zsh"}`;else if(n==="fish")e=`source ${S}`;else if(n==="bash"){let r=sn(n);for(let o of r)if(await v(o)){e=`source ${o}`;break}}e&&t.log(` ${L.cyan(e)}`),t.log(` ${L.cyan("bun --help")}`)}async function de(n){if(n)return n;if(!d()){t.error("Version required in non-interactive mode."),t.info(`Usage: ${G.cyan("bvm use <version>")}`);return}let e=p();if(e.length===0){t.error("No Bun versions installed."),t.info(`Run ${G.cyan("bvm install latest")} to install one.`);return}let r=u(),o=await pe({message:"Select a Bun version to use",initialValue:r??e[0],options:e.map(i=>({value:i,label:`v${i}`,hint:r===i?"current":void 0}))});if(fe(o)){t.warn("Cancelled.");return}return o}async function N(n){let e=await de(n);if(!e)return;let r=ue(f,e,"bun");if(!me(r)){t.error(`Bun version ${e} is not installed.`),t.info(`Run ${G.cyan(`bvm install ${e}`)} to install it.`);return}try{C(B),F(r,m),F(r,x),await on(),t.success(`Now using Bun ${e}`)}catch(o){t.error(`Failed to switch Bun version: ${o.message}`)}}function be(n,e,r,o){let i=[];return n===e&&i.push("latest"),r.has(n)&&i.push("installed"),o===n&&i.push("current"),i.length>0?i.join(", "):void 0}async function ye(n){if(n){if(n==="latest")try{let c=await nn();return c||void 0}catch{throw new Error("Error fetching latest Bun version.")}return n}if(!d()){t.error("Version required in non-interactive mode."),t.info(`Usage: ${R.cyan("bvm install <version>")}`);return}let e=we();e.start("Fetching available Bun versions...");let r;try{r=await U(),e.stop("Fetched available versions")}catch{throw e.stop("Failed to fetch versions"),new Error("Error fetching remote Bun versions.")}if(r.length===0){t.warn("No Bun versions found.");return}let o=new Set(p()),i=u(),s=r[0],a=await ve({message:"Select a Bun version to install",initialValue:s,options:r.map(c=>({value:c,label:`v${c}`,hint:be(c,s,o,i)}))});if(he(a)){t.warn("Cancelled.");return}return a}async function mn(n){let e=await ye(n);if(!e)return;let r=un(f,e),o=un(r,"bun"),i=I(m)&&I(x);if(ln(r)&&ln(o)){t.warn(`Bun ${R.green(`v${e}`)} is already installed.`),await N(e);return}if(ge(r,{recursive:!0}),t.log(`${R.cyan("\u{1F4E5} Installing")} Bun ${R.green(`v${e}`)}...`),await en(e,o),!i){t.warn("No previously installed Bun versions found. Setting up environment..."),C(B);try{await an()}catch(s){t.error(`Failed to configure shell: ${s.message}. Please set up completions manually.`)}await N(e),await cn();return}t.success(`Installed Bun ${R.bold(`v${e}`)}`),await N(e)}import fn from"chalk";function pn(){try{let n=u();t.log(`Current Bun version: ${n?`v${n}`:"none"}`);let e=p();if(e.length===0){t.warn("No Bun versions installed yet.");return}t.log(fn.green(`\u{1F680} Installed Bun versions:
`)),e.forEach(r=>{t.log(j(r,n,new Set(e)))})}catch{t.error("No Bun versions installed yet.")}}async function dn(){try{let n=await U();if(n.length===0){t.warn("No Bun versions found remotely.");return}let e=p(),r=new Set(e),o=u();t.log(fn.green(`\u{1F680} Available Bun versions:
`)),n.forEach(i=>{t.log(j(i,o,r))})}catch(n){t.error(`Error fetching remote versions: ${n instanceof Error?n.message:n}`)}}function gn(){let n=u();if(n===null){t.error("No Bun version is currently active. Install one with: bvm install <version>");return}t.log(`Current Bun version: v${n}`)}import{join as hn,resolve as vn}from"path";import{existsSync as wn,readlinkSync as Be,rmSync as yn}from"fs";import{confirm as xe,isCancel as Bn,select as Se}from"@clack/prompts";import xn from"chalk";function bn(n,e){if(!I(n))return!1;try{if(vn(Be(n))===vn(e))return yn(n,{force:!0}),!0}catch{}return!1}async function $e(n){if(n)return n;if(!d()){t.error("Version required in non-interactive mode."),t.info(`Usage: ${xn.cyan("bvm uninstall <version>")}`);return}let e=p();if(e.length===0){t.error("No Bun versions installed.");return}let r=u(),o=await Se({message:"Select a Bun version to uninstall",initialValue:e.find(i=>i!==r)??e[0],options:e.map(i=>({value:i,label:`v${i}`,hint:r===i?"current":void 0}))});if(Bn(o)){t.warn("Cancelled.");return}return o}async function Ie(n){if(!d())return!0;let r=u()===n,o=await xe({message:r?`Uninstall Bun v${n}? This is your active version.`:`Uninstall Bun v${n}?`});return Bn(o)?(t.warn("Cancelled."),!1):o}async function Sn(n){let e=await $e(n);if(!e)return;let r=hn(f,e),o=hn(r,"bun");if(!wn(r)||!wn(o)){t.warn(`Bun version ${e} is not installed.`);return}if(!await Ie(e))return;let s=bn(m,o),a=bn(x,o);(s||a)&&t.log(`\u{1F517} Removed active symlinks for Bun ${e}.`);try{yn(r,{recursive:!0,force:!0}),t.log(xn.green(`\u{1F5D1}\uFE0F Bun ${e} has been uninstalled.`))}catch(c){t.error(`Failed to uninstall Bun ${e}: ${c.message}`)}}import Nn from"chalk";var In="bunvm",E="1.2.1";var Ne=[{manager:"npm",command:"npm update -g bunvm"},{manager:"pnpm",command:"pnpm update -g bunvm"},{manager:"yarn",command:"yarn global upgrade bunvm"},{manager:"bun",command:"bun update -g bunvm"}];function En(){t.log(`Current bvm version: ${Nn.green(`v${E}`)}`),t.log(""),t.log("To update bvm, run one of:"),t.log("");for(let{manager:n,command:e}of Ne)t.log(` ${Nn.cyan(n.padEnd(5))} ${e}`)}import Ee from"update-notifier";function Pe(n){let e=n.slice(2);return!!(e.includes("upgrade")||e.includes("--version")||e.includes("-V")||e.includes("--help")||e.includes("-h"))}function Pn(n=process.argv){if(!d()||Pe(n))return;Ee({pkg:{name:In,version:E},updateCheckInterval:1e3*60*60*24}).notify({isGlobal:!0,message:"Update available {currentVersion} \u2192 {latestVersion}\nRun `bvm upgrade` for update instructions."})}Pn();var g=new _e;function y(n){Promise.resolve(n()).catch(e=>{let r=e instanceof Error?e.message:"An unexpected error occurred.";t.error(r),process.exit(1)})}g.name("bvm").description("Bun Version Manager - Manage multiple Bun versions").version(E);g.command("install [version]").alias("i").description("Install a specific Bun version (interactive if omitted)").action(n=>y(()=>mn(n)));g.command("use [version]").description("Use a specific Bun version (interactive if omitted)").action(n=>y(()=>N(n)));g.command("uninstall [version]").alias("u").description("Uninstall a specific Bun version (interactive if omitted)").action(n=>y(()=>Sn(n)));g.command("current").description("Display currently activated version of Bun").action(()=>y(gn));g.command("list").alias("ls").description("List installed Bun versions").action(()=>y(pn));g.command("remote").alias("r").description("List remote Bun versions").action(()=>y(dn));g.command("upgrade").description("Show how to update bvm to the latest version").action(()=>y(En));g.parse();
//# sourceMappingURL=bvm.js.map