Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions changelog.d/9622-bun-spawn-terminal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Bun subprocess and terminal support

The `bun` compatibility layer now implements `Bun.spawn` in array and object
forms, including consumable output streams, process lifecycle controls, stdio
file descriptors and `Bun.file` sinks, and structured spawn errors. On POSIX
targets, `Bun.Terminal` attaches subprocesses to a native PTY with data, exit,
drain, write, resize, raw-mode, ref/unref, close, and async-disposal support.
2 changes: 2 additions & 0 deletions crates/perry-api-manifest/src/entries/part_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,8 @@ pub(crate) const API_MANIFEST_PART_4: &[ApiEntry] = &[
method("bun", "hash", false, None),
method("bun", "file", false, None),
method("bun", "write", false, None),
method("bun", "spawn", false, None),
method("bun", "Terminal", false, None),
method("bun", "pathToFileURL", false, None),
method("bun", "fileURLToPath", false, None),
// #8537 — OpenCode compatibility coverage added these dispatch rows
Expand Down
18 changes: 18 additions & 0 deletions crates/perry-codegen/src/lower_call/native_table/bun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ use super::*;
/// `Bun.stdin` / `Bun.stdout` / `Bun.stderr` are property reads (handled by
/// `js_native_module_property_by_name`), not rows here.
pub(crate) const BUN_ROWS: &[NativeModSig] = &[
NativeModSig {
module: "bun",
has_receiver: false,
method: "spawn",
class_filter: None,
runtime: "js_bun_spawn",
args: &[NA_F64, NA_F64],
ret: NR_F64,
},
NativeModSig {
module: "bun",
has_receiver: false,
method: "Terminal",
class_filter: None,
runtime: "js_bun_terminal_new",
args: &[NA_F64],
ret: NR_F64,
},
NativeModSig {
module: "bun",
has_receiver: false,
Expand Down
16 changes: 16 additions & 0 deletions crates/perry-hir/src/lower/expr_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,22 @@ pub(super) fn lower_new(ctx: &mut LoweringContext, new_expr: &ast::NewExpr) -> R
}

if let ast::Expr::Ident(callee_ident) = callee_expr {
// Bun.Terminal returns an already-built runtime object. Like
// bun:ffi's explicit-return constructors below, a named import must
// call the native export directly instead of falling through to the
// generic class path, which would manufacture an empty instance.
if matches!(
ctx.lookup_native_module(callee_ident.sym.as_ref()),
Some(("bun", Some("Terminal")))
) {
return Ok(Expr::NativeMethodCall {
module: "bun".to_string(),
class_name: None,
object: None,
method: "Terminal".to_string(),
args: lower_optional_args(ctx, new_expr.args.as_deref())?,
});
}
// Keep Bun's `Database` distinct from better-sqlite3's same-named
// constructor while still allocating the shared native SQLite handle.
if matches!(
Expand Down
15 changes: 11 additions & 4 deletions crates/perry-hir/src/lower/expr_new/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@ pub(crate) fn lower_new_member_native(
(peel_new_callee(member.obj.as_ref()), &member.prop)
{
let obj_name = obj_ident.sym.as_ref();
if obj_name == "Bun"
&& prop_ident.sym.as_ref() == "Glob"
let is_global_bun = obj_name == "Bun"
&& !ctx.shadows_unqualified_global("Bun")
&& ctx.lookup_native_module("Bun").is_none()
&& ctx.lookup_native_module("Bun").is_none();
let is_bun_namespace = ctx.lookup_builtin_module_alias(obj_name) == Some("bun")
|| ctx
.lookup_native_module(obj_name)
.is_some_and(|(module, export)| {
module == "bun" && (export.is_none() || export == Some("default"))
});
if (is_global_bun || is_bun_namespace)
&& matches!(prop_ident.sym.as_ref(), "Glob" | "Terminal")
{
return Ok(Some(Expr::NativeMethodCall {
module: "bun".to_string(),
class_name: None,
object: None,
method: "Glob".to_string(),
method: prop_ident.sym.to_string(),
args: lower_optional_args(ctx, new_expr.args.as_deref())?,
}));
}
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-runtime/src/bun_compat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
//! `.toString(16)` cache keys match bun-run installs.

mod glob;
mod spawn;
mod string_width;
mod width_tables;
mod wyhash;
Expand All @@ -37,6 +38,7 @@ use crate::value::{js_jsvalue_to_string, JSValue};
use std::io::{Read, Write};

pub use glob::js_bun_glob_new;
pub use spawn::{js_bun_spawn, js_bun_terminal_new};
pub use string_width::bun_string_width;
pub use wyhash::wyhash;

Expand Down
Loading
Loading