1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
import { fetch } from "../fetch";
import { spawn } from "../spawn";
import { chmod, join, rename, rm, tmp, write } from "../fs";
import { unzipSync } from "zlib";
import type { Platform } from "../platform";
import { os, arch, supportedPlatforms } from "../platform";
import { debug, error } from "../console";
declare const version: string;
declare const module: string;
declare const owner: string;
export async function importBun(): Promise<string> {
if (!supportedPlatforms.length) {
throw new Error(`Unsupported platform: ${os} ${arch}`);
}
for (const platform of supportedPlatforms) {
try {
return await requireBun(platform);
} catch (error) {
debug("requireBun failed", error);
}
}
throw new Error(`Failed to install package "${module}"`);
}
async function requireBun(platform: Platform): Promise<string> {
const module = `${owner}/${platform.bin}`;
function resolveBun() {
const exe = require.resolve(join(module, platform.exe));
const { exitCode, stderr, stdout } = spawn(exe, ["--version"]);
if (exitCode === 0) {
return exe;
}
throw new Error(stderr || stdout);
}
try {
return resolveBun();
} catch (cause) {
debug("resolveBun failed", cause);
error(
`Failed to find package "${module}".`,
`You may have used the "--no-optional" flag when running "npm install".`,
);
}
const cwd = join("node_modules", module);
try {
installBun(platform, cwd);
} catch (cause) {
debug("installBun failed", cause);
error(`Failed to install package "${module}" using "npm install".`, cause);
try {
await downloadBun(platform, cwd);
} catch (cause) {
debug("downloadBun failed", cause);
error(`Failed to download package "${module}" from "registry.npmjs.org".`, cause);
}
}
return resolveBun();
}
function installBun(platform: Platform, dst: string): void {
const module = `${owner}/${platform.bin}`;
const cwd = tmp();
try {
write(join(cwd, "package.json"), "{}");
const { exitCode } = spawn(
"npm",
["install", "--loglevel=error", "--prefer-offline", "--no-audit", "--progress=false", `${module}@${version}`],
{
cwd,
stdio: "pipe",
env: {
...process.env,
npm_config_global: undefined,
},
},
);
if (exitCode === 0) {
rename(join(cwd, "node_modules", module), dst);
}
} finally {
try {
rm(cwd);
} catch (error) {
debug("rm failed", error);
// There is nothing to do if the directory cannot be cleaned up.
}
}
}
async function downloadBun(platform: Platform, dst: string): Promise<void> {
const response = await fetch(`https://registry.npmjs.org/${owner}/${platform.bin}/-/${platform.bin}-${version}.tgz`);
const tgz = await response.arrayBuffer();
let buffer: Buffer;
try {
buffer = unzipSync(tgz);
} catch (cause) {
throw new Error("Invalid gzip data", { cause });
}
function str(i: number, n: number): string {
return String.fromCharCode(...buffer.subarray(i, i + n)).replace(/\0.*$/, "");
}
let offset = 0;
while (offset < buffer.length) {
const name = str(offset, 100).replace("package/", "");
const size = parseInt(str(offset + 124, 12), 8);
offset += 512;
if (!isNaN(size)) {
write(join(dst, name), buffer.subarray(offset, offset + size));
if (name === platform.exe) {
try {
chmod(join(dst, name), 0o755);
} catch (error) {
debug("chmod failed", error);
}
}
offset += (size + 511) & ~511;
}
}
}
export function optimizeBun(path: string): void {
if (os === "win32") {
throw new Error(
"You must use Windows Subsystem for Linux, aka. WSL, to run bun. Learn more: https://learn.microsoft.com/en-us/windows/wsl/install",
);
}
const { npm_config_user_agent } = process.env;
if (npm_config_user_agent && /\byarn\//.test(npm_config_user_agent)) {
throw new Error(
"Yarn does not support bun, because it does not allow linking to binaries. To use bun, install using the following command: curl -fsSL https://bun.sh/install | bash",
);
}
try {
rename(path, join(__dirname, "bin", "bun"));
return;
} catch (error) {
debug("optimizeBun failed", error);
}
throw new Error(
"Your package manager doesn't seem to support bun. To use bun, install using the following command: curl -fsSL https://bun.sh/install | bash",
);
}
|