Support installing bun from npm

This commit is contained in:
Jarred Sumner
2021-09-14 16:59:21 -07:00
parent 24522f7d74
commit 5dbbad5cc4
14 changed files with 516 additions and 43 deletions

7
.gitignore vendored
View File

@@ -62,4 +62,9 @@ src/node-fallbacks/node_modules
sign.json sign.json
release/ release/
*.dmg *.dmg
sign.*.json sign.*.json
packages/debug-*
packages/bun-cli/postinstall.js
packages/bun-*/bin/*
packages/bun-cli/bin/*

View File

@@ -1,3 +1,17 @@
OS_NAME := $(shell uname -s | tr '[:upper:]' '[:lower:]')
ARCH_NAME_DENORAMLZIED_1 := $(shell uname -m)
ARCH_NAME_DENORAMLZIED_2 := $(shell tr '[_]' '[--]' <<< $(ARCH_NAME_DENORAMLZIED_1))
ARCH_NAME := $(shell sed s/x86-64/x64/ <<< $(ARCH_NAME_DENORAMLZIED_2))
TRIPLET := $(OS_NAME)-$(ARCH_NAME)
PACKAGE_DIR := packages/bun-cli-$(TRIPLET)
DEBUG_PACKAGE_DIR := packages/debug-bun-cli-$(TRIPLET)
BIN_DIR := $(PACKAGE_DIR)/bin
RELEASE_BIN := $(BIN_DIR)/bun
DEBUG_BIN := $(DEBUG_PACKAGE_DIR)/bin/bun-debug
BUILD_ID := $(shell cat ./build-id)
PACKAGE_JSON_VERSION := 0.0.0-$(BUILD_ID)
BUN_BUILD_TAG := bun-v$(PACKAGE_JSON_VERSION)
bun: vendor build-obj bun-link-lld-release bun: vendor build-obj bun-link-lld-release
vendor: api node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp jsc vendor: api node-fallbacks runtime_js fallback_decoder bun_error mimalloc picohttp jsc
@@ -11,9 +25,12 @@ sign-macos-x64:
sign-macos-aarch64: sign-macos-aarch64:
gon sign.macos-aarch64.json gon sign.macos-aarch64.json
release-macos-x64: build-obj jsc-bindings-mac bun-link-lld-release sign-macos-x64 release-macos-x64-push release-macos-x64: build-obj jsc-bindings-mac bun-link-lld-release
release-macos-aarch64: build-obj jsc-bindings-mac bun-link-lld-release sign-macos-aarch64 release-macos-aarch64: build-obj jsc-bindings-mac bun-link-lld-release sign-macos-aarch64
bin-dir:
@echo $(BIN_DIR)
api: api:
npm install; ./node_modules/.bin/peechy --schema src/api/schema.peechy --esm src/api/schema.js --ts src/api/schema.d.ts --zig src/api/schema.zig npm install; ./node_modules/.bin/peechy --schema src/api/schema.peechy --esm src/api/schema.js --ts src/api/schema.d.ts --zig src/api/schema.zig
@@ -21,10 +38,10 @@ node-fallbacks:
cd src/node-fallbacks; npm install; npm run --silent build cd src/node-fallbacks; npm install; npm run --silent build
fallback_decoder: fallback_decoder:
esbuild --target=esnext --bundle src/fallback.ts --format=iife --platform=browser --minify > src/fallback.out.js @esbuild --target=esnext --bundle src/fallback.ts --format=iife --platform=browser --minify > src/fallback.out.js
runtime_js: runtime_js:
NODE_ENV=production esbuild --define:process.env.NODE_ENV="production" --target=esnext --bundle src/runtime/index.ts --format=iife --platform=browser --global-name=BUN_RUNTIME --minify --external:/bun:* > src/runtime.out.js; cat src/runtime.footer.js >> src/runtime.out.js @NODE_ENV=production esbuild --define:process.env.NODE_ENV="production" --target=esnext --bundle src/runtime/index.ts --format=iife --platform=browser --global-name=BUN_RUNTIME --minify --external:/bun:* > src/runtime.out.js; cat src/runtime.footer.js >> src/runtime.out.js
bun_error: bun_error:
cd packages/bun-error; npm install; npm run --silent build cd packages/bun-error; npm install; npm run --silent build
@@ -37,25 +54,37 @@ jsc-bindings-headers:
mkdir -p src/JavaScript/jsc/bindings-obj/ mkdir -p src/JavaScript/jsc/bindings-obj/
zig build headers zig build headers
BUILD_ID := $(shell cat ./build-id) bump:
bump-build-id:
expr $(BUILD_ID) + 1 > build-id expr $(BUILD_ID) + 1 > build-id
BUN_BUILD_TAG := bun-build-$(BUILD_ID)
build_postinstall:
@esbuild --bundle --format=cjs --platform=node --define:BUN_VERSION="\"$(PACKAGE_JSON_VERSION)\"" packages/bun-cli/scripts/postinstall.ts > packages/bun-cli/postinstall.js
write-package-json-version-cli:
jq -S --raw-output '.version = "${PACKAGE_JSON_VERSION}"' packages/bun-cli/package.json > packages/bun-cli/package.json.new
mv packages/bun-cli/package.json.new packages/bun-cli/package.json
write-package-json-version-arch:
jq -S --raw-output '.version = "${PACKAGE_JSON_VERSION}"' $(PACKAGE_DIR)/package.json > $(PACKAGE_DIR)/package.json.new
mv $(PACKAGE_DIR)/package.json.new $(PACKAGE_DIR)/package.json
tag: tag:
git tag $(BUN_BUILD_TAG) git tag $(BUN_BUILD_TAG)
git push --tags git push --tags
prepare-release: bump-build-id tag release-create prepare-release: build_postinstall tag release-create write-package-json-version-arch write-package-json-version-cli
release-create: release-create:
gh release create --title "Bun - build $(BUILD_ID)" "$(BUN_BUILD_TAG)" gh release create --title "Bun v$(PACKAGE_JSON_VERSION)" "$(BUN_BUILD_TAG)"
release-cli-push:
cd packages/bun-cli && npm pack --pack-destination /tmp/
gh release upload $(BUN_BUILD_TAG) --clobber /tmp/bun-cli-$(PACKAGE_JSON_VERSION).tgz
release-macos-x64-push: release-macos-x64-push:
gh release upload $(BUN_BUILD_TAG) --clobber release/bun-macos-x64.zip cd packages/bun-cli-darwin-x64 && npm pack --pack-destination /tmp/
gh release upload $(BUN_BUILD_TAG) --clobber /tmp/bun-cli-darwin-x64-$(PACKAGE_JSON_VERSION).tgz
jsc-copy-headers: jsc-copy-headers:
find src/JavaScript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/Headers/JavaScriptCore/ -name "*.h" -exec cp {} src/JavaScript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/PrivateHeaders/JavaScriptCore \; find src/JavaScript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/Headers/JavaScriptCore/ -name "*.h" -exec cp {} src/JavaScript/jsc/WebKit/WebKitBuild/Release/JavaScriptCore/PrivateHeaders/JavaScriptCore \;
@@ -140,12 +169,13 @@ bun-link-lld-debug:
bun-link-lld-release: bun-link-lld-release:
clang++ $(BUN_LLD_FLAGS) \ clang++ $(BUN_LLD_FLAGS) \
build/macos-x86_64/bun.o \ packages/bun-cli-darwin-x64/bin/bun.o \
-o build/macos-x86_64/bun \ -o packages/bun-cli-darwin-x64/bin/bun \
-Wl,-dead_strip \ -Wl,-dead_strip \
-ftls-model=local-exec \ -ftls-model=local-exec \
-flto \ -flto \
-O3 -O3
rm packages/bun-cli-darwin-x64/bin/bun.o
bun-link-lld-release-aarch64: bun-link-lld-release-aarch64:
clang++ $(BUN_LLD_FLAGS) \ clang++ $(BUN_LLD_FLAGS) \
@@ -169,3 +199,4 @@ sizegen:
picohttp: picohttp:
clang -O3 -g -c src/deps/picohttpparser.c -Isrc/deps -o src/deps/picohttpparser.o; cd ../../ clang -O3 -g -c src/deps/picohttpparser.c -Isrc/deps -o src/deps/picohttpparser.o; cd ../../

View File

@@ -1,4 +1,4 @@
# Bun: a fast bundler & transpiler for developing web software # Bun
Bun is a new: Bun is a new:

View File

@@ -1 +1 @@
8 10

View File

@@ -24,8 +24,8 @@ pub fn addPicoHTTP(step: *std.build.LibExeObjStep, comptime with_obj: bool) void
// set -gx ICU_INCLUDE_DIRS "/usr/local/opt/icu4c/include" // set -gx ICU_INCLUDE_DIRS "/usr/local/opt/icu4c/include"
// homebrew-provided icu4c // homebrew-provided icu4c
} }
var x64 = "x64";
pub fn build(b: *std.build.Builder) void { pub fn build(b: *std.build.Builder) !void {
// Standard target options allows the person running `zig build` to choose // Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which // what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options // means any target is allowed, and the default is native. Other options
@@ -39,12 +39,39 @@ pub fn build(b: *std.build.Builder) void {
const cwd: []const u8 = b.pathFromRoot("."); const cwd: []const u8 = b.pathFromRoot(".");
var exe: *std.build.LibExeObjStep = undefined; var exe: *std.build.LibExeObjStep = undefined;
var output_dir_buf = std.mem.zeroes([4096]u8); var output_dir_buf = std.mem.zeroes([4096]u8);
var bin_label = if (mode == std.builtin.Mode.Debug) "/debug/" else "/"; var bin_label = if (mode == std.builtin.Mode.Debug) "packages/debug-bun-cli-" else "packages/bun-cli-";
const output_dir = b.pathFromRoot(std.fmt.bufPrint(&output_dir_buf, "build{s}{s}-{s}", .{ bin_label, @tagName(target.getOs().tag), @tagName(target.getCpuArch()) }) catch unreachable);
var triplet_buf: [64]u8 = undefined;
var os_tagname = @tagName(target.getOs().tag);
if (std.mem.eql(u8, os_tagname, "macos")) {
os_tagname = "darwin";
}
std.mem.copy(
u8,
&triplet_buf,
os_tagname,
);
var osname = triplet_buf[0..os_tagname.len];
triplet_buf[osname.len] = '-';
std.mem.copy(u8, triplet_buf[osname.len + 1 ..], @tagName(target.getCpuArch()));
var cpuArchName = triplet_buf[osname.len + 1 ..][0..@tagName(target.getCpuArch()).len];
std.mem.replaceScalar(u8, cpuArchName, '_', '-');
if (std.mem.eql(u8, cpuArchName, "x86-64")) {
std.mem.copy(u8, cpuArchName, "x64");
cpuArchName = cpuArchName[0..3];
}
var triplet = triplet_buf[0 .. osname.len + cpuArchName.len + 1];
const output_dir_base = try std.fmt.bufPrint(&output_dir_buf, "{s}{s}/bin", .{ bin_label, triplet });
const output_dir = b.pathFromRoot(output_dir_base);
const bun_executable_name = if (mode == std.builtin.Mode.Debug) "bun-debug" else "bun";
if (target.getOsTag() == .wasi) { if (target.getOsTag() == .wasi) {
exe.enable_wasmtime = true; exe.enable_wasmtime = true;
exe = b.addExecutable("bun", "src/main_wasi.zig"); exe = b.addExecutable(bun_executable_name, "src/main_wasi.zig");
exe.linkage = .dynamic; exe.linkage = .dynamic;
exe.setOutputDir(output_dir); exe.setOutputDir(output_dir);
} else if (target.getCpuArch().isWasm()) { } else if (target.getCpuArch().isWasm()) {
@@ -54,7 +81,7 @@ pub fn build(b: *std.build.Builder) void {
// ); // );
// exe.is_linking_libc = false; // exe.is_linking_libc = false;
// exe.is_dynamic = true; // exe.is_dynamic = true;
var lib = b.addExecutable("bun", "src/main_wasm.zig"); var lib = b.addExecutable(bun_executable_name, "src/main_wasm.zig");
lib.single_threaded = true; lib.single_threaded = true;
// exe.want_lto = true; // exe.want_lto = true;
// exe.linkLibrary(lib); // exe.linkLibrary(lib);
@@ -94,7 +121,7 @@ pub fn build(b: *std.build.Builder) void {
return; return;
} else { } else {
exe = b.addExecutable("bun", "src/main.zig"); exe = b.addExecutable(bun_executable_name, "src/main.zig");
} }
// exe.setLibCFile("libc.txt"); // exe.setLibCFile("libc.txt");
exe.linkLibC(); exe.linkLibC();
@@ -244,7 +271,7 @@ pub fn build(b: *std.build.Builder) void {
} }
var obj_step = b.step("obj", "Build Bun as a .o file"); var obj_step = b.step("obj", "Build Bun as a .o file");
var obj = b.addObject("bun", exe.root_src.?.path); var obj = b.addObject(bun_executable_name, exe.root_src.?.path);
obj.bundle_compiler_rt = true; obj.bundle_compiler_rt = true;
addPicoHTTP(obj, false); addPicoHTTP(obj, false);
obj.addPackage(.{ obj.addPackage(.{
@@ -275,7 +302,7 @@ pub fn build(b: *std.build.Builder) void {
const run_step = b.step("run", "Run the app"); const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step); run_step.dependOn(&run_cmd.step);
var log_step = b.addLog("Destination: {s}/{s}\n", .{ output_dir, "bun" }); var log_step = b.addLog("Destination: {s}/{s}\n", .{ output_dir, bun_executable_name });
log_step.step.dependOn(&exe.step); log_step.step.dependOn(&exe.step);
var typings_cmd: *std.build.RunStep = typings_exe.run(); var typings_cmd: *std.build.RunStep = typings_exe.run();

View File

@@ -0,0 +1,8 @@
{
"directories": {
"bin": "bin"
},
"name": "bun-cli-darwin-x64",
"repository": "https://github.com/jarred-sumner/bun",
"version": "0.0.0-10"
}

View File

@@ -0,0 +1,2 @@
scripts
reset-bin.js

View File

@@ -1,4 +1,13 @@
{ {
"bin": {
"bun": "bin/bun"
},
"license": "MIT",
"name": "bun-cli", "name": "bun-cli",
"version": "0.0.0-2" "repository": "https://github.com/jarred-sumner/bun",
"scripts": {
"postinstall": "node postinstall.js",
"prepublishOnly": "rm -rf ./bin/bun; chmod +x ./reset-bin.js; cp ./reset-bin.js ./bin/bun"
},
"version": "0.0.0-10"
} }

13
packages/bun-cli/reset-bin.js Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env node
throw new Error(`bun-cli: Failed to install correctly
Make sure you don't have "ignore-scripts" set to true. You can check this with
"npm config get ignore-scripts". If that returns true you can reset it back to
false using "npm config set ignore-scripts false" and then reinstall bun.
If you're using npm v7, make sure your package-lock.json file contains either
"lockfileVersion": 1 or the code "hasInstallScript": true. If it doesn't have
either of those, then it is likely the case that a known bug in npm v7 has
corrupted your package-lock.json file. Regenerating your package-lock.json file
should fix this issue.
`);

View File

@@ -0,0 +1,359 @@
// This is almost verbatim esbuild's postinstall script.
// Thank you @evanw.
import fs = require("fs");
import os = require("os");
import path = require("path");
import zlib = require("zlib");
import https = require("https");
import child_process = require("child_process");
declare const BUN_VERSION: string;
const version = BUN_VERSION;
const binPath = path.join(__dirname, "bin", "bun");
async function installBinaryFromPackage(
name: string,
fromPath: string,
toPath: string
): Promise<void> {
// Try to install from the cache if possible
const cachePath = getCachePath(name);
try {
// Copy from the cache
fs.copyFileSync(cachePath, toPath);
fs.chmodSync(toPath, 0o755);
// Verify that the binary is the correct version
validateBinaryVersion(toPath);
// Mark the cache entry as used for LRU
const now = new Date();
fs.utimesSync(cachePath, now, now);
return;
} catch {}
// Next, try to install using npm. This should handle various tricky cases
// such as environments where requests to npmjs.org will hang (in which case
// there is probably a proxy and/or a custom registry configured instead).
let buffer: Buffer | undefined;
let didFail = false;
try {
buffer = installUsingNPM(name, fromPath);
} catch (err) {
didFail = true;
console.error(`Trying to install "${name}" using npm`);
console.error(
`Failed to install "${name}" using npm: ${(err && err.message) || err}`
);
}
// If that fails, the user could have npm configured incorrectly or could not
// have npm installed. Try downloading directly from npm as a last resort.
if (!buffer) {
const url = `https://registry.npmjs.org/${name}/-/${name}-${version}.tgz`;
console.error(`Trying to download ${JSON.stringify(url)}`);
try {
buffer = extractFileFromTarGzip(await fetch(url), fromPath);
} catch (err) {
console.error(
`Failed to download ${JSON.stringify(url)}: ${
(err && err.message) || err
}`
);
}
}
// Give up if none of that worked
if (!buffer) {
console.error(`Install unsuccessful`);
process.exit(1);
}
// Write out the binary executable that was extracted from the package
fs.writeFileSync(toPath, buffer, { mode: 0o755 });
// Verify that the binary is the correct version
try {
validateBinaryVersion(toPath);
} catch (err) {
console.error(
`The version of the downloaded binary is incorrect: ${
(err && err.message) || err
}`
);
console.error(`Install unsuccessful`);
process.exit(1);
}
// Also try to cache the file to speed up future installs
try {
fs.mkdirSync(path.dirname(cachePath), {
recursive: true,
mode: 0o700, // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
});
fs.copyFileSync(toPath, cachePath);
cleanCacheLRU(cachePath);
} catch {}
if (didFail) console.error(`Install successful`);
}
function validateBinaryVersion(binaryPath: string): void {
const stdout = child_process
.execFileSync(binaryPath, ["--version"])
.toString()
.trim();
if (stdout !== version) {
throw new Error(
`Expected ${JSON.stringify(version)} but got ${JSON.stringify(stdout)}`
);
}
}
function getCachePath(name: string): string {
const home = os.homedir();
const common = ["bun", "bin", `${name}@${version}`];
if (process.platform === "darwin")
return path.join(home, "Library", "Caches", ...common);
if (process.platform === "win32")
return path.join(home, "AppData", "Local", "Cache", ...common);
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
const XDG_CACHE_HOME = process.env.XDG_CACHE_HOME;
if (
process.platform === "linux" &&
XDG_CACHE_HOME &&
path.isAbsolute(XDG_CACHE_HOME)
)
return path.join(XDG_CACHE_HOME, ...common);
return path.join(home, ".cache", ...common);
}
function cleanCacheLRU(fileToKeep: string): void {
// Gather all entries in the cache
const dir = path.dirname(fileToKeep);
const entries: { path: string; mtime: Date }[] = [];
for (const entry of fs.readdirSync(dir)) {
const entryPath = path.join(dir, entry);
try {
const stats = fs.statSync(entryPath);
entries.push({ path: entryPath, mtime: stats.mtime });
} catch {}
}
// Only keep the most recent entries
entries.sort((a, b) => +b.mtime - +a.mtime);
for (const entry of entries.slice(5)) {
try {
fs.unlinkSync(entry.path);
} catch {}
}
}
function fetch(url: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
https
.get(url, (res) => {
if (
(res.statusCode === 301 || res.statusCode === 302) &&
res.headers.location
)
return fetch(res.headers.location).then(resolve, reject);
if (res.statusCode !== 200)
return reject(new Error(`Server responded with ${res.statusCode}`));
let chunks: Buffer[] = [];
res.on("data", (chunk) => chunks.push(chunk));
res.on("end", () => resolve(Buffer.concat(chunks)));
})
.on("error", reject);
});
}
function extractFileFromTarGzip(buffer: Buffer, file: string): Buffer {
try {
buffer = zlib.unzipSync(buffer);
} catch (err) {
throw new Error(
`Invalid gzip data in archive: ${(err && err.message) || err}`
);
}
let str = (i: number, n: number) =>
String.fromCharCode(...buffer.subarray(i, i + n)).replace(/\0.*$/, "");
let offset = 0;
file = `package/${file}`;
while (offset < buffer.length) {
let name = str(offset, 100);
let size = parseInt(str(offset + 124, 12), 8);
offset += 512;
if (!isNaN(size)) {
if (name === file) return buffer.subarray(offset, offset + size);
offset += (size + 511) & ~511;
}
}
throw new Error(`Could not find ${JSON.stringify(file)} in archive`);
}
function installUsingNPM(name: string, file: string): Buffer {
const installDir = path.join(
os.tmpdir(),
"bun-cli-" + Math.random().toString(36).slice(2)
);
fs.mkdirSync(installDir, { recursive: true });
fs.writeFileSync(path.join(installDir, "package.json"), "{}");
// Erase "npm_config_global" so that "npm install --global bun" works.
// Otherwise this nested "npm install" will also be global, and the install
// will deadlock waiting for the global installation lock.
const env = { ...process.env, npm_config_global: undefined };
child_process.execSync(
`npm install --loglevel=error --prefer-offline --no-audit --progress=false ${name}@${version}`,
{ cwd: installDir, stdio: "pipe", env }
);
const buffer = fs.readFileSync(
path.join(installDir, "node_modules", name, file)
);
try {
removeRecursive(installDir);
} catch (e) {
// Removing a file or directory can randomly break on Windows, returning
// EBUSY for an arbitrary length of time. I think this happens when some
// other program has that file or directory open (e.g. an anti-virus
// program). This is fine on Unix because the OS just unlinks the entry
// but keeps the reference around until it's unused. In this case we just
// ignore errors because this directory is in a temporary directory, so in
// theory it should get cleaned up eventually anyway.
}
return buffer;
}
function removeRecursive(dir: string): void {
for (const entry of fs.readdirSync(dir)) {
const entryPath = path.join(dir, entry);
let stats;
try {
stats = fs.lstatSync(entryPath);
} catch (e) {
continue; // Guard against https://github.com/nodejs/node/issues/4760
}
if (stats.isDirectory()) removeRecursive(entryPath);
else fs.unlinkSync(entryPath);
}
fs.rmdirSync(dir);
}
function isYarnBerryOrNewer(): boolean {
const { npm_config_user_agent } = process.env;
if (npm_config_user_agent) {
const match = npm_config_user_agent.match(/yarn\/(\d+)/);
if (match && match[1]) {
return parseInt(match[1], 10) >= 2;
}
}
return false;
}
function installDirectly(name: string) {
if (process.env.BUN_BINARY_PATH) {
fs.copyFileSync(process.env.BUN_BINARY_PATH, binPath);
validateBinaryVersion(binPath);
} else {
// Write to a temporary file, then move the file into place. This is an
// attempt to avoid problems with package managers like pnpm which will
// usually turn each file into a hard link. We don't want to mutate the
// hard-linked file which may be shared with other files.
const tempBinPath = binPath + "__";
installBinaryFromPackage(name, "bin/bun", tempBinPath)
.then(() => fs.renameSync(tempBinPath, binPath))
.catch((e) =>
setImmediate(() => {
throw e;
})
);
}
}
function installWithWrapper(
name: string,
fromPath: string,
toPath: string
): void {
fs.writeFileSync(
binPath,
`#!/usr/bin/env node
const path = require('path');
const bun_exe = path.join(__dirname, '..', ${JSON.stringify(toPath)});
const child_process = require('child_process');
console.warn("[Bun] Yarn 2's lack of binary support slows Bun down. Consider using a different package manager until https://github.com/yarnpkg/berry/issues/882 is fixed.\n");
const { status } = child_process.spawnSync(bun_exe, process.argv.slice(2), { stdio: 'inherit' });
process.exitCode = status === null ? 1 : status;
`
);
const absToPath = path.join(__dirname, toPath);
if (process.env.BUN_BINARY_PATH) {
fs.copyFileSync(process.env.BUN_BINARY_PATH, absToPath);
validateBinaryVersion(absToPath);
} else {
installBinaryFromPackage(name, fromPath, absToPath).catch((e) =>
setImmediate(() => {
throw e;
})
);
}
}
function installOnUnix(name: string): void {
// Yarn 2 is deliberately incompatible with binary modules because the
// developers of Yarn 2 don't think they should be used. See this thread for
// details: https://github.com/yarnpkg/berry/issues/882.
//
// We want to avoid slowing down bun for everyone just because of this
// decision by the Yarn 2 developers, so we explicitly detect if bun is
// being installed using Yarn 2 and install a compatability shim only for
// Yarn 2. Normal package managers can just run the binary directly for
// maximum speed.
if (isYarnBerryOrNewer()) {
installWithWrapper(name, "bin/bun", "bun");
} else {
installDirectly(name);
}
}
function installOnWindows(name: string): void {
installWithWrapper(name, "bun.exe", "bun.exe");
}
const platformKey = `${process.platform} ${os.arch()} ${os.endianness()}`;
const knownWindowsPackages: Record<string, string> = {
// "win32 arm64 LE": "bun-cli-windows-arm64",
// "win32 ia32 LE": "bun-cli-windows-32",
// "win32 x64 LE": "bun-cli-windows-64",
};
const knownUnixlikePackages: Record<string, string> = {
// "android arm64 LE": "bun-cli-android-arm64",
// "darwin arm64 LE": "bun-cli-darwin-arm64",
"darwin x64 LE": "bun-cli-darwin-x64",
// "freebsd arm64 LE": "bun-cli-freebsd-arm64",
// "freebsd x64 LE": "bun-cli-freebsd-64",
// "openbsd x64 LE": "bun-cli-openbsd-64",
// "linux arm LE": "bun-cli-linux-arm",
// "linux arm64 LE": "bun-cli-linux-arm64",
// "linux ia32 LE": "bun-cli-linux-32",
// "linux mips64el LE": "bun-cli-linux-mips64le",
// "linux ppc64 LE": "bun-cli-linux-ppc64le",
// "linux x64 LE": "bun-cli-linux-64",
// "sunos x64 LE": "bun-cli-sunos-64",
};
// Pick a package to install
if (platformKey in knownWindowsPackages) {
installOnWindows(knownWindowsPackages[platformKey]);
} else if (platformKey in knownUnixlikePackages) {
installOnUnix(knownUnixlikePackages[platformKey]);
} else {
console.error(`Unsupported platform: ${platformKey}`);
process.exit(1);
}

View File

@@ -150,7 +150,7 @@ pub const Arguments = struct {
pub const ParamType = clap.Param(clap.Help); pub const ParamType = clap.Param(clap.Help);
const params: [23]ParamType = brk: { const params: [24]ParamType = brk: {
@setEvalBranchQuota(9999); @setEvalBranchQuota(9999);
break :brk [_]ParamType{ break :brk [_]ParamType{
clap.parseParam("--use <STR> Choose a framework, e.g. \"--use next\". It checks first for a package named \"bun-framework-packagename\" and then \"packagename\".") catch unreachable, clap.parseParam("--use <STR> Choose a framework, e.g. \"--use next\". It checks first for a package named \"bun-framework-packagename\" and then \"packagename\".") catch unreachable,
@@ -166,6 +166,7 @@ pub const Arguments = struct {
clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable, clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable,
clap.parseParam("--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent") catch unreachable, clap.parseParam("--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent") catch unreachable,
clap.parseParam("--no-summary   Don't print a summary (when generating .bun") catch unreachable, clap.parseParam("--no-summary   Don't print a summary (when generating .bun") catch unreachable,
clap.parseParam("--version   Print version and exit") catch unreachable,
clap.parseParam("--origin <STR> Rewrite import paths to start with --origin. Default: \"/\"") catch unreachable, clap.parseParam("--origin <STR> Rewrite import paths to start with --origin. Default: \"/\"") catch unreachable,
clap.parseParam("--platform <STR> \"browser\" or \"node\". Defaults to \"browser\"") catch unreachable, clap.parseParam("--platform <STR> \"browser\" or \"node\". Defaults to \"browser\"") catch unreachable,
// clap.parseParam("--production   [not implemented] generate production code") catch unreachable, // clap.parseParam("--production   [not implemented] generate production code") catch unreachable,
@@ -184,6 +185,13 @@ pub const Arguments = struct {
}; };
}; };
fn printVersionAndExit() noreturn {
@setCold(true);
Output.writer().writeAll(Global.package_json_version) catch {};
Output.flush();
std.os.exit(0);
}
pub fn parse(allocator: *std.mem.Allocator, comptime cmd: Command.Tag) !Api.TransformOptions { pub fn parse(allocator: *std.mem.Allocator, comptime cmd: Command.Tag) !Api.TransformOptions {
var diag = clap.Diagnostic{}; var diag = clap.Diagnostic{};
@@ -193,6 +201,10 @@ pub const Arguments = struct {
return err; return err;
}; };
if (args.flag("--version")) {
printVersionAndExit();
}
var cwd_paths = [_]string{args.option("--cwd") orelse try std.process.getCwdAlloc(allocator)}; var cwd_paths = [_]string{args.option("--cwd") orelse try std.process.getCwdAlloc(allocator)};
var cwd = try std.fs.path.resolve(allocator, &cwd_paths); var cwd = try std.fs.path.resolve(allocator, &cwd_paths);
@@ -252,7 +264,7 @@ pub const Arguments = struct {
))) { ))) {
entry_points = entry_points[1..]; entry_points = entry_points[1..];
} }
}, },
.DevCommand => { .DevCommand => {
if (entry_points.len > 0 and (strings.eqlComptime( if (entry_points.len > 0 and (strings.eqlComptime(
entry_points[0], entry_points[0],

View File

@@ -385,6 +385,10 @@ pub const Output = struct {
pub const Global = struct { pub const Global = struct {
pub const build_id = std.fmt.parseInt(u64, std.mem.trim(u8, @embedFile("../build-id"), "\n \r\t"), 10) catch unreachable; pub const build_id = std.fmt.parseInt(u64, std.mem.trim(u8, @embedFile("../build-id"), "\n \r\t"), 10) catch unreachable;
pub const package_json_version = if (isDebug)
std.fmt.comptimePrint("0.0.0-{d}_debug", .{build_id})
else
std.fmt.comptimePrint("0.0.0-{d}", .{build_id});
pub fn panic(comptime fmt: string, args: anytype) noreturn { pub fn panic(comptime fmt: string, args: anytype) noreturn {
@setCold(true); @setCold(true);

View File

@@ -2538,29 +2538,29 @@ pub const Server = struct {
if (std.mem.readIntNative(u32, &addr.ipv4.host.octets) == 0 or std.mem.readIntNative(u128, &addr.ipv6.host.octets) == 0) { if (std.mem.readIntNative(u32, &addr.ipv4.host.octets) == 0 or std.mem.readIntNative(u128, &addr.ipv6.host.octets) == 0) {
if (server.bundler.options.routes.single_page_app_routing) { if (server.bundler.options.routes.single_page_app_routing) {
Output.prettyError( Output.prettyError(
" Bun!! <d>build {d}<r>\n\n\n<d> Link:<r> <b><cyan>http://localhost:{d}<r>\n <d>./{s}/index.html<r> \n\n\n", " Bun!! <d>v{s}<r>\n\n\n Link:<r> <b><cyan>http://localhost:{d}<r>\n <d>./{s}/index.html<r> \n\n\n",
.{ .{
Global.build_id, Global.package_json_version,
addr.ipv4.port, addr.ipv4.port,
resolve_path.relative(server.bundler.fs.top_level_dir, server.bundler.options.routes.static_dir), resolve_path.relative(server.bundler.fs.top_level_dir, server.bundler.options.routes.static_dir),
}, },
); );
} else { } else {
Output.prettyError(" Bun!! <d>build {d}<r>\n\n\n<d> Link:<r> <b><cyan>http://localhost:{d}<r>\n\n\n", .{ Output.prettyError(" Bun!! <d>v{s}<r>\n\n\n<d> Link:<r> <b><cyan>http://localhost:{d}<r>\n\n\n", .{
Global.build_id, Global.package_json_version,
addr.ipv4.port, addr.ipv4.port,
}); });
} }
} else { } else {
if (server.bundler.options.routes.single_page_app_routing) { if (server.bundler.options.routes.single_page_app_routing) {
Output.prettyError(" Bun!! <d>build {d}<r>\n\n\n<d> Link:<r> <b><cyan>http://{s}<r>\n <d>./{s}/index.html<r> \n\n\n", .{ Output.prettyError(" Bun!! <d>v{s}<r>\n\n\n<d> Link:<r> <b><cyan>http://{s}<r>\n <d>./{s}/index.html<r> \n\n\n", .{
Global.build_id, Global.package_json_version,
addr, addr,
resolve_path.relative(server.bundler.fs.top_level_dir, server.bundler.options.routes.static_dir), resolve_path.relative(server.bundler.fs.top_level_dir, server.bundler.options.routes.static_dir),
}); });
} else { } else {
Output.prettyError(" Bun!! <d>build {d}\n\n\n<d> Link:<r> <b><cyan>http://{s}<r>\n\n\n", .{ Output.prettyError(" Bun!! <d>v{s}\n\n\n<d> Link:<r> <b><cyan>http://{s}<r>\n\n\n", .{
Global.build_id, Global.package_json_version,
addr, addr,
}); });
} }

View File

@@ -6,20 +6,23 @@ const resolve_path = @import("./resolver/resolve_path.zig");
const Fs = @import("./fs.zig"); const Fs = @import("./fs.zig");
const Schema = @import("./api/schema.zig"); const Schema = @import("./api/schema.zig");
// packages/bun-cli-*/bin/bun
const BUN_ROOT = "../../../";
const Api = Schema.Api; const Api = Schema.Api;
pub const ErrorCSS = struct { pub const ErrorCSS = struct {
const ErrorCSSPath = "../packages/bun-error/dist/bun-error.css"; const ErrorCSSPath = "packages/bun-error/dist/bun-error.css";
const ErrorCSSPathDev = "../packages/bun-error/bun-error.css"; const ErrorCSSPathDev = "packages/bun-error/bun-error.css";
pub const ProdSourceContent = @embedFile(ErrorCSSPath); pub const ProdSourceContent = @embedFile("../" ++ ErrorCSSPath);
pub fn sourceContent() string { pub fn sourceContent() string {
if (comptime isDebug) { if (comptime isDebug) {
var env = std.process.getEnvMap(default_allocator) catch unreachable; var env = std.process.getEnvMap(default_allocator) catch unreachable;
var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var dirname = std.fs.selfExeDirPath(&out_buffer) catch unreachable; var dirname = std.fs.selfExeDirPath(&out_buffer) catch unreachable;
var paths = [_]string{ dirname, "../../", ErrorCSSPathDev }; var paths = [_]string{ dirname, BUN_ROOT, ErrorCSSPathDev };
const file = std.fs.cwd().openFile( const file = std.fs.cwd().openFile(
resolve_path.joinAbsString(dirname, std.mem.span(&paths), .auto), resolve_path.joinAbsString(dirname, std.mem.span(&paths), .auto),
.{ .{
@@ -35,16 +38,16 @@ pub const ErrorCSS = struct {
}; };
pub const ErrorJS = struct { pub const ErrorJS = struct {
const ErrorJSPath = "../packages/bun-error/dist/index.js"; const ErrorJSPath = "packages/bun-error/dist/index.js";
pub const ProdSourceContent = @embedFile(ErrorJSPath); pub const ProdSourceContent = @embedFile("../" ++ ErrorJSPath);
pub fn sourceContent() string { pub fn sourceContent() string {
if (comptime isDebug) { if (comptime isDebug) {
var env = std.process.getEnvMap(default_allocator) catch unreachable; var env = std.process.getEnvMap(default_allocator) catch unreachable;
var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; var out_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var dirname = std.fs.selfExeDirPath(&out_buffer) catch unreachable; var dirname = std.fs.selfExeDirPath(&out_buffer) catch unreachable;
var paths = [_]string{ dirname, "../../", ErrorJSPath }; var paths = [_]string{ dirname, BUN_ROOT, ErrorJSPath };
const file = std.fs.cwd().openFile( const file = std.fs.cwd().openFile(
resolve_path.joinAbsString(dirname, std.mem.span(&paths), .auto), resolve_path.joinAbsString(dirname, std.mem.span(&paths), .auto),
.{ .{