Compare commits

...

94 Commits

Author SHA1 Message Date
Meghan Denny
99b57b09e9 Merge branch 'main' into nektro-patch-54065 2024-03-28 22:17:36 -07:00
Meghan Denny
3cc7649a68 remove this duplicate section 2024-03-28 21:28:02 -07:00
Meghan Denny
fd8a61898e fix which regression 2024-03-28 17:37:00 -07:00
autofix-ci[bot]
209ea3d1f9 [autofix.ci] apply automated fixes 2024-03-28 23:39:06 +00:00
Meghan Denny
ef509c5b3e add progress indicator and skip unnecessary JSON.stringify call 2024-03-28 16:24:08 -07:00
Meghan Denny
82464d0d39 this file passes now 2024-03-28 16:23:40 -07:00
Meghan Denny
9d5c6f911a rework which again 2024-03-28 16:23:14 -07:00
Meghan Denny
a3bce4ae4c remove this hack 2024-03-28 16:22:42 -07:00
Meghan Denny
59a1670a9f skip running that function on windows, we know it'll return null 2024-03-28 16:21:25 -07:00
Meghan Denny
ba33af4205 this was needed 2024-03-27 19:40:19 -07:00
Meghan Denny
b236911056 this doesn't need to be duped 2024-03-27 19:40:12 -07:00
Meghan Denny
3920288701 no reason to make these undefined 2024-03-27 19:39:55 -07:00
Meghan Denny
f8f1a046bb fix posix test 2024-03-27 17:17:28 -07:00
Meghan Denny
1ea6b09d09 mark new passing tests as passing 2024-03-27 17:14:41 -07:00
Meghan Denny
4c1b1c5310 move windows bin linking shim should work to its own file 2024-03-27 17:14:24 -07:00
Meghan Denny
2f26aba2ce welcome back friends 2024-03-27 14:40:32 -07:00
Meghan Denny
6ed2e1fe02 fix bad merge conflict resolution 2024-03-27 14:40:22 -07:00
Meghan Denny
48aef89bb7 let ci take the wheel 2024-03-27 12:28:24 -07:00
Meghan Denny
fdc9ae0869 shell is bash on this step 2024-03-26 23:53:46 -07:00
Meghan Denny
2298d9959a bye for now 2024-03-26 23:53:35 -07:00
Meghan Denny
98c81af971 try in the same step 2024-03-26 23:24:01 -07:00
Meghan Denny
46506db1e8 Merge branch 'main' into nektro-patch-54065 2024-03-26 22:52:41 -07:00
Meghan Denny
e6679b32c9 another tmpdirSync 2024-03-26 22:50:58 -07:00
Meghan Denny
70003f99db ci: troubleshooting time 2024-03-26 22:50:58 -07:00
Meghan Denny
d7d347f5e5 Merge branch 'main' into nektro-patch-54065 2024-03-26 20:59:12 -07:00
Meghan Denny
ce26cc1881 ci: verdaccio take 5 2024-03-26 20:55:12 -07:00
Meghan Denny
7d45fbc83b use tmpdirSync from harness more 2024-03-26 20:54:05 -07:00
Meghan Denny
f06cbc7d86 remove need for some constCast's 2024-03-26 20:44:38 -07:00
Meghan Denny
9a6005d1fc ci: verdaccio take 4 2024-03-26 18:56:21 -07:00
Meghan Denny
5763765b5b ci: verdaccio take 3 2024-03-26 18:32:57 -07:00
Meghan Denny
0664716e0f ci: use verdaccio service container instead of shell background task 2024-03-26 17:47:07 -07:00
Meghan Denny
66520fba41 test: bun-install-registry: dont delete packageDir 2024-03-26 17:46:35 -07:00
Meghan Denny
733460ff83 napi: use bunExe() 2024-03-26 17:46:16 -07:00
Meghan Denny
7e2fe73288 ci: windows: use bun install 2024-03-25 18:50:58 -07:00
Meghan Denny
b1ca24c864 this passed on windows and now its not 2024-03-25 18:50:54 -07:00
Meghan Denny
64b0410176 fix spawn-streaming-stdin.test.ts regression 2024-03-25 18:50:13 -07:00
Meghan Denny
ee7b1ee1b6 Merge branch 'main' into nektro-patch-54065 2024-03-25 16:04:02 -07:00
Meghan Denny
fef6bf3d1d skip this one on windows too 2024-03-25 16:02:20 -07:00
Meghan Denny
b35a3679bc fixes 2024-03-23 06:06:40 -07:00
Meghan Denny
404972458a bundler: add the extension from the build result 2024-03-23 03:16:11 -07:00
Meghan Denny
50eb4f8e82 this function expects the extension 2024-03-23 03:14:28 -07:00
Meghan Denny
29cf8ee84c tidy 2024-03-23 03:14:04 -07:00
Meghan Denny
d2f0f2a4dd windows: ensure the bun-node-debug folder is always fresh 2024-03-23 03:13:46 -07:00
Meghan Denny
9d9d520d0b ub memory fixes 2024-03-23 03:13:11 -07:00
Meghan Denny
5bcdd0501f these were calling different functions 2024-03-23 03:12:47 -07:00
Meghan Denny
6de2fe0fbb verdaccio is actually installed in the test folder 2024-03-23 03:11:54 -07:00
autofix-ci[bot]
898eba0cf9 [autofix.ci] apply automated fixes 2024-03-23 03:24:47 +00:00
Meghan Denny
a0302fded1 Merge branch 'main' into nektro-patch-54065 2024-03-22 20:23:31 -07:00
Meghan Denny
418c83e072 start verdaccio in ci instead of in test block 2024-03-22 20:07:39 -07:00
Meghan Denny
57b79151d3 forceWaiterThread bun-install-registry tests can be turned back on 2024-03-22 17:22:22 -07:00
Meghan Denny
65dcdff8cf windows: pass test/cli/install/bun-run-bunfig.test.ts 2024-03-22 17:21:58 -07:00
Meghan Denny
4302a43374 which: windows: need to make sure the absolute path exists 2024-03-22 17:21:30 -07:00
Meghan Denny
d71846757c shell: windows: use case insensitive env map 2024-03-22 17:21:03 -07:00
Meghan Denny
1916a68cfd make 'bun add' also not print a crash trace when pm fails cleanly 2024-03-22 17:19:55 -07:00
Meghan Denny
b475a769eb fix recurring finalize crash 2024-03-22 17:18:42 -07:00
Meghan Denny
3a8691fe65 Merge branch 'main' into nektro-patch-22129 2024-03-21 21:36:59 -07:00
Meghan Denny
e4ecd46bd6 tidy 2024-03-21 21:34:55 -07:00
Meghan Denny
d0cab7c108 these pass now 2024-03-21 21:33:41 -07:00
Meghan Denny
8817c77124 skip these because they need if statements in the shell 2024-03-21 21:33:08 -07:00
Meghan Denny
868c694157 disable the test that crashes, unrelated bug 2024-03-21 21:32:48 -07:00
Meghan Denny
51a7a01c7e import-meta.test.js: split these two
only one is failing on windows
2024-03-21 21:30:17 -07:00
Meghan Denny
30c5f39d4c windows: pass bun-run.test.ts 2024-03-21 21:29:33 -07:00
Meghan Denny
3438f9b5ab which: windows: dont check for files with no extensions but do if it matches the extensions we like 2024-03-21 21:28:59 -07:00
Meghan Denny
a2a8f95d41 which: succeed right away if given an absolute path 2024-03-21 21:28:25 -07:00
Meghan Denny
709afb9c71 shell: no need to allocate these messages 2024-03-21 21:27:22 -07:00
Meghan Denny
02db0e43df windows: use directory symlinks for directories 2024-03-21 21:26:31 -07:00
Meghan Denny
935146e9ab we actually did need this 2024-03-21 21:25:20 -07:00
Meghan Denny
b67739b7db update this check for windows too 2024-03-21 21:25:02 -07:00
Meghan Denny
632bdee900 i backed out of bun exec for now 2024-03-21 21:24:47 -07:00
Meghan Denny
7f6394e761 implement this todo 2024-03-21 21:24:28 -07:00
Meghan Denny
1936940043 more tests 2024-03-20 23:43:19 -07:00
Meghan Denny
9168a64f61 more tests 2024-03-20 23:27:12 -07:00
Meghan Denny
53ed865927 these pass now 2024-03-20 23:26:37 -07:00
Meghan Denny
65889dc803 more robust 2024-03-20 23:25:32 -07:00
Meghan Denny
1590631cf9 those arent necessary anymore 2024-03-20 23:24:45 -07:00
Meghan Denny
a6f15e4277 these weren't necessary 2024-03-20 23:24:05 -07:00
Meghan Denny
1fc547fdd9 shell: implement exit builtin 2024-03-20 23:21:41 -07:00
Meghan Denny
49ee19cb41 fix path error here 2024-03-20 23:20:06 -07:00
Meghan Denny
40dcadf0da temp skip some tests failing on windows 2024-03-20 17:54:20 -07:00
Meghan Denny
e8115529d3 fix windows build from poxix build fix 2024-03-20 17:53:19 -07:00
Meghan Denny
7c4616b2a1 Merge branch 'main' into nektro-patch-22129 2024-03-20 01:33:27 -07:00
Meghan Denny
b8b8b26f85 small fixes 2024-03-20 01:31:45 -07:00
Meghan Denny
714331909f add ensureTempNodeGypScript test 2024-03-20 01:30:55 -07:00
Meghan Denny
b3e53e1eb5 improve shebang script writing 2024-03-20 01:30:06 -07:00
Meghan Denny
5332a17551 remove bun exec 2024-03-20 01:28:38 -07:00
Meghan Denny
203022e04c use bun run instead of bun exec 2024-03-20 01:27:46 -07:00
Meghan Denny
c0e84c9076 windows: run: fix path delimeter placement 2024-03-20 01:26:16 -07:00
Meghan Denny
c861a6fcda use bun.exe_suffix in bunx 2024-03-20 01:25:37 -07:00
Meghan Denny
f1a61ceb8e fix selfExePath 2024-03-20 01:25:17 -07:00
Dylan Conway
d2ff2cc1dc fix invalid pointer 2024-03-19 23:16:07 -07:00
Meghan Denny
0b11991811 commit exec_command.zig 2024-03-19 22:23:20 -07:00
Meghan Denny
186456788e use 'bun exec' to run lifecycle scripts on windows 2024-03-19 22:20:24 -07:00
Meghan Denny
d6b815268d cli: add 'bun exec' to run bun shell 2024-03-19 22:19:55 -07:00
Meghan Denny
131bea538d memoize calls to selfExePath 2024-03-19 22:16:21 -07:00
48 changed files with 739 additions and 500 deletions

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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"
}
}

View File

@@ -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 => {

View File

@@ -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 => {

View File

@@ -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),

View File

@@ -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"),
);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};
}
};

View File

@@ -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,

View File

@@ -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(),
};
}

View File

@@ -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,

View File

@@ -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 {};

View File

@@ -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 |_| {}
}

View File

@@ -756,7 +756,8 @@ pub const UpgradeCommand = struct {
}
}
const destination_executable = std.fs.selfExePath(&current_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);

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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 {},

View File

@@ -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,
});

View File

@@ -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);
},
}

View File

@@ -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,

View File

@@ -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),

View File

@@ -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;
}

View File

@@ -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: {

View File

@@ -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: {

View File

@@ -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(() => {

View File

@@ -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 () => {

View File

@@ -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));
}
},

View File

@@ -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();

View File

@@ -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}\`);
}

View File

@@ -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";

View File

@@ -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,
);

View 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);
});
}
});

View File

@@ -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 = "";

View File

@@ -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"),

View File

@@ -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) {

View File

@@ -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";

View File

@@ -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");
});

View 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");
});

View File

@@ -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);

View File

@@ -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);

View File

@@ -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",

View File

@@ -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");

View File

@@ -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,