Merge branch 'jarred/process-change' of github.com:oven-sh/bun into jarred/process-change

This commit is contained in:
Zack Radisic
2024-02-22 13:16:17 -08:00
73 changed files with 2320 additions and 1449 deletions

View File

@@ -157,7 +157,7 @@ pub const IO = struct {
std: struct { captured: ?*bun.ByteList = null },
/// Write/Read to/from file descriptor
fd: bun.FileDescriptor,
/// Buffers the output
/// Buffers the output (handled in Cmd.BufferedIoClosed.close())
pipe,
/// Discards output
ignore,
@@ -856,7 +856,7 @@ pub const Interpreter = struct {
jsobjs.items[0..],
)) {
.result => |i| i,
.err => |e| {
.err => |*e| {
arena.deinit();
throwShellErr(e, .{ .js = globalThis.bunVM().event_loop });
return null;
@@ -1026,7 +1026,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)) {
.err => |e| {
.err => |*e| {
throwShellErr(e, .{ .mini = mini });
return;
},
@@ -1072,7 +1072,7 @@ pub const Interpreter = struct {
const script_heap = try arena.allocator().create(ast.Script);
script_heap.* = script;
var interp = switch (ThisInterpreter.init(mini, bun.default_allocator, &arena, script_heap, jsobjs)) {
.err => |e| {
.err => |*e| {
throwShellErr(e, .{ .mini = mini });
return;
},
@@ -1610,7 +1610,7 @@ pub const Interpreter = struct {
const shell_state = switch (this.base.shell.dupeForSubshell(this.base.interpreter.allocator, io, .cmd_subst)) {
.result => |s| s,
.err => |e| {
throwShellErr(bun.shell.ShellErr.newSys(e), this.base.eventLoop());
this.base.throw(&bun.shell.ShellErr.newSys(e));
return false;
},
};
@@ -1637,7 +1637,7 @@ pub const Interpreter = struct {
const shell_state = switch (this.base.shell.dupeForSubshell(this.base.interpreter.allocator, io, .cmd_subst)) {
.result => |s| s,
.err => |e| {
throwShellErr(bun.shell.ShellErr.newSys(e), this.base.eventLoop());
this.base.throw(&bun.shell.ShellErr.newSys(e));
return false;
},
};
@@ -1790,15 +1790,15 @@ pub const Interpreter = struct {
std.debug.assert(this.child_state == .glob);
}
if (task.err != null) {
switch (task.err.?) {
if (task.err) |*err| {
switch (err.*) {
.syscall => {
throwShellErr(bun.shell.ShellErr.newSys(task.err.?.syscall), this.base.eventLoop());
this.base.throw(&bun.shell.ShellErr.newSys(task.err.?.syscall));
},
.unknown => |errtag| {
throwShellErr(.{
this.base.throw(&.{
.custom = bun.default_allocator.dupe(u8, @errorName(errtag)) catch bun.outOfMemory(),
}, this.base.eventLoop());
});
},
}
}
@@ -2010,6 +2010,10 @@ pub const Interpreter = struct {
.expansion = expansion,
.result = std.ArrayList([:0]const u8).init(allocator),
};
if (bun.Environment.isWindows) {
// event loop here is js event loop
@panic("TODO SHELL WINDOWS!");
}
// this.ref.ref(this.event_loop.virtual_machine);
this.ref.ref(this.event_loop);
@@ -2050,6 +2054,10 @@ pub const Interpreter = struct {
pub fn runFromMainThread(this: *This) void {
print("runFromJS", .{});
if (bun.Environment.isWindows) {
// event loop here is js event loop
@panic("TODO SHELL WINDOWS!");
}
this.expansion.onGlobWalkDone(this);
// this.ref.unref(this.event_loop.virtual_machine);
this.ref.unref(this.event_loop);
@@ -2086,9 +2094,13 @@ pub const Interpreter = struct {
interpreter: *ThisInterpreter,
shell: *ShellState,
pub inline fn eventLoop(this: *State) JSC.EventLoopHandle {
pub inline fn eventLoop(this: *const State) JSC.EventLoopHandle {
return this.interpreter.event_loop;
}
pub fn throw(this: *const State, err: *const bun.shell.ShellErr) void {
throwShellErr(err, this.eventLoop());
}
};
pub const Script = struct {
@@ -2766,7 +2778,7 @@ pub const Interpreter = struct {
}
if (err) |e| {
throwShellErr(shell.ShellErr.newSys(e), this.base.eventLoop());
this.base.throw(&shell.ShellErr.newSys(e));
return;
}
@@ -3202,7 +3214,7 @@ pub const Interpreter = struct {
pub fn onBufferedWriterDone(this: *Cmd, e: ?Syscall.Error) void {
if (e) |err| {
throwShellErr(bun.shell.ShellErr.newSys(err), this.base.eventLoop());
this.base.throw(&bun.shell.ShellErr.newSys(err));
return;
}
std.debug.assert(this.state == .waiting_write_err);
@@ -3461,8 +3473,8 @@ pub const Interpreter = struct {
} };
const subproc = switch (Subprocess.spawnAsync(this.base.eventLoop(), spawn_args, &this.exec.subproc.child)) {
.result => this.exec.subproc.child,
.err => |e| {
throwShellErr(e, this.base.eventLoop());
.err => |*e| {
this.base.throw(e);
return;
},
};
@@ -3975,8 +3987,8 @@ pub const Interpreter = struct {
const path = cmd.redirection_file.items[0..cmd.redirection_file.items.len -| 1 :0];
log("EXPANDED REDIRECT: {s}\n", .{cmd.redirection_file.items[0..]});
const perm = 0o666;
const extra: bun.Mode = if (node.redirect.append) std.os.O.APPEND else std.os.O.TRUNC;
const redirfd = switch (Syscall.openat(cmd.base.shell.cwd_fd, path, std.os.O.WRONLY | std.os.O.CREAT | extra, perm)) {
const flags = node.redirect.toFlags();
const redirfd = switch (Syscall.openat(cmd.base.shell.cwd_fd, path, flags, perm)) {
.err => |e| {
const buf = std.fmt.allocPrint(arena.allocator(), "bun: {s}: {s}", .{ e.toSystemError().message, path }) catch bun.outOfMemory();
cmd.writeFailingError(buf, 1);
@@ -3996,12 +4008,11 @@ pub const Interpreter = struct {
}
},
.jsbuf => |val| {
if (cmd.base.eventLoop() == .mini) @panic("This should never happened");
const global = cmd.base.eventLoop().js.global;
if (interpreter.jsobjs[file.jsbuf.idx].asArrayBuffer(global)) |buf| {
const globalObject = interpreter.event_loop.js.global;
if (interpreter.jsobjs[file.jsbuf.idx].asArrayBuffer(globalObject)) |buf| {
const builtinio: Builtin.BuiltinIO = .{ .arraybuf = .{ .buf = JSC.ArrayBuffer.Strong{
.array_buffer = buf,
.held = JSC.Strong.create(buf.value, global),
.held = JSC.Strong.create(buf.value, globalObject),
}, .i = 0 } };
if (node.redirect.stdin) {
@@ -4031,21 +4042,38 @@ pub const Interpreter = struct {
}
} else {
const jsval = cmd.base.interpreter.jsobjs[val.idx];
global.throw("Unknown JS value used in shell: {}", .{jsval.fmtString(global)});
cmd.base.interpreter.event_loop.js.global.throw("Unknown JS value used in shell: {}", .{jsval.fmtString(globalObject)});
return .yield;
}
},
}
} else if (node.redirect.duplicate_out) {
if (node.redirect.stdout) {
cmd.exec.bltn.stderr = cmd.exec.bltn.stdout;
}
if (node.redirect.stderr) {
cmd.exec.bltn.stdout = cmd.exec.bltn.stderr;
}
}
return .cont;
}
pub inline fn eventLoop(this: *Builtin) JSC.EventLoopHandle {
pub inline fn eventLoop(this: *const Builtin) JSC.EventLoopHandle {
return this.parentCmd().base.eventLoop();
}
pub inline fn parentCmd(this: *Builtin) *Cmd {
pub inline fn throw(this: *const Builtin, err: *const bun.shell.ShellErr) void {
this.parentCmd().base.throw(err);
}
pub inline fn parentCmd(this: *const Builtin) *const Cmd {
const union_ptr = @fieldParentPtr(Cmd.Exec, "bltn", this);
return @fieldParentPtr(Cmd, "exec", union_ptr);
}
pub inline fn parentCmdMut(this: *Builtin) *Cmd {
const union_ptr = @fieldParentPtr(Cmd.Exec, "bltn", this);
return @fieldParentPtr(Cmd, "exec", union_ptr);
}
@@ -4056,7 +4084,7 @@ pub const Interpreter = struct {
// }
this.exit_code = exit_code;
var cmd = this.parentCmd();
var cmd = this.parentCmdMut();
log("builtin done ({s}: exit={d}) cmd to free: ({x})", .{ @tagName(this.kind), exit_code, @intFromPtr(cmd) });
cmd.exit_code = this.exit_code.?;
@@ -4913,11 +4941,11 @@ pub const Interpreter = struct {
if (paths) |p| {
for (p) |path_raw| {
const path = path_raw[0..std.mem.len(path_raw) :0];
var task = ShellLsTask.create(this, this.opts, &this.state.exec.task_count, cwd, path, this.bltn.parentCmd().base.eventLoop());
var task = ShellLsTask.create(this, this.opts, &this.state.exec.task_count, cwd, path, this.bltn.eventLoop());
task.schedule();
}
} else {
var task = ShellLsTask.create(this, this.opts, &this.state.exec.task_count, cwd, ".", this.bltn.parentCmd().base.eventLoop());
var task = ShellLsTask.create(this, this.opts, &this.state.exec.task_count, cwd, ".", this.bltn.eventLoop());
task.schedule();
}
},
@@ -5027,7 +5055,7 @@ pub const Interpreter = struct {
// if (!need_to_write_to_stdout_with_io) return; // yield execution
} else {
if (this.bltn.writeNoIO(.stderr, error_string).asErr()) |theerr| {
throwShellErr(bun.shell.ShellErr.newSys(theerr), this.bltn.eventLoop());
this.bltn.throw(&bun.shell.ShellErr.newSys(theerr));
}
}
}
@@ -5058,7 +5086,7 @@ pub const Interpreter = struct {
defer output.deinit();
if (this.bltn.writeNoIO(.stdout, output.items[0..]).asErr()) |e| {
throwShellErr(bun.shell.ShellErr.newSys(e), this.bltn.eventLoop());
this.bltn.throw(&bun.shell.ShellErr.newSys(e));
return;
}
@@ -6711,7 +6739,7 @@ pub const Interpreter = struct {
const error_string = this.bltn.taskErrorToString(.rm, err);
if (!this.bltn.stderr.needsIO()) {
if (this.bltn.writeNoIO(.stderr, error_string).asErr()) |e| {
throwShellErr(bun.shell.ShellErr.newSys(e), this.bltn.parentCmd().base.eventLoop());
this.bltn.throw(&bun.shell.ShellErr.newSys(e));
return;
}
} else {
@@ -6747,7 +6775,7 @@ pub const Interpreter = struct {
fn writeVerbose(this: *Rm, verbose: *ShellRmTask.DirTask) void {
if (!this.bltn.stdout.needsIO()) {
if (this.bltn.writeNoIO(.stdout, verbose.deleted_entries.items[0..]).asErr()) |err| {
throwShellErr(bun.shell.ShellErr.newSys(err), this.bltn.parentCmd().base.eventLoop());
this.bltn.parentCmd().base.throw(&bun.shell.ShellErr.newSys(err));
return;
}
// _ = this.state.exec.output_done.fetchAdd(1, .SeqCst);
@@ -7377,8 +7405,7 @@ pub const Interpreter = struct {
/// it. IT DOES NOT CLOSE FILE DESCRIPTORS
pub const BufferedWriter =
struct {
pseudoref_count: u32 = 1,
writer: Writer = .{
writer: Writer = if (bun.Environment.isWindows) .{} else .{
.close_fd = false,
},
fd: bun.FileDescriptor = bun.invalid_fd,
@@ -7613,7 +7640,7 @@ pub fn MaybeChild(comptime T: type) type {
pub fn closefd(fd: bun.FileDescriptor) void {
if (Syscall.close2(fd)) |err| {
_ = err;
log("ERR closefd: {d}\n", .{fd});
log("ERR closefd: {}\n", .{fd});
// stderr_mutex.lock();
// defer stderr_mutex.unlock();
// const stderr = std.io.getStdErr().writer();
@@ -7696,6 +7723,11 @@ pub fn ShellTask(
pub fn schedule(this: *@This()) void {
print("schedule", .{});
if (bun.Environment.isWindows) {
// event loop here is js event loop
@panic("TODO SHELL WINDOWS!");
}
this.ref.ref(this.event_loop);
WorkPool.schedule(&this.task);
}
@@ -7742,7 +7774,7 @@ inline fn fastMod(val: anytype, comptime rhs: comptime_int) @TypeOf(val) {
return val & (rhs - 1);
}
fn throwShellErr(e: bun.shell.ShellErr, event_loop: JSC.EventLoopHandle) void {
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),

View File

@@ -67,8 +67,8 @@ pub const ShellErr = union(enum) {
}
}
pub fn throwJS(this: @This(), globalThis: *JSC.JSGlobalObject) void {
switch (this) {
pub fn throwJS(this: *const @This(), globalThis: *JSC.JSGlobalObject) void {
switch (this.*) {
.sys => {
const err = this.sys.toErrorInstance(globalThis);
globalThis.throwValue(err);
@@ -77,7 +77,7 @@ pub const ShellErr = union(enum) {
var str = JSC.ZigString.init(this.custom);
str.markUTF8();
const err_value = str.toErrorInstance(globalThis);
globalThis.vm().throwError(globalThis, err_value);
globalThis.throwValue(err_value);
// this.bunVM().allocator.free(JSC.ZigString.untagged(str._unsafe_ptr_do_not_use)[0..str.len]);
},
.invalid_arguments => {
@@ -441,7 +441,33 @@ pub const AST = struct {
stdout: bool = false,
stderr: bool = false,
append: bool = false,
__unused: u4 = 0,
/// 1>&2 === stdout=true and duplicate_out=true
/// 2>&1 === stderr=true and duplicate_out=true
duplicate_out: bool = false,
__unused: u3 = 0,
pub fn redirectsElsewhere(this: RedirectFlags, io_kind: enum { stdin, stdout, stderr }) bool {
return switch (io_kind) {
.stdin => this.stdin,
.stdout => if (this.duplicate_out) !this.stdout else this.stdout,
.stderr => if (this.duplicate_out) !this.stderr else this.stderr,
};
}
pub fn @"2>&1"() RedirectFlags {
return .{ .stderr = true, .duplicate = true };
}
pub fn @"1>&2"() RedirectFlags {
return .{ .stdout = true, .duplicate = true };
}
pub fn toFlags(this: RedirectFlags) bun.Mode {
const read_write_flags: bun.Mode = if (this.stdin) std.os.O.RDONLY else std.os.O.WRONLY | std.os.O.CREAT;
const extra: bun.Mode = if (this.append) std.os.O.APPEND else std.os.O.TRUNC;
const final_flags: bun.Mode = if (this.stdin) read_write_flags else extra | read_write_flags;
return final_flags;
}
pub fn @"<"() RedirectFlags {
return .{ .stdin = true };
@@ -784,6 +810,7 @@ pub const Parser = struct {
}
const redirect_file = try self.parse_atom() orelse {
if (redirect.duplicate_out) break :redirect_file null;
try self.add_error("Redirection with no file", .{});
return ParseError.Expected;
};
@@ -801,6 +828,8 @@ pub const Parser = struct {
} };
}
const ParsedRedirect = struct { flags: AST.Cmd.RedirectFlags, redirect: AST.Cmd.Redirect };
/// Try to parse an assignment. If no assignment could be parsed then return
/// null and backtrack the parser state
fn parse_assign(self: *Parser) !?AST.Assign {
@@ -1804,9 +1833,67 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
return false;
}
// TODO Arbitrary file descriptor redirect
fn eat_redirect(self: *@This(), first: InputChar) ?AST.Cmd.RedirectFlags {
var flags: AST.Cmd.RedirectFlags = .{};
switch (first.char) {
'0' => flags.stdin = true,
'1' => flags.stdout = true,
'2' => flags.stderr = true,
// Just allow the std file descriptors for now
else => return null,
}
var dir: RedirectDirection = .out;
if (self.peek()) |input| {
if (input.escaped) return null;
switch (input.char) {
'>' => {
_ = self.eat();
dir = .out;
const is_double = self.eat_simple_redirect_operator(dir);
if (is_double) flags.append = true;
if (self.peek()) |peeked| {
if (!peeked.escaped and peeked.char == '&') {
_ = self.eat();
if (self.peek()) |peeked2| {
switch (peeked2.char) {
'1' => {
_ = self.eat();
if (!flags.stdout and flags.stderr) {
flags.duplicate_out = true;
flags.stdout = true;
flags.stderr = false;
} else return null;
},
'2' => {
_ = self.eat();
if (!flags.stderr and flags.stdout) {
flags.duplicate_out = true;
flags.stderr = true;
flags.stdout = false;
} else return null;
},
else => return null,
}
}
}
}
return flags;
},
'<' => {
dir = .in;
const is_double = self.eat_simple_redirect_operator(dir);
if (is_double) flags.append = true;
return flags;
},
else => return null,
}
} else return null;
}
fn eat_redirect_old(self: *@This(), first: InputChar) ?AST.Cmd.RedirectFlags {
var flags: AST.Cmd.RedirectFlags = .{};
if (self.matchesAsciiLiteral("2>&1")) {} else if (self.matchesAsciiLiteral("1>&2")) {} else switch (first.char) {
'0'...'9' => {
// Codepoint int casts are safe here because the digits are in the ASCII range
var count: usize = 1;
@@ -1952,13 +2039,25 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
fn appendStringToStrPool(self: *@This(), bunstr: bun.String) !void {
const start = self.strpool.items.len;
if (bunstr.is8Bit() or bunstr.isUTF8()) {
try self.strpool.appendSlice(bunstr.byteSlice());
} else {
if (bunstr.isUTF16()) {
const utf16 = bunstr.utf16();
const additional = bun.simdutf.simdutf__utf8_length_from_utf16le(utf16.ptr, utf16.len);
try self.strpool.ensureUnusedCapacity(additional);
try bun.strings.convertUTF16ToUTF8Append(&self.strpool, bunstr.utf16());
} else if (bunstr.isUTF8()) {
try self.strpool.appendSlice(bunstr.byteSlice());
} else if (bunstr.is8Bit()) {
if (isAllAscii(bunstr.byteSlice())) {
try self.strpool.appendSlice(bunstr.byteSlice());
} else {
const bytes = bunstr.byteSlice();
const non_ascii_idx = bun.strings.firstNonASCII(bytes) orelse 0;
if (non_ascii_idx > 0) {
try self.strpool.appendSlice(bytes[0..non_ascii_idx]);
}
self.strpool = try bun.strings.allocateLatin1IntoUTF8WithList(self.strpool, self.strpool.items.len, []const u8, bytes[non_ascii_idx..]);
}
}
const end = self.strpool.items.len;
self.j += @intCast(end - start);
@@ -1980,10 +2079,33 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
return std.mem.eql(u8, bytes[0 .. LEX_JS_STRING_PREFIX.len - 1], LEX_JS_STRING_PREFIX[1..]);
}
fn eatJSSubstitutionIdx(self: *@This(), comptime literal: []const u8, comptime name: []const u8, comptime validate: *const fn (*@This(), usize) bool) ?usize {
fn bumpCursorAscii(self: *@This(), new_idx: usize, prev_ascii_char: ?u7, cur_ascii_char: u7) void {
if (comptime encoding == .ascii) {
self.chars.src.i = new_idx;
if (prev_ascii_char) |pc| self.chars.prev = .{ .char = pc };
self.chars.current = .{ .char = cur_ascii_char };
return;
}
self.chars.src.cursor = CodepointIterator.Cursor{
.i = @intCast(new_idx),
.c = cur_ascii_char,
.width = 1,
};
self.chars.src.next_cursor = self.chars.src.cursor;
SrcUnicode.nextCursor(&self.chars.src.iter, &self.chars.src.next_cursor);
if (prev_ascii_char) |pc| self.chars.prev = .{ .char = pc };
self.chars.current = .{ .char = cur_ascii_char };
}
fn matchesAsciiLiteral(self: *@This(), literal: []const u8) bool {
const bytes = self.chars.srcBytesAtCursor();
if (literal.len - 1 >= bytes.len) return null;
if (std.mem.eql(u8, bytes[0 .. literal.len - 1], literal[1..])) {
if (literal.len >= bytes.len) return false;
return std.mem.eql(u8, bytes[0..literal.len], literal[0..]);
}
fn eatJSSubstitutionIdx(self: *@This(), comptime literal: []const u8, comptime name: []const u8, comptime validate: *const fn (*@This(), usize) bool) ?usize {
if (self.matchesAsciiLiteral(literal[1..literal.len])) {
const bytes = self.chars.srcBytesAtCursor();
var i: usize = 0;
var digit_buf: [32]u8 = undefined;
var digit_buf_count: u8 = 0;
@@ -2024,26 +2146,10 @@ pub fn NewLexer(comptime encoding: StringEncoding) type {
// }
// Bump the cursor
brk: {
const new_idx = self.chars.cursorPos() + i;
const prev_ascii_char: ?u7 = if (digit_buf_count == 1) null else @truncate(digit_buf[digit_buf_count - 2]);
const cur_ascii_char: u7 = @truncate(digit_buf[digit_buf_count - 1]);
if (comptime encoding == .ascii) {
self.chars.src.i = new_idx;
if (prev_ascii_char) |pc| self.chars.prev = .{ .char = pc };
self.chars.current = .{ .char = cur_ascii_char };
break :brk;
}
self.chars.src.cursor = CodepointIterator.Cursor{
.i = @intCast(new_idx),
.c = cur_ascii_char,
.width = 1,
};
self.chars.src.next_cursor = self.chars.src.cursor;
SrcUnicode.nextCursor(&self.chars.src.iter, &self.chars.src.next_cursor);
if (prev_ascii_char) |pc| self.chars.prev = .{ .char = pc };
self.chars.current = .{ .char = cur_ascii_char };
}
const new_idx = self.chars.cursorPos() + i;
const prev_ascii_char: ?u7 = if (digit_buf_count == 1) null else @truncate(digit_buf[digit_buf_count - 2]);
const cur_ascii_char: u7 = @truncate(digit_buf[digit_buf_count - 1]);
self.bumpCursorAscii(new_idx, prev_ascii_char, cur_ascii_char);
// return self.string_refs[idx];
return idx;
@@ -2891,16 +2997,15 @@ const SPECIAL_CHARS = [_]u8{ '$', '>', '&', '|', '=', ';', '\n', '{', '}', ',',
const BACKSLASHABLE_CHARS = [_]u8{ '$', '`', '"', '\\' };
pub fn escapeBunStr(bunstr: bun.String, outbuf: *std.ArrayList(u8), comptime add_quotes: bool) !bool {
// latin-1 or ascii
if (bunstr.is8Bit()) {
try escape8Bit(bunstr.byteSlice(), outbuf, add_quotes);
return true;
}
if (bunstr.isUTF16()) {
return try escapeUtf16(bunstr.utf16(), outbuf, add_quotes);
}
// Otherwise is utf-8
try escapeWTF8(bunstr.byteSlice(), outbuf, add_quotes);
if (bunstr.isUTF8()) {
try escapeWTF8(bunstr.byteSlice(), outbuf, add_quotes);
return true;
}
// otherwise should be latin-1 or ascii
try escape8Bit(bunstr.byteSlice(), outbuf, add_quotes);
return true;
}

View File

@@ -520,9 +520,9 @@ pub const ShellSubprocess = struct {
var spawn_options = bun.spawn.SpawnOptions{
.cwd = spawn_args.cwd,
.stdin = spawn_args.stdio[0].toPosix(),
.stdout = spawn_args.stdio[1].toPosix(),
.stderr = spawn_args.stdio[2].toPosix(),
.stdin = spawn_args.stdio[0].asSpawnOption(),
.stdout = spawn_args.stdio[1].asSpawnOption(),
.stderr = spawn_args.stdio[2].asSpawnOption(),
};
spawn_args.argv.append(allocator, null) catch {

View File

@@ -14,7 +14,16 @@ const Output = @import("root").bun.Output;
const PosixSpawn = @import("../bun.js/api/bun/spawn.zig").PosixSpawn;
const os = std.os;
pub const OutKind = enum { stdout, stderr };
pub const OutKind = enum {
stdout,
stderr,
pub fn toFd(this: OutKind) bun.FileDescriptor {
return switch (this) {
.stdout => bun.STDOUT_FD,
.stderr => bun.STDERR_FD,
};
}
};
pub const Stdio = bun.spawn.Stdio;