aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/bun-error/bun-error.css11
-rw-r--r--packages/bun-error/index.tsx50
-rw-r--r--src/__global.zig24
-rw-r--r--src/fallback-backend.html26
-rw-r--r--src/http.zig167
-rw-r--r--src/javascript/jsc/api/server.zig164
-rw-r--r--src/javascript/jsc/base.zig475
-rw-r--r--src/javascript/jsc/javascript.zig13
-rw-r--r--src/javascript/jsc/rare_data.zig3
-rw-r--r--src/js_printer.zig1
-rw-r--r--src/jsc.zig1
-rw-r--r--src/logger.zig6
-rw-r--r--src/open.zig101
-rw-r--r--src/runtime.zig23
14 files changed, 894 insertions, 171 deletions
diff --git a/packages/bun-error/bun-error.css b/packages/bun-error/bun-error.css
index c35dc3843..79a2ffef6 100644
--- a/packages/bun-error/bun-error.css
+++ b/packages/bun-error/bun-error.css
@@ -33,6 +33,17 @@
Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
+.BunErrorRoot--FullPage #BunErrorOverlay-container {
+ position: static;
+ top: unset;
+ right: unset;
+ margin: 60px auto;
+}
+
+.BunError-error-message--quoted {
+ color: rgb(25, 46, 9);
+}
+
:host a {
color: inherit;
}
diff --git a/packages/bun-error/index.tsx b/packages/bun-error/index.tsx
index 832343504..90eb0f004 100644
--- a/packages/bun-error/index.tsx
+++ b/packages/bun-error/index.tsx
@@ -237,6 +237,16 @@ const srcFileURL = (
return new URL("/" + filename, globalThis.location.href).href;
}
+ if (!filename.startsWith("/") && thisCwd) {
+ var orig = filename;
+ filename = thisCwd;
+ if (thisCwd.endsWith("/")) {
+ filename += orig;
+ } else {
+ filename += "/" + orig;
+ }
+ }
+
var base = `/src:${filename}`;
base = appendLineColumnIfNeeded(base, line, column);
@@ -1072,8 +1082,8 @@ const ResolveError = ({ message }: { message: Message }) => {
<div className={`BunError-error-message`}>
Can't import{" "}
- <span className="BunError-error-message--mono">
- {message.on.resolve}
+ <span className="BunError-error-message--mono BunError-error-message--quoted">
+ "{message.on.resolve}"
</span>
</div>
@@ -1189,16 +1199,34 @@ function renderWithFunc(func) {
reactRoot = document.createElement("div");
reactRoot.id = BUN_ERROR_CONTAINER_ID;
- reactRoot.style.visibility = "hidden";
- const link = document.createElement("link");
- link.rel = "stylesheet";
- link.href = new URL("/bun:erro.css", document.baseURI).href;
- link.onload = () => {
- reactRoot.style.visibility = "visible";
- };
+ const fallbackStyleSheet = document.querySelector(
+ "style[data-has-bun-fallback-style]"
+ );
+ if (!fallbackStyleSheet) {
+ reactRoot.style.visibility = "hidden";
+ }
const shadowRoot = root.attachShadow({ mode: "closed" });
- shadowRoot.appendChild(link);
+ if (!fallbackStyleSheet) {
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = new URL("/bun:erro.css", document.baseURI).href;
+ link.onload = () => {
+ reactRoot.style.visibility = "visible";
+ };
+ shadowRoot.appendChild(link);
+ } else {
+ fallbackStyleSheet.remove();
+ shadowRoot.appendChild(fallbackStyleSheet);
+ reactRoot.classList.add("BunErrorRoot--FullPage");
+
+ const page = document.querySelector("style[data-bun-error-page-style]");
+ if (page) {
+ page.remove();
+ shadowRoot.appendChild(page);
+ }
+ }
+
shadowRoot.appendChild(reactRoot);
document.body.appendChild(root);
@@ -1222,6 +1250,8 @@ export function renderFallbackError(fallback: FallbackMessageContainer) {
));
}
+globalThis[Symbol.for("Bun__renderFallbackError")] = renderFallbackError;
+
import { parse as getStackTrace } from "./stack-trace-parser";
var runtimeErrorController: AbortController;
var pending = [];
diff --git a/src/__global.zig b/src/__global.zig
index f9d51ffd7..39ac9496f 100644
--- a/src/__global.zig
+++ b/src/__global.zig
@@ -119,3 +119,27 @@ pub fn crash() noreturn {
const Global = @This();
const string = @import("./global.zig").string;
+
+pub const BunInfo = struct {
+ bun_version: string,
+ platform: Analytics.GenerateHeader.GeneratePlatform.Platform = undefined,
+ framework: string = "",
+ framework_version: string = "",
+
+ const Analytics = @import("./analytics/analytics_thread.zig");
+ const JSON = @import("./json_parser.zig");
+ const JSAst = @import("./js_ast.zig");
+ pub fn generate(comptime Bundler: type, bundler: Bundler, allocator: std.mem.Allocator) !JSAst.Expr {
+ var info = BunInfo{
+ .bun_version = Global.package_json_version,
+ .platform = Analytics.GenerateHeader.GeneratePlatform.forOS(),
+ };
+
+ if (bundler.options.framework) |framework| {
+ info.framework = framework.package;
+ info.framework_version = framework.version;
+ }
+
+ return try JSON.toAST(allocator, BunInfo, info);
+ }
+};
diff --git a/src/fallback-backend.html b/src/fallback-backend.html
new file mode 100644
index 000000000..c7e4d6db7
--- /dev/null
+++ b/src/fallback-backend.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ </head>
+ <body>
+ <script id="__bunfallback" type="binary/peechy">
+ {[blob]s}
+ </script>
+
+ <style data-has-bun-fallback-style>
+ {[bun_error_css]s}
+ </style>
+
+ <style data-bun-error-page-style>
+ {[bun_error_page_css]s}
+ </style>
+
+ <script type="module" async>
+ {[fallback]s};
+ ;{[bun_error]s};
+ ;globalThis[Symbol.for("Bun__renderFallbackError")](globalThis.__BUN_DATA__);
+ </script>
+ </body>
+</html>
diff --git a/src/http.zig b/src/http.zig
index 585df3756..b3b3c0403 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -95,6 +95,7 @@ fn disableSIGPIPESoClosingTheTabDoesntCrash(conn: anytype) void {
&std.mem.toBytes(@as(c_int, 1)),
) catch {};
}
+var http_editor_context: EditorContext = EditorContext{};
pub const RequestContext = struct {
request: Request,
@@ -817,11 +818,11 @@ pub const RequestContext = struct {
pub fn sendJSB(ctx: *RequestContext) !void {
const node_modules_bundle = ctx.bundler.options.node_modules_bundle orelse unreachable;
if (ctx.header("Open-In-Editor") != null) {
- if (Server.editor == null) {
- Server.detectEditor(ctx.bundler.env);
+ if (http_editor_context.editor == null) {
+ http_editor_context.detectEditor(ctx.bundler.env);
}
- if (Server.editor.? != .none) {
+ if (http_editor_context.editor.? != .none) {
var buf: string = "";
if (node_modules_bundle.code_string == null) {
@@ -830,8 +831,8 @@ pub const RequestContext = struct {
buf = node_modules_bundle.code_string.?.str;
}
- Server.openInEditor(
- Server.editor.?,
+ http_editor_context.openInEditor(
+ http_editor_context.editor.?,
buf,
std.fs.path.basename(ctx.url.path),
ctx.bundler.fs.tmpdir(),
@@ -839,7 +840,7 @@ pub const RequestContext = struct {
ctx.header("Editor-Column") orelse "",
);
- if (Server.editor.? != .none) {
+ if (http_editor_context.editor.? != .none) {
try ctx.sendNoContent();
return;
}
@@ -2325,13 +2326,13 @@ pub const RequestContext = struct {
}
if (chunky.rctx.header("Open-In-Editor") != null) {
- if (Server.editor == null) {
- Server.detectEditor(chunky.rctx.bundler.env);
+ if (http_editor_context.editor == null) {
+ http_editor_context.detectEditor(chunky.rctx.bundler.env);
}
- if (Server.editor.? != .none) {
- Server.openInEditor(
- Server.editor.?,
+ if (http_editor_context.editor.? != .none) {
+ http_editor_context.openInEditor(
+ http_editor_context.editor.?,
buf,
std.fs.path.basename(chunky.rctx.url.path),
chunky.rctx.bundler.fs.tmpdir(),
@@ -2339,7 +2340,7 @@ pub const RequestContext = struct {
chunky.rctx.header("Editor-Column") orelse "",
);
- if (Server.editor.? != .none) {
+ if (http_editor_context.editor.? != .none) {
try chunky.rctx.sendNoContent();
return;
}
@@ -2701,8 +2702,8 @@ pub const RequestContext = struct {
}
if (ctx.header("Open-In-Editor") != null) {
- if (Server.editor == null) {
- Server.detectEditor(ctx.bundler.env);
+ if (http_editor_context.editor == null) {
+ http_editor_context.detectEditor(ctx.bundler.env);
}
if (line.len == 0) {
@@ -2717,10 +2718,10 @@ pub const RequestContext = struct {
}
}
- if (Server.editor) |editor| {
+ if (http_editor_context.editor) |editor| {
if (editor != .none) {
- Server.openInEditor(editor, blob.ptr[0..blob.len], id, Fs.FileSystem.instance.tmpdir(), line, column);
- if (Server.editor.? != .none) {
+ http_editor_context.openInEditor(editor, blob.ptr[0..blob.len], id, Fs.FileSystem.instance.tmpdir(), line, column);
+ if (http_editor_context.editor.? != .none) {
defer ctx.done();
try ctx.writeStatus(200);
ctx.appendHeader("Content-Type", MimeType.html.value);
@@ -2866,23 +2867,6 @@ pub const RequestContext = struct {
}
fn sendBunInfoJSON(ctx: *RequestContext) anyerror!void {
- const Info = struct {
- bun_version: string,
- platform: Analytics.GenerateHeader.GeneratePlatform.Platform = undefined,
- framework: string = "",
- framework_version: string = "",
- };
- var info = Info{
- .bun_version = Global.package_json_version,
- .platform = Analytics.GenerateHeader.GeneratePlatform.forOS(),
- };
-
- if (ctx.bundler.options.framework) |framework| {
- info.framework = framework.package;
- info.framework_version = framework.version;
- }
-
- var expr = try JSON.toAST(ctx.allocator, Info, info);
defer ctx.bundler.resetStore();
var buffer_writer = try JSPrinter.BufferWriter.init(default_allocator);
@@ -2890,7 +2874,12 @@ pub const RequestContext = struct {
var writer = JSPrinter.BufferPrinter.init(buffer_writer);
defer writer.ctx.buffer.deinit();
var source = logger.Source.initEmptyFile("info.json");
- _ = try JSPrinter.printJSON(*JSPrinter.BufferPrinter, &writer, expr, &source);
+ _ = try JSPrinter.printJSON(
+ *JSPrinter.BufferPrinter,
+ &writer,
+ try Global.BunInfo.generate(*Bundler, ctx.bundler, ctx.allocator) ,
+ &source,
+ );
const buffer = writer.ctx.written;
ctx.appendHeader("Content-Type", MimeType.json.value);
@@ -2955,20 +2944,20 @@ pub const RequestContext = struct {
.pending => |resolve_result| {
const path = resolve_result.pathConst() orelse return try ctx.sendNotFound();
if (ctx.header("Open-In-Editor") != null) {
- if (Server.editor == null)
- Server.detectEditor(ctx.bundler.env);
+ if (http_editor_context.editor == null)
+ http_editor_context.detectEditor(ctx.bundler.env);
- if (Server.editor) |editor| {
+ if (http_editor_context.editor) |editor| {
if (editor != .none) {
- editor.open(Server.editor_path, path.text, line, column, bun.default_allocator) catch |err| {
+ editor.open(http_editor_context.path, path.text, line, column, bun.default_allocator) catch |err| {
if (editor != .other) {
Output.prettyErrorln("Error {s} opening in {s}", .{ @errorName(err), @tagName(editor) });
}
- Server.editor = Editor.none;
+ http_editor_context.editor = Editor.none;
};
- if (Server.editor.? != .none) {
+ if (http_editor_context.editor.? != .none) {
defer ctx.done();
try ctx.writeStatus(200);
ctx.appendHeader("Content-Type", MimeType.html.value);
@@ -3209,6 +3198,7 @@ var serve_as_package_path = false;
// - Parsing time
// - IO read time
const Editor = @import("./open.zig").Editor;
+const EditorContext = @import("./open.zig").EditorContext;
pub const Server = struct {
log: logger.Log,
@@ -3250,99 +3240,6 @@ pub const Server = struct {
}
}
- pub var editor: ?Editor = null;
- pub var editor_name: string = "";
- pub var editor_path: string = "";
-
- pub fn openInEditor(editor_: Editor, blob: []const u8, id: string, tmpdir: std.fs.Dir, line: string, column: string) void {
- _openInEditor(editor_, blob, id, tmpdir, line, column) catch |err| {
- if (editor_ != .other) {
- Output.prettyErrorln("Error {s} opening in {s}", .{ @errorName(err), @tagName(editor_) });
- }
-
- editor = Editor.none;
- };
- }
-
- fn _openInEditor(editor_: Editor, blob: []const u8, id: string, tmpdir: std.fs.Dir, line: string, column: string) !void {
- var basename_buf: [512]u8 = undefined;
- var basename = std.fs.path.basename(id);
- if (strings.endsWith(basename, ".bun") and basename.len < 499) {
- std.mem.copy(u8, &basename_buf, basename);
- basename_buf[basename.len..][0..3].* = ".js".*;
- basename = basename_buf[0 .. basename.len + 3];
- }
-
- try tmpdir.writeFile(basename, blob);
-
- var opened = try tmpdir.openFile(basename, .{});
- defer opened.close();
- var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
- try editor_.open(
- Server.editor_path,
- try std.os.getFdPath(opened.handle, &path_buf),
- line,
- column,
- default_allocator,
- );
- }
-
- pub fn detectEditor(env: *DotEnv.Loader) void {
- var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
-
- var out: string = "";
- // first: choose from user preference
- if (Server.editor_name.len > 0) {
- // /usr/bin/vim
- if (std.fs.path.isAbsolute(Server.editor_name)) {
- Server.editor = Editor.byName(std.fs.path.basename(Server.editor_name)) orelse Editor.other;
- editor_path = Server.editor_name;
- return;
- }
-
- // "vscode"
- if (Editor.byName(std.fs.path.basename(Server.editor_name))) |editor_| {
- if (Editor.byPATHForEditor(env, editor_, &buf, Fs.FileSystem.instance.top_level_dir, &out)) {
- editor = editor_;
- editor_path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
- return;
- }
-
- // not in path, try common ones
- if (Editor.byFallbackPathForEditor(editor_, &out)) {
- editor = editor_;
- editor_path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
- return;
- }
- }
- }
-
- // EDITOR=code
- if (Editor.detect(env)) |editor_| {
- if (Editor.byPATHForEditor(env, editor_, &buf, Fs.FileSystem.instance.top_level_dir, &out)) {
- editor = editor_;
- editor_path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
- return;
- }
-
- // not in path, try common ones
- if (Editor.byFallbackPathForEditor(editor_, &out)) {
- editor = editor_;
- editor_path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
- return;
- }
- }
-
- // Don't know, so we will just guess based on what exists
- if (Editor.byFallback(env, &buf, Fs.FileSystem.instance.top_level_dir, &out)) |editor_| {
- editor = editor_;
- editor_path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
- return;
- }
-
- Server.editor = Editor.none;
- }
-
threadlocal var filechange_buf: [32]u8 = undefined;
threadlocal var filechange_buf_hinted: [32]u8 = undefined;
@@ -4090,7 +3987,7 @@ pub const Server = struct {
return;
}
- Server.editor_name = debug.editor;
+ http_editor_context.name = debug.editor;
server.bundler.options.macro_remap = debug.macros orelse .{};
diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig
index 7a1cfad23..2beac4d23 100644
--- a/src/javascript/jsc/api/server.zig
+++ b/src/javascript/jsc/api/server.zig
@@ -78,6 +78,8 @@ const VirtualMachine = @import("../javascript.zig").VirtualMachine;
const IOTask = JSC.IOTask;
const is_bindgen = JSC.is_bindgen;
const uws = @import("uws");
+const Fallback = Runtime.Fallback;
+const MimeType = HTTP.MimeType;
const Blob = JSC.WebCore.Blob;
const SendfileContext = struct {
fd: i32,
@@ -154,6 +156,7 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
sendfile: SendfileContext = undefined,
request_js_object: JSC.C.JSObjectRef = null,
request_body_buf: std.ArrayListUnmanaged(u8) = .{},
+ fallback_buf: std.ArrayListUnmanaged(u8) = .{},
pub threadlocal var pool: *RequestContextStackAllocator = undefined;
@@ -194,7 +197,21 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
_: *JSC.JSGlobalObject,
arguments: []const JSC.JSValue,
) void {
- JSC.VirtualMachine.vm.defaultErrorHandler(arguments[0], null);
+ var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(bun.default_allocator);
+ JSC.VirtualMachine.vm.defaultErrorHandler(arguments[0], &exception_list);
+ if (!ctx.resp.hasResponded()) {
+ ctx.renderDefaultError(
+ JSC.VirtualMachine.vm.log,
+ error.PromiseRejection,
+ exception_list.toOwnedSlice(),
+ "",
+ .{},
+ );
+ JSC.VirtualMachine.vm.log.reset();
+ return;
+ } else {
+ exception_list.deinit();
+ }
if (ctx.aborted) {
ctx.finalize();
@@ -205,6 +222,64 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
ctx.finalize();
}
+ pub fn renderDefaultError(
+ this: *RequestContext,
+ log: *logger.Log,
+ err: anyerror,
+ exceptions: []Api.JsException,
+ comptime fmt: string,
+ args: anytype,
+ ) void {
+ this.resp.writeStatus("500 Internal Server Error");
+ this.resp.writeHeader("content-type", MimeType.html.value);
+
+ var allocator = bun.default_allocator;
+
+ var fallback_container = allocator.create(Api.FallbackMessageContainer) catch unreachable;
+ defer allocator.destroy(fallback_container);
+ fallback_container.* = Api.FallbackMessageContainer{
+ .message = std.fmt.allocPrint(allocator, fmt, args) catch unreachable,
+ .router = null,
+ .reason = .fetch_event_handler,
+ .cwd = VirtualMachine.vm.bundler.fs.top_level_dir,
+ .problems = Api.Problems{
+ .code = @truncate(u16, @errorToInt(err)),
+ .name = @errorName(err),
+ .exceptions = exceptions,
+ .build = log.toAPI(allocator) catch unreachable,
+ },
+ };
+
+ if (comptime fmt.len > 0) Output.prettyErrorln(fmt, args);
+ Output.flush();
+
+ var bb = std.ArrayList(u8).init(allocator);
+ var bb_writer = bb.writer();
+
+ Fallback.renderBackend(
+ allocator,
+ fallback_container,
+ @TypeOf(bb_writer),
+ bb_writer,
+ ) catch unreachable;
+ if (this.resp.tryEnd(bb.items, bb.items.len)) {
+ bb.clearAndFree();
+ this.finalize();
+ return;
+ }
+
+ this.fallback_buf = std.ArrayListUnmanaged(u8){ .items = bb.items, .capacity = bb.capacity };
+ this.resp.onWritable(*RequestContext, onWritableFallback, this);
+ }
+
+ pub fn onWritableFallback(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool {
+ if (this.aborted) {
+ return false;
+ }
+
+ return this.sendWritableBytes(this.fallback_buf.items, write_offset, resp);
+ }
+
pub fn create(this: *RequestContext, server: *ThisServer, req: *uws.Request, resp: *App.Response) void {
this.* = .{
.resp = resp,
@@ -257,6 +332,8 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
this.response_headers.?.deref();
this.response_headers = null;
}
+
+ this.fallback_buf.clearAndFree(bun.default_allocator);
}
pub fn finalize(this: *RequestContext) void {
this.finalizeWithoutDeinit();
@@ -370,9 +447,12 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
return false;
var bytes = this.blob.sharedView();
+ return this.sendWritableBytes(bytes, write_offset, resp);
+ }
- bytes = bytes[@minimum(bytes.len, @truncate(usize, write_offset))..];
- if (resp.tryEnd(bytes, this.blob.size)) {
+ pub fn sendWritableBytes(this: *RequestContext, bytes_: []const u8, write_offset: c_ulong, resp: *App.Response) bool {
+ var bytes = bytes_[@minimum(bytes_.len, @truncate(usize, write_offset))..];
+ if (resp.tryEnd(bytes, bytes_.len)) {
this.finalize();
return true;
} else {
@@ -583,7 +663,61 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
}
};
+ pub fn onBunInfoRequest(_: *ThisServer, req: *uws.Request, resp: *App.Response) void {
+ if (comptime JSC.is_bindgen) return undefined;
+ req.setYield(false);
+ var stack_fallback = std.heap.stackFallback(8096, bun.default_allocator);
+ var allocator = stack_fallback.get();
+
+ var buffer_writer = js_printer.BufferWriter.init(allocator) catch unreachable;
+ var writer = js_printer.BufferPrinter.init(buffer_writer);
+ defer writer.ctx.buffer.deinit();
+ var source = logger.Source.initEmptyFile("info.json");
+ _ = js_printer.printJSON(
+ *js_printer.BufferPrinter,
+ &writer,
+ bun.Global.BunInfo.generate(*Bundler, &JSC.VirtualMachine.vm.bundler, allocator) catch unreachable,
+ &source,
+ ) catch unreachable;
+
+ resp.writeStatus("200 OK");
+ resp.writeHeader("Content-Type", MimeType.json.value);
+ resp.writeHeader("Cache-Control", "public, max-age=3600");
+ resp.writeHeaderInt("Age", 0);
+ const buffer = writer.ctx.written;
+ resp.end(buffer, false);
+ }
+ pub fn onSrcRequest(_: *ThisServer, req: *uws.Request, resp: *App.Response) void {
+ if (comptime JSC.is_bindgen) return undefined;
+ req.setYield(false);
+ if (req.header("open-in-editor") == null) {
+ resp.writeStatus("501 Not Implemented");
+ resp.end("Viewing source without opening in editor is not implemented yet!", false);
+ return;
+ }
+
+ var ctx = &JSC.VirtualMachine.vm.rareData().editor_context;
+ ctx.autoDetectEditor(JSC.VirtualMachine.vm.bundler.env);
+ var line: ?string = req.header("editor-line");
+ var column: ?string = req.header("editor-column");
+
+ if (ctx.editor) |editor| {
+ resp.writeStatus("200 Opened");
+ resp.end("Opened in editor", false);
+ var url = req.url()["/src:".len..];
+ if (strings.indexOfChar(url, ':')) |colon| {
+ url = url[0..colon];
+ }
+ editor.open(ctx.path, url, line, column, bun.default_allocator) catch Output.prettyErrorln("Failed to open editor", .{});
+ } else {
+ resp.writeStatus("500 Missing Editor :(");
+ resp.end("Please set your editor in bunfig.toml", false);
+ }
+ }
+
pub fn onRequest(this: *ThisServer, req: *uws.Request, resp: *App.Response) void {
+ if (comptime JSC.is_bindgen) return undefined;
+
req.setYield(false);
var ctx = this.request_pool_allocator.create(RequestContext) catch @panic("ran out of memory");
ctx.create(this, req, resp);
@@ -613,12 +747,30 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
return;
}
- if (ctx.response_jsvalue.isUndefinedOrNull() or ctx.response_jsvalue.isError() or ctx.response_jsvalue.isAggregateError(this.globalThis) or ctx.response_jsvalue.isException(this.globalThis.vm())) {
+ if (ctx.response_jsvalue.isUndefinedOrNull()) {
req.setYield(true);
ctx.finalize();
return;
}
+ if (ctx.response_jsvalue.isError() or ctx.response_jsvalue.isAggregateError(this.globalThis) or ctx.response_jsvalue.isException(this.globalThis.vm())) {
+ var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(bun.default_allocator);
+ JSC.VirtualMachine.vm.defaultErrorHandler(ctx.response_jsvalue, &exception_list);
+ if (!ctx.resp.hasResponded()) {
+ ctx.renderDefaultError(
+ JSC.VirtualMachine.vm.log,
+ error.ExceptionOcurred,
+ exception_list.toOwnedSlice(),
+ "Unhandled exception in request handler",
+ .{},
+ );
+ JSC.VirtualMachine.vm.log.reset();
+ return;
+ } else {
+ exception_list.deinit();
+ }
+ }
+
JSC.C.JSValueProtect(this.globalThis.ref(), ctx.response_jsvalue.asObjectRef());
if (ctx.response_jsvalue.as(JSC.WebCore.Response)) |response| {
@@ -650,6 +802,10 @@ pub fn NewServer(comptime ssl_enabled: bool) type {
pub fn listen(this: *ThisServer) void {
this.app = App.create(.{});
this.app.any("/*", *ThisServer, this, onRequest);
+
+ this.app.get("/bun:info", *ThisServer, this, onBunInfoRequest);
+ this.app.get("/src:/*", *ThisServer, this, onSrcRequest);
+
this.app.listenWithConfig(*ThisServer, this, onListen, .{
.port = this.default_server.getPort().?,
.host = bun.default_allocator.dupeZ(u8, this.default_server.displayHostname()) catch unreachable,
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index 13de8770d..1652df07b 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -943,9 +943,6 @@ pub fn NewClassWithInstanceType(
const class_definition_ptr = &complete_definition;
pub fn get() callconv(.C) [*c]js.JSClassRef {
- if (comptime JSC.is_bindgen)
- unreachable;
-
var lazy = lazy_ref;
if (!lazy.loaded) {
@@ -977,9 +974,6 @@ pub fn NewClassWithInstanceType(
}
pub fn make(ctx: js.JSContextRef, ptr: *ZigType) js.JSObjectRef {
- if (comptime JSC.is_bindgen)
- unreachable;
-
var real_ptr = JSPrivateDataPtr.init(ptr).ptr();
if (comptime Environment.allow_assert) {
std.debug.assert(JSPrivateDataPtr.isValidPtr(real_ptr));
@@ -1030,9 +1024,6 @@ pub fn NewClassWithInstanceType(
prop: js.JSStringRef,
exception: js.ExceptionRef,
) callconv(.C) js.JSValueRef {
- if (comptime JSC.is_bindgen)
- unreachable;
-
var this: ObjectPtrType(ZigType) = if (comptime ZigType == void) void{} else GetJSPrivateData(ZigType, obj) orelse return js.JSValueMakeUndefined(ctx);
const Field = @TypeOf(@field(
@@ -1104,9 +1095,6 @@ pub fn NewClassWithInstanceType(
value: js.JSValueRef,
exception: js.ExceptionRef,
) callconv(.C) bool {
- if (comptime JSC.is_bindgen)
- unreachable;
-
var this = GetJSPrivateData(ZigType, obj) orelse return false;
switch (comptime @typeInfo(@TypeOf(@field(
@@ -1141,6 +1129,460 @@ pub fn NewClassWithInstanceType(
return definition;
}
+ const GetterNameFormatter = struct {
+ index: usize = 0,
+
+ pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ try writer.writeAll(std.mem.span(class_name_str));
+ try writer.writeAll("_get_");
+ const property_name = property_names[this.index];
+ try writer.writeAll(std.mem.span(property_name));
+ }
+ };
+
+ const SetterNameFormatter = struct {
+ index: usize = 0,
+
+ pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ try writer.writeAll(std.mem.span(class_name_str));
+ try writer.writeAll("_set_");
+ const property_name = property_names[this.index];
+ try writer.writeAll(std.mem.span(property_name));
+ }
+ };
+
+ const FunctionNameFormatter = struct {
+ index: usize = 0,
+
+ pub fn format(this: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ try writer.writeAll(std.mem.span(class_name_str));
+ try writer.writeAll("_fn_");
+ const property_name = function_names[this.index];
+ try writer.writeAll(std.mem.span(property_name));
+ }
+ };
+
+ const PropertyDeclaration = struct {
+ index: usize = 0,
+ pub fn format(this: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ const property = definition.staticValues[this.index];
+
+ if (property.getProperty != null) {
+ try writer.writeAll("static JSC_DECLARE_CUSTOM_GETTER(");
+ const getter_name = GetterNameFormatter{ .index = this.index };
+ try getter_name.format(fmt, opts, writer);
+ try writer.writeAll(");\n");
+ }
+
+ if (property.setProperty != null) {
+ try writer.writeAll("static JSC_DECLARE_CUSTOM_SETTER(");
+ const getter_name = SetterNameFormatter{ .index = this.index };
+ try getter_name.format(fmt, opts, writer);
+ try writer.writeAll(");\n");
+ }
+ }
+ };
+
+ const FunctionDeclaration = struct {
+ index: usize = 0,
+ pub fn format(this: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ const function = definition.staticFunctions[this.index];
+
+ if (function.callAsFunction != null) {
+ try writer.writeAll("static JSC_DECLARE_HOST_FUNCTION(");
+ const getter_name = FunctionNameFormatter{ .index = this.index };
+ try getter_name.format(fmt, opts, writer);
+ try writer.writeAll(");\n");
+ }
+ }
+ };
+
+ const PropertyDefinition = struct {
+ index: usize = 0,
+ pub fn format(this: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ const property = definition.staticValues[this.index];
+
+ if (property.getProperty != null) {
+ try writer.writeAll("static JSC_DEFINE_CUSTOM_GETTER(");
+ const getter_name = GetterNameFormatter{ .index = this.index };
+ try getter_name.format(fmt, opts, writer);
+ try writer.writeAll(", (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::PropertyName)) {\n");
+ try std.fmt.format(
+ writer,
+ \\ JSC::VM& vm = globalObject->vm();
+ \\ Bun::{[name]s}* thisObject = JSC::jsDynamicCast<Bun::{[name]s}*>(vm, JSValue::decode(thisValue));
+ \\ if (UNLIKELY(!thisObject)) {{
+ \\ return JSValue::encode(JSC::jsUndefined());
+ \\ }}
+ \\
+ \\ auto clientData = Bun::clientData(vm);
+ \\ auto scope = DECLARE_THROW_SCOPE(vm);
+ \\
+ ,
+ .{ .name = std.mem.span(class_name_str) },
+ );
+ if (ZigType == void) {
+ try std.fmt.format(
+ writer,
+ \\ JSC::EncodedJSValue result = Zig__{[getter]any}(globalObject);
+ ,
+ .{ .getter = getter_name },
+ );
+ } else {
+ try std.fmt.format(
+ writer,
+ \\ JSC::EncodedJSValue result = Zig__{[getter]any}(globalObject, thisObject->m_ptr);
+ ,
+ .{ .getter = getter_name },
+ );
+ }
+
+ try writer.writeAll(
+ \\ JSC::JSObject *obj = JSC::JSValue::decode(result).getObject();
+ \\
+ \\ if (UNLIKELY(obj != nullptr && obj->isErrorInstance())) {
+ \\ scope.throwException(globalObject, obj);
+ \\ return JSValue::encode(JSC::jsUndefined());
+ \\ }
+ \\
+ \\ scope.release();
+ \\
+ \\ return result;
+ );
+
+ try writer.writeAll("}\n");
+ }
+
+ if (property.setProperty != null) {
+ try writer.writeAll("JSC_DEFINE_CUSTOM_SETTER(");
+ const getter_name = SetterNameFormatter{ .index = this.index };
+ try getter_name.format(fmt, opts, writer);
+ try writer.writeAll(", (JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue value, JSC::PropertyName)) {\n");
+ try std.fmt.format(writer,
+ \\ JSC::VM& vm = globalObject->vm();
+ \\ Bun::{[name]s}* thisObject = JSC::jsDynamicCast<Bun::{[name]s}*>(vm, JSValue::decode(thisValue));
+ \\ if (UNLIKELY(!thisObject)) {{
+ \\ return false;
+ \\ }}
+ \\
+ \\ auto clientData = Bun::clientData(vm);
+ \\ auto scope = DECLARE_THROW_SCOPE(vm);
+ \\
+ \\
+ , .{ .name = getter_name });
+ try writer.writeAll("};\n");
+ }
+ }
+ };
+
+ const PropertyDeclarationsFormatter = struct {
+ pub fn format(_: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ for (definition.staticValues[0 .. static_values_ptr.len - 1]) |_, i| {
+ const property = PropertyDeclaration{ .index = i };
+ try property.format(fmt, opts, writer);
+ }
+ }
+ };
+
+ const PropertyDefinitionsFormatter = struct {
+ pub fn format(_: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ if (static_values_ptr.len > 1) {
+ for (definition.staticValues[0 .. static_values_ptr.len - 1]) |_, i| {
+ const property = PropertyDefinition{ .index = i };
+ try property.format(fmt, opts, writer);
+ }
+ }
+ }
+ };
+
+ const FunctionDefinitionsFormatter = struct {
+ pub fn format(_: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = writer;
+ _ = opts;
+ // for (static_properties[0 .. static_properties.len - 1]) |_, i| {
+ // const property = FunctionDefinition{ .index = i };
+ // try property.format(fmt, opts, writer);
+ // }
+ }
+ };
+
+ const FunctionDeclarationsFormatter = struct {
+ pub fn format(_: @This(), comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
+ _ = fmt;
+ _ = writer;
+ const definition = getDefinition();
+ if (static_functions__.len > 1) {
+ for (definition.staticFunctions[0 .. static_functions__.len - 1]) |_, i| {
+ const function = FunctionDeclaration{ .index = i };
+ try function.format(fmt, opts, writer);
+ }
+ }
+ }
+ };
+
+ pub fn @"generateC++Header"(writer: anytype) !void {
+ const header_file =
+ \\// AUTO-GENERATED FILE
+ \\#pragma once
+ \\
+ \\#include "BunBuiltinNames.h"
+ \\#include "BunClientData.h"
+ \\#include "root.h"
+ \\
+ \\
+ \\namespace Bun {{
+ \\
+ \\using namespace JSC;
+ \\using namespace Zig;
+ \\
+ \\class {[name]s} : public JSNonFinalObject {{
+ \\ using Base = JSNonFinalObject;
+ \\
+ \\public:
+ \\ {[name]s}(JSC::VM& vm, Structure* structure) : Base(vm, structure) {{}}
+ \\
+ \\
+ \\ DECLARE_INFO;
+ \\
+ \\ static constexpr unsigned StructureFlags = Base::StructureFlags;
+ \\ template<typename CellType, SubspaceAccess> static GCClient::IsoSubspace* subspaceFor(VM& vm)
+ \\ {{
+ \\ return &vm.cellSpace();
+ \\ }}
+ \\ static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject,
+ \\ JSC::JSValue prototype)
+ \\ {{
+ \\ return JSC::Structure::create(vm, globalObject, prototype,
+ \\ JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
+ \\ }}
+ \\
+ \\ static {[name]s}* create(JSC::VM& vm, JSC::Structure* structure)
+ \\ {{
+ \\ {[name]s}* accessor = new (NotNull, JSC::allocateCell<{[name]s}>(vm)) {[name]s}(vm, structure);
+ \\ accessor->finishCreation(vm);
+ \\ return accessor;
+ \\ }}
+ \\
+ \\ void finishCreation(JSC::VM& vm);
+ \\
+ \\}};
+ \\
+ \\}} // namespace Bun
+ \\
+ ;
+ _ = writer;
+ _ = header_file;
+ const Opts = struct { name: string };
+ try writer.print(header_file, Opts{
+ .name = std.mem.span(name),
+ });
+ }
+
+ const LookupTableFormatter = struct {
+ // example:
+ //
+ // /* Source for IntlLocalePrototype.lut.h
+ // @begin localePrototypeTable
+ // maximize intlLocalePrototypeFuncMaximize DontEnum|Function 0
+ // minimize intlLocalePrototypeFuncMinimize DontEnum|Function 0
+ // toString intlLocalePrototypeFuncToString DontEnum|Function 0
+ // baseName intlLocalePrototypeGetterBaseName DontEnum|ReadOnly|CustomAccessor
+ // calendar intlLocalePrototypeGetterCalendar DontEnum|ReadOnly|CustomAccessor
+ // calendars intlLocalePrototypeGetterCalendars DontEnum|ReadOnly|CustomAccessor
+ // caseFirst intlLocalePrototypeGetterCaseFirst DontEnum|ReadOnly|CustomAccessor
+ // collation intlLocalePrototypeGetterCollation DontEnum|ReadOnly|CustomAccessor
+ // collations intlLocalePrototypeGetterCollations DontEnum|ReadOnly|CustomAccessor
+ // hourCycle intlLocalePrototypeGetterHourCycle DontEnum|ReadOnly|CustomAccessor
+ // hourCycles intlLocalePrototypeGetterHourCycles DontEnum|ReadOnly|CustomAccessor
+ // numeric intlLocalePrototypeGetterNumeric DontEnum|ReadOnly|CustomAccessor
+ // numberingSystem intlLocalePrototypeGetterNumberingSystem DontEnum|ReadOnly|CustomAccessor
+ // numberingSystems intlLocalePrototypeGetterNumberingSystems DontEnum|ReadOnly|CustomAccessor
+ // language intlLocalePrototypeGetterLanguage DontEnum|ReadOnly|CustomAccessor
+ // script intlLocalePrototypeGetterScript DontEnum|ReadOnly|CustomAccessor
+ // region intlLocalePrototypeGetterRegion DontEnum|ReadOnly|CustomAccessor
+ // timeZones intlLocalePrototypeGetterTimeZones DontEnum|ReadOnly|CustomAccessor
+ // textInfo intlLocalePrototypeGetterTextInfo DontEnum|ReadOnly|CustomAccessor
+ // weekInfo intlLocalePrototypeGetterWeekInfo DontEnum|ReadOnly|CustomAccessor
+ // @end
+ // */
+ pub fn format(_: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ const definition = getDefinition();
+ try writer.writeAll("/* Source for ");
+ try writer.writeAll(std.mem.span(definition.className));
+ try writer.writeAll(".lut.h\n");
+ try writer.writeAll("@begin ");
+ try writer.writeAll(std.mem.span(definition.className));
+ try writer.writeAll("HashTableValues \n");
+ var middle_padding: usize = 0;
+ if (property_names.len > 0) {
+ for (property_names) |prop| {
+ middle_padding = @maximum(prop.len, middle_padding);
+ }
+ }
+ if (function_names.len > 0) {
+ for (function_names[0..function_names.len]) |_name| {
+ middle_padding = @maximum(std.mem.span(_name).len, middle_padding);
+ }
+ }
+
+ if (property_names.len > 0) {
+ comptime var i: usize = 0;
+ inline while (i < property_names.len) : (i += 1) {
+ try writer.writeAll(" ");
+ const name_ = property_names[i];
+ try writer.writeAll(name_);
+ try writer.writeAll(" ");
+ var k: usize = 0;
+ while (k < middle_padding - name_.len) : (k += 1) {
+ try writer.writeAll(" ");
+ }
+
+ try writer.print("{any} ", .{GetterNameFormatter{ .index = i }});
+
+ k = 0;
+
+ while (k < middle_padding - name_.len) : (k += 1) {
+ try writer.writeAll(" ");
+ }
+
+ try writer.writeAll("CustomAccessor");
+ if (options.read_only or @hasField(@TypeOf(@field(properties, property_names[i])), "ro")) {
+ try writer.writeAll("|ReadOnly");
+ }
+
+ if (@hasField(@TypeOf(@field(properties, property_names[i])), "enumerable") and !@field(properties, property_names[i])) {
+ try writer.writeAll("|DontEnum");
+ }
+
+ try writer.writeAll("\n");
+ }
+ }
+ if (function_names.len > 0) {
+ comptime var i: usize = 0;
+ inline while (i < function_names.len) : (i += 1) {
+ try writer.writeAll(" ");
+ const name_ = function_names[i];
+ try writer.writeAll(name_);
+ try writer.writeAll(" ");
+ var k: usize = 0;
+ while (k < middle_padding - name_.len) : (k += 1) {
+ try writer.writeAll(" ");
+ }
+
+ try writer.print("{any} ", .{FunctionNameFormatter{ .index = i }});
+ k = 0;
+
+ while (k < middle_padding - name_.len) : (k += 1) {
+ try writer.writeAll(" ");
+ }
+ var read_only_ = false;
+ if (options.read_only or @hasField(@TypeOf(comptime @field(staticFunctions, function_names[i])), "ro")) {
+ read_only_ = true;
+ try writer.writeAll("ReadOnly");
+ }
+
+ if (comptime std.meta.trait.isContainer(
+ @TypeOf(comptime @field(staticFunctions, function_names[i])),
+ ) and
+ @hasField(@TypeOf(comptime @field(
+ staticFunctions,
+ function_names[i],
+ )), "enumerable") and !@field(staticFunctions, function_names[i]).enumerable) {
+ if (read_only_) {
+ try writer.writeAll("|");
+ }
+ try writer.writeAll("DontEnum");
+ }
+
+ try writer.writeAll("Function 1");
+
+ try writer.writeAll("\n");
+ }
+ }
+
+ try writer.writeAll("@end\n*/\n");
+ }
+ };
+
+ pub fn @"generateC++Class"(writer: anytype) !void {
+ const implementation_file =
+ \\// AUTO-GENERATED FILE
+ \\
+ \\#include "{[name]s}.generated.h"
+ \\#include "{[name]s}.lut.h"
+ \\
+ \\namespace Bun {{
+ \\
+ \\{[lut]any}
+ \\
+ \\using JSGlobalObject = JSC::JSGlobalObject;
+ \\using Exception = JSC::Exception;
+ \\using JSValue = JSC::JSValue;
+ \\using JSString = JSC::JSString;
+ \\using JSModuleLoader = JSC::JSModuleLoader;
+ \\using JSModuleRecord = JSC::JSModuleRecord;
+ \\using Identifier = JSC::Identifier;
+ \\using SourceOrigin = JSC::SourceOrigin;
+ \\using JSObject = JSC::JSObject;
+ \\using JSNonFinalObject = JSC::JSNonFinalObject;
+ \\namespace JSCastingHelpers = JSC::JSCastingHelpers;
+ \\
+ \\#pragma mark - Function Declarations
+ \\
+ \\{[function_declarations]any}
+ \\
+ \\#pragma mark - Property Declarations
+ \\
+ \\{[property_declarations]any}
+ \\
+ \\#pragma mark - Function Definitions
+ \\
+ \\{[function_definitions]any}
+ \\
+ \\#pragma mark - Property Definitions
+ \\
+ \\{[property_definitions]any}
+ \\
+ \\const JSC::ClassInfo {[name]s}::s_info = {{ "{[name]s}", &Base::s_info, &{[name]s}HashTableValues, nullptr, CREATE_METHOD_TABLE([name]s) }};
+ \\
+ \\ void {[name]s}::finishCreation(JSC::VM& vm) {{
+ \\ Base::finishCreation(vm);
+ \\ auto clientData = Bun::clientData(vm);
+ \\ JSC::JSGlobalObject *globalThis = globalObject();
+ \\
+ \\
+ \\#pragma mark - Property Initializers
+ \\
+ \\{[property_initializers]any}
+ \\
+ \\#pragma mark - Function Initializers
+ \\
+ \\{[function_initializers]any}
+ \\
+ \\ }}
+ \\
+ \\}} // namespace Bun
+ \\
+ ;
+
+ try writer.print(implementation_file, .{
+ .name = std.mem.span(class_name_str),
+ .function_initializers = @as(string, ""),
+ .property_initializers = @as(string, ""),
+ .function_declarations = FunctionDeclarationsFormatter{},
+ .property_declarations = FunctionDeclarationsFormatter{},
+ .function_definitions = FunctionDefinitionsFormatter{},
+ .property_definitions = PropertyDefinitionsFormatter{},
+ .lut = LookupTableFormatter{},
+ });
+ }
+
// This should only be run at comptime
pub fn typescriptModuleDeclaration() d.ts.module {
comptime var class = options.ts.module;
@@ -1325,8 +1767,6 @@ pub fn NewClassWithInstanceType(
_: js.JSObjectRef,
props: js.JSPropertyNameAccumulatorRef,
) callconv(.C) void {
- if (comptime JSC.is_bindgen)
- unreachable;
if (comptime property_name_refs.len > 0) {
comptime var i: usize = 0;
if (!property_name_refs_set) {
@@ -1480,7 +1920,7 @@ pub fn NewClassWithInstanceType(
return comptime class;
}
- const static_properties = brk: {
+ const static_properties: [property_names.len + 1]js.JSStaticValue = brk: {
var props: [property_names.len + 1]js.JSStaticValue = undefined;
std.mem.set(
js.JSStaticValue,
@@ -1660,7 +2100,7 @@ pub fn NewClassWithInstanceType(
}
const base_def_ = generateDef(JSC.C.JSClassDefinition);
- const static_functions__ = generateDef([function_name_literals.len + 1]js.JSStaticFunction);
+ const static_functions__: [function_name_literals.len + 1]js.JSStaticFunction = generateDef([function_name_literals.len + 1]js.JSStaticFunction);
const static_functions_ptr = &static_functions__;
const static_values_ptr = &static_properties;
const class_name_str: stringZ = options.name;
@@ -1709,9 +2149,6 @@ pub fn JSError(
ctx: js.JSContextRef,
exception: ExceptionValueRef,
) void {
- if (comptime JSC.is_bindgen)
- unreachable;
-
var error_args: [1]js.JSValueRef = undefined;
@setCold(true);
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index eebeae8e9..ca8c72f75 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -161,8 +161,6 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type {
}
pub fn runFromJS(this: This) void {
- if (comptime JSC.is_bindgen)
- unreachable;
var promise_value = this.promise;
var promise = promise_value.asInternalPromise() orelse {
if (comptime @hasDecl(Context, "deinit")) {
@@ -492,6 +490,17 @@ pub const VirtualMachine = struct {
source_mappings: SavedSourceMap = undefined,
response_objects_pool: ?*Response.Pool = null,
+
+ rare_data: ?*JSC.RareData = null,
+
+ pub inline fn rareData(this: *VirtualMachine) *JSC.RareData {
+ return this.rare_data orelse brk: {
+ this.rare_data = this.allocator.create(JSC.RareData) catch unreachable;
+ this.rare_data.?.* = .{};
+ break :brk this.rare_data.?;
+ };
+ }
+
pub inline fn eventLoop(this: *VirtualMachine) *EventLoop {
return this.event_loop;
}
diff --git a/src/javascript/jsc/rare_data.zig b/src/javascript/jsc/rare_data.zig
new file mode 100644
index 000000000..72bf45fbd
--- /dev/null
+++ b/src/javascript/jsc/rare_data.zig
@@ -0,0 +1,3 @@
+const EditorContext = @import("../../open.zig").EditorContext;
+
+editor_context: EditorContext = EditorContext{},
diff --git a/src/js_printer.zig b/src/js_printer.zig
index fbf20b07b..85df7fdc8 100644
--- a/src/js_printer.zig
+++ b/src/js_printer.zig
@@ -636,7 +636,6 @@ pub fn NewPrinter(
}
}
- //
fn printBunJestImportStatement(p: *Printer, import: S.Import) void {
p.print("const ");
diff --git a/src/jsc.zig b/src/jsc.zig
index e159441bb..07e6ae5e6 100644
--- a/src/jsc.zig
+++ b/src/jsc.zig
@@ -4,6 +4,7 @@ pub const is_bindgen = @import("std").meta.globalOption("bindgen", bool) orelse
pub usingnamespace @import("./javascript/jsc/bindings/exports.zig");
pub usingnamespace @import("./javascript/jsc/bindings/bindings.zig");
pub usingnamespace @import("./javascript/jsc/base.zig");
+pub const RareData = @import("./javascript/jsc/rare_data.zig");
pub usingnamespace @import("./javascript/jsc/javascript.zig");
pub const C = @import("./javascript/jsc/javascript_core_c_api.zig");
pub const WebCore = @import("./javascript/jsc/webcore.zig");
diff --git a/src/logger.zig b/src/logger.zig
index b99d346dc..edc33db4d 100644
--- a/src/logger.zig
+++ b/src/logger.zig
@@ -491,6 +491,12 @@ pub const Log = struct {
msgs: ArrayList(Msg),
level: Level = if (Environment.isDebug) Level.info else Level.warn,
+ pub fn reset(this: *Log) void {
+ this.msgs.clearRetainingCapacity();
+ this.warnings = 0;
+ this.errors = 0;
+ }
+
pub var default_log_level = if (Environment.isDebug) Level.info else Level.warn;
pub fn hasAny(this: *const Log) bool {
diff --git a/src/open.zig b/src/open.zig
index 4afa5ae08..f129dfc54 100644
--- a/src/open.zig
+++ b/src/open.zig
@@ -324,3 +324,104 @@ pub const Editor = enum(u8) {
child_process.deinit();
}
};
+
+pub const EditorContext = struct {
+ editor: ?Editor = null,
+ name: string = "",
+ path: string = "",
+ const Fs = @import("./fs.zig");
+
+ pub fn openInEditor(this: *EditorContext, editor_: Editor, blob: []const u8, id: string, tmpdir: std.fs.Dir, line: string, column: string) void {
+ _openInEditor(this.path, editor_, blob, id, tmpdir, line, column) catch |err| {
+ if (editor_ != .other) {
+ Output.prettyErrorln("Error {s} opening in {s}", .{ @errorName(err), @tagName(editor_) });
+ }
+
+ this.editor = Editor.none;
+ };
+ }
+
+ fn _openInEditor(path: string, editor_: Editor, blob: []const u8, id: string, tmpdir: std.fs.Dir, line: string, column: string) !void {
+ var basename_buf: [512]u8 = undefined;
+ var basename = std.fs.path.basename(id);
+ if (strings.endsWith(basename, ".bun") and basename.len < 499) {
+ std.mem.copy(u8, &basename_buf, basename);
+ basename_buf[basename.len..][0..3].* = ".js".*;
+ basename = basename_buf[0 .. basename.len + 3];
+ }
+
+ try tmpdir.writeFile(basename, blob);
+
+ var opened = try tmpdir.openFile(basename, .{});
+ defer opened.close();
+ var path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
+ try editor_.open(
+ path,
+ try std.os.getFdPath(opened.handle, &path_buf),
+ line,
+ column,
+ default_allocator,
+ );
+ }
+
+ pub fn autoDetectEditor(this: *EditorContext, env: *DotEnv.Loader) void {
+ if (this.editor == null) {
+ this.detectEditor(env);
+ }
+ }
+ pub fn detectEditor(this: *EditorContext, env: *DotEnv.Loader) void {
+ var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
+
+ var out: string = "";
+ // first: choose from user preference
+ if (this.name.len > 0) {
+ // /usr/bin/vim
+ if (std.fs.path.isAbsolute(this.name)) {
+ this.editor = Editor.byName(std.fs.path.basename(this.name)) orelse Editor.other;
+ this.path = this.name;
+ return;
+ }
+
+ // "vscode"
+ if (Editor.byName(std.fs.path.basename(this.name))) |editor_| {
+ if (Editor.byPATHForEditor(env, editor_, &buf, Fs.FileSystem.instance.top_level_dir, &out)) {
+ this.editor = editor_;
+ this.path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
+ return;
+ }
+
+ // not in path, try common ones
+ if (Editor.byFallbackPathForEditor(editor_, &out)) {
+ this.editor = editor_;
+ this.path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
+ return;
+ }
+ }
+ }
+
+ // EDITOR=code
+ if (Editor.detect(env)) |editor_| {
+ if (Editor.byPATHForEditor(env, editor_, &buf, Fs.FileSystem.instance.top_level_dir, &out)) {
+ this.editor = editor_;
+ this.path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
+ return;
+ }
+
+ // not in path, try common ones
+ if (Editor.byFallbackPathForEditor(editor_, &out)) {
+ this.editor = editor_;
+ this.path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
+ return;
+ }
+ }
+
+ // Don't know, so we will just guess based on what exists
+ if (Editor.byFallback(env, &buf, Fs.FileSystem.instance.top_level_dir, &out)) |editor_| {
+ this.editor = editor_;
+ this.path = Fs.FileSystem.instance.dirname_store.append(string, out) catch unreachable;
+ return;
+ }
+
+ this.editor = Editor.none;
+ }
+};
diff --git a/src/runtime.zig b/src/runtime.zig
index e33b31961..ecce90a74 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -70,6 +70,7 @@ pub const ErrorJS = struct {
pub const Fallback = struct {
pub const ProdSourceContent = @embedFile("./fallback.out.js");
pub const HTMLTemplate = @embedFile("./fallback.html");
+ pub const HTMLBackendTemplate = @embedFile("./fallback-backend.html");
const Base64FallbackMessage = struct {
msg: *const Api.FallbackMessageContainer,
@@ -160,6 +161,28 @@ pub const Fallback = struct {
.entry_point = entry_point,
});
}
+
+ pub fn renderBackend(
+ allocator: std.mem.Allocator,
+ msg: *const Api.FallbackMessageContainer,
+ comptime WriterType: type,
+ writer: WriterType,
+ ) !void {
+ const PrintArgs = struct {
+ blob: Base64FallbackMessage,
+ bun_error_css: string,
+ bun_error: string,
+ fallback: string,
+ bun_error_page_css: string,
+ };
+ try writer.print(HTMLBackendTemplate, PrintArgs{
+ .blob = Base64FallbackMessage{ .msg = msg, .allocator = allocator },
+ .bun_error_css = ErrorCSS.sourceContent(),
+ .bun_error = ErrorJS.sourceContent(),
+ .bun_error_page_css = "",
+ .fallback = scriptContent(),
+ });
+ }
};
pub const Runtime = struct {