aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-07 03:21:58 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-07 03:21:58 -0700
commit1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3 (patch)
treec942103d08048141f417a4b75130326b53b73cb9
parentd59e7b27b0b525126fe5011f5ab393f9f5e6659a (diff)
downloadbun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.tar.gz
bun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.tar.zst
bun-1d1a70c21fc1f6e9e618cb52262e21bc37ac4be3.zip
WIP error css
Former-commit-id: 36f03bf491cf274f68361e334a706538464ee271
-rw-r--r--README.md15
-rw-r--r--examples/hello-next/babel.js.REMOVED.git-id1
-rw-r--r--examples/hello-next/bun-framework-next/bun-error/powered-by.pngbin0 -> 2863 bytes
-rw-r--r--examples/hello-next/bun-framework-next/bun-error/powered-by.webpbin0 -> 1316 bytes
-rw-r--r--examples/hello-next/bun-framework-next/bun-runtime-error.ts163
-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
19 files changed, 616 insertions, 215 deletions
diff --git a/README.md b/README.md
index 996378b49..7ecf3e6f4 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Bun is a new:
All in one fast &amp; easy-to-use tool. Instead of 1,000 node_modules for development, you only need Bun.
-Bun is experimental software. Join [Bun's Discord](https://bun.sh/discord) for help and have a look at [things that don't work yet](#things-that-dont-work-yet). Most notably, this early version of Bun is not for building
+Bun is experimental software. Join [Bun's Discord](https://bun.sh/discord) for help and have a look at [things that don't work yet](#things-that-dont-work-yet).
## Install:
@@ -100,7 +100,18 @@ Bun is a project with incredibly large scope, and it's early days.
| [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) (in SSR) | Bun.js |
| `bun run` command | Bun.js |
-All of these are planned to work eventually, maybe with the exception of `setTimeout` inside of SSR.
+## Limitations & intended usage
+
+Bun is great for building websites &amp; webapps. For libraries, consider using Rollup or esbuild instead. Bun currently doesn't minify code and Bun's dead code elimination doesn't look beyond the current file.
+
+Bun is focused on:
+
+- Development, not production
+- Compatibility with existing frameworks & tooling
+
+Ideally, most projects can use Bun with their existing tooling while making few changes to their codebase. That means using Bun in development, and continuing to use Webpack, esbuild, or another bundler in production. Using two bundlers might sound strange at first, but after all the production-only AST transforms, minification, and special development/production-only imported files...it's not far from the status quo.
+
+# Configuration
# Building from source
diff --git a/examples/hello-next/babel.js.REMOVED.git-id b/examples/hello-next/babel.js.REMOVED.git-id
deleted file mode 100644
index c6e41fc71..000000000
--- a/examples/hello-next/babel.js.REMOVED.git-id
+++ /dev/null
@@ -1 +0,0 @@
-e2e5ce1979cde7a27f105fc408e375bbc6a9d78d \ No newline at end of file
diff --git a/examples/hello-next/bun-framework-next/bun-error/powered-by.png b/examples/hello-next/bun-framework-next/bun-error/powered-by.png
new file mode 100644
index 000000000..7e71f1357
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/bun-error/powered-by.png
Binary files differ
diff --git a/examples/hello-next/bun-framework-next/bun-error/powered-by.webp b/examples/hello-next/bun-framework-next/bun-error/powered-by.webp
new file mode 100644
index 000000000..0f48488ea
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/bun-error/powered-by.webp
Binary files differ
diff --git a/examples/hello-next/bun-framework-next/bun-runtime-error.ts b/examples/hello-next/bun-framework-next/bun-runtime-error.ts
new file mode 100644
index 000000000..331040b36
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/bun-runtime-error.ts
@@ -0,0 +1,163 @@
+// Based on https://github.com/stacktracejs/error-stack-parser/blob/master/error-stack-parser.js
+
+import type {
+ StackFrame as StackFrameType,
+ StackFramePosition,
+ StackFrameScope,
+} from "../../../src/api/schema";
+
+export class StackFrame implements StackFrameType {
+ function_name: string;
+ file: string;
+ position: StackFramePosition;
+ scope: StackFrameScope;
+ lineText: string = "";
+ constructor({
+ functionName: function_name = "",
+ fileName: file = "",
+ lineNumber: line = -1,
+ columnNumber: column = -1,
+ source = "",
+ }) {
+ this.function_name = function_name;
+ this.file = file;
+ if (source) this.lineText = source;
+ this.scope = 3;
+ this.position = {
+ line: line,
+ source_offset: -1,
+ line_start: -1,
+ line_stop: -1,
+ column_start: column,
+ column_stop: -1,
+ expression_start: -1,
+ expression_stop: -1,
+ };
+ }
+}
+
+const FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
+const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
+const SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
+
+export default class RuntimeError {
+ original: Error;
+ stack: StackFrame[];
+
+ static from(error: Error) {
+ const runtime = new RuntimeError();
+ runtime.original = error;
+ runtime.stack = this.parseStack(error);
+ return RuntimeError;
+ }
+
+ /**
+ * Given an Error object, extract the most information from it.
+ *
+ * @param {Error} error object
+ * @return {Array} of StackFrames
+ */
+ static parseStack(error) {
+ if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
+ return this.parseV8OrIE(error);
+ } else if (error.stack) {
+ return this.parseFFOrSafari(error);
+ } else {
+ return [];
+ }
+ }
+
+ // Separate line and column numbers from a string of the form: (URI:Line:Column)
+ static extractLocation(urlLike) {
+ // Fail-fast but return locations like "(native)"
+ if (urlLike.indexOf(":") === -1) {
+ return [urlLike];
+ }
+
+ var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
+ var parts = regExp.exec(urlLike.replace(/[()]/g, ""));
+ return [parts[1], parts[2] || undefined, parts[3] || undefined];
+ }
+
+ static parseV8OrIE(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !!line.match(CHROME_IE_STACK_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ if (line.indexOf("(eval ") > -1) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ line = line
+ .replace(/eval code/g, "eval")
+ .replace(/(\(eval at [^()]*)|(\),.*$)/g, "");
+ }
+ var sanitizedLine = line.replace(/^\s+/, "").replace(/\(eval code/g, "(");
+
+ // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
+ // case it has spaces in it, as the string is split on \s+ later on
+ var location = sanitizedLine.match(/ (\((.+):(\d+):(\d+)\)$)/);
+
+ // remove the parenthesized location from the line, if it was matched
+ sanitizedLine = location
+ ? sanitizedLine.replace(location[0], "")
+ : sanitizedLine;
+
+ var tokens = sanitizedLine.split(/\s+/).slice(1);
+ // if a location was matched, pass it to extractLocation() otherwise pop the last token
+ var locationParts = this.extractLocation(
+ location ? location[1] : tokens.pop()
+ );
+ var functionName = tokens.join(" ") || undefined;
+ var fileName =
+ ["eval", "<anonymous>"].indexOf(locationParts[0]) > -1
+ ? undefined
+ : locationParts[0];
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: fileName,
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }, this);
+ }
+
+ static parseFFOrSafari(error) {
+ var filtered = error.stack.split("\n").filter(function (line) {
+ return !line.match(SAFARI_NATIVE_CODE_REGEXP);
+ }, this);
+
+ return filtered.map(function (line) {
+ // Throw away eval information until we implement stacktrace.js/stackframe#8
+ if (line.indexOf(" > eval") > -1) {
+ line = line.replace(
+ / line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,
+ ":$1"
+ );
+ }
+
+ if (line.indexOf("@") === -1 && line.indexOf(":") === -1) {
+ // Safari eval frames only have function names and nothing else
+ return new StackFrame({
+ functionName: line,
+ });
+ } else {
+ var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
+ var matches = line.match(functionNameRegex);
+ var functionName = matches && matches[1] ? matches[1] : undefined;
+ var locationParts = this.extractLocation(
+ line.replace(functionNameRegex, "")
+ );
+
+ return new StackFrame({
+ functionName: functionName,
+ fileName: locationParts[0],
+ lineNumber: locationParts[1],
+ columnNumber: locationParts[2],
+ source: line,
+ });
+ }
+ }, this);
+ }
+}
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.");
},