mirror of
https://github.com/oven-sh/bun
synced 2026-03-01 21:11:03 +01:00
Compare commits
94 Commits
snoglobe/e
...
nektro-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99b57b09e9 | ||
|
|
3cc7649a68 | ||
|
|
fd8a61898e | ||
|
|
209ea3d1f9 | ||
|
|
ef509c5b3e | ||
|
|
82464d0d39 | ||
|
|
9d5c6f911a | ||
|
|
a3bce4ae4c | ||
|
|
59a1670a9f | ||
|
|
ba33af4205 | ||
|
|
b236911056 | ||
|
|
3920288701 | ||
|
|
f8f1a046bb | ||
|
|
1ea6b09d09 | ||
|
|
4c1b1c5310 | ||
|
|
2f26aba2ce | ||
|
|
6ed2e1fe02 | ||
|
|
48aef89bb7 | ||
|
|
fdc9ae0869 | ||
|
|
2298d9959a | ||
|
|
98c81af971 | ||
|
|
46506db1e8 | ||
|
|
e6679b32c9 | ||
|
|
70003f99db | ||
|
|
d7d347f5e5 | ||
|
|
ce26cc1881 | ||
|
|
7d45fbc83b | ||
|
|
f06cbc7d86 | ||
|
|
9a6005d1fc | ||
|
|
5763765b5b | ||
|
|
0664716e0f | ||
|
|
66520fba41 | ||
|
|
733460ff83 | ||
|
|
7e2fe73288 | ||
|
|
b1ca24c864 | ||
|
|
64b0410176 | ||
|
|
ee7b1ee1b6 | ||
|
|
fef6bf3d1d | ||
|
|
b35a3679bc | ||
|
|
404972458a | ||
|
|
50eb4f8e82 | ||
|
|
29cf8ee84c | ||
|
|
d2f0f2a4dd | ||
|
|
9d9d520d0b | ||
|
|
5bcdd0501f | ||
|
|
6de2fe0fbb | ||
|
|
898eba0cf9 | ||
|
|
a0302fded1 | ||
|
|
418c83e072 | ||
|
|
57b79151d3 | ||
|
|
65dcdff8cf | ||
|
|
4302a43374 | ||
|
|
d71846757c | ||
|
|
1916a68cfd | ||
|
|
b475a769eb | ||
|
|
3a8691fe65 | ||
|
|
e4ecd46bd6 | ||
|
|
d0cab7c108 | ||
|
|
8817c77124 | ||
|
|
868c694157 | ||
|
|
51a7a01c7e | ||
|
|
30c5f39d4c | ||
|
|
3438f9b5ab | ||
|
|
a2a8f95d41 | ||
|
|
709afb9c71 | ||
|
|
02db0e43df | ||
|
|
935146e9ab | ||
|
|
b67739b7db | ||
|
|
632bdee900 | ||
|
|
7f6394e761 | ||
|
|
1936940043 | ||
|
|
9168a64f61 | ||
|
|
53ed865927 | ||
|
|
65889dc803 | ||
|
|
1590631cf9 | ||
|
|
a6f15e4277 | ||
|
|
1fc547fdd9 | ||
|
|
49ee19cb41 | ||
|
|
40dcadf0da | ||
|
|
e8115529d3 | ||
|
|
7c4616b2a1 | ||
|
|
b8b8b26f85 | ||
|
|
714331909f | ||
|
|
b3e53e1eb5 | ||
|
|
5332a17551 | ||
|
|
203022e04c | ||
|
|
c0e84c9076 | ||
|
|
c861a6fcda | ||
|
|
f1a61ceb8e | ||
|
|
d2ff2cc1dc | ||
|
|
0b11991811 | ||
|
|
186456788e | ||
|
|
d6b815268d | ||
|
|
131bea538d |
2
.github/workflows/bun-linux-build.yml
vendored
2
.github/workflows/bun-linux-build.yml
vendored
@@ -253,6 +253,8 @@ jobs:
|
||||
# # Core filenames will be of the form executable.pid.timestamp:
|
||||
# sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern'
|
||||
|
||||
- id: verdaccio
|
||||
run: bun run verdaccio &
|
||||
- id: test
|
||||
name: Test (node runner)
|
||||
env:
|
||||
|
||||
2
.github/workflows/bun-mac-aarch64.yml
vendored
2
.github/workflows/bun-mac-aarch64.yml
vendored
@@ -422,6 +422,8 @@ jobs:
|
||||
bun install --verbose
|
||||
bun install --cwd=test --verbose
|
||||
bun install --cwd=packages/bun-internal-test --verbose
|
||||
- id: verdaccio
|
||||
run: bun run verdaccio &
|
||||
- id: test
|
||||
name: Test (node runner)
|
||||
env:
|
||||
|
||||
2
.github/workflows/bun-mac-x64-baseline.yml
vendored
2
.github/workflows/bun-mac-x64-baseline.yml
vendored
@@ -409,6 +409,8 @@ jobs:
|
||||
bun install --verbose
|
||||
bun install --cwd=test --verbose
|
||||
bun install --cwd=packages/bun-internal-test --verbose
|
||||
- id: verdaccio
|
||||
run: bun run verdaccio &
|
||||
- id: test
|
||||
name: Test (node runner)
|
||||
env:
|
||||
|
||||
2
.github/workflows/bun-mac-x64.yml
vendored
2
.github/workflows/bun-mac-x64.yml
vendored
@@ -406,6 +406,8 @@ jobs:
|
||||
bun install --verbose
|
||||
bun install --cwd=test --verbose
|
||||
bun install --cwd=packages/bun-internal-test --verbose
|
||||
- id: verdaccio
|
||||
run: bun run verdaccio &
|
||||
- id: test
|
||||
name: Test (node runner)
|
||||
env:
|
||||
|
||||
12
.github/workflows/bun-windows.yml
vendored
12
.github/workflows/bun-windows.yml
vendored
@@ -377,6 +377,7 @@ jobs:
|
||||
Build failed on Windows ${{ matrix.arch }}${{ matrix.cpu == 'nehalem' && ' Baseline' || '' }}
|
||||
|
||||
**[Build Output](https://github.com/oven-sh/bun/actions/runs/${{github.run_id}})** | [Commit](https://github.com/oven-sh/bun/commits/${{github.sha}})
|
||||
|
||||
windows-test:
|
||||
name: Test
|
||||
runs-on: windows-small
|
||||
@@ -422,13 +423,9 @@ jobs:
|
||||
packages: bash
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# bun install --verbose
|
||||
# bun install --cwd=test --verbose
|
||||
# bun install --cwd=packages/bun-internal-test --verbose
|
||||
|
||||
npm install
|
||||
cd test && npm install
|
||||
cd ../packages/bun-internal-test && npm install
|
||||
bun install --verbose
|
||||
bun install --cwd=test --verbose
|
||||
bun install --cwd=packages/bun-internal-test --verbose
|
||||
cd ../..
|
||||
- id: test
|
||||
name: Run tests
|
||||
@@ -441,6 +438,7 @@ jobs:
|
||||
BUN_PATH_BASE: ${{runner.temp}}
|
||||
BUN_PATH: release/${{env.tag}}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile/bun.exe
|
||||
run: |
|
||||
cd test && node ./node_modules/verdaccio/bin/verdaccio -c cli/install/registry/verdaccio.yaml &
|
||||
node packages/bun-internal-test/src/runner.node.mjs || true
|
||||
shell: bash
|
||||
- uses: sarisia/actions-status-discord@v1
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"lint:fix": "eslint './**/*.d.ts' --cache --fix",
|
||||
"test": "node packages/bun-internal-test/src/runner.node.mjs ./build/bun-debug",
|
||||
"test:release": "node packages/bun-internal-test/src/runner.node.mjs ./build-release/bun",
|
||||
"verdaccio": "cd test && bun run verdaccio -c cli/install/registry/verdaccio.yaml",
|
||||
"update-known-failures": "node packages/bun-internal-test/src/update-known-windows-failures.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,21 +277,18 @@ pub const StandaloneModuleGraph = struct {
|
||||
}.toClean;
|
||||
|
||||
const cloned_executable_fd: bun.FileDescriptor = brk: {
|
||||
var self_buf: [bun.MAX_PATH_BYTES + 1]u8 = undefined;
|
||||
const self_exe = std.fs.selfExePath(&self_buf) catch |err| {
|
||||
const self_exeZ = bun.selfExePath() catch |err| {
|
||||
Output.prettyErrorln("<r><red>error<r><d>:<r> failed to get self executable path: {s}", .{@errorName(err)});
|
||||
Global.exit(1);
|
||||
};
|
||||
self_buf[self_exe.len] = 0;
|
||||
const self_exeZ = self_buf[0..self_exe.len :0];
|
||||
|
||||
if (comptime Environment.isWindows) {
|
||||
// copy self and then open it for writing
|
||||
|
||||
var in_buf: bun.WPathBuffer = undefined;
|
||||
strings.copyU8IntoU16(&in_buf, self_exeZ);
|
||||
in_buf[self_exe.len] = 0;
|
||||
const in = in_buf[0..self_exe.len :0];
|
||||
in_buf[self_exeZ.len] = 0;
|
||||
const in = in_buf[0..self_exeZ.len :0];
|
||||
var out_buf: bun.WPathBuffer = undefined;
|
||||
strings.copyU8IntoU16(&out_buf, zname);
|
||||
out_buf[zname.len] = 0;
|
||||
@@ -733,10 +730,8 @@ pub const StandaloneModuleGraph = struct {
|
||||
.mac => {
|
||||
// Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
|
||||
// opened with no modification.
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
const self_exe_path = try std.fs.selfExePath(&buf);
|
||||
buf[self_exe_path.len] = 0;
|
||||
const file = try std.fs.openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{});
|
||||
const self_exe_path = try bun.selfExePath();
|
||||
const file = try std.fs.openFileAbsoluteZ(self_exe_path.ptr, .{});
|
||||
return bun.toFD(file.handle);
|
||||
},
|
||||
.windows => {
|
||||
|
||||
@@ -1261,9 +1261,10 @@ pub const Subprocess = struct {
|
||||
} else {
|
||||
subprocess.flags.has_stdin_destructor_called = false;
|
||||
subprocess.weak_file_sink_stdin_ptr = pipe;
|
||||
if (@intFromPtr(pipe.signal.ptr) == @intFromPtr(subprocess)) {
|
||||
if (pipe.signal.ptr == @as(?*anyopaque, @ptrCast(subprocess))) {
|
||||
pipe.signal.clear();
|
||||
}
|
||||
|
||||
return pipe.toJSWithDestructor(
|
||||
globalThis,
|
||||
JSC.WebCore.SinkDestructor.Ptr.init(subprocess),
|
||||
@@ -1283,12 +1284,11 @@ pub const Subprocess = struct {
|
||||
|
||||
return switch (this.*) {
|
||||
.pipe => |pipe| {
|
||||
if (pipe.signal.ptr == @as(*anyopaque, @ptrCast(this))) {
|
||||
if (pipe.signal.ptr == @as(?*anyopaque, @ptrCast(this))) {
|
||||
pipe.signal.clear();
|
||||
}
|
||||
|
||||
pipe.deref();
|
||||
|
||||
this.* = .{ .ignore = {} };
|
||||
},
|
||||
.buffer => {
|
||||
|
||||
@@ -5792,13 +5792,8 @@ pub const NodeFS = struct {
|
||||
const target: [:0]u8 = args.old_path.sliceZWithForceCopy(&this.sync_error_buf, true);
|
||||
// UV does not normalize slashes in symlink targets, but Node does
|
||||
// See https://github.com/oven-sh/bun/issues/8273
|
||||
//
|
||||
// TODO: investigate if simd can be easily used here
|
||||
for (target) |*c| {
|
||||
if (c.* == '/') {
|
||||
c.* = '\\';
|
||||
}
|
||||
}
|
||||
bun.path.posixToPlatformInPlace(u8, target);
|
||||
|
||||
return Syscall.symlinkUV(
|
||||
target,
|
||||
args.new_path.sliceZ(&to_buf),
|
||||
|
||||
@@ -4852,12 +4852,10 @@ pub const Process = struct {
|
||||
}
|
||||
|
||||
pub fn getExecPath(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
|
||||
var buf: bun.PathBuffer = undefined;
|
||||
const out = std.fs.selfExePath(&buf) catch {
|
||||
const out = bun.selfExePath() catch {
|
||||
// if for any reason we are unable to get the executable path, we just return argv[0]
|
||||
return getArgv0(globalObject);
|
||||
};
|
||||
|
||||
return JSC.ZigString.fromUTF8(out).toValueGC(globalObject);
|
||||
}
|
||||
|
||||
@@ -4935,7 +4933,7 @@ pub const Process = struct {
|
||||
bun.String.static("bun"),
|
||||
);
|
||||
} else {
|
||||
const exe_path = std.fs.selfExePathAlloc(allocator) catch null;
|
||||
const exe_path = bun.selfExePath() catch null;
|
||||
args_list.appendAssumeCapacity(
|
||||
if (exe_path) |str| bun.String.fromUTF8(str) else bun.String.static("bun"),
|
||||
);
|
||||
|
||||
23
src/bun.zig
23
src/bun.zig
@@ -1562,7 +1562,7 @@ pub fn reloadProcess(
|
||||
}
|
||||
|
||||
// we must clone selfExePath incase the argv[0] was not an absolute path (what appears in the terminal)
|
||||
const exec_path = (allocator.dupeZ(u8, std.fs.selfExePathAlloc(allocator) catch unreachable) catch unreachable).ptr;
|
||||
const exec_path = (bun.selfExePath() catch unreachable).ptr;
|
||||
|
||||
// we clone argv so that the memory address isn't the same as the libc one
|
||||
const newargv = @as([*:null]?[*:0]const u8, @ptrCast(dupe_argv.ptr));
|
||||
@@ -2033,9 +2033,9 @@ pub const posix = struct {
|
||||
|
||||
pub const win32 = struct {
|
||||
const w = std.os.windows;
|
||||
pub var STDOUT_FD: FileDescriptor = undefined;
|
||||
pub var STDERR_FD: FileDescriptor = undefined;
|
||||
pub var STDIN_FD: FileDescriptor = undefined;
|
||||
pub var STDOUT_FD = invalid_fd;
|
||||
pub var STDERR_FD = invalid_fd;
|
||||
pub var STDIN_FD = invalid_fd;
|
||||
|
||||
const watcherChildEnv: [:0]const u16 = strings.toUTF16LiteralZ("_BUN_WATCHER_CHILD");
|
||||
// magic exit code to indicate to the watcher manager that the child process should be re-spawned
|
||||
@@ -2816,6 +2816,21 @@ pub fn linuxKernelVersion() Semver.Version {
|
||||
return @import("./analytics.zig").GenerateHeader.GeneratePlatform.kernelVersion();
|
||||
}
|
||||
|
||||
pub fn selfExePath() ![:0]u8 {
|
||||
const memo = struct {
|
||||
var set = false;
|
||||
// this is lame; open issues to fix std soon
|
||||
var value: [4096]u8 = undefined;
|
||||
var len: usize = 0;
|
||||
};
|
||||
if (memo.set) return memo.value[0..memo.len :0];
|
||||
const init = try std.fs.selfExePath(&memo.value);
|
||||
memo.len = init.len;
|
||||
memo.value[memo.len] = 0;
|
||||
memo.set = true;
|
||||
return memo.value[0..memo.len :0];
|
||||
}
|
||||
|
||||
pub const exe_suffix = if (Environment.isWindows) ".exe" else "";
|
||||
|
||||
pub const spawnSync = @This().spawn.sync.spawn;
|
||||
|
||||
@@ -1218,11 +1218,11 @@ pub const Command = struct {
|
||||
};
|
||||
|
||||
pub fn isBunX(argv0: []const u8) bool {
|
||||
return strings.endsWithComptime(argv0, "bunx") or (Environment.isDebug and strings.endsWithComptime(argv0, "bunx-debug"));
|
||||
return strings.endsWithComptime(argv0, "bunx" ++ bun.exe_suffix);
|
||||
}
|
||||
|
||||
pub fn isNode(argv0: []const u8) bool {
|
||||
return strings.endsWithComptime(argv0, "node");
|
||||
return strings.endsWithComptime(argv0, "node" ++ bun.exe_suffix);
|
||||
}
|
||||
|
||||
pub fn which() Tag {
|
||||
@@ -1251,7 +1251,7 @@ pub const Command = struct {
|
||||
return .BunxCommand;
|
||||
}
|
||||
|
||||
if (isNode(without_exe)) {
|
||||
if (isNode(argv0)) {
|
||||
@import("./deps/zig-clap/clap/streaming.zig").warn_on_unrecognized_flag = false;
|
||||
pretend_to_be_node = true;
|
||||
return .RunAsNodeCommand;
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
const Command = @import("../cli.zig").Command;
|
||||
const bun = @import("root").bun;
|
||||
const PackageManager = @import("../install/install.zig").PackageManager;
|
||||
|
||||
pub const AddCommand = struct {
|
||||
pub fn exec(ctx: Command.Context) !void {
|
||||
try PackageManager.add(ctx);
|
||||
PackageManager.add(ctx) catch |err| switch (err) {
|
||||
error.InstallFailed,
|
||||
error.InvalidPackageJSON,
|
||||
=> {
|
||||
const log = &bun.CLI.Cli.log_;
|
||||
log.printForLogLevel(bun.Output.errorWriter()) catch {};
|
||||
bun.Global.exit(1);
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -423,18 +423,11 @@ pub const BunxCommand = struct {
|
||||
|
||||
debug("bunx_cache_dir: {s}", .{bunx_cache_dir});
|
||||
|
||||
const bin_extension = switch (Environment.os) {
|
||||
.windows => ".exe",
|
||||
.mac => "",
|
||||
.linux => "",
|
||||
.wasm => "",
|
||||
};
|
||||
|
||||
var absolute_in_cache_dir_buf: bun.PathBuffer = undefined;
|
||||
var absolute_in_cache_dir = std.fmt.bufPrint(
|
||||
&absolute_in_cache_dir_buf,
|
||||
bun.pathLiteral("{s}/node_modules/.bin/{s}{s}"),
|
||||
.{ bunx_cache_dir, initial_bin_name, bin_extension },
|
||||
.{ bunx_cache_dir, initial_bin_name, bun.exe_suffix },
|
||||
) catch return error.PathTooLong;
|
||||
|
||||
const passthrough = passthrough_list.items;
|
||||
@@ -524,7 +517,7 @@ pub const BunxCommand = struct {
|
||||
if (getBinName(&this_bundler, root_dir_fd, bunx_cache_dir, initial_bin_name)) |package_name_for_bin| {
|
||||
// if we check the bin name and its actually the same, we don't need to check $PATH here again
|
||||
if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) {
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bin_extension }) catch unreachable;
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable;
|
||||
|
||||
// Only use the system-installed version if there is no version specified
|
||||
if (update_request.version.literal.isEmpty()) {
|
||||
@@ -574,7 +567,7 @@ pub const BunxCommand = struct {
|
||||
}
|
||||
|
||||
var args = std.BoundedArray([]const u8, 7).fromSlice(&.{
|
||||
try std.fs.selfExePathAlloc(ctx.allocator),
|
||||
try bun.selfExePath(),
|
||||
"add",
|
||||
install_param,
|
||||
"--no-summary",
|
||||
@@ -646,7 +639,7 @@ pub const BunxCommand = struct {
|
||||
else => {},
|
||||
}
|
||||
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, bun.pathLiteral("{s}/node_modules/.bin/{s}{s}"), .{ bunx_cache_dir, initial_bin_name, bin_extension }) catch unreachable;
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, bun.pathLiteral("{s}/node_modules/.bin/{s}{s}"), .{ bunx_cache_dir, initial_bin_name, bun.exe_suffix }) catch unreachable;
|
||||
|
||||
// Similar to "npx":
|
||||
//
|
||||
@@ -675,7 +668,7 @@ pub const BunxCommand = struct {
|
||||
// 2. The "bin" is possibly not the same as the package name, so we load the package.json to figure out what "bin" to use
|
||||
if (getBinNameFromTempDirectory(&this_bundler, bunx_cache_dir, result_package_name)) |package_name_for_bin| {
|
||||
if (!strings.eqlLong(package_name_for_bin, initial_bin_name, true)) {
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bin_extension }) catch unreachable;
|
||||
absolute_in_cache_dir = std.fmt.bufPrint(&absolute_in_cache_dir_buf, "{s}/node_modules/.bin/{s}{s}", .{ bunx_cache_dir, package_name_for_bin, bun.exe_suffix }) catch unreachable;
|
||||
|
||||
if (bun.which(
|
||||
&path_buf,
|
||||
|
||||
@@ -1400,7 +1400,7 @@ pub const CreateCommand = struct {
|
||||
if (!create_options.skip_install) {
|
||||
npm_client_ = NPMClient{
|
||||
.tag = .bun,
|
||||
.bin = try std.fs.selfExePathAlloc(ctx.allocator),
|
||||
.bin = try bun.selfExePath(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ pub const InitCommand = struct {
|
||||
if (exists("package.json")) {
|
||||
var process = std.ChildProcess.init(
|
||||
&.{
|
||||
try std.fs.selfExePathAlloc(alloc),
|
||||
try bun.selfExePath(),
|
||||
"install",
|
||||
},
|
||||
alloc,
|
||||
|
||||
@@ -46,7 +46,7 @@ pub const InstallCompletionsCommand = struct {
|
||||
|
||||
const bunx_name = if (Environment.isDebug) "bunx-debug" else "bunx";
|
||||
|
||||
fn installBunxSymlinkPosix(allocator: std.mem.Allocator, cwd: []const u8) !void {
|
||||
fn installBunxSymlinkPosix(cwd: []const u8) !void {
|
||||
var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
// don't install it if it's already there
|
||||
@@ -54,7 +54,7 @@ pub const InstallCompletionsCommand = struct {
|
||||
return;
|
||||
|
||||
// first try installing the symlink into the same directory as the bun executable
|
||||
const exe = try std.fs.selfExePathAlloc(allocator);
|
||||
const exe = try bun.selfExePath();
|
||||
var target_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
var target = std.fmt.bufPrint(&target_buf, "{s}/" ++ bunx_name, .{std.fs.path.dirname(exe).?}) catch unreachable;
|
||||
std.os.symlink(exe, target) catch {
|
||||
@@ -89,7 +89,7 @@ pub const InstallCompletionsCommand = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn installBunxSymlinkWindows(_: std.mem.Allocator, _: []const u8) !void {
|
||||
fn installBunxSymlinkWindows(_: []const u8) !void {
|
||||
// Because symlinks are not always allowed on windows,
|
||||
// `bunx.exe` on windows is a hardlink to `bun.exe`
|
||||
// for this to work, we need to delete and recreate the hardlink every time
|
||||
@@ -130,11 +130,11 @@ pub const InstallCompletionsCommand = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn installBunxSymlink(allocator: std.mem.Allocator, cwd: []const u8) !void {
|
||||
fn installBunxSymlink(cwd: []const u8) !void {
|
||||
if (Environment.isWindows) {
|
||||
try installBunxSymlinkWindows(allocator, cwd);
|
||||
try installBunxSymlinkWindows(cwd);
|
||||
} else {
|
||||
try installBunxSymlinkPosix(allocator, cwd);
|
||||
try installBunxSymlinkPosix(cwd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ pub const InstallCompletionsCommand = struct {
|
||||
Global.exit(fail_exit_code);
|
||||
};
|
||||
|
||||
installBunxSymlink(allocator, cwd) catch {};
|
||||
installBunxSymlink(cwd) catch {};
|
||||
|
||||
if (Environment.isWindows) {
|
||||
installUninstallerWindows() catch {};
|
||||
|
||||
@@ -459,10 +459,7 @@ pub const RunCommand = struct {
|
||||
if (Environment.isWindows and bun.FeatureFlags.windows_bunx_fast_path and bun.strings.hasSuffixComptime(executable, ".exe")) {
|
||||
std.debug.assert(std.fs.path.isAbsolute(executable));
|
||||
|
||||
// Using @constCast is safe because we know that
|
||||
// `direct_launch_buffer` is the data destination that assumption is
|
||||
// backed by the immediate assertion.
|
||||
var wpath = @constCast(bun.strings.toNTPath(&BunXFastPath.direct_launch_buffer, executable));
|
||||
var wpath = bun.strings.toNTPath(&BunXFastPath.direct_launch_buffer, executable);
|
||||
std.debug.assert(bun.isSliceInBufferT(u16, wpath, &BunXFastPath.direct_launch_buffer));
|
||||
|
||||
std.debug.assert(wpath.len > bun.windows.nt_object_prefix.len + ".exe".len);
|
||||
@@ -678,7 +675,7 @@ pub const RunCommand = struct {
|
||||
} ++ if (!Environment.isDebug)
|
||||
"/bun-node" ++ if (Environment.git_sha_short.len > 0) "-" ++ Environment.git_sha_short else ""
|
||||
else
|
||||
"/bun-debug-node";
|
||||
"/bun-node-debug";
|
||||
|
||||
pub fn bunNodeFileUtf8(allocator: std.mem.Allocator) ![:0]const u8 {
|
||||
if (!Environment.isWindows) return bun_node_dir;
|
||||
@@ -706,8 +703,6 @@ pub const RunCommand = struct {
|
||||
return try allocator.dupeZ(u8, target_path_buffer[0 .. converted.len + file_name.len :0]);
|
||||
}
|
||||
|
||||
var self_exe_bin_path_buf: [bun.MAX_PATH_BYTES + 1]u8 = undefined;
|
||||
|
||||
pub fn createFakeTemporaryNodeExecutable(PATH: *std.ArrayList(u8), optional_bun_path: *string) !void {
|
||||
// If we are already running as "node", the path should exist
|
||||
if (CLI.pretend_to_be_node) return;
|
||||
@@ -722,7 +717,7 @@ pub const RunCommand = struct {
|
||||
argv0 = bun.argv()[0];
|
||||
} else if (optional_bun_path.len == 0) {
|
||||
// otherwise, ask the OS for the absolute path
|
||||
var self = try std.fs.selfExePath(&self_exe_bin_path_buf);
|
||||
var self = try bun.selfExePath();
|
||||
if (self.len > 0) {
|
||||
self.ptr[self.len] = 0;
|
||||
argv0 = @as([*:0]const u8, @ptrCast(self.ptr));
|
||||
@@ -790,8 +785,16 @@ pub const RunCommand = struct {
|
||||
"-" ++ Environment.git_sha_short
|
||||
else
|
||||
"";
|
||||
@memcpy(target_path_buffer[prefix.len..][len..].ptr, comptime bun.strings.w(dir_name));
|
||||
const dir_slice = target_path_buffer[0 .. prefix.len + len + dir_name.len];
|
||||
|
||||
if (Environment.isDebug) {
|
||||
const dir_slice_u8 = std.unicode.utf16leToUtf8Alloc(bun.default_allocator, dir_slice) catch @panic("oom");
|
||||
defer bun.default_allocator.free(dir_slice_u8);
|
||||
std.fs.deleteTreeAbsolute(dir_slice_u8) catch {};
|
||||
std.fs.makeDirAbsolute(dir_slice_u8) catch @panic("huh?");
|
||||
}
|
||||
|
||||
const image_path = bun.windows.exePathW();
|
||||
inline for (.{ "node.exe", "bun.exe" }) |name| {
|
||||
const file_name = dir_name ++ "\\" ++ name ++ "\x00";
|
||||
@@ -817,6 +820,9 @@ pub const RunCommand = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (PATH.items.len > 0 and PATH.items[PATH.items.len - 1] != std.fs.path.delimiter) {
|
||||
try PATH.append(std.fs.path.delimiter);
|
||||
}
|
||||
|
||||
// The reason for the extra delim is because we are going to append the system PATH
|
||||
// later on. this is done by the caller, and explains why we are adding bun_node_dir
|
||||
@@ -900,7 +906,7 @@ pub const RunCommand = struct {
|
||||
|
||||
if (this_bundler.env.get("npm_execpath") == null) {
|
||||
// we don't care if this fails
|
||||
if (std.fs.selfExePathAlloc(ctx.allocator)) |self_exe_path| {
|
||||
if (bun.selfExePath()) |self_exe_path| {
|
||||
this_bundler.env.map.putDefault("npm_execpath", self_exe_path) catch unreachable;
|
||||
} else |_| {}
|
||||
}
|
||||
|
||||
@@ -756,7 +756,8 @@ pub const UpgradeCommand = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const destination_executable = std.fs.selfExePath(¤t_executable_buf) catch return error.UpgradeFailedMissingExecutable;
|
||||
const destination_executable = bun.selfExePath() catch return error.UpgradeFailedMissingExecutable;
|
||||
@memcpy(current_executable_buf[0..destination_executable.len], destination_executable);
|
||||
current_executable_buf[destination_executable.len] = 0;
|
||||
|
||||
const target_filename_ = std.fs.path.basename(destination_executable);
|
||||
|
||||
@@ -388,7 +388,7 @@ fn extract(this: *const ExtractTarball, tgz_bytes: []const u8) !Install.ExtractD
|
||||
.npm => folder_name[name.len + 1 ..],
|
||||
else => folder_name,
|
||||
},
|
||||
.{},
|
||||
.{ .is_directory = true },
|
||||
) catch break :create_index;
|
||||
}
|
||||
|
||||
|
||||
@@ -1387,19 +1387,13 @@ pub const PackageInstall = struct {
|
||||
if (comptime Environment.isWindows) {
|
||||
var fd_path_buf: bun.PathBuffer = undefined;
|
||||
|
||||
// buf[0] = '\\';
|
||||
// buf[1] = '\\';
|
||||
// buf[2] = '?';
|
||||
// buf[3] = '\\';
|
||||
// buf[0..4].* = bun.windows.nt_maxpath_prefix;
|
||||
// const dest_path = try bun.getFdPath(subdir.fd, &fd_path_buf);
|
||||
// strings.copyU8IntoU16(buf[4..], dest_path);
|
||||
// buf[dest_path.len + 4] = '\\';
|
||||
// to_copy_buf = buf[dest_path.len + 5 ..];
|
||||
|
||||
// buf2[0] = '\\';
|
||||
// buf2[1] = '\\';
|
||||
// buf2[2] = '?';
|
||||
// buf2[3] = '\\';
|
||||
// buf2[0..4].* = bun.windows.nt_maxpath_prefix;
|
||||
// const cache_path = try bun.getFdPath(cached_package_dir.fd, &fd_path_buf);
|
||||
// strings.copyU8IntoU16(buf2[4..], cache_path);
|
||||
// buf2[cache_path.len + 4] = '\\';
|
||||
@@ -2580,9 +2574,6 @@ pub const PackageManager = struct {
|
||||
}
|
||||
|
||||
pub fn ensureTempNodeGypScript(this: *PackageManager) !void {
|
||||
if (Environment.isWindows) {
|
||||
Output.debug("TODO: VERIFY ensureTempNodeGypScript WORKS!!", .{});
|
||||
}
|
||||
if (this.node_gyp_tempdir_name.len > 0) return;
|
||||
|
||||
const tempdir = this.getTemporaryDirectory();
|
||||
@@ -2618,26 +2609,34 @@ pub const PackageManager = struct {
|
||||
};
|
||||
defer node_gyp_file.close();
|
||||
|
||||
var bytes: string = switch (Environment.os) {
|
||||
else => "#!/usr/bin/env node\nrequire(\"child_process\").spawnSync(\"bun\",[\"x\",\"node-gyp\",...process.argv.slice(2)],{stdio:\"inherit\"})",
|
||||
.windows => "@node -e \"require('child_process').spawnSync('bun',['x','node-gyp',...process.argv.slice(2)],{stdio:'inherit'})\"",
|
||||
const shebang = switch (Environment.os) {
|
||||
.windows =>
|
||||
\\0</* :{
|
||||
\\ @echo off
|
||||
\\ node %~f0 %*
|
||||
\\ exit /b %errorlevel%
|
||||
\\:} */0;
|
||||
\\
|
||||
,
|
||||
else =>
|
||||
\\#!/usr/bin/env node
|
||||
\\
|
||||
,
|
||||
};
|
||||
const content =
|
||||
\\const child_process = require("child_process");
|
||||
\\child_process.spawnSync("bun", ["x", "node-gyp", ...process.argv.slice(2)], { stdio: "inherit" });
|
||||
\\
|
||||
;
|
||||
|
||||
node_gyp_file.writeAll(shebang ++ content) catch |err| {
|
||||
Output.prettyErrorln("<r><red>error<r>: <b><red>{s}<r> writing to " ++ file_name ++ " file", .{@errorName(err)});
|
||||
Global.crash();
|
||||
};
|
||||
var index: usize = 0;
|
||||
while (index < bytes.len) {
|
||||
switch (bun.sys.write(bun.toFD(node_gyp_file.handle), bytes[index..])) {
|
||||
.result => |written| {
|
||||
index += written;
|
||||
},
|
||||
.err => |err| {
|
||||
Output.prettyErrorln("<r><red>error<r>: <b><red>{s}<r> writing to " ++ file_name ++ " file", .{@tagName(err.getErrno())});
|
||||
Global.crash();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Add our node-gyp tempdir to the path
|
||||
const existing_path = this.env.get("PATH") orelse "";
|
||||
var PATH = try std.ArrayList(u8).initCapacity(bun.default_allocator, existing_path.len + 1 + this.node_gyp_tempdir_name.len);
|
||||
var PATH = try std.ArrayList(u8).initCapacity(bun.default_allocator, existing_path.len + 1 + this.temp_dir_name.len + 1 + this.node_gyp_tempdir_name.len);
|
||||
try PATH.appendSlice(existing_path);
|
||||
if (existing_path.len > 0 and existing_path[existing_path.len - 1] != std.fs.path.delimiter)
|
||||
try PATH.append(std.fs.path.delimiter);
|
||||
@@ -2646,15 +2645,16 @@ pub const PackageManager = struct {
|
||||
try PATH.appendSlice(this.node_gyp_tempdir_name);
|
||||
try this.env.map.put("PATH", PATH.items);
|
||||
|
||||
const node_gyp_abs_dir = try bun.fmt.bufPrint(&path_buf, "{s}" ++ .{std.fs.path.sep} ++ "{s}", .{
|
||||
const npm_config_node_gyp = try bun.fmt.bufPrint(&path_buf, "{s}{s}{s}{s}{s}", .{
|
||||
strings.withoutTrailingSlash(this.temp_dir_name),
|
||||
std.fs.path.sep_str,
|
||||
strings.withoutTrailingSlash(this.node_gyp_tempdir_name),
|
||||
std.fs.path.sep_str,
|
||||
file_name,
|
||||
});
|
||||
try this.env.map.putAllocKeyAndValue(this.allocator, "BUN_WHICH_IGNORE_CWD", node_gyp_abs_dir);
|
||||
|
||||
path_buf[node_gyp_abs_dir.len] = std.fs.path.sep;
|
||||
@memcpy(path_buf[node_gyp_abs_dir.len + 1 ..][0.."node-gyp".len], "node-gyp");
|
||||
const npm_config_node_gyp = path_buf[0 .. node_gyp_abs_dir.len + 1 + "node-gyp".len];
|
||||
const node_gyp_abs_dir = std.fs.path.dirname(npm_config_node_gyp).?;
|
||||
try this.env.map.putAllocKeyAndValue(this.allocator, "BUN_WHICH_IGNORE_CWD", node_gyp_abs_dir);
|
||||
try this.env.map.putAllocKeyAndValue(this.allocator, "npm_config_node_gyp", npm_config_node_gyp);
|
||||
}
|
||||
|
||||
@@ -3331,8 +3331,8 @@ pub const PackageManager = struct {
|
||||
.folder => {
|
||||
// relative to cwd
|
||||
const folder_path = this.lockfile.str(&version.value.folder);
|
||||
var buf2: bun.PathBuffer = undefined;
|
||||
const folder_path_abs = if (std.fs.path.isAbsolute(folder_path)) folder_path else blk: {
|
||||
var buf2: bun.PathBuffer = undefined;
|
||||
break :blk Path.joinAbsStringBuf(FileSystem.instance.top_level_dir, &buf2, &[_]string{folder_path}, .auto);
|
||||
};
|
||||
const res = FolderResolution.getOrPut(.{ .relative = .folder }, version, folder_path_abs, this);
|
||||
@@ -3354,8 +3354,8 @@ pub const PackageManager = struct {
|
||||
// package name hash should be used to find workspace path from map
|
||||
const workspace_path_raw: *const String = this.lockfile.workspace_paths.getPtr(@truncate(name_hash)) orelse &version.value.workspace;
|
||||
const workspace_path = this.lockfile.str(workspace_path_raw);
|
||||
var buf2: bun.PathBuffer = undefined;
|
||||
const workspace_path_u8 = if (std.fs.path.isAbsolute(workspace_path)) workspace_path else blk: {
|
||||
var buf2: bun.PathBuffer = undefined;
|
||||
break :blk Path.joinAbsStringBuf(FileSystem.instance.top_level_dir, &buf2, &[_]string{workspace_path}, .auto);
|
||||
};
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ pub const LifecycleScriptSubprocess = struct {
|
||||
this.current_script_index = next_script_index;
|
||||
this.has_called_process_exit = false;
|
||||
|
||||
const shell_bin = bun.CLI.RunCommand.findShell(env.get("PATH") orelse "", cwd) orelse return error.MissingShell;
|
||||
const shell_bin = if (Environment.isWindows) null else bun.CLI.RunCommand.findShell(env.get("PATH") orelse "", cwd) orelse null;
|
||||
|
||||
var copy_script = try std.ArrayList(u8).initCapacity(manager.allocator, original_script.script.len + 1);
|
||||
defer copy_script.deinit();
|
||||
@@ -134,13 +134,39 @@ pub const LifecycleScriptSubprocess = struct {
|
||||
|
||||
const combined_script: [:0]u8 = copy_script.items[0 .. copy_script.items.len - 1 :0];
|
||||
|
||||
var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
var path_buf2: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
const lifecycle_script_path, var lifecycle_script_file = if (shell_bin == null) blk: {
|
||||
const tempdir = this.manager.getTemporaryDirectory();
|
||||
var lifecycle_script_name = bun.span(try bun.fs.FileSystem.instance.tmpname("lifecycle-script", &path_buf, 12345));
|
||||
@memcpy(path_buf[lifecycle_script_name.len..].ptr, ".bun.sh\x00");
|
||||
lifecycle_script_name.len += 7;
|
||||
|
||||
const lifecycle_script_file = try tempdir.createFile(lifecycle_script_name, .{});
|
||||
errdefer lifecycle_script_file.close();
|
||||
try lifecycle_script_file.writeAll(combined_script);
|
||||
const lifecycle_script_path = try bun.getFdPath(lifecycle_script_file.handle, &path_buf2);
|
||||
path_buf2[lifecycle_script_path.len] = 0;
|
||||
|
||||
break :blk .{
|
||||
path_buf2[0..lifecycle_script_path.len :0],
|
||||
@as(?std.fs.File, lifecycle_script_file),
|
||||
};
|
||||
} else .{ "", null };
|
||||
if (lifecycle_script_file) |*f| f.close();
|
||||
|
||||
log("{s} - {s} $ {s}", .{ this.package_name, this.scriptName(), combined_script });
|
||||
|
||||
var argv = [_]?[*:0]const u8{
|
||||
shell_bin,
|
||||
if (Environment.isWindows) "/c" else "-c",
|
||||
var argv = if (shell_bin != null and !Environment.isWindows) [_]?[*:0]const u8{
|
||||
shell_bin.?,
|
||||
"-c",
|
||||
combined_script,
|
||||
null,
|
||||
} else [_]?[*:0]const u8{
|
||||
try bun.selfExePath(),
|
||||
"run",
|
||||
lifecycle_script_path,
|
||||
null,
|
||||
};
|
||||
if (Environment.isWindows) {
|
||||
this.stdout.source = .{ .pipe = bun.default_allocator.create(uv.Pipe) catch bun.outOfMemory() };
|
||||
@@ -169,6 +195,7 @@ pub const LifecycleScriptSubprocess = struct {
|
||||
.windows = if (Environment.isWindows)
|
||||
.{
|
||||
.loop = JSC.EventLoopHandle.init(&manager.event_loop),
|
||||
.verbatim_arguments = true,
|
||||
}
|
||||
else {},
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
//! use undefined memory.
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const string = []const u8;
|
||||
const bun = @import("root").bun;
|
||||
const os = std.os;
|
||||
const Arena = std.heap.ArenaAllocator;
|
||||
@@ -504,16 +505,23 @@ pub const CowEnvMap = Cow(EnvMap, struct {
|
||||
|
||||
pub const EnvMap = struct {
|
||||
map: MapType,
|
||||
|
||||
pub const Iterator = MapType.Iterator;
|
||||
|
||||
const MapType = std.ArrayHashMap(EnvStr, EnvStr, struct {
|
||||
pub fn hash(self: @This(), s: EnvStr) u32 {
|
||||
_ = self;
|
||||
if (bun.Environment.isWindows) {
|
||||
return bun.CaseInsensitiveASCIIStringContext.hash(undefined, s.slice());
|
||||
}
|
||||
return std.array_hash_map.hashString(s.slice());
|
||||
}
|
||||
pub fn eql(self: @This(), a: EnvStr, b: EnvStr, b_index: usize) bool {
|
||||
_ = self;
|
||||
_ = b_index;
|
||||
if (bun.Environment.isWindows) {
|
||||
return bun.CaseInsensitiveASCIIStringContext.eql(undefined, a.slice(), b.slice(), undefined);
|
||||
}
|
||||
return std.array_hash_map.eqlString(a.slice(), b.slice());
|
||||
}
|
||||
}, true);
|
||||
@@ -1139,18 +1147,21 @@ pub const Interpreter = struct {
|
||||
|
||||
pub fn initAndRunFromFile(mini: *JSC.MiniEventLoop, path: []const u8) !bun.shell.ExitCode {
|
||||
var arena = bun.ArenaAllocator.init(bun.default_allocator);
|
||||
defer arena.deinit();
|
||||
const src = src: {
|
||||
var file = try std.fs.cwd().openFile(path, .{});
|
||||
defer file.close();
|
||||
break :src try file.reader().readAllAlloc(arena.allocator(), std.math.maxInt(u32));
|
||||
};
|
||||
defer arena.deinit();
|
||||
return initAndRunFromFileSource(mini, &arena, path, src);
|
||||
}
|
||||
|
||||
pub fn initAndRunFromFileSource(mini: *JSC.MiniEventLoop, arena: *bun.ArenaAllocator, path: string, src: string) !bun.shell.ExitCode {
|
||||
const jsobjs: []JSValue = &[_]JSValue{};
|
||||
var out_parser: ?bun.shell.Parser = null;
|
||||
var out_lex_result: ?bun.shell.LexResult = null;
|
||||
const script = ThisInterpreter.parse(
|
||||
&arena,
|
||||
arena,
|
||||
src,
|
||||
jsobjs,
|
||||
&[_]bun.String{},
|
||||
@@ -1174,7 +1185,7 @@ pub const Interpreter = struct {
|
||||
};
|
||||
const script_heap = try arena.allocator().create(ast.Script);
|
||||
script_heap.* = script;
|
||||
var interp = switch (ThisInterpreter.init(.{ .mini = mini }, bun.default_allocator, &arena, script_heap, jsobjs)) {
|
||||
var interp = switch (ThisInterpreter.init(.{ .mini = mini }, bun.default_allocator, arena, script_heap, jsobjs)) {
|
||||
.err => |*e| {
|
||||
throwShellErr(e, .{ .mini = mini });
|
||||
return 1;
|
||||
@@ -3903,7 +3914,9 @@ pub const Interpreter = struct {
|
||||
args_slice: ?[]const [:0]const u8 = null,
|
||||
cwd: bun.FileDescriptor,
|
||||
|
||||
impl: union(Kind) {
|
||||
impl: RealImpl,
|
||||
|
||||
const RealImpl = union(Kind) {
|
||||
cat: Cat,
|
||||
touch: Touch,
|
||||
mkdir: Mkdir,
|
||||
@@ -3915,7 +3928,8 @@ pub const Interpreter = struct {
|
||||
rm: Rm,
|
||||
mv: Mv,
|
||||
ls: Ls,
|
||||
},
|
||||
exit: Exit,
|
||||
};
|
||||
|
||||
const Result = @import("../result.zig").Result;
|
||||
|
||||
@@ -3931,6 +3945,7 @@ pub const Interpreter = struct {
|
||||
rm,
|
||||
mv,
|
||||
ls,
|
||||
exit,
|
||||
|
||||
pub fn parentType(this: Kind) type {
|
||||
_ = this;
|
||||
@@ -3949,6 +3964,7 @@ pub const Interpreter = struct {
|
||||
.rm => "usage: rm [-f | -i] [-dIPRrvWx] file ...\n unlink [--] file\n",
|
||||
.mv => "usage: mv [-f | -i | -n] [-hv] source target\n mv [-f | -i | -n] [-v] source ... directory\n",
|
||||
.ls => "usage: ls [-@ABCFGHILOPRSTUWabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]\n",
|
||||
.exit => "usage: exit [n]\n",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3965,6 +3981,7 @@ pub const Interpreter = struct {
|
||||
.rm => "rm",
|
||||
.mv => "mv",
|
||||
.ls => "ls",
|
||||
.exit => "exit",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4126,6 +4143,7 @@ pub const Interpreter = struct {
|
||||
.pwd => this.callImplWithType(Pwd, Ret, "pwd", field, args_),
|
||||
.mv => this.callImplWithType(Mv, Ret, "mv", field, args_),
|
||||
.ls => this.callImplWithType(Ls, Ret, "ls", field, args_),
|
||||
.exit => this.callImplWithType(Exit, Ret, "exit", field, args_),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4195,26 +4213,6 @@ pub const Interpreter = struct {
|
||||
};
|
||||
|
||||
switch (kind) {
|
||||
.cat => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.cat = Cat{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.touch => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.touch = Touch{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.mkdir => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.mkdir = Mkdir{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.@"export" => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.@"export" = Export{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.rm => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.rm = Rm{
|
||||
@@ -4231,36 +4229,10 @@ pub const Interpreter = struct {
|
||||
},
|
||||
};
|
||||
},
|
||||
.cd => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.cd = Cd{
|
||||
.bltn = &cmd.exec.bltn,
|
||||
},
|
||||
};
|
||||
},
|
||||
.which => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.which = Which{
|
||||
.bltn = &cmd.exec.bltn,
|
||||
},
|
||||
};
|
||||
},
|
||||
.pwd => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.pwd = Pwd{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.mv => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.mv = Mv{ .bltn = &cmd.exec.bltn },
|
||||
};
|
||||
},
|
||||
.ls => {
|
||||
cmd.exec.bltn.impl = .{
|
||||
.ls = Ls{
|
||||
.bltn = &cmd.exec.bltn,
|
||||
},
|
||||
};
|
||||
inline else => |tag| {
|
||||
cmd.exec.bltn.impl = @unionInit(RealImpl, @tagName(tag), .{
|
||||
.bltn = &cmd.exec.bltn,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8663,6 +8635,84 @@ pub const Interpreter = struct {
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const Exit = struct {
|
||||
bltn: *Builtin,
|
||||
state: enum {
|
||||
idle,
|
||||
waiting_io,
|
||||
err,
|
||||
done,
|
||||
} = .idle,
|
||||
|
||||
pub fn start(this: *Exit) Maybe(void) {
|
||||
const args = this.bltn.argsSlice();
|
||||
switch (args.len) {
|
||||
0 => {
|
||||
this.bltn.done(0);
|
||||
return Maybe(void).success;
|
||||
},
|
||||
1 => {
|
||||
const first_arg = args[0][0..std.mem.len(args[0]) :0];
|
||||
const exit_code: ExitCode = std.fmt.parseInt(u8, first_arg, 10) catch |err| switch (err) {
|
||||
error.Overflow => @intCast((std.fmt.parseInt(usize, first_arg, 10) catch return this.fail("exit: numeric argument required")) % 256),
|
||||
error.InvalidCharacter => return this.fail("exit: numeric argument required"),
|
||||
};
|
||||
this.bltn.done(exit_code);
|
||||
return Maybe(void).success;
|
||||
},
|
||||
else => {
|
||||
return this.fail("exit: too many arguments");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn fail(this: *Exit, msg: string) Maybe(void) {
|
||||
if (this.bltn.stderr.needsIO()) {
|
||||
this.state = .waiting_io;
|
||||
this.bltn.stderr.enqueue(this, msg);
|
||||
return Maybe(void).success;
|
||||
}
|
||||
_ = this.bltn.writeNoIO(.stderr, msg);
|
||||
this.bltn.done(1);
|
||||
return Maybe(void).success;
|
||||
}
|
||||
|
||||
pub fn next(this: *Exit) void {
|
||||
while (!(this.state == .err or this.state == .done)) {
|
||||
switch (this.state) {
|
||||
.waiting_io => return,
|
||||
.idle, .done, .err => unreachable,
|
||||
}
|
||||
}
|
||||
if (this.state == .done) {
|
||||
this.bltn.done(1);
|
||||
return;
|
||||
}
|
||||
if (this.state == .err) {
|
||||
this.bltn.done(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onIOWriterChunk(this: *Exit, _: usize, maybe_e: ?JSC.SystemError) void {
|
||||
if (comptime bun.Environment.allow_assert) {
|
||||
std.debug.assert(this.state == .waiting_io);
|
||||
}
|
||||
if (maybe_e) |e| {
|
||||
defer e.deref();
|
||||
this.state = .err;
|
||||
this.next();
|
||||
return;
|
||||
}
|
||||
this.state = .done;
|
||||
this.next();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *Exit) void {
|
||||
_ = this;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/// This type is reference counted, but deinitialization is queued onto the event loop
|
||||
@@ -9615,7 +9665,7 @@ inline fn fastMod(val: anytype, comptime rhs: comptime_int) @TypeOf(val) {
|
||||
return val & (rhs - 1);
|
||||
}
|
||||
|
||||
fn throwShellErr(e: *const bun.shell.ShellErr, event_loop: JSC.EventLoopHandle) void {
|
||||
pub fn throwShellErr(e: *const bun.shell.ShellErr, event_loop: JSC.EventLoopHandle) void {
|
||||
switch (event_loop) {
|
||||
.mini => e.throwMini(),
|
||||
.js => e.throwJS(event_loop.js.global),
|
||||
@@ -9671,6 +9721,7 @@ pub const IOWriterChildPtr = struct {
|
||||
Interpreter.Builtin.Touch,
|
||||
Interpreter.Builtin.Touch.ShellTouchOutputTask,
|
||||
Interpreter.Builtin.Cat,
|
||||
Interpreter.Builtin.Exit,
|
||||
shell.subproc.PipeReader.CapturedWriter,
|
||||
});
|
||||
|
||||
|
||||
@@ -108,23 +108,20 @@ pub const ShellErr = union(enum) {
|
||||
pub fn throwMini(this: @This()) void {
|
||||
defer this.deinit(bun.default_allocator);
|
||||
switch (this) {
|
||||
.sys => {
|
||||
const err = this.sys;
|
||||
const str = std.fmt.allocPrint(bun.default_allocator, "bunsh: {s}: {}", .{ err.message, err.path }) catch bun.outOfMemory();
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error <b>{s}<r>", .{str});
|
||||
.sys => |err| {
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error: <b>bunsh: {s}: {}<r>", .{ err.message, err.path });
|
||||
bun.Global.exit(1);
|
||||
},
|
||||
.custom => {
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error <b>{s}<r>", .{this.custom});
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error: <b>{s}<r>", .{this.custom});
|
||||
bun.Global.exit(1);
|
||||
},
|
||||
.invalid_arguments => {
|
||||
const str = std.fmt.allocPrint(bun.default_allocator, "bunsh: invalid arguments: {s}", .{this.invalid_arguments.val}) catch bun.outOfMemory();
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error <b>{s}<r>", .{str});
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error: <b>bunsh: invalid arguments: {s}<r>", .{this.invalid_arguments.val});
|
||||
bun.Global.exit(1);
|
||||
},
|
||||
.todo => {
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error <b>TODO: {s}<r>", .{this.todo});
|
||||
bun.Output.prettyErrorln("<r><red>error<r>: Failed to due to error: <b>TODO: {s}<r>", .{this.todo});
|
||||
bun.Global.exit(1);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -702,6 +702,10 @@ pub inline fn endsWithComptime(self: string, comptime str: anytype) bool {
|
||||
return self.len >= str.len and eqlComptimeIgnoreLen(self[self.len - str.len .. self.len], comptime str);
|
||||
}
|
||||
|
||||
pub inline fn endsWithGenericComptime(comptime T: type, self: []const T, comptime str: []const T) bool {
|
||||
return self.len >= str.len and @call(bun.callmod_inline, eqlComptimeCheckLenWithType, .{ T, self[self.len - str.len .. self.len], str, false });
|
||||
}
|
||||
|
||||
pub inline fn startsWithChar(self: string, char: u8) bool {
|
||||
return self.len > 0 and self[0] == char;
|
||||
}
|
||||
@@ -1743,7 +1747,7 @@ pub fn isWindowsAbsolutePathMissingDriveLetter(comptime T: type, chars: []const
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn fromWPath(buf: []u8, utf16: []const u16) [:0]const u8 {
|
||||
pub fn fromWPath(buf: []u8, utf16: []const u16) [:0]u8 {
|
||||
std.debug.assert(buf.len > 0);
|
||||
const encode_into_result = copyUTF16IntoUTF8(buf[0 .. buf.len - 1], []const u16, utf16, false);
|
||||
std.debug.assert(encode_into_result.written < buf.len);
|
||||
@@ -1751,7 +1755,7 @@ pub fn fromWPath(buf: []u8, utf16: []const u16) [:0]const u8 {
|
||||
return buf[0..encode_into_result.written :0];
|
||||
}
|
||||
|
||||
pub fn toNTPath(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toNTPath(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
if (!std.fs.path.isAbsoluteWindows(utf8)) {
|
||||
return toWPathNormalized(wbuf, utf8);
|
||||
}
|
||||
@@ -1760,14 +1764,14 @@ pub fn toNTPath(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
return wbuf[0 .. toWPathNormalized(wbuf[4..], utf8).len + 4 :0];
|
||||
}
|
||||
|
||||
pub fn addNTPathPrefix(wbuf: []u16, utf16: []const u16) [:0]const u16 {
|
||||
pub fn addNTPathPrefix(wbuf: []u16, utf16: []const u16) [:0]u16 {
|
||||
wbuf[0..bun.windows.nt_object_prefix.len].* = bun.windows.nt_object_prefix;
|
||||
@memcpy(wbuf[bun.windows.nt_object_prefix.len..][0..utf16.len], utf16);
|
||||
wbuf[utf16.len + bun.windows.nt_object_prefix.len] = 0;
|
||||
return wbuf[0 .. utf16.len + bun.windows.nt_object_prefix.len :0];
|
||||
}
|
||||
|
||||
pub fn addNTPathPrefixIfNeeded(wbuf: []u16, utf16: []const u16) [:0]const u16 {
|
||||
pub fn addNTPathPrefixIfNeeded(wbuf: []u16, utf16: []const u16) [:0]u16 {
|
||||
if (hasPrefixComptimeType(u16, utf16, bun.windows.nt_object_prefix)) {
|
||||
@memcpy(wbuf[0..utf16.len], utf16);
|
||||
wbuf[utf16.len] = 0;
|
||||
@@ -1779,13 +1783,13 @@ pub fn addNTPathPrefixIfNeeded(wbuf: []u16, utf16: []const u16) [:0]const u16 {
|
||||
// These are the same because they don't have rules like needing a trailing slash
|
||||
pub const toNTDir = toNTPath;
|
||||
|
||||
pub fn toExtendedPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toExtendedPathNormalized(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
std.debug.assert(wbuf.len > 4);
|
||||
wbuf[0..4].* = bun.windows.nt_maxpath_prefix;
|
||||
return wbuf[0 .. toWPathNormalized(wbuf[4..], utf8).len + 4 :0];
|
||||
}
|
||||
|
||||
pub fn toWPathNormalizeAutoExtend(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toWPathNormalizeAutoExtend(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
if (std.fs.path.isAbsoluteWindows(utf8)) {
|
||||
return toExtendedPathNormalized(wbuf, utf8);
|
||||
}
|
||||
@@ -1793,7 +1797,7 @@ pub fn toWPathNormalizeAutoExtend(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
return toWPathNormalized(wbuf, utf8);
|
||||
}
|
||||
|
||||
pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toWPathNormalized(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
var renormalized: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
|
||||
var path_to_use = normalizeSlashesOnly(&renormalized, utf8, '\\');
|
||||
@@ -1823,7 +1827,7 @@ pub fn normalizeSlashesOnly(buf: []u8, utf8: []const u8, comptime desired_slash:
|
||||
return utf8;
|
||||
}
|
||||
|
||||
pub fn toWDirNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toWDirNormalized(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
var renormalized: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
var path_to_use = utf8;
|
||||
|
||||
@@ -1840,11 +1844,11 @@ pub fn toWDirNormalized(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
return toWDirPath(wbuf, path_to_use);
|
||||
}
|
||||
|
||||
pub fn toWPath(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toWPath(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
return toWPathMaybeDir(wbuf, utf8, false);
|
||||
}
|
||||
|
||||
pub fn toWDirPath(wbuf: []u16, utf8: []const u8) [:0]const u16 {
|
||||
pub fn toWDirPath(wbuf: []u16, utf8: []const u8) [:0]u16 {
|
||||
return toWPathMaybeDir(wbuf, utf8, true);
|
||||
}
|
||||
|
||||
@@ -1865,7 +1869,7 @@ pub fn assertIsValidWindowsPath(comptime T: type, path: []const T) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toWPathMaybeDir(wbuf: []u16, utf8: []const u8, comptime add_trailing_lash: bool) [:0]const u16 {
|
||||
pub fn toWPathMaybeDir(wbuf: []u16, utf8: []const u8, comptime add_trailing_lash: bool) [:0]u16 {
|
||||
std.debug.assert(wbuf.len > 0);
|
||||
|
||||
var result = bun.simdutf.convert.utf8.to.utf16.with_errors.le(
|
||||
@@ -5393,6 +5397,15 @@ pub fn convertUTF8toUTF16InBuffer(
|
||||
return buf[0..result];
|
||||
}
|
||||
|
||||
pub fn convertUTF8toUTF16InBufferZ(
|
||||
buf: []u16,
|
||||
input: []const u8,
|
||||
) [:0]u16 {
|
||||
const converted = convertUTF8toUTF16InBuffer(buf, input);
|
||||
buf[converted.len] = 0;
|
||||
return buf[0..converted.len :0];
|
||||
}
|
||||
|
||||
pub fn convertUTF16toUTF8InBuffer(
|
||||
buf: []u8,
|
||||
input: []const u16,
|
||||
|
||||
@@ -277,7 +277,7 @@ const WindowsWatcher = struct {
|
||||
var nt_name = w.UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @constCast(wpath.ptr),
|
||||
.Buffer = wpath.ptr,
|
||||
};
|
||||
var attr = w.OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
|
||||
|
||||
@@ -15,15 +15,19 @@ fn isValid(buf: *bun.PathBuffer, segment: []const u8, bin: []const u8) ?u16 {
|
||||
// Like /usr/bin/which but without needing to exec a child process
|
||||
// Remember to resolve the symlink if necessary
|
||||
pub fn which(buf: *bun.PathBuffer, path: []const u8, cwd: []const u8, bin: []const u8) ?[:0]const u8 {
|
||||
if (bin.len == 0) return null;
|
||||
if (bun.Environment.os == .windows) {
|
||||
var convert_buf: bun.WPathBuffer = undefined;
|
||||
const result = whichWin(&convert_buf, path, cwd, bin) orelse return null;
|
||||
|
||||
var convert_buf_bin: bun.WPathBuffer = undefined;
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBufferZ(&convert_buf_bin, bin);
|
||||
|
||||
const result = whichWin(&convert_buf, path, cwd, bin_utf16) orelse return null;
|
||||
const result_converted = bun.strings.convertUTF16toUTF8InBuffer(buf, result) catch unreachable;
|
||||
buf[result_converted.len] = 0;
|
||||
std.debug.assert(result_converted.ptr == buf.ptr);
|
||||
return buf[0..result_converted.len :0];
|
||||
}
|
||||
if (bin.len == 0) return null;
|
||||
|
||||
// handle absolute paths
|
||||
if (std.fs.path.isAbsolute(bin)) {
|
||||
@@ -53,40 +57,28 @@ pub fn which(buf: *bun.PathBuffer, path: []const u8, cwd: []const u8, bin: []con
|
||||
return null;
|
||||
}
|
||||
|
||||
const win_extensionsW = .{
|
||||
const win_extensions = [_][:0]const u16{
|
||||
bun.strings.w("exe"),
|
||||
bun.strings.w("cmd"),
|
||||
bun.strings.w("bat"),
|
||||
};
|
||||
const win_extensions = .{
|
||||
"exe",
|
||||
"cmd",
|
||||
"bat",
|
||||
};
|
||||
|
||||
pub fn endsWithExtension(str: []const u8) bool {
|
||||
if (str.len < 4) return false;
|
||||
if (str[str.len - 4] != '.') return false;
|
||||
const file_ext = str[str.len - 3 ..];
|
||||
inline for (win_extensions) |ext| {
|
||||
comptime std.debug.assert(ext.len == 3);
|
||||
if (bun.strings.eqlComptimeCheckLenWithType(u8, file_ext, ext, false)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Check if the WPathBuffer holds a existing file path, checking also for windows extensions variants like .exe, .cmd and .bat (internally used by whichWin)
|
||||
fn searchBin(buf: *bun.WPathBuffer, path_size: usize, check_windows_extensions: bool) ?[:0]const u16 {
|
||||
if (!check_windows_extensions)
|
||||
// On Windows, files without extensions are not executable
|
||||
// Therefore, we should only care about this check when the file already has an extension.
|
||||
if (bun.sys.existsOSPath(buf[0..path_size :0], true))
|
||||
return buf[0..path_size :0];
|
||||
|
||||
if (!check_windows_extensions) {
|
||||
const bin = buf[0..path_size :0];
|
||||
inline for (win_extensions) |ext| {
|
||||
if (bun.strings.endsWithGenericComptime(u16, bin, .{'.'} ++ ext)) {
|
||||
if (bun.sys.existsOSPath(bin, true)) {
|
||||
return bin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (check_windows_extensions) {
|
||||
buf[path_size] = '.';
|
||||
buf[path_size + 1 + 3] = 0;
|
||||
inline for (win_extensionsW) |ext| {
|
||||
inline for (win_extensions) |ext| {
|
||||
@memcpy(buf[path_size + 1 .. path_size + 1 + 3], ext);
|
||||
if (bun.sys.existsOSPath(buf[0 .. path_size + 1 + ext.len :0], true))
|
||||
return buf[0 .. path_size + 1 + ext.len :0];
|
||||
@@ -96,9 +88,9 @@ fn searchBin(buf: *bun.WPathBuffer, path_size: usize, check_windows_extensions:
|
||||
}
|
||||
|
||||
/// Check if bin file exists in this path (internally used by whichWin)
|
||||
fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *[bun.MAX_PATH_BYTES]u8, path: []const u8, bin: []const u8, check_windows_extensions: bool) ?[:0]const u16 {
|
||||
fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *[bun.MAX_PATH_BYTES]u8, path: []const u8, bin: [:0]const u16, check_windows_extensions: bool) ?[:0]const u16 {
|
||||
if (path.len == 0) return null;
|
||||
const segment = if (std.fs.path.isAbsolute(path)) (PosixToWinNormalizer.resolveCWDWithExternalBuf(path_buf, path) catch return null) else path;
|
||||
const segment = if (!std.fs.path.isAbsolute(path)) (PosixToWinNormalizer.resolveCWDWithExternalBuf(path_buf, path) catch return null) else path;
|
||||
const segment_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, segment);
|
||||
|
||||
var segment_len = segment.len;
|
||||
@@ -109,8 +101,8 @@ fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *[bun.MAX_PATH_BYTES]u8, pat
|
||||
segment_utf16_len += 1;
|
||||
}
|
||||
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf[segment_len..], bin);
|
||||
const path_size = segment_utf16_len + bin_utf16.len;
|
||||
@memcpy(buf[segment_len..].ptr, bin);
|
||||
const path_size = segment_utf16_len + bin.len;
|
||||
buf[path_size] = 0;
|
||||
|
||||
return searchBin(buf, path_size, check_windows_extensions);
|
||||
@@ -119,21 +111,22 @@ fn searchBinInPath(buf: *bun.WPathBuffer, path_buf: *[bun.MAX_PATH_BYTES]u8, pat
|
||||
/// This is the windows version of `which`.
|
||||
/// It operates on wide strings.
|
||||
/// It is similar to Get-Command in powershell.
|
||||
pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: []const u8) ?[:0]const u16 {
|
||||
if (bin.len == 0) return null;
|
||||
var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
pub fn whichWin(buf: *bun.WPathBuffer, path: []const u8, cwd: []const u8, bin: [:0]const u16) ?[:0]const u16 {
|
||||
var check_windows_extensions = true;
|
||||
inline for (win_extensions) |ext| {
|
||||
if (bun.strings.endsWithGenericComptime(u16, bin, .{'.'} ++ ext)) {
|
||||
check_windows_extensions = false;
|
||||
|
||||
const check_windows_extensions = !endsWithExtension(bin);
|
||||
|
||||
// handle absolute paths
|
||||
if (std.fs.path.isAbsolute(bin)) {
|
||||
const normalized_bin = PosixToWinNormalizer.resolveCWDWithExternalBuf(&path_buf, bin) catch return null;
|
||||
const bin_utf16 = bun.strings.convertUTF8toUTF16InBuffer(buf, normalized_bin);
|
||||
buf[bin_utf16.len] = 0;
|
||||
return searchBin(buf, bin_utf16.len, check_windows_extensions);
|
||||
if (std.fs.path.isAbsoluteWindowsWTF16(bin)) {
|
||||
if (bun.sys.existsOSPath(bin, true)) {
|
||||
return bin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if bin is in cwd
|
||||
var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
|
||||
if (searchBinInPath(buf, &path_buf, cwd, bin, check_windows_extensions)) |bin_path| {
|
||||
return bin_path;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ var { describe, test, expect } = testForFile(import.meta.path);
|
||||
// For debug, all files are written to $TEMP/bun-bundle-tests/lower
|
||||
|
||||
describe("bundler", () => {
|
||||
return;
|
||||
itBundled("lower/LowerOptionalCatchNameCollisionNoBundle", {
|
||||
// GENERATED
|
||||
files: {
|
||||
|
||||
@@ -11,6 +11,11 @@ import type { Matchers } from "bun:test";
|
||||
import { PluginBuilder } from "bun";
|
||||
import * as esbuild from "esbuild";
|
||||
|
||||
const exe_extension = (() => {
|
||||
if (process.platform === "win32") return ".exe";
|
||||
return "";
|
||||
})();
|
||||
|
||||
/** Dedent module does a bit too much with their stuff. we will be much simpler */
|
||||
function dedent(str: string | TemplateStringsArray, ...args: any[]) {
|
||||
// https://github.com/tc39/proposal-string-cooked#motivation
|
||||
@@ -1260,7 +1265,7 @@ for (const [key, blob] of build.outputs) {
|
||||
cmd: [
|
||||
...(compile ? [] : [(run.runtime ?? "bun") === "bun" ? bunExe() : "node"]),
|
||||
...(run.bunArgs ?? []),
|
||||
file,
|
||||
path.extname(path.basename(file)).length > 0 ? file : file + exe_extension,
|
||||
...(run.args ?? []),
|
||||
] as [string, ...string[]],
|
||||
env: {
|
||||
|
||||
@@ -4,11 +4,12 @@ import { mkdtempSync, realpathSync, rmSync, writeFileSync } from "fs";
|
||||
import { bunExe, bunEnv } from "harness";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { tmpdirSync } from "./dummy.registry";
|
||||
|
||||
let cwd: string;
|
||||
|
||||
beforeEach(() => {
|
||||
cwd = mkdtempSync(join(realpathSync(tmpdir()), "bad-workspace.test"));
|
||||
cwd = tmpdirSync("bad-workspace-test-");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
requested,
|
||||
root_url,
|
||||
setHandler,
|
||||
tmpdirSync,
|
||||
} from "./dummy.registry";
|
||||
|
||||
beforeAll(dummyBeforeAll);
|
||||
@@ -33,7 +34,7 @@ beforeAll(() => {
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
add_dir = await mkdtemp(join(await realpath(tmpdir()), "bun-add.test"));
|
||||
add_dir = tmpdirSync("bun-add.test-");
|
||||
await dummyBeforeEach();
|
||||
});
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -23,11 +23,8 @@ expect.extend({
|
||||
toHaveWorkspaceLink: async function (package_dir: string, [link, real]: [string, string]) {
|
||||
const isWindows = process.platform === "win32";
|
||||
if (!isWindows) {
|
||||
// expect(await readlink(join(package_dir, "node_modules", "Bar"))).toBeWorkspaceLink(join("..", "bar"));
|
||||
// expect(await readlink(join(package_dir, "node_modules", link))).toBeWorkspaceLink(join("..", real));
|
||||
return expect(await readlink(join(package_dir, "node_modules", link))).toBeWorkspaceLink(join("..", real));
|
||||
} else {
|
||||
// expect(await readlink(join(package_dir, "node_modules", link))).toBeWorkspaceLink(join(package_dir, real));
|
||||
return expect(await readlink(join(package_dir, "node_modules", link))).toBeWorkspaceLink(join(package_dir, real));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { realpathSync, chmodSync } from "fs";
|
||||
import { bunEnv, bunExe, isWindows, tempDirWithFiles, toTOMLString } from "harness";
|
||||
import { join } from "path";
|
||||
|
||||
describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
const runCmd = cmd === "bun" ? ["-c=bunfig.toml", "run"] : ["-c=bunfig.toml"];
|
||||
@@ -17,14 +16,13 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
bun,
|
||||
},
|
||||
});
|
||||
const which = "which";
|
||||
|
||||
const cwd = tempDirWithFiles("run.where.node." + cmd2, {
|
||||
const cwd = tempDirWithFiles("run.where.node", {
|
||||
"bunfig.toml": bunfig,
|
||||
"package.json": JSON.stringify(
|
||||
{
|
||||
scripts: {
|
||||
"where-node": `${which} node`,
|
||||
"where-node": `which node`,
|
||||
},
|
||||
},
|
||||
null,
|
||||
@@ -51,11 +49,12 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
} else {
|
||||
expect(realpathSync(nodeBin)).toBe(realpathSync(node));
|
||||
}
|
||||
expect(result.success).toBeTrue();
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(["bun", "system", "default"])(`run.shell = "%s"`, shellStr => {
|
||||
if (isWindows && shellStr === "system") return; // windows always uses the bun shell now
|
||||
const shell = shellStr === "default" ? (isWindows ? "bun" : "system") : shellStr;
|
||||
const command_not_found =
|
||||
isWindows && shell === "system" ? "is not recognized as an internal or external command" : "command not found";
|
||||
@@ -85,7 +84,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
cmd: [bunExe(), ...runCmd, "startScript"],
|
||||
env: bunEnv,
|
||||
stderr: "pipe",
|
||||
stdout: "ignore",
|
||||
stdout: "pipe",
|
||||
stdin: "ignore",
|
||||
cwd,
|
||||
});
|
||||
@@ -95,7 +94,7 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
} else {
|
||||
expect(result.stderr.toString().trim()).toContain("$ echo 1");
|
||||
}
|
||||
expect(result.success).toBeTrue();
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
test("command not found", async () => {
|
||||
const bunfig = toTOMLString({
|
||||
@@ -127,11 +126,6 @@ describe.each(["bun run", "bun"])(`%s`, cmd => {
|
||||
});
|
||||
|
||||
const err = result.stderr.toString().trim();
|
||||
if (shell === "bun") {
|
||||
expect(err).toStartWith("bun: ");
|
||||
} else {
|
||||
expect(err).not.toStartWith("bun: ");
|
||||
}
|
||||
expect(err).toContain(command_not_found);
|
||||
expect(err).toContain("this-should-start-with-bun-in-the-error-message");
|
||||
expect(result.success).toBeFalse();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { afterEach, beforeEach, expect, it, describe } from "bun:test";
|
||||
import { bunEnv, bunExe, bunEnv as env, isWindows } from "harness";
|
||||
import { mkdtemp, realpath, rm, writeFile, exists } from "fs/promises";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { join, sep } from "path";
|
||||
import { readdirSorted } from "./dummy.registry";
|
||||
|
||||
let run_dir: string;
|
||||
@@ -324,7 +324,7 @@ it("should download dependencies to run local file", async () => {
|
||||
import { file } from "bun";
|
||||
import decompress from "decompress@4.2.1";
|
||||
|
||||
const buffer = await file("${filePath}").arrayBuffer();
|
||||
const buffer = await file("${filePath.replaceAll(sep, "/")}").arrayBuffer();
|
||||
for (const entry of await decompress(Buffer.from(buffer))) {
|
||||
console.log(\`\${entry.type}: \${entry.path}\`);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// @known-failing-on-windows: 21 failing
|
||||
// file packages use subshells which bun shell does not support yet.
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { test, expect, describe, beforeAll } from "bun:test";
|
||||
|
||||
@@ -1,46 +1,23 @@
|
||||
import { file, spawn } from "bun";
|
||||
import { bunExe, bunEnv as env, isWindows, toBeValidBin, toHaveBins } from "harness";
|
||||
import { join } from "path";
|
||||
import { bunExe, bunEnv as env, isWindows, toBeValidBin, toHaveBins, writeShebangScript } from "harness";
|
||||
import { join, sep } from "path";
|
||||
import { mkdtempSync, realpathSync, copyFileSync, mkdirSync } from "fs";
|
||||
import { rm, writeFile, mkdir, exists, cp } from "fs/promises";
|
||||
import { readdirSorted } from "../dummy.registry";
|
||||
import { readdirSorted, tmpdirSync } from "../dummy.registry";
|
||||
import { tmpdir } from "os";
|
||||
import { fork, ChildProcess } from "child_process";
|
||||
import { beforeAll, afterAll, beforeEach, afterEach, test, expect, describe } from "bun:test";
|
||||
import { f } from "js/bun/http/js-sink-sourmap-fixture/index.mjs";
|
||||
|
||||
expect.extend({
|
||||
toBeValidBin,
|
||||
toHaveBins,
|
||||
});
|
||||
|
||||
var verdaccioServer: ChildProcess;
|
||||
var testCounter: number = 0;
|
||||
var port: number = 4873;
|
||||
var packageDir: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
verdaccioServer = fork(
|
||||
await import.meta.resolve("verdaccio/bin/verdaccio"),
|
||||
["-c", join(import.meta.dir, "verdaccio.yaml"), "-l", `${port}`],
|
||||
{ silent: true, execPath: "bun" },
|
||||
);
|
||||
|
||||
await new Promise<void>(done => {
|
||||
verdaccioServer.on("message", (msg: { verdaccio_started: boolean }) => {
|
||||
if (msg.verdaccio_started) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
verdaccioServer.kill();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
packageDir = mkdtempSync(join(realpathSync(tmpdir()), "bun-install-registry-" + testCounter++ + "-"));
|
||||
packageDir = tmpdirSync("bun-install-registry-" + testCounter++ + "-");
|
||||
await writeFile(
|
||||
join(packageDir, "bunfig.toml"),
|
||||
`
|
||||
@@ -51,10 +28,6 @@ registry = "http://localhost:${port}/"
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
0 && (await rm(packageDir, { force: true, recursive: true }));
|
||||
});
|
||||
|
||||
describe.each(["--production", "without --production"])("%s", flag => {
|
||||
const prod = flag === "--production";
|
||||
const order = ["devDependencies", "dependencies"];
|
||||
@@ -870,7 +843,7 @@ test("it should install with missing bun.lockb, node_modules, and/or cache", asy
|
||||
expect(err).not.toContain("panic:");
|
||||
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
expect.stringContaining("Checked 19 installs across 23 packages (no changes)"),
|
||||
"Checked 19 installs across 23 packages (no changes)",
|
||||
]);
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
@@ -2050,7 +2023,7 @@ test("it should re-populate .bin folder if package is reinstalled", async () =>
|
||||
"",
|
||||
" + what-bin@1.5.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(Bun.which("what-bin", { PATH: join(packageDir, "node_modules", ".bin") })).toBe(
|
||||
@@ -2379,7 +2352,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + all-lifecycle-scripts@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await file(join(packageDir, "preinstall.txt")).text()).toBe("preinstall exists!");
|
||||
@@ -2434,7 +2407,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + all-lifecycle-scripts@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
|
||||
expect(await file(join(packageDir, "preinstall.txt")).text()).toBe("preinstall!");
|
||||
@@ -2548,7 +2521,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(await exists(join(packageDir, "packages", "pkg2", "postprepare.txt"))).toBeFalse();
|
||||
});
|
||||
|
||||
test("dependency lifecycle scripts run before root lifecycle scripts", async () => {
|
||||
test.skipIf(isWindows)("dependency lifecycle scripts run before root lifecycle scripts", async () => {
|
||||
const script = '[[ -f "./node_modules/uses-what-bin-slow/what-bin.txt" ]]';
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
@@ -2627,7 +2600,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + all-lifecycle-scripts@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
expect.stringContaining(" 1 package installed"),
|
||||
"",
|
||||
" Blocked 3 postinstalls. Run `bun pm untrusted` for details.",
|
||||
"",
|
||||
@@ -2876,7 +2849,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
" + lifecycle-postinstall@1.0.0",
|
||||
"",
|
||||
// @ts-ignore
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(err).toContain("Saved lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
@@ -2901,7 +2874,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + lifecycle-postinstall@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(err).not.toContain("Saved lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
@@ -2961,12 +2934,13 @@ for (const forceWaiterThread of [false, true]) {
|
||||
" + another-init-cwd@1.0.0",
|
||||
" + lifecycle-init-cwd@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await file(join(packageDir, "test.txt")).text()).toBe(packageDir + "/");
|
||||
expect(await file(join(packageDir, "node_modules/lifecycle-init-cwd/test.txt")).text()).toBe(packageDir + "/");
|
||||
expect(await file(join(packageDir, "node_modules/another-init-cwd/test.txt")).text()).toBe(packageDir + "/");
|
||||
const packageDir_expected = packageDir.replaceAll(sep, "/") + "/";
|
||||
expect(await file(join(packageDir, "test.txt")).text()).toBe(packageDir_expected);
|
||||
expect(await file(join(packageDir, "node_modules/lifecycle-init-cwd/test.txt")).text()).toBe(packageDir_expected);
|
||||
expect(await file(join(packageDir, "node_modules/another-init-cwd/test.txt")).text()).toBe(packageDir_expected);
|
||||
});
|
||||
|
||||
test("failing lifecycle script should print output", async () => {
|
||||
@@ -3005,7 +2979,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
name: "fooooooooo",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
preinstall: `${bunExe().replace(/\\/g, "\\\\")} -e "throw new Error('Oops!')"`,
|
||||
preinstall: `${bunExe()} -e "throw new Error('Oops!')"`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -3025,6 +2999,43 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(err).toContain('error: preinstall script from "fooooooooo" exited with 1');
|
||||
});
|
||||
|
||||
test("exit 0 in lifecycle scripts works", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
postinstall: "exit 0",
|
||||
prepare: "exit 0",
|
||||
postprepare: "exit 0",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: testEnv,
|
||||
});
|
||||
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err).toContain("No packages! Deleted empty lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("panic:");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
expect.stringContaining("done"),
|
||||
"",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
test("--ignore-scripts should skip lifecycle scripts", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
@@ -3193,7 +3204,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + node-gyp@1.5.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "build.node"))).toBeTrue();
|
||||
@@ -3251,7 +3262,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + node-gyp@1.5.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "build.node"))).toBeTrue();
|
||||
@@ -3291,7 +3302,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + node-gyp@1.5.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "build.node"))).toBeFalse();
|
||||
@@ -3379,7 +3390,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(await exists(join(packageDir, "node_modules", "lifecycle-install-test", "postinstall.txt"))).toBeTrue();
|
||||
});
|
||||
|
||||
test("root lifecycle scripts should wait for dependency lifecycle scripts", async () => {
|
||||
test.skipIf(isWindows)("root lifecycle scripts should wait for dependency lifecycle scripts", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
@@ -3465,7 +3476,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
|
||||
test("reach max concurrent scripts", async () => {
|
||||
const scripts = {
|
||||
"preinstall": `${bunExe().replace(/\\/g, "\\\\")} -e "Bun.sleepSync(500)"`,
|
||||
"preinstall": `${bunExe()} -e "Bun.sleepSync(500)"`,
|
||||
};
|
||||
|
||||
const dependenciesList = await createPackagesWithScripts(4, scripts);
|
||||
@@ -3494,9 +3505,9 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
test("stress test", async () => {
|
||||
test.skip("stress test", async () => {
|
||||
const dependenciesList = await createPackagesWithScripts(500, {
|
||||
"postinstall": `${bunExe().replace(/\\/g, "\\\\")} --version`,
|
||||
"postinstall": `${bunExe()} --version`,
|
||||
});
|
||||
|
||||
// the script is quick, default number for max concurrent scripts
|
||||
@@ -3525,7 +3536,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
test("it should install and use correct binary version", async () => {
|
||||
test.skip("it should install and use correct binary version", async () => {
|
||||
// this should install `what-bin` in two places:
|
||||
//
|
||||
// - node_modules/.bin/what-bin@1.5.0
|
||||
@@ -3673,6 +3684,40 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
test("npm_config_node_gyp should be set and usable in lifecycle scripts: basic", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
scripts: {
|
||||
install: "echo $npm_config_node_gyp > npm_config_node_gyp.txt",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const { stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: testEnv,
|
||||
});
|
||||
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err).not.toContain("Saved lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("panic:");
|
||||
expect(err).toContain("v");
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
|
||||
expect(await exists(join(packageDir, "npm_config_node_gyp.txt"))).toBeTrue();
|
||||
const ext = isWindows ? ".cmd" : "";
|
||||
expect(await file(join(packageDir, "npm_config_node_gyp.txt")).text()).toEndWith(`${sep}node-gyp${ext}\n`);
|
||||
});
|
||||
|
||||
test("npm_config_node_gyp should be set and usable in lifecycle scripts", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
@@ -3693,13 +3738,13 @@ for (const forceWaiterThread of [false, true]) {
|
||||
env: testEnv,
|
||||
});
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err).not.toContain("Saved lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("panic:");
|
||||
expect(err).toContain("v");
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
// if this test fails, `electron` might be removed from the default list
|
||||
@@ -3733,7 +3778,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + electron@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(out).not.toContain("Blocked");
|
||||
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
|
||||
@@ -3902,7 +3947,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + electron@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeFalse();
|
||||
@@ -4096,7 +4141,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" installed no-deps@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "node_modules", "no-deps"))).toBeTrue();
|
||||
@@ -4134,7 +4179,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" installed no-deps@2.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "node_modules", "no-deps"))).toBeTrue();
|
||||
@@ -4370,7 +4415,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + electron@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await exists(join(packageDir, "node_modules", "electron", "preinstall.txt"))).toBeTrue();
|
||||
@@ -4428,7 +4473,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
postinstall: 'node -p \'require("fs").writeFileSync("postinstall.txt", "postinstall")\'',
|
||||
postinstall: `node -p 'require("fs").writeFileSync("postinstall.txt","postinstall")'`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -4457,6 +4502,41 @@ for (const forceWaiterThread of [false, true]) {
|
||||
|
||||
expect(await exists(join(packageDir, "postinstall.txt"))).toBeTrue();
|
||||
});
|
||||
test.todo("ensureTempNodeGypScript works", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
preinstall: "node-gyp --version",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const originalPath = env.PATH;
|
||||
env.PATH = "";
|
||||
|
||||
let { stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
stdin: "ignore",
|
||||
env,
|
||||
});
|
||||
|
||||
env.PATH = originalPath;
|
||||
|
||||
let err = await Bun.readableStreamToText(stderr);
|
||||
expect(err).toContain("No packages! Deleted empty lockfile");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("warn:");
|
||||
expect(err).not.toContain("panic:");
|
||||
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
test("bun pm trust and untrusted on missing package", async () => {
|
||||
await writeFile(
|
||||
@@ -4681,7 +4761,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
"",
|
||||
" + uses-what-bin@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
expect.stringContaining(" 1 package installed"),
|
||||
"",
|
||||
" Blocked 1 postinstall. Run `bun pm untrusted` for details.",
|
||||
"",
|
||||
@@ -4701,20 +4781,20 @@ for (const forceWaiterThread of [false, true]) {
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("warn:");
|
||||
out = await Bun.readableStreamToText(stdout);
|
||||
expect(out).toContain("./node_modules/uses-what-bin @1.0.0");
|
||||
expect(out).toContain("./node_modules/uses-what-bin @1.0.0".replaceAll("/", sep));
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
describe.if(!forceWaiterThread || process.platform === "linux")("does not use 100% cpu", async () => {
|
||||
test("install", async () => {
|
||||
test.skip("install", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
scripts: {
|
||||
preinstall: `${bunExe().replace(/\\/g, "\\\\")} -e "Bun.sleepSync(1000)"`,
|
||||
preinstall: `${bunExe()} -e "Bun.sleepSync(1000)"`,
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -4732,7 +4812,7 @@ for (const forceWaiterThread of [false, true]) {
|
||||
|
||||
expect(proc.resourceUsage()?.cpuTime.total).toBeLessThan(750_000);
|
||||
});
|
||||
test("bun pm trust", async () => {
|
||||
test.skip("bun pm trust", async () => {
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
@@ -4904,12 +4984,10 @@ test("it should be able to find binary in node_modules/.bin from parent director
|
||||
|
||||
await cp(join(packageDir, "bunfig.toml"), join(packageDir, "morePackageDir", "bunfig.toml"));
|
||||
|
||||
await await writeFile(
|
||||
await writeShebangScript(
|
||||
join(packageDir, "node_modules", ".bin", "missing-bin"),
|
||||
`#!/usr/bin/env node
|
||||
require("fs").writeFileSync("missing-bin.txt", "missing-bin@WHAT");
|
||||
`,
|
||||
{ mode: 0o777 },
|
||||
"node",
|
||||
`require("fs").writeFileSync("missing-bin.txt", "missing-bin@WHAT");`,
|
||||
);
|
||||
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
@@ -4931,7 +5009,7 @@ require("fs").writeFileSync("missing-bin.txt", "missing-bin@WHAT");
|
||||
"",
|
||||
" + what-bin@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
" 1 package installed",
|
||||
]);
|
||||
expect(await exited).toBe(0);
|
||||
expect(await file(join(packageDir, "morePackageDir", "missing-bin.txt")).text()).toBe("missing-bin@WHAT");
|
||||
@@ -6617,159 +6695,3 @@ describe("yarn tests", () => {
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
// This test is to verify that BinLinkingShim.zig creates correct shim files as
|
||||
// well as bun_shim_impl.exe works in various edge cases. There are many fast
|
||||
// paths for many many cases.
|
||||
test.if(isWindows)(
|
||||
"windows bin linking shim should work",
|
||||
async () => {
|
||||
expect(process.platform).toBe("win32"); // extra check
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
dependencies: {
|
||||
"bunx-bins": "*",
|
||||
},
|
||||
}),
|
||||
);
|
||||
console.log(packageDir);
|
||||
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install", "--dev"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
});
|
||||
|
||||
var err = await new Response(stderr).text();
|
||||
var out = await new Response(stdout).text();
|
||||
console.log(err);
|
||||
expect(err).toContain("Saved lockfile");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("panic:");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
" + bunx-bins@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
]);
|
||||
|
||||
const temp_bin_dir = join(packageDir, "temp");
|
||||
mkdirSync(temp_bin_dir);
|
||||
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
const target = join(temp_bin_dir, "a".repeat(i) + ".exe");
|
||||
copyFileSync(bunExe(), target);
|
||||
}
|
||||
|
||||
copyFileSync(join(packageDir, "node_modules\\bunx-bins\\native.exe"), join(temp_bin_dir, "native.exe"));
|
||||
|
||||
const PATH = process.env.PATH + ";" + temp_bin_dir;
|
||||
|
||||
const bins = [
|
||||
{ bin: "bin1", name: "bin1" },
|
||||
{ bin: "bin2", name: "bin2" },
|
||||
{ bin: "bin3", name: "bin3" },
|
||||
{ bin: "bin4", name: "bin4" },
|
||||
{ bin: "bin5", name: "bin5" },
|
||||
{ bin: "bin6", name: "bin6" },
|
||||
{ bin: "bin7", name: "bin7" },
|
||||
{ bin: "bin-node", name: "bin-node" },
|
||||
{ bin: "bin-bun", name: "bin-bun" },
|
||||
{ bin: "bin-py", name: "bin-py" },
|
||||
{ bin: "native", name: "exe" },
|
||||
{ bin: "uses-native", name: `exe ${packageDir}\\node_modules\\bunx-bins\\uses-native.ts` },
|
||||
];
|
||||
|
||||
// `bun run ${bin} arg1 arg2`
|
||||
for (const { bin, name } of bins) {
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "run", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
}
|
||||
|
||||
// `bun --bun run ${bin} arg1 arg2`
|
||||
for (const { bin, name } of bins) {
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "--bun", "run", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
}
|
||||
|
||||
// `bun --bun x ${bin} arg1 arg2`
|
||||
for (const { bin, name } of bins) {
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "--bun", "x", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
}
|
||||
|
||||
// `${bin} arg1 arg2`
|
||||
for (const { bin, name } of bins) {
|
||||
var { stdout, stderr, exited } = spawn({
|
||||
cmd: [join(packageDir, "node_modules", ".bin", bin + ".exe"), "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
}
|
||||
},
|
||||
60_000,
|
||||
);
|
||||
|
||||
167
test/cli/install/registry/bun-install-windowsshim.test.ts
Normal file
167
test/cli/install/registry/bun-install-windowsshim.test.ts
Normal file
@@ -0,0 +1,167 @@
|
||||
import { test, expect, describe } from "bun:test";
|
||||
import { bunExe, bunEnv as env, isWindows } from "harness";
|
||||
import { writeFile } from "fs/promises";
|
||||
import { spawn } from "bun";
|
||||
import { tmpdirSync } from "../dummy.registry";
|
||||
import { join } from "path";
|
||||
import { copyFileSync, mkdirSync } from "fs";
|
||||
|
||||
// This test is to verify that BinLinkingShim.zig creates correct shim files as
|
||||
// well as bun_shim_impl.exe works in various edge cases. There are many fast
|
||||
// paths for many many cases.
|
||||
describe.if(isWindows)("windows bin linking shim should work", async () => {
|
||||
if (!isWindows) return;
|
||||
const port: number = 4873;
|
||||
const packageDir = tmpdirSync("bun-install-windowsshim-");
|
||||
await writeFile(
|
||||
join(packageDir, "bunfig.toml"),
|
||||
`
|
||||
[install]
|
||||
cache = false
|
||||
registry = "http://localhost:${port}/"
|
||||
`,
|
||||
);
|
||||
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
JSON.stringify({
|
||||
name: "foo",
|
||||
version: "1.0.0",
|
||||
dependencies: {
|
||||
"bunx-bins": "*",
|
||||
},
|
||||
}),
|
||||
);
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "install", "--dev"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env,
|
||||
});
|
||||
const err = await new Response(stderr).text();
|
||||
const out = await new Response(stdout).text();
|
||||
console.log(err);
|
||||
expect(err).toContain("Saved lockfile");
|
||||
expect(err).not.toContain("error:");
|
||||
expect(err).not.toContain("panic:");
|
||||
expect(err).not.toContain("not found");
|
||||
expect(out.replace(/\s*\[[0-9\.]+m?s\]\s*$/, "").split(/\r?\n/)).toEqual([
|
||||
"",
|
||||
" + bunx-bins@1.0.0",
|
||||
"",
|
||||
expect.stringContaining("1 package installed"),
|
||||
]);
|
||||
const temp_bin_dir = join(packageDir, "temp");
|
||||
mkdirSync(temp_bin_dir);
|
||||
for (let i = 1; i <= 7; i++) {
|
||||
const target = join(temp_bin_dir, "a".repeat(i) + ".exe");
|
||||
copyFileSync(bunExe(), target);
|
||||
}
|
||||
copyFileSync(join(packageDir, "node_modules\\bunx-bins\\native.exe"), join(temp_bin_dir, "native.exe"));
|
||||
const PATH = process.env.PATH + ";" + temp_bin_dir;
|
||||
|
||||
const bins = [
|
||||
{ bin: "bin1", name: "bin1" },
|
||||
{ bin: "bin2", name: "bin2" },
|
||||
{ bin: "bin3", name: "bin3" },
|
||||
{ bin: "bin4", name: "bin4" },
|
||||
{ bin: "bin5", name: "bin5" },
|
||||
{ bin: "bin6", name: "bin6" },
|
||||
{ bin: "bin7", name: "bin7" },
|
||||
{ bin: "bin-node", name: "bin-node" },
|
||||
{ bin: "bin-bun", name: "bin-bun" },
|
||||
// { bin: "bin-py", name: "bin-py" },
|
||||
{ bin: "native", name: "exe" },
|
||||
{ bin: "uses-native", name: `exe ${packageDir}\\node_modules\\bunx-bins\\uses-native.ts` },
|
||||
];
|
||||
|
||||
for (const { bin, name } of bins) {
|
||||
test(`bun run ${bin} arg1 arg2`, async () => {
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "run", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
for (const { bin, name } of bins) {
|
||||
test(`bun --bun run ${bin} arg1 arg2`, async () => {
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "--bun", "run", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
for (const { bin, name } of bins) {
|
||||
test(`bun --bun x ${bin} arg1 arg2`, async () => {
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
cmd: [bunExe(), "--bun", "x", bin, "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
}
|
||||
|
||||
for (const { bin, name } of bins) {
|
||||
test(`${bin} arg1 arg2`, async () => {
|
||||
const { stdout, stderr, exited } = spawn({
|
||||
cmd: [join(packageDir, "node_modules", ".bin", bin + ".exe"), "arg1", "arg2"],
|
||||
cwd: packageDir,
|
||||
stdout: "pipe",
|
||||
stdin: "pipe",
|
||||
stderr: "pipe",
|
||||
env: {
|
||||
...env,
|
||||
Path: PATH,
|
||||
},
|
||||
});
|
||||
expect(stderr).toBeDefined();
|
||||
const err = await new Response(stderr).text();
|
||||
expect(err.trim()).toBe("");
|
||||
const out = await new Response(stdout).text();
|
||||
expect(out.trim()).toBe(`i am ${name} arg1 arg2`);
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { gc as bunGC, unsafe, which } from "bun";
|
||||
import { describe, test, expect, afterAll, beforeAll } from "bun:test";
|
||||
import { readlink, readFile } from "fs/promises";
|
||||
import { isAbsolute } from "path";
|
||||
import { readlink, readFile, writeFile } from "fs/promises";
|
||||
import { isAbsolute, sep, join } from "path";
|
||||
import { openSync, closeSync } from "node:fs";
|
||||
|
||||
export const isMacOS = process.platform === "darwin";
|
||||
@@ -36,6 +36,7 @@ for (let key in bunEnv) {
|
||||
}
|
||||
|
||||
export function bunExe() {
|
||||
if (isWindows) return process.execPath.replaceAll("\\", "/");
|
||||
return process.execPath;
|
||||
}
|
||||
|
||||
@@ -112,9 +113,8 @@ export function hideFromStackTrace(block: CallableFunction) {
|
||||
export function tempDirWithFiles(basename: string, files: Record<string, string | Record<string, string>>): string {
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var { tmpdir } = require("os");
|
||||
|
||||
const dir = fs.mkdtempSync(path.join(fs.realpathSync(tmpdir()), basename + "_"));
|
||||
const dir = tmpdirSync(basename + "_");
|
||||
for (const [name, contents] of Object.entries(files)) {
|
||||
if (typeof contents === "object") {
|
||||
const entries = Object.entries(contents);
|
||||
@@ -236,9 +236,8 @@ export async function runWithErrorPromise(cb: () => unknown): Promise<Error | un
|
||||
}
|
||||
|
||||
export function fakeNodeRun(dir: string, file: string | string[], env?: Record<string, string>) {
|
||||
var path = require("path");
|
||||
const result = Bun.spawnSync([bunExe(), "--bun", "node", ...(Array.isArray(file) ? file : [file])], {
|
||||
cwd: dir ?? path.dirname(file),
|
||||
cwd: dir,
|
||||
env: {
|
||||
...bunEnv,
|
||||
NODE_ENV: undefined,
|
||||
@@ -504,6 +503,7 @@ function failTestsOnBlockingWriteCall() {
|
||||
failTestsOnBlockingWriteCall();
|
||||
|
||||
import { heapStats } from "bun:jsc";
|
||||
import { tmpdirSync } from "cli/install/dummy.registry";
|
||||
export function dumpStats() {
|
||||
const stats = heapStats();
|
||||
const { objectTypeCounts, protectedObjectTypeCounts } = stats;
|
||||
@@ -554,12 +554,30 @@ export function toTOMLString(opts: object) {
|
||||
const props = makeFlatPropertyMap(opts);
|
||||
let ret = "";
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (value === undefined) continue;
|
||||
ret += `${key} = ${JSON.stringify(value)}` + "\n";
|
||||
if (!value) continue;
|
||||
ret += `${key} = ${JSON.stringify(value)}\n`;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const shebang_posix = (program: string) => `#!/usr/bin/env ${program}
|
||||
`;
|
||||
|
||||
const shebang_windows = (program: string) => `0</* :{
|
||||
@echo off
|
||||
${program} %~f0 %*
|
||||
exit /b %errorlevel%
|
||||
:} */0;
|
||||
`;
|
||||
|
||||
export function writeShebangScript(path: string, program: string, data: string) {
|
||||
if (!isWindows) {
|
||||
return writeFile(path, shebang_posix(program) + "\n" + data, { mode: 0o777 });
|
||||
} else {
|
||||
return writeFile(path + ".cmd", shebang_windows(program) + "\n" + data);
|
||||
}
|
||||
}
|
||||
|
||||
export async function* forEachLine(iter: AsyncIterable<NodeJS.TypedArray | ArrayBufferLike>) {
|
||||
var decoder = new (require("string_decoder").StringDecoder)("utf8");
|
||||
var str = "";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @known-failing-on-windows: 1 failing
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { rm, writeFile, mkdir, exists, cp } from "fs/promises";
|
||||
import { bunExe, bunEnv as env } from "harness";
|
||||
@@ -6,10 +5,11 @@ import { mkdtempSync, realpathSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { spawn } from "bun";
|
||||
import { tmpdirSync } from "cli/install/dummy.registry";
|
||||
|
||||
describe("esbuild integration test", () => {
|
||||
test("install and use esbuild", async () => {
|
||||
const packageDir = mkdtempSync(join(realpathSync(tmpdir()), "bun-esbuild-test-"));
|
||||
const packageDir = tmpdirSync("bun-esbuild-test-");
|
||||
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
@@ -53,7 +53,7 @@ describe("esbuild integration test", () => {
|
||||
});
|
||||
|
||||
test("install and use estrella", async () => {
|
||||
const packageDir = mkdtempSync(join(realpathSync(tmpdir()), "bun-ebuild-estrella-test-"));
|
||||
const packageDir = tmpdirSync("bun-ebuild-estrella-test-");
|
||||
|
||||
await writeFile(
|
||||
join(packageDir, "package.json"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawnSync, spawn } from "bun";
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
import { bunExe, bunEnv } from "harness";
|
||||
import { join } from "lodash";
|
||||
|
||||
describe("should work for static input", () => {
|
||||
const inputs = [
|
||||
@@ -11,6 +12,7 @@ describe("should work for static input", () => {
|
||||
"Hello\nWorld\n",
|
||||
"1",
|
||||
"💕 Red Heart ✨ Sparkles 🔥 Fire\n💕 Red Heart ✨ Sparkles\n💕 Red Heart\n💕\n\nnormal",
|
||||
"a\n§\nb",
|
||||
];
|
||||
|
||||
for (let input of inputs) {
|
||||
@@ -34,6 +36,7 @@ describe("should work for streaming input", () => {
|
||||
"Hello\nWorld\n",
|
||||
"1",
|
||||
"💕 Red Heart ✨ Sparkles 🔥 Fire\n 💕 Red Heart ✨ Sparkles\n 💕 Red Heart\n 💕 \n\nnormal",
|
||||
"a\n§\nb",
|
||||
];
|
||||
|
||||
for (let input of inputs) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// @known-failing-on-windows: 1 failing
|
||||
import { expect, it } from "bun:test";
|
||||
import { bunEnv, bunExe, expectMaxObjectTypeCount, isWindows } from "harness";
|
||||
import { connect, fileURLToPath, SocketHandler, spawn } from "bun";
|
||||
|
||||
@@ -255,16 +255,18 @@ it("import.meta paths have the correct slash", () => {
|
||||
expect(import.meta.url).not.toInclude("\\");
|
||||
});
|
||||
|
||||
it("import.meta is correct in a module that was imported with a query param", async () => {
|
||||
it("import.meta is correct in a module that was imported with a query param esm", async () => {
|
||||
const esm = (await import("./other.js?foo=bar")).default;
|
||||
const cjs = require("./other-cjs.js?foo=bar").meta;
|
||||
|
||||
expect(esm.url).toBe(new URL("./other.js?foo=bar", import.meta.url).toString());
|
||||
expect(cjs.url).toBe(new URL("./other-cjs.js?foo=bar", import.meta.url).toString());
|
||||
expect(esm.path).toBe(join(import.meta.dir, "./other.js"));
|
||||
expect(cjs.path).toBe(join(import.meta.dir, "./other-cjs.js"));
|
||||
expect(esm.dir).toBe(import.meta.dir);
|
||||
expect(cjs.dir).toBe(import.meta.dir);
|
||||
expect(esm.file).toBe("other.js");
|
||||
});
|
||||
|
||||
it("import.meta is correct in a module that was imported with a query param cjs", async () => {
|
||||
const cjs = require("./other-cjs.js?foo=bar").meta;
|
||||
expect(cjs.url).toBe(new URL("./other-cjs.js?foo=bar", import.meta.url).toString());
|
||||
expect(cjs.path).toBe(join(import.meta.dir, "./other-cjs.js"));
|
||||
expect(cjs.dir).toBe(import.meta.dir);
|
||||
expect(cjs.file).toBe("other-cjs.js");
|
||||
});
|
||||
|
||||
22
test/js/bun/shell/commands/exit.test.ts
Normal file
22
test/js/bun/shell/commands/exit.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { $ } from "bun";
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { TestBuilder } from "../test_builder";
|
||||
import { sortedShellOutput } from "../util";
|
||||
import { join } from "path";
|
||||
|
||||
describe("exit", async () => {
|
||||
TestBuilder.command`exit`.exitCode(0).runAsTest("works");
|
||||
|
||||
describe("argument sets exit code", async () => {
|
||||
for (const arg of [0, 2, 11]) {
|
||||
TestBuilder.command`exit ${arg}`.exitCode(arg).runAsTest(`${arg}`);
|
||||
}
|
||||
});
|
||||
|
||||
TestBuilder.command`exit 3 5`.exitCode(1).stderr("exit: too many arguments").runAsTest("too many arguments");
|
||||
|
||||
TestBuilder.command`exit 62757836`.exitCode(204).runAsTest("exit code wraps u8");
|
||||
|
||||
// prettier-ignore
|
||||
TestBuilder.command`exit abc`.exitCode(1).stderr("exit: numeric argument required").runAsTest("numeric argument required");
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
// @known-failing-on-windows: 1 failing
|
||||
// flaky with setTimeout
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
import { createDenoTest } from "deno:harness";
|
||||
const { test, assert, assertEquals, assertThrows } = createDenoTest(import.meta.path);
|
||||
|
||||
@@ -5,8 +5,9 @@ import { realpathSync, mkdtempSync } from "fs";
|
||||
import { tmpdir } from "os";
|
||||
import { join } from "path";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
import { tmpdirSync } from "cli/install/dummy.registry";
|
||||
|
||||
const socket_domain = mkdtempSync(join(realpathSync(tmpdir()), "node-net"));
|
||||
const socket_domain = tmpdirSync("node-net-");
|
||||
|
||||
it("should support net.isIP()", () => {
|
||||
expect(isIP("::1")).toBe(6);
|
||||
|
||||
12
test/js/third_party/esbuild/esbuild-test.js
vendored
12
test/js/third_party/esbuild/esbuild-test.js
vendored
@@ -1,6 +1,7 @@
|
||||
import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
|
||||
{
|
||||
console.log(1);
|
||||
const result = await transform("console.log('hello world')", {
|
||||
loader: "js",
|
||||
target: "node12",
|
||||
@@ -11,7 +12,8 @@ import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
}
|
||||
|
||||
{
|
||||
const hugeString = `console.log(${JSON.stringify("a".repeat(1000000))});`;
|
||||
console.log(2);
|
||||
const hugeString = `console.log("${"a".repeat(1000000)}");`;
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const result = await transform(hugeString, {
|
||||
@@ -25,6 +27,7 @@ import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
}
|
||||
|
||||
{
|
||||
console.log(3);
|
||||
const result = transformSync("console.log('hello world')", {
|
||||
loader: "js",
|
||||
target: "node12",
|
||||
@@ -35,6 +38,7 @@ import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
}
|
||||
|
||||
{
|
||||
console.log(4);
|
||||
const result = await build({
|
||||
stdin: {
|
||||
"contents": "console.log('hello world')",
|
||||
@@ -50,9 +54,10 @@ import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
}
|
||||
|
||||
{
|
||||
const contents = `console.log(${JSON.stringify("a".repeat(1000000))});`;
|
||||
const contents = `console.log("${"a".repeat(1000000)}");`;
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
console.log(5);
|
||||
const result = await build({
|
||||
target: "node12",
|
||||
write: false,
|
||||
@@ -69,7 +74,8 @@ import { build, buildSync, transform, transformSync } from "esbuild";
|
||||
}
|
||||
|
||||
{
|
||||
const result = buildSync({
|
||||
console.log(6);
|
||||
const result = await build({
|
||||
stdin: {
|
||||
"contents": "console.log('hello world')",
|
||||
"loader": "js",
|
||||
|
||||
@@ -7,8 +7,9 @@ import { gzipSync } from "zlib";
|
||||
import { join } from "path";
|
||||
import { gc, withoutAggressiveGC, gcTick, isWindows } from "harness";
|
||||
import net from "net";
|
||||
import { tmpdirSync } from "cli/install/dummy.registry";
|
||||
|
||||
const tmp_dir = mkdtempSync(join(realpathSync(tmpdir()), "fetch.test"));
|
||||
const tmp_dir = tmpdirSync("fetch-test-");
|
||||
|
||||
const fixture = readFileSync(join(import.meta.dir, "fetch.js.txt"), "utf8").replaceAll("\r\n", "\n");
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ describe("napi", () => {
|
||||
beforeAll(() => {
|
||||
// build gyp
|
||||
const install = spawnSync({
|
||||
cmd: ["bun", "install", "--verbose"],
|
||||
cmd: [bunExe(), "install", "--verbose"],
|
||||
cwd: join(__dirname, "napi-app"),
|
||||
stderr: "inherit",
|
||||
env: bunEnv,
|
||||
|
||||
Reference in New Issue
Block a user