aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/schema.d.ts1
-rw-r--r--src/api/schema.js8
-rw-r--r--src/api/schema.peechy1
-rw-r--r--src/api/schema.zig5
-rw-r--r--src/bundler.zig76
-rw-r--r--src/fallback.version2
-rw-r--r--src/http.zig332
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp2
-rw-r--r--src/javascript/jsc/javascript.zig75
-rw-r--r--src/js_parser/js_parser.zig20
-rw-r--r--src/linker.zig26
-rw-r--r--src/runtime.zig27
-rw-r--r--src/runtime/hmr.ts60
-rw-r--r--src/string_immutable.zig17
14 files changed, 440 insertions, 212 deletions
diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts
index 3fd082f51..159cf4061 100644
--- a/src/api/schema.d.ts
+++ b/src/api/schema.d.ts
@@ -534,6 +534,7 @@ type uint32 = number;
export interface WebsocketMessageWelcome {
epoch: uint32;
javascriptReloader: Reloader;
+ cwd: string;
}
export interface WebsocketMessageFileChangeNotification {
diff --git a/src/api/schema.js b/src/api/schema.js
index b4e98f44f..1b928aa20 100644
--- a/src/api/schema.js
+++ b/src/api/schema.js
@@ -2405,6 +2405,7 @@ function decodeWebsocketMessageWelcome(bb) {
result["epoch"] = bb.readUint32();
result["javascriptReloader"] = Reloader[bb.readByte()];
+ result["cwd"] = bb.readString();
return result;
}
@@ -2426,6 +2427,13 @@ bb.writeByte(encoded);
throw new Error("Missing required field \"javascriptReloader\"");
}
+ var value = message["cwd"];
+ if (value != null) {
+ bb.writeString(value);
+ } else {
+ throw new Error("Missing required field \"cwd\"");
+ }
+
}
function decodeWebsocketMessageFileChangeNotification(bb) {
diff --git a/src/api/schema.peechy b/src/api/schema.peechy
index 17bda9654..a74059dca 100644
--- a/src/api/schema.peechy
+++ b/src/api/schema.peechy
@@ -451,6 +451,7 @@ struct WebsocketMessage {
struct WebsocketMessageWelcome {
uint32 epoch;
Reloader javascriptReloader;
+ string cwd;
}
struct WebsocketMessageFileChangeNotification {
diff --git a/src/api/schema.zig b/src/api/schema.zig
index 593b02bdd..980346698 100644
--- a/src/api/schema.zig
+++ b/src/api/schema.zig
@@ -2410,18 +2410,23 @@ epoch: u32 = 0,
/// javascriptReloader
javascript_reloader: Reloader,
+/// cwd
+cwd: []const u8,
+
pub fn decode(reader: anytype) anyerror!WebsocketMessageWelcome {
var this = std.mem.zeroes(WebsocketMessageWelcome);
this.epoch = try reader.readValue(u32);
this.javascript_reloader = try reader.readValue(Reloader);
+ this.cwd = try reader.readValue([]const u8);
return this;
}
pub fn encode(this: *const @This(), writer: anytype) anyerror!void {
try writer.writeInt(this.epoch);
try writer.writeEnum(this.javascript_reloader);
+ try writer.writeValue(this.cwd);
}
};
diff --git a/src/bundler.zig b/src/bundler.zig
index 484a25201..8b13df4a2 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -901,14 +901,17 @@ pub fn NewBundler(cache_files: bool) type {
const basename = std.fs.path.basename(std.mem.span(destination));
const extname = std.fs.path.extension(basename);
- javascript_bundle.import_from_name = try std.fmt.allocPrint(
- this.allocator,
- "/{s}.{x}.bun",
- .{
- basename[0 .. basename.len - extname.len],
- etag_u64,
- },
- );
+ javascript_bundle.import_from_name = if (bundler.options.platform == .bun)
+ "/node_modules.server.bun"
+ else
+ try std.fmt.allocPrint(
+ this.allocator,
+ "/{s}.{x}.bun",
+ .{
+ basename[0 .. basename.len - extname.len],
+ etag_u64,
+ },
+ );
javascript_bundle_container.bundle_format_version = current_version;
javascript_bundle_container.bundle = javascript_bundle;
@@ -2820,36 +2823,9 @@ pub const ClientEntryPoint = struct {
code = try std.fmt.bufPrint(
&entry.code_buffer,
\\globalThis.Bun_disableCSSImports = true;
- \\var lastErrorHandler = globalThis.onerror;
- \\var loaded = {{boot: false, entry: false, onError: null}};
- \\if (!lastErrorHandler || !lastErrorHandler.__onceTag) {{
- \\ globalThis.onerror = function (evt) {{
- \\ if (this.onError && typeof this.onError == 'function') {{
- \\ this.onError(evt, loaded);
- \\ }}
- \\ console.error(evt.error);
- \\ debugger;
- \\ }};
- \\ globalThis.onerror.__onceTag = true;
- \\ globalThis.onerror.loaded = loaded;
- \\}}
- \\
\\import boot from '{s}';
- \\loaded.boot = true;
- \\if ('setLoaded' in boot) boot.setLoaded(loaded);
\\import * as EntryPoint from '{s}{s}';
- \\loaded.entry = true;
- \\
- \\if (!boot) {{
- \\ const now = Date.now();
- \\ debugger;
- \\ const elapsed = Date.now() - now;
- \\ if (elapsed < 1000) {{
- \\ throw new Error('Expected framework to export default a function. Instead, framework exported:', Object.keys(boot));
- \\ }}
- \\}}
- \\
- \\boot(EntryPoint, loaded);
+ \\boot(EntryPoint);
,
.{
client,
@@ -2860,36 +2836,10 @@ pub const ClientEntryPoint = struct {
} else {
code = try std.fmt.bufPrint(
&entry.code_buffer,
- \\var lastErrorHandler = globalThis.onerror;
- \\var loaded = {{boot: false, entry: false, onError: null}};
- \\if (!lastErrorHandler || !lastErrorHandler.__onceTag) {{
- \\ globalThis.onerror = function (evt) {{
- \\ if (this.onError && typeof this.onError == 'function') {{
- \\ this.onError(evt, loaded);
- \\ }}
- \\ console.error(evt.error);
- \\ debugger;
- \\ }};
- \\ globalThis.onerror.__onceTag = true;
- \\ globalThis.onerror.loaded = loaded;
- \\}}
- \\
\\import boot from '{s}';
- \\loaded.boot = true;
\\if ('setLoaded' in boot) boot.setLoaded(loaded);
\\import * as EntryPoint from '{s}{s}';
- \\loaded.entry = true;
- \\
- \\if (!boot) {{
- \\ const now = Date.now();
- \\ debugger;
- \\ const elapsed = Date.now() - now;
- \\ if (elapsed < 1000) {{
- \\ throw new Error('Expected framework to export default a function. Instead, framework exported:', Object.keys(boot));
- \\ }}
- \\}}
- \\
- \\boot(EntryPoint, loaded);
+ \\boot(EntryPoint);
,
.{
client,
diff --git a/src/fallback.version b/src/fallback.version
index fb76dcb20..75f3323de 100644
--- a/src/fallback.version
+++ b/src/fallback.version
@@ -1 +1 @@
-c098be5f3e938123 \ No newline at end of file
+a5559a0075104616 \ No newline at end of file
diff --git a/src/http.zig b/src/http.zig
index 856502294..c8ca3157b 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -12,6 +12,7 @@ const logger = @import("logger.zig");
const Fs = @import("./fs.zig");
const Options = @import("./options.zig");
const Fallback = @import("./runtime.zig").Fallback;
+const ErrorCSS = @import("./runtime.zig").ErrorCSS;
const Css = @import("css_scanner.zig");
const NodeModuleBundle = @import("./node_module_bundle.zig").NodeModuleBundle;
const resolve_path = @import("./resolver/resolve_path.zig");
@@ -639,6 +640,7 @@ pub const RequestContext = struct {
value: Value,
id: u32,
timestamp: u32,
+ log: logger.Log,
bytes: []const u8 = "",
approximate_newline_count: usize = 0,
pub const Value = union(Tag) {
@@ -650,7 +652,7 @@ pub const RequestContext = struct {
fail,
};
};
- pub fn build(this: *WatchBuilder, id: u32, from_timestamp: u32) !WatchBuildResult {
+ pub fn build(this: *WatchBuilder, id: u32, from_timestamp: u32, allocator: *std.mem.Allocator) !WatchBuildResult {
if (this.count == 0) {
var writer = try js_printer.BufferWriter.init(this.allocator);
this.printer = js_printer.BufferPrinter.init(writer);
@@ -658,8 +660,8 @@ pub const RequestContext = struct {
}
defer this.count += 1;
- var log = logger.Log.init(this.allocator);
- errdefer log.deinit();
+
+ var log = logger.Log.init(allocator);
const index = std.mem.indexOfScalar(u32, this.watcher.watchlist.items(.hash), id) orelse {
@@ -667,6 +669,7 @@ pub const RequestContext = struct {
return WatchBuildResult{
.value = .{ .fail = std.mem.zeroes(Api.WebsocketMessageBuildFailure) },
.id = id,
+ .log = log,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
};
};
@@ -674,7 +677,6 @@ pub const RequestContext = struct {
const file_path_str = this.watcher.watchlist.items(.file_path)[index];
const fd = this.watcher.watchlist.items(.fd)[index];
const loader = this.watcher.watchlist.items(.loader)[index];
-
const path = Fs.Path.init(file_path_str);
var old_log = this.bundler.log;
this.bundler.setLog(&log);
@@ -695,7 +697,7 @@ pub const RequestContext = struct {
this.bundler.resetStore();
var parse_result = this.bundler.parse(
- this.bundler.allocator,
+ allocator,
path,
loader,
0,
@@ -704,26 +706,64 @@ pub const RequestContext = struct {
null,
) orelse {
return WatchBuildResult{
- .value = .{ .fail = std.mem.zeroes(Api.WebsocketMessageBuildFailure) },
+ .value = .{
+ .fail = .{
+ .id = id,
+ .from_timestamp = from_timestamp,
+ .loader = loader.toAPI(),
+ .module_path = this.bundler.fs.relativeTo(file_path_str),
+ .log = try log.toAPI(allocator),
+ },
+ },
.id = id,
+ .log = log,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
};
};
this.printer.ctx.reset();
+ {
+ var old_allocator = this.bundler.linker.allocator;
+ this.bundler.linker.allocator = allocator;
+ defer this.bundler.linker.allocator = old_allocator;
+ this.bundler.linker.link(
+ Fs.Path.init(file_path_str),
+ &parse_result,
+ .absolute_url,
+ false,
+ ) catch |err| {
+ return WatchBuildResult{
+ .value = .{
+ .fail = .{
+ .id = id,
+ .from_timestamp = from_timestamp,
+ .loader = loader.toAPI(),
+ .module_path = this.bundler.fs.relativeTo(file_path_str),
+ .log = try log.toAPI(allocator),
+ },
+ },
- try this.bundler.linker.link(
- Fs.Path.init(file_path_str),
- &parse_result,
- .absolute_url,
- false,
- );
+ .id = id,
+ .timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
+ };
+ };
+ }
var written = this.bundler.print(parse_result, @TypeOf(&this.printer), &this.printer, .esm) catch |err| {
return WatchBuildResult{
- .value = .{ .fail = std.mem.zeroes(Api.WebsocketMessageBuildFailure) },
+ .value = .{
+ .fail = .{
+ .id = id,
+ .from_timestamp = from_timestamp,
+ .loader = loader.toAPI(),
+ .module_path = this.bundler.fs.relativeTo(file_path_str),
+ .log = try log.toAPI(allocator),
+ },
+ },
.id = id,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
};
};
@@ -735,13 +775,13 @@ pub const RequestContext = struct {
.loader = parse_result.loader.toAPI(),
.module_path = this.bundler.fs.relativeTo(file_path_str),
.blob_length = @truncate(u32, written),
- // .log = std.mem.zeroes(Api.Log),
},
},
.id = id,
.bytes = this.printer.ctx.written,
.approximate_newline_count = parse_result.ast.approximate_newline_count,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
};
},
.css => {
@@ -767,7 +807,7 @@ pub const RequestContext = struct {
const count = brk: {
if (this.bundler.options.hot_module_reloading) {
- break :brk try CSSBundlerHMR.bundle(
+ break :brk CSSBundlerHMR.bundle(
file_path_str,
this.bundler.fs,
&this.printer,
@@ -780,7 +820,7 @@ pub const RequestContext = struct {
&this.bundler.linker,
);
} else {
- break :brk try CSSBundler.bundle(
+ break :brk CSSBundler.bundle(
file_path_str,
this.bundler.fs,
&this.printer,
@@ -793,6 +833,21 @@ pub const RequestContext = struct {
&this.bundler.linker,
);
}
+ } catch {
+ return WatchBuildResult{
+ .value = .{
+ .fail = .{
+ .id = id,
+ .from_timestamp = from_timestamp,
+ .loader = loader.toAPI(),
+ .module_path = this.bundler.fs.relativeTo(file_path_str),
+ .log = try log.toAPI(allocator),
+ },
+ },
+ .id = id,
+ .timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
+ };
};
return WatchBuildResult{
@@ -811,6 +866,7 @@ pub const RequestContext = struct {
.approximate_newline_count = count.approximate_newline_count,
// .approximate_newline_count = parse_result.ast.approximate_newline_count,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
};
},
else => {
@@ -818,6 +874,7 @@ pub const RequestContext = struct {
.value = .{ .fail = std.mem.zeroes(Api.WebsocketMessageBuildFailure) },
.id = id,
.timestamp = WebsocketHandler.toTimestamp(Server.global_start_time.read()),
+ .log = log,
};
},
}
@@ -1420,31 +1477,34 @@ pub const RequestContext = struct {
.kind = .welcome,
};
var cmd_reader: ApiReader = undefined;
- var byte_buf: [32]u8 = undefined;
- var fbs = std.io.fixedBufferStream(&byte_buf);
- var writer = ByteApiWriter.init(&fbs);
-
- try msg.encode(&writer);
- var reloader = Api.Reloader.disable;
- if (ctx.bundler.options.hot_module_reloading) {
- reloader = Api.Reloader.live;
- if (ctx.bundler.options.jsx.supports_fast_refresh) {
- if (ctx.bundler.options.node_modules_bundle) |bundle| {
- if (bundle.hasFastRefresh()) {
- reloader = Api.Reloader.fast_refresh;
+ {
+ var byte_buf: [32 + std.fs.MAX_PATH_BYTES]u8 = undefined;
+ var fbs = std.io.fixedBufferStream(&byte_buf);
+ var writer = ByteApiWriter.init(&fbs);
+
+ try msg.encode(&writer);
+ var reloader = Api.Reloader.disable;
+ if (ctx.bundler.options.hot_module_reloading) {
+ reloader = Api.Reloader.live;
+ if (ctx.bundler.options.jsx.supports_fast_refresh) {
+ if (ctx.bundler.options.node_modules_bundle) |bundle| {
+ if (bundle.hasFastRefresh()) {
+ reloader = Api.Reloader.fast_refresh;
+ }
}
}
}
- }
- const welcome_message = Api.WebsocketMessageWelcome{
- .epoch = WebsocketHandler.toTimestamp(handler.ctx.timer.start_time),
- .javascript_reloader = reloader,
- };
- try welcome_message.encode(&writer);
- if ((try handler.websocket.writeBinary(fbs.getWritten())) == 0) {
- handler.tombstone = true;
- is_socket_closed = true;
- Output.prettyErrorln("<r><red>ERR:<r> <b>Websocket failed to write.<r>", .{});
+ const welcome_message = Api.WebsocketMessageWelcome{
+ .epoch = WebsocketHandler.toTimestamp(handler.ctx.timer.start_time),
+ .javascript_reloader = reloader,
+ .cwd = handler.ctx.bundler.fs.top_level_dir,
+ };
+ try welcome_message.encode(&writer);
+ if ((try handler.websocket.writeBinary(fbs.getWritten())) == 0) {
+ handler.tombstone = true;
+ is_socket_closed = true;
+ Output.prettyErrorln("<r><red>ERR:<r> <b>Websocket failed to write.<r>", .{});
+ }
}
while (!handler.tombstone) {
@@ -1485,7 +1545,11 @@ pub const RequestContext = struct {
switch (cmd.kind) {
.build => {
var request = try Api.WebsocketCommandBuild.decode(&cmd_reader);
- var build_result = try handler.builder.build(request.id, cmd.timestamp);
+
+ var arena = std.heap.ArenaAllocator.init(default_allocator);
+ defer arena.deinit();
+
+ var build_result = try handler.builder.build(request.id, cmd.timestamp, &arena.allocator);
const file_path = switch (build_result.value) {
.fail => |fail| fail.module_path,
.success => |fail| fail.module_path,
@@ -1993,6 +2057,26 @@ pub const RequestContext = struct {
return;
}
+ if (strings.eqlComptime(path, "erro.css")) {
+ const buffer = ErrorCSS.sourceContent();
+ ctx.appendHeader("Content-Type", MimeType.css.value);
+ if (FeatureFlags.strong_etags_for_built_files) {
+ const did_send = ctx.writeETag(buffer) catch false;
+ if (did_send) return;
+ }
+
+ if (buffer.len == 0) {
+ return try ctx.sendNoContent();
+ }
+ const send_body = ctx.method == .GET;
+ defer ctx.done();
+ try ctx.writeStatus(200);
+ try ctx.prepareToSendBody(buffer.len, false);
+ if (!send_body) return;
+ _ = try ctx.writeSocket(buffer, SOCKET_FLAGS);
+ return;
+ }
+
if (strings.eqlComptime(path, "fallback")) {
const resolved = try ctx.bundler.resolver.resolve(ctx.bundler.fs.top_level_dir, ctx.bundler.options.framework.?.fallback.path, .stmt);
const resolved_path = resolved.pathConst() orelse return try ctx.sendNotFound();
@@ -2012,6 +2096,77 @@ pub const RequestContext = struct {
return;
}
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest
+ pub fn isScriptOrStyleRequest(ctx: *RequestContext) bool {
+ const header_ = ctx.header("Sec-Fetch-Dest") orelse return false;
+ return strings.eqlComptime(header_.value, "script") or
+ strings.eqlComptime(header_.value, "style");
+ }
+
+ fn handleSrcURL(ctx: *RequestContext, server: *Server) !void {
+ var input_path = ctx.url.path["src:".len..];
+ while (std.mem.indexOfScalar(u8, input_path, ':')) |i| {
+ input_path = input_path[0..i];
+ }
+ if (input_path.len == 0) return ctx.sendNotFound();
+
+ const pathname = Fs.PathName.init(input_path);
+ const result = try ctx.buildFile(input_path, pathname.ext);
+
+ switch (result.file.value) {
+ .pending => |resolve_result| {
+ const path = resolve_result.pathConst() orelse return try ctx.sendNotFound();
+
+ var needs_close = false;
+ const fd = if (resolve_result.file_fd != 0)
+ resolve_result.file_fd
+ else brk: {
+ var file = std.fs.openFileAbsoluteZ(path.textZ(), .{ .read = true }) catch |err| {
+ Output.prettyErrorln("Failed to open {s} due to error {s}", .{ path.text, @errorName(err) });
+ return try ctx.sendInternalError(err);
+ };
+ needs_close = true;
+ break :brk file.handle;
+ };
+ defer {
+ if (needs_close) {
+ std.os.close(fd);
+ }
+ }
+
+ const content_length = brk: {
+ var file = std.fs.File{ .handle = fd };
+ var stat = file.stat() catch |err| {
+ Output.prettyErrorln("Failed to read {s} due to error {s}", .{ path.text, @errorName(err) });
+ return try ctx.sendInternalError(err);
+ };
+ break :brk stat.size;
+ };
+
+ if (content_length == 0) {
+ return try ctx.sendNoContent();
+ }
+
+ ctx.appendHeader("Content-Type", "text/plain");
+ defer ctx.done();
+
+ try ctx.writeStatus(200);
+ try ctx.prepareToSendBody(content_length, false);
+
+ _ = try std.os.sendfile(
+ ctx.conn.client.socket.fd,
+ fd,
+ 0,
+ content_length,
+ &[_]std.os.iovec_const{},
+ &[_]std.os.iovec_const{},
+ 0,
+ );
+ },
+ else => return try ctx.sendNotFound(),
+ }
+ }
+
pub fn handleReservedRoutes(ctx: *RequestContext, server: *Server) !bool {
if (strings.eqlComptime(ctx.url.extname, "bun") and ctx.bundler.options.node_modules_bundle != null) {
try ctx.sendJSB();
@@ -2023,59 +2178,66 @@ pub const RequestContext = struct {
return true;
}
- if (ctx.url.path.len > "bun:".len and strings.eqlComptimeIgnoreLen(ctx.url.path[0.."bun:".len], "bun:")) {
+ const isMaybePrefix = ctx.url.path.len > "bun:".len;
+ if (isMaybePrefix and strings.eqlComptimeIgnoreLen(ctx.url.path[0.."bun:".len], "bun:")) {
try ctx.handleBunURL(server);
return true;
+ } else if (isMaybePrefix and strings.eqlComptimeIgnoreLen(ctx.url.path[0.."src:".len], "src:")) {
+ try ctx.handleSrcURL(server);
+ return true;
}
return false;
}
- pub fn handleGet(ctx: *RequestContext) !void {
- const result = brk: {
- if (ctx.bundler.options.isFrontendFrameworkEnabled()) {
- if (serve_as_package_path) {
- break :brk try ctx.bundler.buildFile(
- &ctx.log,
- ctx.allocator,
- ctx.url.pathWithoutAssetPrefix(ctx.bundler.options.routes.asset_prefix_path),
- ctx.url.extname,
- true,
- true,
- );
- } else {
- break :brk try ctx.bundler.buildFile(
- &ctx.log,
- ctx.allocator,
- ctx.url.pathWithoutAssetPrefix(ctx.bundler.options.routes.asset_prefix_path),
- ctx.url.extname,
- true,
- false,
- );
- }
+ pub inline fn buildFile(ctx: *RequestContext, path_name: string, extname: string) !bundler.ServeResult {
+ if (ctx.bundler.options.isFrontendFrameworkEnabled()) {
+ if (serve_as_package_path) {
+ return try ctx.bundler.buildFile(
+ &ctx.log,
+ ctx.allocator,
+ path_name,
+ extname,
+ true,
+ true,
+ );
} else {
- if (serve_as_package_path) {
- break :brk try ctx.bundler.buildFile(
- &ctx.log,
- ctx.allocator,
- ctx.url.pathWithoutAssetPrefix(ctx.bundler.options.routes.asset_prefix_path),
- ctx.url.extname,
- false,
- true,
- );
- } else {
- break :brk try ctx.bundler.buildFile(
- &ctx.log,
- ctx.allocator,
- ctx.url.pathWithoutAssetPrefix(ctx.bundler.options.routes.asset_prefix_path),
- ctx.url.extname,
- false,
- false,
- );
- }
+ return try ctx.bundler.buildFile(
+ &ctx.log,
+ ctx.allocator,
+ path_name,
+ extname,
+ true,
+ false,
+ );
}
- };
-
+ } else {
+ if (serve_as_package_path) {
+ return try ctx.bundler.buildFile(
+ &ctx.log,
+ ctx.allocator,
+ path_name,
+ extname,
+ false,
+ true,
+ );
+ } else {
+ return try ctx.bundler.buildFile(
+ &ctx.log,
+ ctx.allocator,
+ path_name,
+ extname,
+ false,
+ false,
+ );
+ }
+ }
+ }
+ pub fn handleGet(ctx: *RequestContext) !void {
+ const result = try ctx.buildFile(
+ ctx.url.pathWithoutAssetPrefix(ctx.bundler.options.routes.asset_prefix_path),
+ ctx.url.extname,
+ );
try @call(.{ .modifier = .always_inline }, RequestContext.renderServeResult, .{ ctx, result });
}
@@ -2583,7 +2745,9 @@ pub const Server = struct {
pub fn detectFastRefresh(this: *Server) void {
defer this.bundler.resetStore();
- _ = this.bundler.resolver.resolve(this.bundler.fs.top_level_dir, "react-refresh/runtime", .internal) catch |err| {
+ // 1. Try react refresh
+ _ = this.bundler.resolver.resolve(this.bundler.fs.top_level_dir, this.bundler.options.jsx.refresh_runtime, .internal) catch |err| {
+ // 2. Try react refresh from import source perspective
this.bundler.options.jsx.supports_fast_refresh = false;
return;
};
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index edebae6de..226abced6 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -84,7 +84,7 @@ extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObje
void *console_client) {
JSC::Options::useSourceProviderCache() = true;
JSC::Options::useUnlinkedCodeBlockJettisoning() = false;
- JSC::Options::useTopLevelAwait() = true;
+ // JSC::Options::useTopLevelAwait() = true;
JSC::Options::exposeInternalModuleLoader() = true;
std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 14b010b17..24b59f91f 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -324,6 +324,7 @@ pub const Bun = struct {
);
};
+const bun_file_import_path = "/node_modules.server.bun";
pub const LazyClasses = [_]type{};
pub const Module = struct {
@@ -459,7 +460,7 @@ pub const VirtualMachine = struct {
std.debug.assert(VirtualMachine.vm_loaded);
std.debug.assert(VirtualMachine.vm.global == global);
- if (vm.node_modules != null and strings.eql(vm.bundler.linker.nodeModuleBundleImportPath(), _specifier)) {
+ if (vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) {
// We kind of need an abstraction around this.
// Basically we should subclass JSC::SourceCode with:
// - hash
@@ -471,11 +472,11 @@ pub const VirtualMachine = struct {
return ResolvedSource{
.allocator = null,
.source_code = ZigString.init(code),
- .specifier = ZigString.init(vm.bundler.linker.nodeModuleBundleImportPath()),
- .source_url = ZigString.init(vm.bundler.options.node_modules_bundle_pretty_path),
+ .specifier = ZigString.init(bun_file_import_path),
+ .source_url = ZigString.init(bun_file_import_path[1..]),
.hash = 0, // TODO
.bytecodecache_fd = std.math.lossyCast(u64, vm.node_modules.?.fetchByteCodeCache(
- vm.bundler.options.node_modules_bundle_pretty_path,
+ bun_file_import_path[1..],
&vm.bundler.fs.fs,
) orelse 0),
};
@@ -658,8 +659,8 @@ pub const VirtualMachine = struct {
if (vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) {
ret.path = Runtime.Runtime.Imports.Name;
return;
- } else if (vm.node_modules != null and strings.eql(specifier, vm.bundler.linker.nodeModuleBundleImportPath())) {
- ret.path = vm.bundler.linker.nodeModuleBundleImportPath();
+ } else if (vm.node_modules != null and strings.eql(specifier, bun_file_import_path)) {
+ ret.path = bun_file_import_path;
return;
} else if (strings.eqlComptime(specifier, main_file_name)) {
ret.result = null;
@@ -713,7 +714,7 @@ pub const VirtualMachine = struct {
if (node_modules_bundle.findModuleIDInPackage(package, package_relative_path) == null) break :node_module_checker;
- ret.path = vm.bundler.linker.nodeModuleBundleImportPath();
+ ret.path = bun_file_import_path;
return;
}
}
@@ -910,7 +911,7 @@ pub const VirtualMachine = struct {
// We first import the node_modules bundle. This prevents any potential TDZ issues.
// The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
if (this.node_modules != null) {
- promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(vm.bundler.linker.nodeModuleBundleImportPath())));
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(bun_file_import_path)));
this.global.vm().drainMicrotasks();
@@ -1208,33 +1209,43 @@ pub const VirtualMachine = struct {
writer.writeByteNTimes(' ', pad) catch unreachable;
const top = exception.stack.frames()[0];
var remainder = std.mem.trim(u8, source.text, "\n");
- const prefix = remainder[0..@intCast(usize, top.position.column_start)];
- const underline = remainder[@intCast(usize, top.position.column_start)..@intCast(usize, top.position.column_stop)];
- const suffix = remainder[@intCast(usize, top.position.column_stop)..];
+ if (@intCast(usize, top.position.column_stop) > remainder.len) {
+ writer.print(
+ comptime Output.prettyFmt(
+ "<r><d>{d} |<r> {s}\n",
+ allow_ansi_color,
+ ),
+ .{ source.line, remainder },
+ ) catch unreachable;
+ } else {
+ const prefix = remainder[0..@intCast(usize, top.position.column_start)];
+ const underline = remainder[@intCast(usize, top.position.column_start)..@intCast(usize, top.position.column_stop)];
+ const suffix = remainder[@intCast(usize, top.position.column_stop)..];
+
+ writer.print(
+ comptime Output.prettyFmt(
+ "<r><d>{d} |<r> {s}<red>{s}<r>{s}<r>\n<r>",
+ allow_ansi_color,
+ ),
+ .{
+ source.line,
+ prefix,
+ underline,
+ suffix,
+ },
+ ) catch unreachable;
+ var first_non_whitespace = @intCast(u32, top.position.column_start);
+ while (first_non_whitespace < source.text.len and source.text[first_non_whitespace] == ' ') {
+ first_non_whitespace += 1;
+ }
+ const indent = @intCast(usize, pad) + " | ".len + first_non_whitespace + 1;
- writer.print(
- comptime Output.prettyFmt(
- "<r><d>{d} |<r> {s}<red>{s}<r>{s}<r>\n<r>",
+ writer.writeByteNTimes(' ', indent) catch unreachable;
+ writer.print(comptime Output.prettyFmt(
+ "<red><b>^<r>\n",
allow_ansi_color,
- ),
- .{
- source.line,
- prefix,
- underline,
- suffix,
- },
- ) catch unreachable;
- var first_non_whitespace = @intCast(u32, top.position.column_start);
- while (first_non_whitespace < source.text.len and source.text[first_non_whitespace] == ' ') {
- first_non_whitespace += 1;
+ ), .{}) catch unreachable;
}
- const indent = @intCast(usize, pad) + " | ".len + first_non_whitespace + 1;
-
- writer.writeByteNTimes(' ', indent) catch unreachable;
- writer.print(comptime Output.prettyFmt(
- "<red><b>^<r>\n",
- allow_ansi_color,
- ), .{}) catch unreachable;
if (name.len > 0 and message.len > 0) {
writer.print(comptime Output.prettyFmt(" <r><red><b>{s}<r><d>:<r> <b>{s}<r>\n", allow_ansi_color), .{
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index ca6e38edb..274b79c20 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -1442,14 +1442,6 @@ fn notimpl() noreturn {
Global.panic("Not implemented yet!!", .{});
}
-fn lexerpanic() noreturn {
- Global.panic("LexerPanic", .{});
-}
-
-fn fail() noreturn {
- Global.panic("Something went wrong :cry;", .{});
-}
-
const ExprBindingTuple = struct { expr: ?ExprNodeIndex = null, binding: ?Binding = null, override_expr: ?ExprNodeIndex = null };
const TempRef = struct {
@@ -5486,7 +5478,7 @@ pub fn NewParser(
if (p.lexer.token == .t_default) {
if (foundDefault) {
try p.log.addRangeError(p.source, p.lexer.range(), "Multiple default clauses are not allowed");
- fail();
+ return error.SyntaxError;
}
foundDefault = true;
@@ -5673,7 +5665,7 @@ pub fn NewParser(
if (p.lexer.isContextualKeyword("of") or isForAwait) {
if (bad_let_range) |r| {
try p.log.addRangeError(p.source, r, "\"let\" must be wrapped in parentheses to be used as an expression here");
- fail();
+ return error.SyntaxError;
}
if (isForAwait and !p.lexer.isContextualKeyword("of")) {
@@ -5989,7 +5981,7 @@ pub fn NewParser(
try p.log.addError(p.source, logger.Loc{
.start = loc.start + 5,
}, "Unexpected newline after \"throw\"");
- fail();
+ return error.SyntaxError;
}
const expr = try p.parseExpr(.lowest);
try p.lexer.expectOrInsertSemicolon();
@@ -6656,7 +6648,7 @@ pub fn NewParser(
// Commas after spread elements are not allowed
if (has_spread and p.lexer.token == .t_comma) {
p.log.addRangeError(p.source, p.lexer.range(), "Unexpected \",\" after rest pattern") catch unreachable;
- fail();
+ return error.SyntaxError;
}
}
@@ -6703,7 +6695,7 @@ pub fn NewParser(
// Commas after spread elements are not allowed
if (property.flags.is_spread and p.lexer.token == .t_comma) {
p.log.addRangeError(p.source, p.lexer.range(), "Unexpected \",\" after rest pattern") catch unreachable;
- fail();
+ return error.SyntaxError;
}
if (p.lexer.token != .t_comma) {
@@ -7450,7 +7442,7 @@ pub fn NewParser(
// Newlines are not allowed before "=>"
if (p.lexer.has_newline_before) {
try p.log.addRangeError(p.source, p.lexer.range(), "Unexpected newline before \"=>\"");
- fail();
+ return error.SyntaxError;
}
try p.lexer.expect(T.t_equals_greater_than);
diff --git a/src/linker.zig b/src/linker.zig
index dea14f675..a1d72663f 100644
--- a/src/linker.zig
+++ b/src/linker.zig
@@ -166,8 +166,9 @@ pub fn NewLinker(comptime BundlerType: type) type {
}
pub inline fn nodeModuleBundleImportPath(this: *const ThisLinker) string {
- return if (this.options.platform != .bun and
- this.options.node_modules_bundle_url.len > 0)
+ if (this.options.platform == .bun) return "/node_modules.server.bun";
+
+ return if (this.options.node_modules_bundle_url.len > 0)
this.options.node_modules_bundle_url
else
this.options.node_modules_bundle.?.bundle.import_from_name;
@@ -198,6 +199,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
var externals = std.ArrayList(u32).init(linker.allocator);
var needs_bundle = false;
var first_bundled_index: ?u32 = null;
+ var had_resolve_errors = false;
// Step 1. Resolve imports & requires
switch (result.loader) {
@@ -349,18 +351,21 @@ pub fn NewLinker(comptime BundlerType: type) type {
result.ast.needs_runtime = true;
}
} else |err| {
+ had_resolve_errors = true;
+
switch (err) {
error.ModuleNotFound => {
- if (Resolver.isPackagePath(import_record.path.text)) {
+ if (import_record.path.text.len > 0 and Resolver.isPackagePath(import_record.path.text)) {
if (linker.options.platform.isWebLike() and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) {
try linker.log.addResolveError(
&result.source,
import_record.range,
linker.allocator,
- "Could not resolve: \"{s}\". Try setting --platform=\"node\"",
+ "Could not resolve: \"{s}\". Try setting --platform=\"node\" (after bun build exists)",
.{import_record.path.text},
import_record.kind,
);
+ continue;
} else {
try linker.log.addResolveError(
&result.source,
@@ -370,6 +375,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
.{import_record.path.text},
import_record.kind,
);
+ continue;
}
} else {
try linker.log.addResolveError(
@@ -386,6 +392,17 @@ pub fn NewLinker(comptime BundlerType: type) type {
}
},
else => {
+ try linker.log.addResolveError(
+ &result.source,
+ import_record.range,
+ linker.allocator,
+ "{s} resolving \"{s}\"",
+ .{
+ @errorName(err),
+ import_record.path.text,
+ },
+ import_record.kind,
+ );
continue;
},
}
@@ -394,6 +411,7 @@ pub fn NewLinker(comptime BundlerType: type) type {
},
else => {},
}
+ if (had_resolve_errors) return error.LinkError;
result.ast.externals = externals.toOwnedSlice();
if (result.ast.needs_runtime and result.ast.runtime_import_record_id == null) {
diff --git a/src/runtime.zig b/src/runtime.zig
index c426ccb07..32c5f752e 100644
--- a/src/runtime.zig
+++ b/src/runtime.zig
@@ -8,6 +8,33 @@ const Schema = @import("./api/schema.zig");
const Api = Schema.Api;
+const ErrorCSSPath = "../examples/hello-next/bun-framework-next/bun-error.css";
+
+pub const ErrorCSS = struct {
+ pub const ProdSourceContent = @embedFile(ErrorCSSPath);
+
+ pub fn sourceContent() string {
+ if (comptime isDebug) {
+ var dirpath = std.fs.path.dirname(@src().file).?;
+ var env = std.process.getEnvMap(default_allocator) catch unreachable;
+
+ const dir = std.mem.replaceOwned(
+ u8,
+ default_allocator,
+ dirpath,
+ "jarred",
+ env.get("USER").?,
+ ) catch unreachable;
+ var runtime_path = std.fs.path.join(default_allocator, &[_]string{ dir, ErrorCSSPath }) catch unreachable;
+ const file = std.fs.openFileAbsolute(runtime_path, .{}) catch unreachable;
+ defer file.close();
+ return file.readToEndAlloc(default_allocator, (file.stat() catch unreachable).size) catch unreachable;
+ } else {
+ return ProdSourceContent;
+ }
+ }
+};
+
pub const Fallback = struct {
pub const ProdSourceContent = @embedFile("./fallback.out.js");
pub const HTMLTemplate = @embedFile("./fallback.html");
diff --git a/src/runtime/hmr.ts b/src/runtime/hmr.ts
index 2c4f8eb35..dc72f469d 100644
--- a/src/runtime/hmr.ts
+++ b/src/runtime/hmr.ts
@@ -373,6 +373,8 @@ if (typeof window !== "undefined") {
css: new CSSLoader(),
};
+ sessionId: number;
+
start() {
if (runOnce) {
__hmrlog.warn(
@@ -448,6 +450,7 @@ if (typeof window !== "undefined") {
// key: module id
// value: server-timestamp
builds = new Map<number, number>();
+ cwd: string;
indexOfModuleId(id: number): number {
return HMRModule.dependencies.graph.indexOf(id);
@@ -586,6 +589,25 @@ if (typeof window !== "undefined") {
}
this.client = new HMRClient();
+ // if (
+ // "sessionStorage" in globalThis &&
+ // globalThis.sessionStorage.getItem("bun-hmr-session-id")
+ // ) {
+ // this.client.sessionId = parseInt(
+ // globalThis.sessionStorage.getItem("bun-hmr-session-id"),
+ // 16
+ // );
+ // } else {
+ // this.client.sessionId = Math.floor(Math.random() * 65534);
+ // if ("sessionStorage" in globalThis) {
+ // try {
+ // globalThis.sessionStorage.setItem(
+ // "bun-hmr-session-id",
+ // this.client.sessionId.toString(16)
+ // );
+ // } catch (exception) {}
+ // }
+ // }
this.client.verbose = verbose;
this.client.start();
globalThis["__BUN_HMR"] = this.client;
@@ -596,20 +618,29 @@ if (typeof window !== "undefined") {
const build = API.decodeWebsocketMessageBuildFailure(buffer);
const id = build.id;
- const index = this.indexOfModuleId(id);
- // Ignore build failures of modules that are not loaded
- if (index === -1) {
- return;
- }
+ // const index = this.indexOfModuleId(id);
+ // // Ignore build failures of modules that are not loaded
+ // if (index === -1) {
+ // this.maybeReportBuildFailure(build);
+ // return;
+ // }
+
+ // // Build failed for a module we didn't request?
+ // const minTimestamp = this.builds.get(index);
+ // if (!minTimestamp) {
+ // return;
+ // }
+ // const fail = API.decodeWebsocketMessageBuildFailure(buffer);
+
+ this.reportBuildFailure(build);
+ }
- // Build failed for a module we didn't request?
- const minTimestamp = this.builds.get(index);
- if (!minTimestamp) {
- return;
- }
- const fail = API.decodeWebsocketMessageBuildFailure(buffer);
- // TODO: finish this.
- __hmrlog.error("Build failed", fail.module_path);
+ maybeReportBuildFailure(failure: API.WebsocketMessageBuildFailure) {
+ globalThis.renderBuildFailure(failure, this.cwd);
+ }
+ reportBuildFailure(failure: API.WebsocketMessageBuildFailure) {
+ __hmrlog.error("Build failed", failure.module_path);
+ globalThis.renderBuildFailure(failure, this.cwd);
}
verbose = false;
@@ -849,6 +880,7 @@ if (typeof window !== "undefined") {
const welcome = API.decodeWebsocketMessageWelcome(buffer);
this.epoch = welcome.epoch;
this.javascriptReloader = welcome.javascriptReloader;
+ this.cwd = welcome.cwd;
if (!this.epoch) {
__hmrlog.warn("Internal HMR error");
}
@@ -1306,6 +1338,8 @@ if (typeof window !== "undefined") {
document.addEventListener("onimportcss", HMRClient.onCSSImport, {
passive: true,
});
+
+ window.addEventListener("error", HMRClient.onError, { passive: true });
}
}
diff --git a/src/string_immutable.zig b/src/string_immutable.zig
index 055239c1a..238706f93 100644
--- a/src/string_immutable.zig
+++ b/src/string_immutable.zig
@@ -363,6 +363,23 @@ inline fn eqlComptimeCheckLen(self: string, comptime alt: anytype, comptime chec
const second = comptime std.mem.readIntNative(u64, alt[8..15]);
return ((comptime !check_len) or self.len == alt.len) and first == std.mem.readIntNative(u64, self[0..8]) and second == std.mem.readIntNative(u64, self[8..16]);
},
+ 23 => {
+ const first = comptime std.mem.readIntNative(u64, alt[0..8]);
+ const second = comptime std.mem.readIntNative(u64, alt[8..15]);
+ return ((comptime !check_len) or self.len == alt.len) and
+ first == std.mem.readIntNative(u64, self[0..8]) and
+ second == std.mem.readIntNative(u64, self[8..16]) and
+ eqlComptimeIgnoreLen(self[16..23], comptime alt[16..23]);
+ },
+ 24 => {
+ const first = comptime std.mem.readIntNative(u64, alt[0..8]);
+ const second = comptime std.mem.readIntNative(u64, alt[8..16]);
+ const third = comptime std.mem.readIntNative(u64, alt[16..24]);
+ return ((comptime !check_len) or self.len == alt.len) and
+ first == std.mem.readIntNative(u64, self[0..8]) and
+ second == std.mem.readIntNative(u64, self[8..16]) and
+ third == std.mem.readIntNative(u64, self[16..24]);
+ },
else => {
@compileError(alt ++ " is too long.");
},