diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | bench/install/README.md | 26 | ||||
-rwxr-xr-x | packages/bun-lambda/runtime.ts | 6 | ||||
-rw-r--r-- | src/bun.js/api/bun.zig | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBuffer.cpp | 1 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 2 | ||||
-rw-r--r-- | src/bundler.zig | 6 | ||||
-rw-r--r-- | src/cli/run_command.zig | 4 | ||||
-rw-r--r-- | src/env_loader.zig | 89 | ||||
-rw-r--r-- | src/install/install.zig | 2 | ||||
-rw-r--r-- | src/install/lockfile.zig | 2 | ||||
-rw-r--r-- | src/io/io_darwin.zig | 6 | ||||
-rw-r--r-- | src/resolver/resolve_path.zig | 4 | ||||
-rw-r--r-- | test/cli/run/env.test.ts | 43 | ||||
-rw-r--r-- | test/js/bun/io/bun-write.test.js | 9 | ||||
-rw-r--r-- | test/js/node/buffer.test.js | 13 | ||||
-rw-r--r-- | test/js/node/path/path.test.js | 13 |
17 files changed, 182 insertions, 48 deletions
@@ -1880,7 +1880,7 @@ PACKAGE_MAP = --pkg-begin async_io $(BUN_DIR)/src/io/io_darwin.zig --pkg-begin b .PHONY: base64 base64: - cd $(BUN_DEPS_DIR)/base64 && make clean && cmake $(CMAKE_FLAGS) . && make + cd $(BUN_DEPS_DIR)/base64 && make clean && rm -rf CMakeCache.txt CMakeFiles && cmake $(CMAKE_FLAGS) . && make cp $(BUN_DEPS_DIR)/base64/libbase64.a $(BUN_DEPS_OUT_DIR)/libbase64.a .PHONY: cold-jsc-start diff --git a/bench/install/README.md b/bench/install/README.md index e9b617995..42739a1aa 100644 --- a/bench/install/README.md +++ b/bench/install/README.md @@ -1,11 +1,33 @@ # `install` benchmark -Requires [`hyperfine`](https://github.com/sharkdp/hyperfine) +Requires [`hyperfine`](https://github.com/sharkdp/hyperfine). The goal of this benchmark is to compare installation performance of Bun with other package managers _when caches are hot_. -``` +### With lockfile, online mode + +To run the benchmark with the standard "install" command for each package manager: + +```sh $ hyperfine --prepare 'rm -rf node_modules' --warmup 1 --runs 3 'bun install' 'pnpm install' 'yarn' 'npm install' ``` +### With lockfile, offline mode + +Even though all packages are cached, some tools may hit the npm API during the version resolution step. (This is not the same as re-downloading a package.) To entirely avoid network calls, the other package managers require `--prefer-offline/--offline` flag. To run the benchmark using "offline" mode: + +```sh +$ hyperfine --prepare 'rm -rf node_modules' --runs 1 'bun install' 'pnpm install --prefer-offline' 'yarn --offline' 'npm install --prefer-offline' +``` + +### Without lockfile, offline mode + +To run the benchmark with offline mode but without lockfiles: + +```sh +$ hyperfine --prepare 'rm -rf node_modules' --warmup 1 'rm bun.lockb && bun install' 'rm pnpm-lock.yaml && pnpm install --prefer-offline' 'rm yarn.lock && yarn --offline' 'rm package-lock.json && npm install --prefer-offline' +``` + +## + To check that the app is working as expected: ``` diff --git a/packages/bun-lambda/runtime.ts b/packages/bun-lambda/runtime.ts index 4b38b6dfa..b4e08944d 100755 --- a/packages/bun-lambda/runtime.ts +++ b/packages/bun-lambda/runtime.ts @@ -22,11 +22,11 @@ function log(level: string, ...args: any[]): void { if (!args.length) { return; } - const message = Bun.inspect(...args).replace(/\n/g, "\r"); + const messages = args.map(arg => Bun.inspect(arg).replace(/\n/g, "\r")); if (requestId === undefined) { - logger(level, message); + logger(level, ...messages); } else { - logger(level, `RequestId: ${requestId}`, message); + logger(level, `RequestId: ${requestId}`, ...messages); } } diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 36e52821f..966c82d38 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -4431,7 +4431,7 @@ pub const EnvironmentVariables = struct { var vm = globalObject.bunVM(); var sliced = name.toSlice(vm.allocator); defer sliced.deinit(); - const value = vm.bundler.env.map.map.get(sliced.slice()) orelse return null; + const value = vm.bundler.env.map.get(sliced.slice()) orelse return null; return ZigString.initUTF8(value); } }; diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index ad901b0e4..9227e47b3 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -351,6 +351,7 @@ static EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalObject, case WebCore::BufferEncodingType::utf8: case WebCore::BufferEncodingType::base64: case WebCore::BufferEncodingType::base64url: + case WebCore::BufferEncodingType::hex: case WebCore::BufferEncodingType::ascii: case WebCore::BufferEncodingType::latin1: { result = Bun__encoding__constructFromUTF16(lexicalGlobalObject, view.characters16(), view.length(), static_cast<uint8_t>(encoding)); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 0d73c31c5..752fcb3a4 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -670,7 +670,7 @@ pub const VirtualMachine = struct { } if (map.map.fetchSwapRemove("BUN_INTERNAL_IPC_FD")) |kv| { - if (std.fmt.parseInt(i32, kv.value, 10) catch null) |fd| { + if (std.fmt.parseInt(i32, kv.value.value, 10) catch null) |fd| { this.initIPCInstance(fd); } else { Output.printErrorln("Failed to parse BUN_INTERNAL_IPC_FD", .{}); diff --git a/src/bundler.zig b/src/bundler.zig index 66443d165..76fbc02ee 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -472,11 +472,11 @@ pub const Bundler = struct { } if (!has_production_env and this.options.isTest()) { - try this.env.load(&this.fs.fs, dir, .@"test"); + try this.env.load(dir, .@"test"); } else if (this.options.production) { - try this.env.load(&this.fs.fs, dir, .production); + try this.env.load(dir, .production); } else { - try this.env.load(&this.fs.fs, dir, .development); + try this.env.load(dir, .development); } }, .disable => { diff --git a/src/cli/run_command.zig b/src/cli/run_command.zig index f637bfe6c..dcb5312b1 100644 --- a/src/cli/run_command.zig +++ b/src/cli/run_command.zig @@ -525,9 +525,9 @@ pub const RunCommand = struct { if (root_dir_info.getEntries(0)) |dir| { // Run .env again if it exists in a parent dir if (this_bundler.options.production) { - this_bundler.env.load(&this_bundler.fs.fs, dir, .production) catch {}; + this_bundler.env.load(dir, .production) catch {}; } else { - this_bundler.env.load(&this_bundler.fs.fs, dir, .development) catch {}; + this_bundler.env.load(dir, .development) catch {}; } } } diff --git a/src/env_loader.zig b/src/env_loader.zig index 7c2c38a99..7f0be98a7 100644 --- a/src/env_loader.zig +++ b/src/env_loader.zig @@ -231,7 +231,7 @@ pub const Loader = struct { if (behavior == .prefix) { while (iter.next()) |entry| { - const value: string = entry.value_ptr.*; + const value: string = entry.value_ptr.value; if (strings.startsWith(entry.key_ptr.*, prefix)) { const key_str = std.fmt.allocPrint(key_allocator, "process.env.{s}", .{entry.key_ptr.*}) catch unreachable; @@ -282,12 +282,12 @@ pub const Loader = struct { } } else { while (iter.next()) |entry| { - const value: string = if (entry.value_ptr.*.len == 0) empty_string_value else entry.value_ptr.*; + const value: string = if (entry.value_ptr.value.len == 0) empty_string_value else entry.value_ptr.value; const key = std.fmt.allocPrint(key_allocator, "process.env.{s}", .{entry.key_ptr.*}) catch unreachable; e_strings[0] = js_ast.E.String{ - .data = if (entry.value_ptr.*.len > 0) - @as([*]u8, @ptrFromInt(@intFromPtr(entry.value_ptr.*.ptr)))[0..value.len] + .data = if (entry.value_ptr.value.len > 0) + @as([*]u8, @ptrFromInt(@intFromPtr(entry.value_ptr.value.ptr)))[0..value.len] else &[_]u8{}, }; @@ -370,7 +370,6 @@ pub const Loader = struct { // .env goes last pub fn load( this: *Loader, - fs: *Fs.FileSystem.RealFS, dir: *Fs.FileSystem.DirEntry, comptime suffix: enum { development, production, @"test" }, ) !void { @@ -380,19 +379,19 @@ pub const Loader = struct { switch (comptime suffix) { .development => { if (dir.hasComptimeQuery(".env.development.local")) { - try this.loadEnvFile(fs, dir_handle, ".env.development.local", false); + try this.loadEnvFile(dir_handle, ".env.development.local", false, true); Analytics.Features.dotenv = true; } }, .production => { if (dir.hasComptimeQuery(".env.production.local")) { - try this.loadEnvFile(fs, dir_handle, ".env.production.local", false); + try this.loadEnvFile(dir_handle, ".env.production.local", false, true); Analytics.Features.dotenv = true; } }, .@"test" => { if (dir.hasComptimeQuery(".env.test.local")) { - try this.loadEnvFile(fs, dir_handle, ".env.test.local", false); + try this.loadEnvFile(dir_handle, ".env.test.local", false, true); Analytics.Features.dotenv = true; } }, @@ -400,7 +399,7 @@ pub const Loader = struct { if (comptime suffix != .@"test") { if (dir.hasComptimeQuery(".env.local")) { - try this.loadEnvFile(fs, dir_handle, ".env.local", false); + try this.loadEnvFile(dir_handle, ".env.local", false, false); Analytics.Features.dotenv = true; } } @@ -408,26 +407,26 @@ pub const Loader = struct { switch (comptime suffix) { .development => { if (dir.hasComptimeQuery(".env.development")) { - try this.loadEnvFile(fs, dir_handle, ".env.development", false); + try this.loadEnvFile(dir_handle, ".env.development", false, true); Analytics.Features.dotenv = true; } }, .production => { if (dir.hasComptimeQuery(".env.production")) { - try this.loadEnvFile(fs, dir_handle, ".env.production", false); + try this.loadEnvFile(dir_handle, ".env.production", false, true); Analytics.Features.dotenv = true; } }, .@"test" => { if (dir.hasComptimeQuery(".env.test")) { - try this.loadEnvFile(fs, dir_handle, ".env.test", false); + try this.loadEnvFile(dir_handle, ".env.test", false, true); Analytics.Features.dotenv = true; } }, } if (dir.hasComptimeQuery(".env")) { - try this.loadEnvFile(fs, dir_handle, ".env", false); + try this.loadEnvFile(dir_handle, ".env", false, false); Analytics.Features.dotenv = true; } @@ -487,8 +486,13 @@ pub const Loader = struct { Output.flush(); } - pub fn loadEnvFile(this: *Loader, fs: *Fs.FileSystem.RealFS, dir: std.fs.Dir, comptime base: string, comptime override: bool) !void { - _ = fs; + pub fn loadEnvFile( + this: *Loader, + dir: std.fs.Dir, + comptime base: string, + comptime override: bool, + comptime conditional: bool, + ) !void { if (@field(this, base) != null) { return; } @@ -565,6 +569,7 @@ pub const Loader = struct { this.map, override, false, + conditional, ); @field(this, base) = source; @@ -789,6 +794,7 @@ const Parser = struct { map: *Map, comptime override: bool, comptime is_process: bool, + comptime conditional: bool, ) void { var count = map.map.count(); while (this.pos < this.src.len) { @@ -804,19 +810,25 @@ const Parser = struct { // https://github.com/oven-sh/bun/issues/1262 if (comptime !override) continue; } else { - allocator.free(entry.value_ptr.*); + allocator.free(entry.value_ptr.value); } } - entry.value_ptr.* = allocator.dupe(u8, value) catch unreachable; + entry.value_ptr.* = .{ + .value = allocator.dupe(u8, value) catch unreachable, + .conditional = conditional, + }; } if (comptime !is_process) { var it = map.iter(); while (it.next()) |entry| { if (count > 0) { count -= 1; - } else if (expandValue(map, entry.value_ptr.*)) |value| { - allocator.free(entry.value_ptr.*); - entry.value_ptr.* = allocator.dupe(u8, value) catch unreachable; + } else if (expandValue(map, entry.value_ptr.value)) |value| { + allocator.free(entry.value_ptr.value); + entry.value_ptr.* = .{ + .value = allocator.dupe(u8, value) catch unreachable, + .conditional = conditional, + }; } } } @@ -828,14 +840,19 @@ const Parser = struct { map: *Map, comptime override: bool, comptime is_process: bool, + comptime conditional: bool, ) void { var parser = Parser{ .src = source.contents }; - parser._parse(allocator, map, override, is_process); + parser._parse(allocator, map, override, is_process, conditional); } }; pub const Map = struct { - const HashTable = bun.StringArrayHashMap(string); + const HashTableValue = struct { + value: string, + conditional: bool, + }; + const HashTable = bun.StringArrayHashMap(HashTableValue); map: HashTable, @@ -848,10 +865,10 @@ pub const Map = struct { var it = env_map.iterator(); var i: usize = 0; while (it.next()) |pair| : (i += 1) { - const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.len + 1, 0); + const env_buf = try arena.allocSentinel(u8, pair.key_ptr.len + pair.value_ptr.value.len + 1, 0); bun.copy(u8, env_buf, pair.key_ptr.*); env_buf[pair.key_ptr.len] = '='; - bun.copy(u8, env_buf[pair.key_ptr.len + 1 ..], pair.value_ptr.*); + bun.copy(u8, env_buf[pair.key_ptr.len + 1 ..], pair.value_ptr.value); envp_buf[i] = env_buf.ptr; } std.debug.assert(i == envp_count); @@ -864,7 +881,10 @@ pub const Map = struct { var iter_ = this.map.iterator(); while (iter_.next()) |entry| { - try env_map.putMove(bun.constStrToU8(entry.key_ptr.*), bun.constStrToU8(entry.value_ptr.*)); + // Allow var from .env.development or .env.production to be loaded again. Also don't clone empty vars. + if (!entry.value_ptr.conditional and entry.value_ptr.value.len > 0) { + try env_map.putMove(bun.constStrToU8(entry.key_ptr.*), bun.constStrToU8(entry.value_ptr.value)); + } } return env_map; @@ -879,7 +899,10 @@ pub const Map = struct { } pub inline fn put(this: *Map, key: string, value: string) !void { - try this.map.put(key, value); + try this.map.put(key, .{ + .value = value, + .conditional = false, + }); } pub fn jsonStringify(self: *const @This(), writer: anytype) !void { @@ -907,22 +930,28 @@ pub const Map = struct { this: *const Map, key: string, ) ?string { - return this.map.get(key); + return if (this.map.get(key)) |entry| entry.value else null; } pub fn get_( this: *const Map, key: string, ) ?string { - return this.map.get(key); + return if (this.map.get(key)) |entry| entry.value else null; } pub inline fn putDefault(this: *Map, key: string, value: string) !void { - _ = try this.map.getOrPutValue(key, value); + _ = try this.map.getOrPutValue(key, .{ + .value = value, + .conditional = false, + }); } pub inline fn getOrPut(this: *Map, key: string, value: string) !void { - _ = try this.map.getOrPutValue(key, value); + _ = try this.map.getOrPutValue(key, .{ + .value = value, + .conditional = false, + }); } }; diff --git a/src/install/install.zig b/src/install/install.zig index e8b95b820..bf6422cc7 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -5243,7 +5243,7 @@ pub const PackageManager = struct { }; env.loadProcess(); - try env.load(&fs.fs, entries_option.entries, .production); + try env.load(entries_option.entries, .production); if (env.map.get("BUN_INSTALL_VERBOSE") != null) { PackageManager.verbose_install = true; diff --git a/src/install/lockfile.zig b/src/install/lockfile.zig index 0b5e0d7bc..a9bee2d27 100644 --- a/src/install/lockfile.zig +++ b/src/install/lockfile.zig @@ -983,7 +983,7 @@ pub const Printer = struct { }; env_loader.loadProcess(); - try env_loader.load(&fs.fs, entries_option.entries, .production); + try env_loader.load(entries_option.entries, .production); var log = logger.Log.init(allocator); try options.load( allocator, diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index 226a4d284..cb2d15afb 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -1350,6 +1350,12 @@ pub fn read( struct { fn doOperation(op: anytype) ReadError!usize { while (true) { + if (op.positional) { + const rc = os.system.lseek(op.fd, @intCast(op.offset), 0); + if (rc == -1) { + return error.Unseekable; + } + } const rc = os.system.read( op.fd, op.buf, diff --git a/src/resolver/resolve_path.zig b/src/resolver/resolve_path.zig index 4800d7d90..f74211709 100644 --- a/src/resolver/resolve_path.zig +++ b/src/resolver/resolve_path.zig @@ -471,7 +471,7 @@ pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Pla Fs.FileSystem.instance.top_level_dir, &relative_from_buf, &[_][]const u8{ - normalizeStringBuf(from, relative_from_buf[1..], false, platform, true), + normalizeStringBuf(from, relative_from_buf[1..], true, platform, true), }, platform, ); @@ -484,7 +484,7 @@ pub fn relativePlatform(from: []const u8, to: []const u8, comptime platform: Pla Fs.FileSystem.instance.top_level_dir, &relative_to_buf, &[_][]const u8{ - normalizeStringBuf(to, relative_to_buf[1..], false, platform, true), + normalizeStringBuf(to, relative_to_buf[1..], true, platform, true), }, platform, ); diff --git a/test/cli/run/env.test.ts b/test/cli/run/env.test.ts index 159793e27..3ed300477 100644 --- a/test/cli/run/env.test.ts +++ b/test/cli/run/env.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { bunRun, bunTest, tempDirWithFiles, bunExe, bunEnv } from "harness"; +import { bunRun, bunRunAsScript, bunTest, tempDirWithFiles, bunExe, bunEnv } from "harness"; import path from "path"; function bunRunWithoutTrim(file: string, env?: Record<string, string>) { @@ -255,6 +255,47 @@ test(".env process variables no comments", () => { expect(stdout).toBe('test#1 "test#2"'); }); +describe("package scripts load from .env.production and .env.development", () => { + test("NODE_ENV=production", () => { + const dir = tempDirWithFiles("dotenv-package-script-prod", { + "index.ts": "console.log(process.env.TEST);", + "package.json": ` + { + "name": "foo", + "version": "2.0", + "scripts": { + "test": "NODE_ENV=production ${bunExe()} run index.ts", + } + } + `, + ".env.production": "TEST=prod", + ".env.development": "TEST=dev", + }); + + const { stdout } = bunRunAsScript(dir, "test"); + expect(stdout).toBe("prod"); + }); + test("NODE_ENV=development", () => { + const dir = tempDirWithFiles("dotenv-package-script-prod", { + "index.ts": "console.log(process.env.TEST);", + "package.json": ` + { + "name": "foo", + "version": "2.0", + "scripts": { + "test": "NODE_ENV=development ${bunExe()} run index.ts", + } + } + `, + ".env.production": "TEST=prod", + ".env.development": "TEST=dev", + }); + + const { stdout } = bunRunAsScript(dir, "test"); + expect(stdout).toBe("dev"); + }); +}); + test(".env escaped dollar sign", () => { const dir = tempDirWithFiles("dotenv-dollar", { ".env": "FOO=foo\nBAR=\\$FOO", diff --git a/test/js/bun/io/bun-write.test.js b/test/js/bun/io/bun-write.test.js index b67df9405..f435d2ceb 100644 --- a/test/js/bun/io/bun-write.test.js +++ b/test/js/bun/io/bun-write.test.js @@ -291,6 +291,15 @@ it.skip("Bun.write('output.html', HTMLRewriter.transform(Bun.file)))", async don done(); }); +it("offset should work #4963", async () => { + const filename = tmpdir() + "/bun.test.offset.txt"; + await Bun.write(filename, "contents"); + const file = Bun.file(filename); + const slice = file.slice(2, file.size); + const contents = await slice.text(); + expect(contents).toBe("ntents"); +}); + it("#2674", async () => { const file = path.join(import.meta.dir, "big-stdout.js"); diff --git a/test/js/node/buffer.test.js b/test/js/node/buffer.test.js index 7c3d16536..afc9cdee8 100644 --- a/test/js/node/buffer.test.js +++ b/test/js/node/buffer.test.js @@ -2572,3 +2572,16 @@ it("construct buffer from UTF16, issue #3914", () => { const buf = Buffer.from(str, "latin1"); expect(buf).toStrictEqual(raw); }); + +it("construct buffer from hex, issue #4919", () => { + const data = "测试63e9f6c4b04fa8c80f3fb0ee"; + + const slice1 = data.substring(0, 2); + const slice2 = data.substring(2); + + const buf1 = Buffer.from(slice1, "hex"); + const buf2 = Buffer.from(slice2, "hex"); + + expect(buf1).toStrictEqual(Buffer.from([])); + expect(buf2).toStrictEqual(Buffer.from([0x63, 0xe9, 0xf6, 0xc4, 0xb0, 0x4f, 0xa8, 0xc8, 0x0f, 0x3f, 0xb0, 0xee])); +}); diff --git a/test/js/node/path/path.test.js b/test/js/node/path/path.test.js index 3c8a04d72..5865d6182 100644 --- a/test/js/node/path/path.test.js +++ b/test/js/node/path/path.test.js @@ -415,6 +415,9 @@ it("path.join", () => { it("path.relative", () => { const failures = []; + const cwd = process.cwd(); + const cwdParent = path.dirname(cwd); + const parentIsRoot = cwdParent == "/"; const relativeTests = [ // [ @@ -477,6 +480,16 @@ it("path.relative", () => { ["/webp4ck-hot-middleware", "/webpack/buildin/module.js", "../webpack/buildin/module.js"], ["/webpack-hot-middleware", "/webp4ck/buildin/module.js", "../webp4ck/buildin/module.js"], ["/var/webpack-hot-middleware", "/var/webpack/buildin/module.js", "../webpack/buildin/module.js"], + ["/app/node_modules/pkg", "../static", `../../..${parentIsRoot ? "" : cwdParent}/static`], + ["/app/node_modules/pkg", "../../static", `../../..${parentIsRoot ? "" : path.dirname(cwdParent)}/static`], + ["/app", "../static", `..${parentIsRoot ? "" : cwdParent}/static`], + ["/app", "../".repeat(64) + "static", "../static"], + [".", "../static", cwd == "/" ? "static" : "../static"], + ["/", "../static", parentIsRoot ? "static" : `${cwdParent}/static`.slice(1)], + ["../", "../", ""], + ["../", "../../", parentIsRoot ? "" : ".."], + ["../../", "../", parentIsRoot ? "" : path.basename(cwdParent)], + ["../../", "../../", ""], ], ], ]; |