aboutsummaryrefslogtreecommitdiff
path: root/packages/bun-release/src/npm/install.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/bun-release/src/npm/install.ts')
-rw-r--r--packages/bun-release/src/npm/install.ts159
1 files changed, 159 insertions, 0 deletions
diff --git a/packages/bun-release/src/npm/install.ts b/packages/bun-release/src/npm/install.ts
new file mode 100644
index 000000000..ac6f2e670
--- /dev/null
+++ b/packages/bun-release/src/npm/install.ts
@@ -0,0 +1,159 @@
+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",
+ );
+}