diff options
Diffstat (limited to 'packages/bun-npm/src')
| -rw-r--r-- | packages/bun-npm/src/install.ts | 159 | ||||
| -rw-r--r-- | packages/bun-npm/src/platform.ts | 100 | ||||
| -rw-r--r-- | packages/bun-npm/src/util.ts | 212 |
3 files changed, 0 insertions, 471 deletions
diff --git a/packages/bun-npm/src/install.ts b/packages/bun-npm/src/install.ts deleted file mode 100644 index 9eabd2c41..000000000 --- a/packages/bun-npm/src/install.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { fetch, chmod, join, rename, rm, tmp, write, spawn } from "./util"; -import { unzipSync } from "zlib"; -import type { Platform } from "./platform"; -import { os, arch, supportedPlatforms } from "./platform"; - -declare const npmVersion: string; -declare const npmPackage: string; -declare const npmOwner: 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) { - console.debug("requireBun failed", error); - } - } - throw new Error(`Failed to install package "${npmPackage}"`); -} - -async function requireBun(platform: Platform): Promise<string> { - const npmPackage = `${npmOwner}/${platform.bin}`; - function resolveBun() { - const exe = require.resolve(join(npmPackage, platform.exe)); - const { exitCode, stderr, stdout } = spawn(exe, ["--version"]); - if (exitCode === 0) { - return exe; - } - throw new Error(stderr || stdout); - } - try { - return resolveBun(); - } catch (error) { - console.debug("resolveBun failed", error); - console.error( - `Failed to find package "${npmPackage}".`, - `You may have used the "--no-optional" flag when running "npm install".`, - ); - } - const cwd = join("node_modules", npmPackage); - try { - installBun(platform, cwd); - } catch (error) { - console.debug("installBun failed", error); - console.error( - `Failed to install package "${npmPackage}" using "npm install".`, - error, - ); - try { - await downloadBun(platform, cwd); - } catch (error) { - console.debug("downloadBun failed", error); - console.error( - `Failed to download package "${npmPackage}" from "registry.npmjs.org".`, - error, - ); - } - } - return resolveBun(); -} - -function installBun(platform: Platform, dst: string): void { - const npmPackage = `${npmOwner}/${platform.bin}`; - const cwd = tmp(); - try { - write(join(cwd, "package.json"), "{}"); - const { exitCode } = spawn( - "npm", - [ - "install", - "--loglevel=error", - "--prefer-offline", - "--no-audit", - "--progress=false", - `${npmPackage}@${npmVersion}`, - ], - { - cwd, - stdio: "pipe", - env: { - ...process.env, - npm_config_global: undefined, - }, - }, - ); - if (exitCode === 0) { - rename(join(cwd, "node_modules", npmPackage), dst); - } - } finally { - try { - rm(cwd); - } catch (error) { - console.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/${npmOwner}/${platform.bin}/-/${platform.bin}-${npmVersion}.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) { - console.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) { - console.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", - ); -} diff --git a/packages/bun-npm/src/platform.ts b/packages/bun-npm/src/platform.ts deleted file mode 100644 index a01cc3ddc..000000000 --- a/packages/bun-npm/src/platform.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { read, spawn } from "./util"; - -export const os = process.platform; - -export const arch = - os === "darwin" && process.arch === "x64" && isRosetta2() - ? "arm64" - : process.arch; - -export const avx2 = - (arch === "x64" && os === "linux" && isLinuxAVX2()) || - (os === "darwin" && isDarwinAVX2()); - -export type Platform = { - os: string; - arch: string; - avx2?: boolean; - bin: string; - exe: string; -}; - -export const platforms: Platform[] = [ - { - os: "darwin", - arch: "arm64", - bin: "bun-darwin-aarch64", - exe: "bin/bun", - }, - { - os: "darwin", - arch: "x64", - avx2: true, - bin: "bun-darwin-x64", - exe: "bin/bun", - }, - { - os: "darwin", - arch: "x64", - bin: "bun-darwin-x64-baseline", - exe: "bin/bun", - }, - { - os: "linux", - arch: "arm64", - bin: "bun-linux-aarch64", - exe: "bin/bun", - }, - { - os: "linux", - arch: "x64", - avx2: true, - bin: "bun-linux-x64", - exe: "bin/bun", - }, - { - os: "linux", - arch: "x64", - bin: "bun-linux-x64-baseline", - exe: "bin/bun", - }, -]; - -export const supportedPlatforms: Platform[] = platforms - .filter( - (platform) => - platform.os === os && platform.arch === arch && (!platform.avx2 || avx2), - ) - .sort((a, b) => (a.avx2 === b.avx2 ? 0 : a.avx2 ? -1 : 1)); - -function isLinuxAVX2(): boolean { - try { - return read("/proc/cpuinfo").includes("avx2"); - } catch (error) { - console.debug("isLinuxAVX2 failed", error); - return false; - } -} - -function isDarwinAVX2(): boolean { - try { - const { exitCode, stdout } = spawn("sysctl", ["-n", "machdep.cpu"]); - return exitCode === 0 && stdout.includes("AVX2"); - } catch (error) { - console.debug("isDarwinAVX2 failed", error); - return false; - } -} - -function isRosetta2(): boolean { - try { - const { exitCode, stdout } = spawn("sysctl", [ - "-n", - "sysctl.proc_translated", - ]); - return exitCode === 0 && stdout.includes("1"); - } catch (error) { - console.debug("isRosetta2 failed", error); - return false; - } -} diff --git a/packages/bun-npm/src/util.ts b/packages/bun-npm/src/util.ts deleted file mode 100644 index e2cf19ee0..000000000 --- a/packages/bun-npm/src/util.ts +++ /dev/null @@ -1,212 +0,0 @@ -import fs from "fs"; -import path, { dirname } from "path"; -import { tmpdir } from "os"; -import child_process from "child_process"; - -if (process.env["DEBUG"] !== "1") { - console.debug = () => {}; -} - -export function join(...paths: (string | string[])[]): string { - return path.join(...paths.flat(2)); -} - -export function tmp(): string { - const path = fs.mkdtempSync(join(tmpdir(), "bun-")); - console.debug("tmp", path); - return path; -} - -export function rm(path: string): void { - console.debug("rm", path); - try { - fs.rmSync(path, { recursive: true }); - return; - } catch (error) { - console.debug("rmSync failed", error); - // Did not exist before Node.js v14. - // Attempt again with older, slower implementation. - } - let stats: fs.Stats; - try { - stats = fs.lstatSync(path); - } catch (error) { - console.debug("lstatSync failed", error); - // The file was likely deleted, so return early. - return; - } - if (!stats.isDirectory()) { - fs.unlinkSync(path); - return; - } - try { - fs.rmdirSync(path, { recursive: true }); - return; - } catch (error) { - console.debug("rmdirSync failed", error); - // Recursive flag did not exist before Node.js X. - // Attempt again with older, slower implementation. - } - for (const filename of fs.readdirSync(path)) { - rm(join(path, filename)); - } - fs.rmdirSync(path); -} - -export function rename(path: string, newPath: string): void { - console.debug("rename", path, newPath); - try { - fs.renameSync(path, newPath); - return; - } catch (error) { - console.debug("renameSync failed", error); - // If there is an error, delete the new path and try again. - } - try { - rm(newPath); - } catch (error) { - console.debug("rm failed", error); - // The path could have been deleted already. - } - fs.renameSync(path, newPath); -} - -export function write( - path: string, - content: string | ArrayBuffer | ArrayBufferView, -): void { - console.debug("write", path); - try { - fs.writeFileSync(path, content); - return; - } catch (error) { - console.debug("writeFileSync failed", error); - // If there is an error, ensure the parent directory - // exists and try again. - try { - fs.mkdirSync(dirname(path), { recursive: true }); - } catch (error) { - console.debug("mkdirSync failed", error); - // The directory could have been created already. - } - fs.writeFileSync(path, content); - } -} - -export function read(path: string): string { - console.debug("read", path); - return fs.readFileSync(path, "utf-8"); -} - -export function chmod(path: string, mode: fs.Mode): void { - console.debug("chmod", path, mode); - fs.chmodSync(path, mode); -} - -export function copy(path: string, newPath: string): void { - console.debug("copy", path, newPath); - try { - fs.copyFileSync(path, newPath); - return; - } catch (error) { - console.debug("copyFileSync failed", error); - } - write(newPath, read(path)); -} - -export function exists(path: string): boolean { - console.debug("exists", path); - try { - return fs.existsSync(path); - } catch (error) { - console.debug("existsSync failed", error); - } - return false; -} - -export function spawn( - cmd: string, - args: string[], - options: child_process.SpawnOptions = {}, -): { - exitCode: number; - stdout: string; - stderr: string; -} { - console.debug("spawn", [cmd, ...args].join(" ")); - const { status, stdout, stderr } = child_process.spawnSync(cmd, args, { - stdio: "pipe", - encoding: "utf-8", - ...options, - }); - return { - exitCode: status ?? 1, - stdout, - stderr, - }; -} - -export type Response = { - readonly status: number; - arrayBuffer(): Promise<ArrayBuffer>; - json<T>(): Promise<T>; -}; - -export const fetch = "fetch" in globalThis ? webFetch : nodeFetch; - -async function webFetch(url: string, assert?: boolean): Promise<Response> { - const response = await globalThis.fetch(url); - console.debug("fetch", url, response.status); - if (assert !== false && !isOk(response.status)) { - throw new Error(`${response.status}: ${url}`); - } - return response; -} - -async function nodeFetch(url: string, assert?: boolean): Promise<Response> { - const { get } = await import("node:http"); - return new Promise((resolve, reject) => { - get(url, (response) => { - console.debug("get", url, response.statusCode); - const status = response.statusCode ?? 501; - if (response.headers.location && isRedirect(status)) { - return nodeFetch(url).then(resolve, reject); - } - if (assert !== false && !isOk(status)) { - return reject(new Error(`${status}: ${url}`)); - } - const body: Buffer[] = []; - response.on("data", (chunk) => { - body.push(chunk); - }); - response.on("end", () => { - resolve({ - status, - async arrayBuffer() { - return Buffer.concat(body).buffer as ArrayBuffer; - }, - async json() { - const text = Buffer.concat(body).toString("utf-8"); - return JSON.parse(text); - }, - }); - }); - }).on("error", reject); - }); -} - -function isOk(status: number): boolean { - return status === 200; -} - -function isRedirect(status: number): boolean { - switch (status) { - case 301: // Moved Permanently - case 308: // Permanent Redirect - case 302: // Found - case 307: // Temporary Redirect - case 303: // See Other - return true; - } - return false; -} |
