aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/api
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-08-25 21:08:41 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-25 21:08:41 -0700
commitd98a93c3181426ea0565193303d3e63d3796231c (patch)
tree71a8d96bc4b75d902643ec877c03070cca8fcf45 /src/bun.js/api
parentf70bb2497b2406e89afec3ee8a36a3b10ef66334 (diff)
downloadbun-d98a93c3181426ea0565193303d3e63d3796231c.tar.gz
bun-d98a93c3181426ea0565193303d3e63d3796231c.tar.zst
bun-d98a93c3181426ea0565193303d3e63d3796231c.zip
Automatically hot reload Bun.serve() (#4344)
* Automatically hot reload Bun.serve() * Update doc * Update example --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src/bun.js/api')
-rw-r--r--src/bun.js/api/bun.zig60
-rw-r--r--src/bun.js/api/server.classes.ts4
-rw-r--r--src/bun.js/api/server.zig108
3 files changed, 151 insertions, 21 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index a9d7ed970..71b065993 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2481,7 +2481,7 @@ pub fn serve(
callframe: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
const arguments = callframe.arguments(2).slice();
- const config: JSC.API.ServerConfig = brk: {
+ var config: JSC.API.ServerConfig = brk: {
var exception_ = [1]JSC.JSValueRef{null};
var exception = &exception_;
@@ -2497,6 +2497,40 @@ pub fn serve(
var exception_value: *JSC.JSValue = undefined;
+ if (config.allow_hot) {
+ if (globalObject.bunVM().hotMap()) |hot| {
+ if (config.id.len == 0) {
+ config.id = config.computeID(globalObject.allocator());
+ }
+
+ if (hot.getEntry(config.id)) |entry| {
+ switch (entry.tag()) {
+ @field(@TypeOf(entry.tag()), @typeName(JSC.API.HTTPServer)) => {
+ var server: *JSC.API.HTTPServer = entry.as(JSC.API.HTTPServer);
+ server.onReloadFromZig(&config, globalObject);
+ return server.thisObject;
+ },
+ @field(@TypeOf(entry.tag()), @typeName(JSC.API.DebugHTTPServer)) => {
+ var server: *JSC.API.DebugHTTPServer = entry.as(JSC.API.DebugHTTPServer);
+ server.onReloadFromZig(&config, globalObject);
+ return server.thisObject;
+ },
+ @field(@TypeOf(entry.tag()), @typeName(JSC.API.DebugHTTPSServer)) => {
+ var server: *JSC.API.DebugHTTPSServer = entry.as(JSC.API.DebugHTTPSServer);
+ server.onReloadFromZig(&config, globalObject);
+ return server.thisObject;
+ },
+ @field(@TypeOf(entry.tag()), @typeName(JSC.API.HTTPSServer)) => {
+ var server: *JSC.API.HTTPSServer = entry.as(JSC.API.HTTPSServer);
+ server.onReloadFromZig(&config, globalObject);
+ return server.thisObject;
+ },
+ else => {},
+ }
+ }
+ }
+ }
+
// Listen happens on the next tick!
// This is so we can return a Server object
if (config.ssl_config != null) {
@@ -2515,6 +2549,12 @@ pub fn serve(
obj.protect();
server.thisObject = obj;
+
+ if (config.allow_hot) {
+ if (globalObject.bunVM().hotMap()) |hot| {
+ hot.insert(config.id, server);
+ }
+ }
return obj;
} else {
var server = JSC.API.HTTPSServer.init(config, globalObject.ptr());
@@ -2530,6 +2570,12 @@ pub fn serve(
const obj = server.toJS(globalObject);
obj.protect();
server.thisObject = obj;
+
+ if (config.allow_hot) {
+ if (globalObject.bunVM().hotMap()) |hot| {
+ hot.insert(config.id, server);
+ }
+ }
return obj;
}
} else {
@@ -2547,6 +2593,12 @@ pub fn serve(
const obj = server.toJS(globalObject);
obj.protect();
server.thisObject = obj;
+
+ if (config.allow_hot) {
+ if (globalObject.bunVM().hotMap()) |hot| {
+ hot.insert(config.id, server);
+ }
+ }
return obj;
} else {
var server = JSC.API.HTTPServer.init(config, globalObject.ptr());
@@ -2563,6 +2615,12 @@ pub fn serve(
obj.protect();
server.thisObject = obj;
+
+ if (config.allow_hot) {
+ if (globalObject.bunVM().hotMap()) |hot| {
+ hot.insert(config.id, server);
+ }
+ }
return obj;
}
}
diff --git a/src/bun.js/api/server.classes.ts b/src/bun.js/api/server.classes.ts
index 7db8a3444..544f37ce6 100644
--- a/src/bun.js/api/server.classes.ts
+++ b/src/bun.js/api/server.classes.ts
@@ -27,6 +27,10 @@ function generate(name) {
port: {
getter: "getPort",
},
+ id: {
+ getter: "getId",
+ cache: true,
+ },
pendingRequests: {
getter: "getPendingRequests",
},
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index 81a50a5a7..01f06ebf2 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -158,6 +158,36 @@ pub const ServerConfig = struct {
inspector: bool = false,
reuse_port: bool = false,
+ id: []const u8 = "",
+ allow_hot: bool = true,
+
+ pub fn computeID(this: *const ServerConfig, allocator: std.mem.Allocator) []const u8 {
+ var arraylist = std.ArrayList(u8).init(allocator);
+ var writer = arraylist.writer();
+
+ writer.writeAll("[http]-") catch {};
+ switch (this.address) {
+ .tcp => {
+ if (this.address.tcp.hostname) |host| {
+ writer.print("tcp:{s}:{d}", .{
+ bun.sliceTo(host, 0),
+ this.address.tcp.port,
+ }) catch {};
+ } else {
+ writer.print("tcp:localhost:{d}", .{
+ this.address.tcp.port,
+ }) catch {};
+ }
+ },
+ .unix => {
+ writer.print("unix:{s}", .{
+ bun.sliceTo(this.address.unix, 0),
+ }) catch {};
+ },
+ }
+
+ return arraylist.items;
+ }
pub const SSLConfig = struct {
server_name: [*c]const u8 = null,
@@ -794,6 +824,23 @@ pub const ServerConfig = struct {
}
}
+ if (arg.get(global, "id")) |id| {
+ if (id.isUndefinedOrNull()) {
+ args.allow_hot = false;
+ } else {
+ const id_str = id.toSlice(
+ global,
+ bun.default_allocator,
+ );
+
+ if (id_str.len > 0) {
+ args.id = (id_str.cloneIfNeeded(bun.default_allocator) catch unreachable).slice();
+ } else {
+ args.allow_hot = false;
+ }
+ }
+ }
+
if (arg.get(global, "development")) |dev| {
args.development = dev.coerce(bool, global);
args.reuse_port = !args.development;
@@ -4867,26 +4914,8 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
return JSC.jsBoolean(true);
}
- pub fn onReload(
- this: *ThisServer,
- globalThis: *JSC.JSGlobalObject,
- callframe: *JSC.CallFrame,
- ) callconv(.C) JSC.JSValue {
- const arguments = callframe.arguments(1).slice();
- if (arguments.len < 1) {
- globalThis.throwNotEnoughArguments("reload", 1, 0);
- return .zero;
- }
-
- var args_slice = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments);
- defer args_slice.deinit();
- var exception_ref = [_]JSC.C.JSValueRef{null};
- var exception: JSC.C.ExceptionRef = &exception_ref;
- var new_config = ServerConfig.fromJS(globalThis, &args_slice, exception);
- if (exception.* != null) {
- globalThis.throwValue(exception_ref[0].?.value());
- return .zero;
- }
+ pub fn onReloadFromZig(this: *ThisServer, new_config: *ServerConfig, globalThis: *JSC.JSGlobalObject) void {
+ httplog("onReload", .{});
// only reload those two
if (this.config.onRequest != new_config.onRequest) {
@@ -4915,6 +4944,30 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
this.config.websocket = ws.*;
} // we don't remove it
}
+ }
+
+ pub fn onReload(
+ this: *ThisServer,
+ globalThis: *JSC.JSGlobalObject,
+ callframe: *JSC.CallFrame,
+ ) callconv(.C) JSC.JSValue {
+ const arguments = callframe.arguments(1).slice();
+ if (arguments.len < 1) {
+ globalThis.throwNotEnoughArguments("reload", 1, 0);
+ return .zero;
+ }
+
+ var args_slice = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), arguments);
+ defer args_slice.deinit();
+ var exception_ref = [_]JSC.C.JSValueRef{null};
+ var exception: JSC.C.ExceptionRef = &exception_ref;
+ var new_config = ServerConfig.fromJS(globalThis, &args_slice, exception);
+ if (exception.* != null) {
+ globalThis.throwValue(exception_ref[0].?.value());
+ return .zero;
+ }
+
+ this.onReloadFromZig(&new_config, globalThis);
return this.thisObject;
}
@@ -5066,6 +5119,15 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
return JSC.JSValue.jsNumber(listener.getLocalPort());
}
+ pub fn getId(
+ this: *ThisServer,
+ globalThis: *JSC.JSGlobalObject,
+ ) callconv(.C) JSC.JSValue {
+ var str = bun.String.create(this.config.id);
+ defer str.deref();
+ return str.toJS(globalThis);
+ }
+
pub fn getPendingRequests(
this: *ThisServer,
_: *JSC.JSGlobalObject,
@@ -5170,6 +5232,12 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
}
pub fn stop(this: *ThisServer, abrupt: bool) void {
+ if (this.config.allow_hot and this.config.id.len > 0) {
+ if (this.globalThis.bunVM().hotMap()) |hot| {
+ hot.remove(this.config.id);
+ }
+ }
+
this.stopListening(abrupt);
this.deinitIfWeCan();
}