diff options
author | 2021-09-07 03:21:58 -0700 | |
---|---|---|
committer | 2021-09-07 03:21:58 -0700 | |
commit | 1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3 (patch) | |
tree | c942103d08048141f417a4b75130326b53b73cb9 /src | |
parent | d59e7b27b0b525126fe5011f5ab393f9f5e6659a (diff) | |
download | bun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.tar.gz bun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.tar.zst bun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.zip |
WIP error css
Former-commit-id: 36f03bf491cf274f68361e334a706538464ee271
Diffstat (limited to 'src')
-rw-r--r-- | src/api/schema.d.ts | 1 | ||||
-rw-r--r-- | src/api/schema.js | 8 | ||||
-rw-r--r-- | src/api/schema.peechy | 1 | ||||
-rw-r--r-- | src/api/schema.zig | 5 | ||||
-rw-r--r-- | src/bundler.zig | 76 | ||||
-rw-r--r-- | src/fallback.version | 2 | ||||
-rw-r--r-- | src/http.zig | 332 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 2 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 75 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 20 | ||||
-rw-r--r-- | src/linker.zig | 26 | ||||
-rw-r--r-- | src/runtime.zig | 27 | ||||
-rw-r--r-- | src/runtime/hmr.ts | 60 | ||||
-rw-r--r-- | src/string_immutable.zig | 17 |
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."); }, |