mirror of
https://github.com/oven-sh/bun
synced 2026-02-08 09:58:55 +00:00
Compare commits
2 Commits
dylan/pyth
...
jarred/imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c75516f20 | ||
|
|
0b1376b3ca |
@@ -1811,8 +1811,8 @@ pub const HotUpdateContext = struct {
|
||||
|
||||
const subslice = rc.resolved_index_cache[start..][0..rc.sources.len];
|
||||
|
||||
comptime assert(@alignOf(IncrementalGraph(side).FileIndex.Optional) == @alignOf(u32));
|
||||
comptime assert(@sizeOf(IncrementalGraph(side).FileIndex.Optional) == @sizeOf(u32));
|
||||
// comptime assert(@alignOf(IncrementalGraph(side).FileIndex.Optional) == @alignOf(u32));
|
||||
// comptime assert(@sizeOf(IncrementalGraph(side).FileIndex.Optional) == @sizeOf(u32));
|
||||
return @ptrCast(&subslice[i.get()]);
|
||||
}
|
||||
};
|
||||
@@ -2825,7 +2825,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
// code because there is only one instance of the server. Instead,
|
||||
// it stores which module graphs it is a part of. This makes sure
|
||||
// that recompilation knows what bundler options to use.
|
||||
.server => packed struct(u8) {
|
||||
.server => struct {
|
||||
/// Is this file built for the Server graph.
|
||||
is_rsc: bool,
|
||||
/// Is this file built for the SSR graph.
|
||||
@@ -2871,7 +2871,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
code_len: u32,
|
||||
flags: Flags,
|
||||
|
||||
const Flags = packed struct(u32) {
|
||||
const Flags = struct {
|
||||
/// If the file has an error, the failure can be looked up
|
||||
/// in the `.failures` map.
|
||||
failed: bool,
|
||||
@@ -2890,10 +2890,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
unused: enum(u26) { unused } = .unused,
|
||||
};
|
||||
|
||||
comptime {
|
||||
assert(@sizeOf(@This()) == @sizeOf(u64) * 2);
|
||||
assert(@alignOf(@This()) == @alignOf([*]u8));
|
||||
}
|
||||
// comptime {
|
||||
// assert(@sizeOf(@This()) == @sizeOf(u64) * 2);
|
||||
// assert(@alignOf(@This()) == @alignOf([*]u8));
|
||||
// }
|
||||
|
||||
fn initJavaScript(code_slice: []const u8, flags: Flags) @This() {
|
||||
assert(flags.kind == .js);
|
||||
@@ -3007,11 +3007,6 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
},
|
||||
} else .empty;
|
||||
}
|
||||
|
||||
comptime {
|
||||
assert(@sizeOf(@This()) == @sizeOf(u32) * 5);
|
||||
assert(@alignOf(@This()) == @alignOf(u32));
|
||||
}
|
||||
};
|
||||
|
||||
// If this data structure is not clear, see `DirectoryWatchStore.Dep`
|
||||
@@ -3692,7 +3687,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
|
||||
/// Returns the key that was inserted.
|
||||
pub fn insertEmpty(g: *@This(), abs_path: []const u8) bun.OOM![]const u8 {
|
||||
comptime assert(side == .client); // not implemented
|
||||
// comptime assert(side == .client); // not implemented
|
||||
g.owner().graph_safety_lock.assertLocked();
|
||||
const gop = try g.bundled_files.getOrPut(g.owner().allocator, abs_path);
|
||||
if (!gop.found_existing) {
|
||||
@@ -3783,7 +3778,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
};
|
||||
|
||||
if (!found_existing) {
|
||||
comptime assert(mode == .abs_path);
|
||||
// comptime assert(mode == .abs_path);
|
||||
gop.key_ptr.* = try bun.default_allocator.dupe(u8, key);
|
||||
try g.first_dep.append(g.owner().allocator, .none);
|
||||
try g.first_import.append(g.owner().allocator, .none);
|
||||
@@ -4614,7 +4609,7 @@ pub const SerializedFailure = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub const Packed = packed struct(u32) {
|
||||
pub const Packed = struct {
|
||||
kind: enum(u2) { none, route, client, server },
|
||||
data: u30,
|
||||
|
||||
@@ -5061,29 +5056,12 @@ const HmrTopic = enum(u8) {
|
||||
_,
|
||||
|
||||
pub const max_count = @typeInfo(HmrTopic).@"enum".fields.len;
|
||||
pub const Bits = @Type(.{ .@"struct" = .{
|
||||
.backing_integer = @Type(.{ .int = .{
|
||||
.bits = max_count,
|
||||
.signedness = .unsigned,
|
||||
} }),
|
||||
.fields = &brk: {
|
||||
const enum_fields = @typeInfo(HmrTopic).@"enum".fields;
|
||||
var fields: [enum_fields.len]std.builtin.Type.StructField = undefined;
|
||||
for (enum_fields, &fields) |e, *s| {
|
||||
s.* = .{
|
||||
.name = e.name,
|
||||
.type = bool,
|
||||
.default_value_ptr = &false,
|
||||
.is_comptime = false,
|
||||
.alignment = 0,
|
||||
};
|
||||
}
|
||||
break :brk fields;
|
||||
},
|
||||
.decls = &.{},
|
||||
.is_tuple = false,
|
||||
.layout = .@"packed",
|
||||
} });
|
||||
pub const Bits = struct {
|
||||
hot_update: bool = false,
|
||||
errors: bool = false,
|
||||
browser_error: bool = false,
|
||||
visualizer: bool = false,
|
||||
};
|
||||
};
|
||||
|
||||
const HmrSocket = struct {
|
||||
@@ -5263,7 +5241,7 @@ fn markAllRouteChildrenFailed(dev: *DevServer, route_index: Route.Index) void {
|
||||
/// This task informs the DevServer's thread about new files to be bundled.
|
||||
pub const HotReloadEvent = struct {
|
||||
/// Align to cache lines to eliminate contention.
|
||||
const Aligned = struct { aligned: HotReloadEvent align(std.atomic.cache_line) };
|
||||
const Aligned = struct { aligned: HotReloadEvent };
|
||||
|
||||
owner: *DevServer,
|
||||
/// Initialized in WatcherAtomics.watcherReleaseAndSubmitEvent
|
||||
@@ -5391,14 +5369,14 @@ const WatcherAtomics = struct {
|
||||
/// once. Memory is reused by swapping between these two. These items are
|
||||
/// aligned to cache lines to reduce contention, since these structures are
|
||||
/// carefully passed between two threads.
|
||||
events: [2]HotReloadEvent.Aligned align(std.atomic.cache_line),
|
||||
events: [2]HotReloadEvent.Aligned,
|
||||
/// 0 - no watch
|
||||
/// 1 - has fired additional watch
|
||||
/// 2+ - new events available, watcher is waiting on bundler to finish
|
||||
watcher_events_emitted: std.atomic.Value(u32),
|
||||
/// Which event is the watcher holding on to.
|
||||
/// This is not atomic because only the watcher thread uses this value.
|
||||
current: u1 align(std.atomic.cache_line),
|
||||
current: u1,
|
||||
|
||||
watcher_has_event: std.debug.SafetyLock,
|
||||
dev_server_has_event: std.debug.SafetyLock,
|
||||
@@ -5641,7 +5619,7 @@ pub fn numSubscribers(dev: *DevServer, topic: HmrTopic) u32 {
|
||||
return if (dev.server) |s| s.numSubscribers(&.{@intFromEnum(topic)}) else 0;
|
||||
}
|
||||
|
||||
const SafeFileId = packed struct(u32) {
|
||||
const SafeFileId = struct {
|
||||
side: bake.Side,
|
||||
index: u30,
|
||||
unused: enum(u1) { unused = 0 } = .unused,
|
||||
@@ -5719,7 +5697,7 @@ fn relativePath(dev: *const DevServer, path: []const u8) []const u8 {
|
||||
}
|
||||
|
||||
fn dumpStateDueToCrash(dev: *DevServer) !void {
|
||||
comptime assert(bun.FeatureFlags.bake_debugging_features);
|
||||
// comptime assert(bun.FeatureFlags.bake_debugging_features);
|
||||
|
||||
// being conservative about how much stuff is put on the stack.
|
||||
var filepath_buf: [@min(4096, bun.MAX_PATH_BYTES)]u8 = undefined;
|
||||
@@ -5757,7 +5735,7 @@ fn dumpStateDueToCrash(dev: *DevServer) !void {
|
||||
Output.note("Dumped incremental bundler graph to {}", .{bun.fmt.quote(filepath)});
|
||||
}
|
||||
|
||||
const RouteIndexAndRecurseFlag = packed struct(u32) {
|
||||
const RouteIndexAndRecurseFlag = struct {
|
||||
route_index: Route.Index,
|
||||
/// Set true for layout
|
||||
should_recurse_when_visiting: bool,
|
||||
@@ -5771,7 +5749,7 @@ pub const EntryPointList = struct {
|
||||
|
||||
pub const empty: EntryPointList = .{ .set = .{} };
|
||||
|
||||
const Flags = packed struct(u8) {
|
||||
const Flags = struct {
|
||||
client: bool = false,
|
||||
server: bool = false,
|
||||
ssr: bool = false,
|
||||
@@ -5918,7 +5896,7 @@ pub const Assets = struct {
|
||||
.mime_type = mime_type,
|
||||
.server = assets.owner().server orelse unreachable,
|
||||
});
|
||||
comptime assert(@TypeOf(slice.items(.hash)[0]) == void);
|
||||
// comptime assert(@TypeOf(slice.items(.hash)[0]) == void);
|
||||
assets.needs_reindex = true;
|
||||
return .{ .index = @intCast(i) };
|
||||
} else {
|
||||
|
||||
@@ -219,4 +219,16 @@ export default [
|
||||
},
|
||||
klass: {},
|
||||
}),
|
||||
define({
|
||||
name: "PageBundle",
|
||||
noConstructor: true,
|
||||
finalize: true,
|
||||
proto: {
|
||||
src: {
|
||||
getter: "getSrc",
|
||||
cache: true,
|
||||
},
|
||||
},
|
||||
klass: {},
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -179,14 +179,16 @@ pub const StaticRoute = @import("./server/StaticRoute.zig");
|
||||
|
||||
const HTMLBundle = JSC.API.HTMLBundle;
|
||||
const HTMLBundleRoute = HTMLBundle.HTMLBundleRoute;
|
||||
const PageBundle = JSC.API.PageBundle;
|
||||
const PageBundleRoute = PageBundle.PageBundleRoute;
|
||||
|
||||
pub const AnyStaticRoute = union(enum) {
|
||||
StaticRoute: *StaticRoute,
|
||||
HTMLBundleRoute: *HTMLBundleRoute,
|
||||
|
||||
PageBundleRoute: *PageBundleRoute,
|
||||
pub fn memoryCost(this: AnyStaticRoute) usize {
|
||||
return switch (this) {
|
||||
.StaticRoute => |static_route| static_route.memoryCost(),
|
||||
.HTMLBundleRoute => |html_bundle_route| html_bundle_route.memoryCost(),
|
||||
inline else => |route| route.memoryCost(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -194,6 +196,7 @@ pub const AnyStaticRoute = union(enum) {
|
||||
switch (this) {
|
||||
.StaticRoute => |static_route| static_route.server = server,
|
||||
.HTMLBundleRoute => |html_bundle_route| html_bundle_route.server = server,
|
||||
.PageBundleRoute => |page_bundle_route| page_bundle_route.server = server,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +204,7 @@ pub const AnyStaticRoute = union(enum) {
|
||||
switch (this) {
|
||||
.StaticRoute => |static_route| static_route.deref(),
|
||||
.HTMLBundleRoute => |html_bundle_route| html_bundle_route.deref(),
|
||||
.PageBundleRoute => |page_bundle_route| page_bundle_route.deref(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,10 +212,16 @@ pub const AnyStaticRoute = union(enum) {
|
||||
switch (this) {
|
||||
.StaticRoute => |static_route| static_route.ref(),
|
||||
.HTMLBundleRoute => |html_bundle_route| html_bundle_route.ref(),
|
||||
.PageBundleRoute => |page_bundle_route| page_bundle_route.ref(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromJS(globalThis: *JSC.JSGlobalObject, argument: JSC.JSValue, dedupe_html_bundle_map: *std.AutoHashMap(*HTMLBundle, *HTMLBundleRoute)) bun.JSError!AnyStaticRoute {
|
||||
pub fn fromJS(
|
||||
globalThis: *JSC.JSGlobalObject,
|
||||
argument: JSC.JSValue,
|
||||
dedupe_html_bundle_map: *std.AutoHashMap(*HTMLBundle, *HTMLBundleRoute),
|
||||
dedupe_page_bundle_map: *std.AutoHashMap(*PageBundle, *PageBundleRoute),
|
||||
) bun.JSError!AnyStaticRoute {
|
||||
if (argument.as(HTMLBundle)) |html_bundle| {
|
||||
const entry = try dedupe_html_bundle_map.getOrPut(html_bundle);
|
||||
if (!entry.found_existing) {
|
||||
@@ -223,6 +233,17 @@ pub const AnyStaticRoute = union(enum) {
|
||||
return .{ .HTMLBundleRoute = entry.value_ptr.* };
|
||||
}
|
||||
|
||||
if (argument.as(PageBundle)) |page_bundle| {
|
||||
const entry = try dedupe_page_bundle_map.getOrPut(page_bundle);
|
||||
if (!entry.found_existing) {
|
||||
entry.value_ptr.* = PageBundleRoute.init(page_bundle);
|
||||
} else {
|
||||
entry.value_ptr.*.ref();
|
||||
}
|
||||
|
||||
return .{ .PageBundleRoute = entry.value_ptr.* };
|
||||
}
|
||||
|
||||
return .{ .StaticRoute = try StaticRoute.fromJS(globalThis, argument) };
|
||||
}
|
||||
};
|
||||
@@ -1118,6 +1139,12 @@ pub const ServerConfig = struct {
|
||||
return global.throwInvalidArguments("Bun.serve expects an object", .{});
|
||||
}
|
||||
|
||||
if (try arg.get(global, "development")) |dev| {
|
||||
args.development = dev.coerce(bool, global);
|
||||
args.reuse_port = !args.development;
|
||||
}
|
||||
if (global.hasException()) return error.JSError;
|
||||
|
||||
if (try arg.get(global, "static")) |static| {
|
||||
if (!static.isObject()) {
|
||||
return global.throwInvalidArguments("Bun.serve expects 'static' to be an object shaped like { [pathname: string]: Response }", .{});
|
||||
@@ -1131,6 +1158,8 @@ pub const ServerConfig = struct {
|
||||
|
||||
var dedupe_html_bundle_map = std.AutoHashMap(*HTMLBundle, *HTMLBundleRoute).init(bun.default_allocator);
|
||||
defer dedupe_html_bundle_map.deinit();
|
||||
var dedupe_page_bundle_map = std.AutoHashMap(*PageBundle, *PageBundleRoute).init(bun.default_allocator);
|
||||
defer dedupe_page_bundle_map.deinit();
|
||||
|
||||
errdefer {
|
||||
for (args.static_routes.items) |*static_route| {
|
||||
@@ -1153,7 +1182,7 @@ pub const ServerConfig = struct {
|
||||
return global.throwInvalidArguments("Invalid static route \"{s}\". Please encode all non-ASCII characters in the path.", .{path});
|
||||
}
|
||||
|
||||
const route = try AnyStaticRoute.fromJS(global, value, &dedupe_html_bundle_map);
|
||||
const route = try AnyStaticRoute.fromJS(global, value, &dedupe_html_bundle_map, &dedupe_page_bundle_map);
|
||||
args.static_routes.append(.{
|
||||
.path = path,
|
||||
.route = route,
|
||||
@@ -1162,10 +1191,11 @@ pub const ServerConfig = struct {
|
||||
|
||||
// When HTML bundles are provided, ensure DevServer options are ready
|
||||
// The presence of these options causes Bun.serve to initialize things.
|
||||
if (bun.bake.DevServer.enabled and dedupe_html_bundle_map.count() > 0) {
|
||||
if (!args.development and bun.bake.DevServer.enabled and dedupe_html_bundle_map.count() > 0) {
|
||||
// TODO: this should be the dir with bunfig??
|
||||
const root = bun.fs.FileSystem.instance.top_level_dir;
|
||||
var arena = std.heap.ArenaAllocator.init(bun.default_allocator);
|
||||
errdefer arena.deinit();
|
||||
const framework = try bun.bake.Framework.auto(arena.allocator(), &global.bunVM().transpiler.resolver);
|
||||
args.bake = .{
|
||||
.arena = arena,
|
||||
@@ -1278,12 +1308,6 @@ pub const ServerConfig = struct {
|
||||
}
|
||||
if (global.hasException()) return error.JSError;
|
||||
|
||||
if (try arg.get(global, "development")) |dev| {
|
||||
args.development = dev.coerce(bool, global);
|
||||
args.reuse_port = !args.development;
|
||||
}
|
||||
if (global.hasException()) return error.JSError;
|
||||
|
||||
if (try arg.getTruthy(global, "app")) |bake_args_js| {
|
||||
if (args.bake != null) {
|
||||
// "app" is likely to be removed in favor of the HTML loader.
|
||||
@@ -5818,6 +5842,7 @@ const ServePlugins = struct {
|
||||
plugin: *bun.JSC.API.JSBundler.Plugin,
|
||||
promise: JSC.JSPromise.Strong,
|
||||
html_bundle_routes: std.ArrayListUnmanaged(*HTMLBundleRoute),
|
||||
page_bundle_routes: std.ArrayListUnmanaged(*PageBundleRoute),
|
||||
dev_server: ?*bun.bake.DevServer,
|
||||
},
|
||||
loaded: *bun.JSC.API.JSBundler.Plugin,
|
||||
@@ -5834,6 +5859,7 @@ const ServePlugins = struct {
|
||||
|
||||
pub const Callback = union(enum) {
|
||||
html_bundle_route: *HTMLBundleRoute,
|
||||
page_bundle_route: *PageBundleRoute,
|
||||
dev_server: *bun.bake.DevServer,
|
||||
};
|
||||
|
||||
@@ -5863,6 +5889,10 @@ const ServePlugins = struct {
|
||||
route.ref();
|
||||
try pending.html_bundle_routes.append(bun.default_allocator, route);
|
||||
},
|
||||
.page_bundle_route => |route| {
|
||||
route.ref();
|
||||
try pending.page_bundle_routes.append(bun.default_allocator, route);
|
||||
},
|
||||
.dev_server => |server| {
|
||||
assert(pending.dev_server == null or pending.dev_server == server); // one dev server per server
|
||||
pending.dev_server = server;
|
||||
@@ -5903,7 +5933,8 @@ const ServePlugins = struct {
|
||||
this.state = .{ .pending = .{
|
||||
.promise = JSC.JSPromise.Strong.init(global),
|
||||
.plugin = plugin,
|
||||
.html_bundle_routes = .empty,
|
||||
.html_bundle_routes = .{},
|
||||
.page_bundle_routes = .{},
|
||||
.dev_server = null,
|
||||
} };
|
||||
|
||||
@@ -5969,11 +6000,20 @@ const ServePlugins = struct {
|
||||
const pending = &this.state.pending;
|
||||
const plugin = pending.plugin;
|
||||
pending.promise.deinit();
|
||||
defer pending.html_bundle_routes.deinit(bun.default_allocator);
|
||||
var html_bundle_routes = pending.html_bundle_routes;
|
||||
var page_bundle_routes = pending.page_bundle_routes;
|
||||
pending.html_bundle_routes = .{};
|
||||
pending.page_bundle_routes = .{};
|
||||
defer html_bundle_routes.deinit(bun.default_allocator);
|
||||
defer page_bundle_routes.deinit(bun.default_allocator);
|
||||
|
||||
this.state = .{ .loaded = plugin };
|
||||
|
||||
for (pending.html_bundle_routes.items) |route| {
|
||||
for (html_bundle_routes.items) |route| {
|
||||
route.onPluginsResolved(plugin) catch bun.outOfMemory();
|
||||
route.deref();
|
||||
}
|
||||
for (page_bundle_routes.items) |route| {
|
||||
route.onPluginsResolved(plugin) catch bun.outOfMemory();
|
||||
route.deref();
|
||||
}
|
||||
@@ -5997,14 +6037,23 @@ const ServePlugins = struct {
|
||||
const pending = &this.state.pending;
|
||||
pending.plugin.deinit();
|
||||
pending.promise.deinit();
|
||||
defer pending.html_bundle_routes.deinit(bun.default_allocator);
|
||||
var html_bundle_routes = pending.html_bundle_routes;
|
||||
var page_bundle_routes = pending.page_bundle_routes;
|
||||
pending.html_bundle_routes = .{};
|
||||
pending.page_bundle_routes = .{};
|
||||
defer html_bundle_routes.deinit(bun.default_allocator);
|
||||
defer page_bundle_routes.deinit(bun.default_allocator);
|
||||
|
||||
this.state = .err;
|
||||
|
||||
Output.errGeneric("Failed to load plugins for Bun.serve:", .{});
|
||||
global.bunVM().runErrorHandler(err, null);
|
||||
|
||||
for (pending.html_bundle_routes.items) |route| {
|
||||
for (html_bundle_routes.items) |route| {
|
||||
route.onPluginsRejected() catch bun.outOfMemory();
|
||||
route.deref();
|
||||
}
|
||||
for (page_bundle_routes.items) |route| {
|
||||
route.onPluginsRejected() catch bun.outOfMemory();
|
||||
route.deref();
|
||||
}
|
||||
@@ -7471,10 +7520,10 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp
|
||||
.StaticRoute => |static_route| {
|
||||
ServerConfig.applyStaticRoute(any_server, ssl_enabled, app, *StaticRoute, static_route, entry.path);
|
||||
},
|
||||
.HTMLBundleRoute => |html_bundle_route| {
|
||||
ServerConfig.applyStaticRoute(any_server, ssl_enabled, app, *HTMLBundleRoute, html_bundle_route, entry.path);
|
||||
inline .PageBundleRoute, .HTMLBundleRoute => |route| {
|
||||
ServerConfig.applyStaticRoute(any_server, ssl_enabled, app, @TypeOf(route), route, entry.path);
|
||||
if (dev_server) |dev| {
|
||||
dev.html_router.put(dev.allocator, entry.path, html_bundle_route) catch bun.outOfMemory();
|
||||
dev.html_router.put(dev.allocator, entry.path, route) catch bun.outOfMemory();
|
||||
}
|
||||
needs_plugins = true;
|
||||
},
|
||||
|
||||
@@ -254,7 +254,7 @@ pub const HTMLBundleRoute = struct {
|
||||
bun.default_allocator,
|
||||
);
|
||||
completion_task.started_at_ns = bun.getRoughTickCount().ns();
|
||||
completion_task.html_build_task = this;
|
||||
completion_task.build_task = .{ .html = this };
|
||||
this.state = .{ .building = completion_task };
|
||||
|
||||
// While we're building, ensure this doesn't get freed.
|
||||
@@ -477,7 +477,7 @@ const JSBundler = JSC.API.JSBundler;
|
||||
const HTTPResponse = bun.uws.AnyResponse;
|
||||
const uws = bun.uws;
|
||||
const AnyServer = JSC.API.AnyServer;
|
||||
const StaticRoute = @import("./StaticRoute.zig");
|
||||
const StaticRoute = bun.server.StaticRoute;
|
||||
|
||||
const debug = bun.Output.scoped(.HTMLBundle, true);
|
||||
const strings = bun.strings;
|
||||
|
||||
479
src/bun.js/api/server/PageBundle.zig
Normal file
479
src/bun.js/api/server/PageBundle.zig
Normal file
@@ -0,0 +1,479 @@
|
||||
//! This object is a description of an HTML bundle. It is created by importing an
|
||||
//! HTML file, and can be passed to the `static` option in `Bun.serve`. The build
|
||||
//! is done lazily (state held in PageBundle.Route or DevServer.RouteBundle.HTML).
|
||||
pub const PageBundle = @This();
|
||||
pub usingnamespace JSC.Codegen.JSPageBundle;
|
||||
// PageBundle can be owned by JavaScript as well as any number of Server instances.
|
||||
pub usingnamespace bun.NewRefCounted(PageBundle, deinit, null);
|
||||
|
||||
ref_count: u32 = 1,
|
||||
global: *JSGlobalObject,
|
||||
path: []const u8,
|
||||
|
||||
/// Initialize an PageBundle given a path.
|
||||
pub fn init(global: *JSGlobalObject, path: []const u8) !*PageBundle {
|
||||
return PageBundle.new(.{
|
||||
.global = global,
|
||||
.path = try bun.default_allocator.dupe(u8, path),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn finalize(this: *PageBundle) void {
|
||||
this.deref();
|
||||
}
|
||||
|
||||
pub fn deinit(this: *PageBundle) void {
|
||||
bun.default_allocator.free(this.path);
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn getSrc(this: *PageBundle, globalObject: *JSGlobalObject) JSValue {
|
||||
var str = bun.String.createUTF8(this.path);
|
||||
return str.transferToJS(globalObject);
|
||||
}
|
||||
|
||||
// TODO: Rename to `Route`
|
||||
/// An PageBundle can be used across multiple server instances, an
|
||||
/// PageBundle.Route can only be used on one server, but is also
|
||||
/// reference-counted because a server can have multiple instances of the same
|
||||
/// html file on multiple endpoints.
|
||||
pub const PageBundleRoute = struct {
|
||||
/// Rename to `bundle`
|
||||
html_bundle: *PageBundle,
|
||||
ref_count: u32 = 1,
|
||||
// TODO: attempt to remove the null case. null is only present during server
|
||||
// initialization as only a ServerConfig object is present.
|
||||
server: ?AnyServer = null,
|
||||
/// When using DevServer, this value is never read or written to.
|
||||
state: State,
|
||||
/// Written and read by DevServer to identify if this route has been
|
||||
/// registered with the bundler.
|
||||
dev_server_id: bun.bake.DevServer.RouteBundle.Index.Optional = .none,
|
||||
/// When state == .pending, incomplete responses are stored here.
|
||||
pending_responses: std.ArrayListUnmanaged(*PendingResponse) = .{},
|
||||
|
||||
/// One PageBundle.Route can be specified multiple times
|
||||
pub usingnamespace bun.NewRefCounted(@This(), _deinit, null);
|
||||
|
||||
pub fn memoryCost(this: *const PageBundleRoute) usize {
|
||||
var cost: usize = 0;
|
||||
cost += @sizeOf(PageBundleRoute);
|
||||
cost += this.pending_responses.items.len * @sizeOf(PendingResponse);
|
||||
cost += this.state.memoryCost();
|
||||
return cost;
|
||||
}
|
||||
|
||||
pub fn init(html_bundle: *PageBundle) *PageBundleRoute {
|
||||
html_bundle.ref();
|
||||
return PageBundleRoute.new(.{
|
||||
.html_bundle = html_bundle,
|
||||
.pending_responses = .{},
|
||||
.ref_count = 1,
|
||||
.server = null,
|
||||
.state = .pending,
|
||||
});
|
||||
}
|
||||
|
||||
pub const State = union(enum) {
|
||||
pending,
|
||||
building: ?*bun.BundleV2.JSBundleCompletionTask,
|
||||
rendering,
|
||||
err: bun.logger.Log,
|
||||
html: *StaticRoute,
|
||||
|
||||
pub fn deinit(this: *State) void {
|
||||
switch (this.*) {
|
||||
.err => |*log| {
|
||||
log.deinit();
|
||||
},
|
||||
.building => |completion| if (completion) |c| {
|
||||
c.cancelled = true;
|
||||
c.deref();
|
||||
},
|
||||
.html => {
|
||||
this.html.deref();
|
||||
},
|
||||
.pending => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memoryCost(this: *const State) usize {
|
||||
return switch (this.*) {
|
||||
.pending => 0,
|
||||
.building => 0,
|
||||
.err => |log| log.memoryCost(),
|
||||
.html => |html| html.memoryCost(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fn _deinit(this: *PageBundleRoute) void {
|
||||
for (this.pending_responses.items) |pending_response| {
|
||||
pending_response.deref();
|
||||
}
|
||||
this.pending_responses.deinit(bun.default_allocator);
|
||||
this.html_bundle.deref();
|
||||
this.state.deinit();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn onRequest(this: *PageBundleRoute, req: *uws.Request, resp: HTTPResponse) void {
|
||||
this.onAnyRequest(req, resp, false);
|
||||
}
|
||||
|
||||
pub fn onHEADRequest(this: *PageBundleRoute, req: *uws.Request, resp: HTTPResponse) void {
|
||||
this.onAnyRequest(req, resp, true);
|
||||
}
|
||||
|
||||
fn onAnyRequest(this: *PageBundleRoute, req: *uws.Request, resp: HTTPResponse, is_head: bool) void {
|
||||
this.ref();
|
||||
defer this.deref();
|
||||
const server: AnyServer = this.server orelse {
|
||||
resp.endWithoutBody(true);
|
||||
return;
|
||||
};
|
||||
|
||||
if (server.config().development) {
|
||||
// Simpler development workflow which rebundles on every request.
|
||||
if (this.state == .html) {
|
||||
this.state.html.deref();
|
||||
this.state = .pending;
|
||||
} else if (this.state == .err) {
|
||||
this.state.err.deinit();
|
||||
this.state = .pending;
|
||||
}
|
||||
}
|
||||
|
||||
state: switch (this.state) {
|
||||
.pending => {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onRequest: {s} - pending", .{req.url()});
|
||||
this.scheduleBundle(server) catch bun.outOfMemory();
|
||||
continue :state this.state;
|
||||
},
|
||||
.building => {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onRequest: {s} - building", .{req.url()});
|
||||
|
||||
// create the PendingResponse, add it to the list
|
||||
var pending = PendingResponse.new(.{
|
||||
.method = bun.http.Method.which(req.method()) orelse {
|
||||
resp.writeStatus("405 Method Not Allowed");
|
||||
resp.endWithoutBody(true);
|
||||
return;
|
||||
},
|
||||
.resp = resp,
|
||||
.server = this.server,
|
||||
.route = this,
|
||||
.ref_count = 1,
|
||||
});
|
||||
|
||||
this.pending_responses.append(bun.default_allocator, pending) catch bun.outOfMemory();
|
||||
|
||||
this.ref();
|
||||
pending.ref();
|
||||
resp.onAborted(*PendingResponse, PendingResponse.onAborted, pending);
|
||||
req.setYield(false);
|
||||
},
|
||||
.err => |log| {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onRequest: {s} - err", .{req.url()});
|
||||
_ = log; // TODO: use the code from DevServer.zig to render the error
|
||||
resp.endWithoutBody(true);
|
||||
},
|
||||
.html => |html| {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onRequest: {s} - html", .{req.url()});
|
||||
if (is_head) {
|
||||
html.onHEADRequest(req, resp);
|
||||
} else {
|
||||
html.onRequest(req, resp);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedule a bundle to be built.
|
||||
/// If success, bumps the ref count and returns true;
|
||||
fn scheduleBundle(this: *PageBundleRoute, server: AnyServer) !void {
|
||||
switch (server.getOrLoadPlugins(.{ .page_bundle_route = this })) {
|
||||
.err => this.state = .{ .err = bun.logger.Log.init(bun.default_allocator) },
|
||||
.ready => |plugins| try onPluginsResolved(this, plugins),
|
||||
.pending => this.state = .{ .building = null },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onPluginsResolved(this: *PageBundleRoute, plugins: ?*bun.JSC.API.JSBundler.Plugin) !void {
|
||||
const global = this.html_bundle.global;
|
||||
const server = this.server.?;
|
||||
const development = server.config().development;
|
||||
const vm = global.bunVM();
|
||||
|
||||
var config: JSBundler.Config = .{};
|
||||
errdefer config.deinit(bun.default_allocator);
|
||||
try config.entry_points.insert(this.html_bundle.path);
|
||||
try config.public_path.appendChar('/');
|
||||
config.target = .browser;
|
||||
|
||||
if (bun.CLI.Command.get().args.serve_minify_identifiers) |minify_identifiers| {
|
||||
config.minify.identifiers = minify_identifiers;
|
||||
} else if (!development) {
|
||||
config.minify.identifiers = true;
|
||||
}
|
||||
|
||||
if (bun.CLI.Command.get().args.serve_minify_whitespace) |minify_whitespace| {
|
||||
config.minify.whitespace = minify_whitespace;
|
||||
} else if (!development) {
|
||||
config.minify.whitespace = true;
|
||||
}
|
||||
|
||||
if (bun.CLI.Command.get().args.serve_minify_syntax) |minify_syntax| {
|
||||
config.minify.syntax = minify_syntax;
|
||||
} else if (!development) {
|
||||
config.minify.syntax = true;
|
||||
}
|
||||
|
||||
if (!development) {
|
||||
config.define.put("process.env.NODE_ENV", "\"production\"") catch bun.outOfMemory();
|
||||
config.jsx.development = false;
|
||||
} else {
|
||||
config.force_node_env = .development;
|
||||
config.jsx.development = true;
|
||||
}
|
||||
config.source_map = .linked;
|
||||
|
||||
const completion_task = try bun.BundleV2.createAndScheduleCompletionTask(
|
||||
config,
|
||||
plugins,
|
||||
global,
|
||||
vm.eventLoop(),
|
||||
bun.default_allocator,
|
||||
);
|
||||
completion_task.started_at_ns = bun.getRoughTickCount().ns();
|
||||
completion_task.build_task = .{ .page = this };
|
||||
this.state = .{ .building = completion_task };
|
||||
|
||||
// While we're building, ensure this doesn't get freed.
|
||||
this.ref();
|
||||
}
|
||||
|
||||
pub fn onPluginsRejected(this: *PageBundleRoute) !void {
|
||||
debug("PageBundleRoute(0x{x}) plugins rejected", .{@intFromPtr(this)});
|
||||
this.state = .{ .err = bun.logger.Log.init(bun.default_allocator) };
|
||||
this.resumePendingResponses();
|
||||
}
|
||||
|
||||
pub fn onComplete(this: *PageBundleRoute, completion_task: *bun.BundleV2.JSBundleCompletionTask) void {
|
||||
// For the build task.
|
||||
defer this.deref();
|
||||
|
||||
switch (completion_task.result) {
|
||||
.err => |err| {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onComplete: err - {s}", .{@errorName(err)});
|
||||
this.state = .{ .err = bun.logger.Log.init(bun.default_allocator) };
|
||||
completion_task.log.cloneToWithRecycled(&this.state.err, true) catch bun.outOfMemory();
|
||||
|
||||
if (this.server) |server| {
|
||||
if (server.config().development) {
|
||||
switch (bun.Output.enable_ansi_colors_stderr) {
|
||||
inline else => |enable_ansi_colors| {
|
||||
var writer = bun.Output.errorWriterBuffered();
|
||||
this.state.err.printWithEnableAnsiColors(&writer, enable_ansi_colors) catch {};
|
||||
writer.context.flush() catch {};
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
.value => |bundle| {
|
||||
if (bun.Environment.enable_logs)
|
||||
debug("onComplete: success", .{});
|
||||
// Find the HTML entry point and create static routes
|
||||
const server: AnyServer = this.server orelse return;
|
||||
const globalThis = server.globalThis();
|
||||
const output_files = bundle.output_files.items;
|
||||
|
||||
if (server.config().development) {
|
||||
const now = bun.getRoughTickCount().ns();
|
||||
const duration = now - completion_task.started_at_ns;
|
||||
var duration_f64: f64 = @floatFromInt(duration);
|
||||
duration_f64 /= std.time.ns_per_s;
|
||||
|
||||
bun.Output.printElapsed(duration_f64);
|
||||
var byte_length: u64 = 0;
|
||||
for (output_files) |*output_file| {
|
||||
byte_length += output_file.size_without_sourcemap;
|
||||
}
|
||||
|
||||
bun.Output.prettyErrorln(" <green>bundle<r> {s} <d>{d:.2} KB<r>", .{ std.fs.path.basename(this.html_bundle.path), @as(f64, @floatFromInt(byte_length)) / 1000.0 });
|
||||
bun.Output.flush();
|
||||
}
|
||||
|
||||
var this_html_route: ?*StaticRoute = null;
|
||||
|
||||
// Create static routes for each output file
|
||||
for (output_files) |*output_file| {
|
||||
const blob = JSC.WebCore.AnyBlob{ .Blob = output_file.toBlob(bun.default_allocator, globalThis) catch bun.outOfMemory() };
|
||||
var headers = JSC.WebCore.Headers{ .allocator = bun.default_allocator };
|
||||
headers.append("Content-Type", blob.Blob.contentTypeOrMimeType() orelse output_file.loader.toMimeType().value) catch bun.outOfMemory();
|
||||
// Do not apply etags to html.
|
||||
if (output_file.loader != .html and output_file.value == .buffer) {
|
||||
var hashbuf: [64]u8 = undefined;
|
||||
const etag_str = std.fmt.bufPrint(&hashbuf, "{}", .{bun.fmt.hexIntLower(output_file.hash)}) catch bun.outOfMemory();
|
||||
headers.append("ETag", etag_str) catch bun.outOfMemory();
|
||||
if (!server.config().development and (output_file.output_kind == .chunk))
|
||||
headers.append("Cache-Control", "public, max-age=31536000") catch bun.outOfMemory();
|
||||
}
|
||||
|
||||
// Add a SourceMap header if we have a source map index
|
||||
// and it's in development mode.
|
||||
if (server.config().development) {
|
||||
if (output_file.source_map_index != std.math.maxInt(u32)) {
|
||||
var route_path = output_files[output_file.source_map_index].dest_path;
|
||||
if (strings.hasPrefixComptime(route_path, "./") or strings.hasPrefixComptime(route_path, ".\\")) {
|
||||
route_path = route_path[1..];
|
||||
}
|
||||
headers.append("SourceMap", route_path) catch bun.outOfMemory();
|
||||
}
|
||||
}
|
||||
|
||||
const static_route = StaticRoute.new(.{
|
||||
.blob = blob,
|
||||
.server = server,
|
||||
.status_code = 200,
|
||||
.headers = headers,
|
||||
.cached_blob_size = blob.size(),
|
||||
});
|
||||
|
||||
if (this_html_route == null and output_file.output_kind == .@"entry-point") {
|
||||
if (output_file.loader == .html) {
|
||||
this_html_route = static_route;
|
||||
}
|
||||
}
|
||||
|
||||
var route_path = output_file.dest_path;
|
||||
|
||||
// The route path gets cloned inside of appendStaticRoute.
|
||||
if (strings.hasPrefixComptime(route_path, "./") or strings.hasPrefixComptime(route_path, ".\\")) {
|
||||
route_path = route_path[1..];
|
||||
}
|
||||
|
||||
server.appendStaticRoute(route_path, .{ .StaticRoute = static_route }) catch bun.outOfMemory();
|
||||
}
|
||||
|
||||
const html_route: *StaticRoute = this_html_route orelse @panic("Internal assertion failure: HTML entry point not found in PageBundle.");
|
||||
const html_route_clone = html_route.clone(globalThis) catch bun.outOfMemory();
|
||||
this.state = .{ .html = html_route_clone };
|
||||
|
||||
if (!(server.reloadStaticRoutes() catch bun.outOfMemory())) {
|
||||
// Server has shutdown, so it won't receive any new requests
|
||||
// TODO: handle this case
|
||||
}
|
||||
},
|
||||
.pending => unreachable,
|
||||
}
|
||||
|
||||
// Handle pending responses
|
||||
this.resumePendingResponses();
|
||||
}
|
||||
|
||||
pub fn resumePendingResponses(this: *PageBundleRoute) void {
|
||||
var pending = this.pending_responses;
|
||||
defer pending.deinit(bun.default_allocator);
|
||||
this.pending_responses = .{};
|
||||
for (pending.items) |pending_response| {
|
||||
defer pending_response.deref(); // First ref for being in the pending items array.
|
||||
|
||||
const resp = pending_response.resp;
|
||||
const method = pending_response.method;
|
||||
if (!pending_response.is_response_pending) {
|
||||
// Aborted
|
||||
continue;
|
||||
}
|
||||
// Second ref for UWS abort callback.
|
||||
defer pending_response.deref();
|
||||
|
||||
pending_response.is_response_pending = false;
|
||||
resp.clearAborted();
|
||||
|
||||
switch (this.state) {
|
||||
.html => |html| {
|
||||
if (method == .HEAD) {
|
||||
html.onHEAD(resp);
|
||||
} else {
|
||||
html.on(resp);
|
||||
}
|
||||
},
|
||||
.err => |log| {
|
||||
if (this.server.?.config().development) {
|
||||
_ = log; // TODO: use the code from DevServer.zig to render the error
|
||||
} else {
|
||||
// To protect privacy, do not show errors to end users in production.
|
||||
// TODO: Show a generic error page.
|
||||
}
|
||||
resp.writeStatus("500 Build Failed");
|
||||
resp.endWithoutBody(false);
|
||||
},
|
||||
else => {
|
||||
resp.endWithoutBody(false);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an in-flight response before the bundle has finished building.
|
||||
pub const PendingResponse = struct {
|
||||
method: bun.http.Method,
|
||||
resp: HTTPResponse,
|
||||
ref_count: u32 = 1,
|
||||
is_response_pending: bool = true,
|
||||
server: ?AnyServer = null,
|
||||
route: *PageBundleRoute,
|
||||
|
||||
pub usingnamespace bun.NewRefCounted(@This(), destroyInternal, null);
|
||||
|
||||
fn destroyInternal(this: *PendingResponse) void {
|
||||
if (this.is_response_pending) {
|
||||
this.resp.clearAborted();
|
||||
this.resp.clearOnWritable();
|
||||
this.resp.endWithoutBody(true);
|
||||
}
|
||||
this.route.deref();
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
pub fn onAborted(this: *PendingResponse, resp: HTTPResponse) void {
|
||||
_ = resp; // autofix
|
||||
bun.debugAssert(this.is_response_pending == true);
|
||||
this.is_response_pending = false;
|
||||
|
||||
// Technically, this could be the final ref count, but we don't want to risk it
|
||||
this.route.ref();
|
||||
defer this.route.deref();
|
||||
|
||||
while (std.mem.indexOfScalar(*PendingResponse, this.route.pending_responses.items, this)) |index| {
|
||||
_ = this.route.pending_responses.orderedRemove(index);
|
||||
this.route.deref();
|
||||
}
|
||||
|
||||
this.deref();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const bun = @import("root").bun;
|
||||
const std = @import("std");
|
||||
const JSC = bun.JSC;
|
||||
const JSValue = JSC.JSValue;
|
||||
const JSGlobalObject = JSC.JSGlobalObject;
|
||||
const JSString = JSC.JSString;
|
||||
const JSValueRef = JSC.JSValueRef;
|
||||
const JSBundler = JSC.API.JSBundler;
|
||||
const HTTPResponse = bun.uws.AnyResponse;
|
||||
const uws = bun.uws;
|
||||
const AnyServer = JSC.API.AnyServer;
|
||||
const StaticRoute = bun.server.StaticRoute;
|
||||
|
||||
const debug = bun.Output.scoped(.PageBundle, true);
|
||||
const strings = bun.strings;
|
||||
@@ -79,6 +79,7 @@ pub const Classes = struct {
|
||||
pub const S3Client = JSC.WebCore.S3Client;
|
||||
pub const S3Stat = JSC.WebCore.S3Stat;
|
||||
pub const HTMLBundle = JSC.API.HTMLBundle;
|
||||
pub const PageBundle = JSC.API.PageBundle;
|
||||
|
||||
pub const StatFs = JSC.Node.StatFSSmall;
|
||||
pub const BigIntStatFs = JSC.Node.StatFSBig;
|
||||
|
||||
@@ -2102,6 +2102,33 @@ pub const ModuleLoader = struct {
|
||||
};
|
||||
},
|
||||
|
||||
.page_reference => {
|
||||
if (flags.disableTranspiling()) {
|
||||
return ResolvedSource{
|
||||
.allocator = null,
|
||||
.source_code = bun.String.empty,
|
||||
.specifier = input_specifier,
|
||||
.source_url = input_specifier.createIfDifferent(path.text),
|
||||
.hash = 0,
|
||||
.tag = .esm,
|
||||
};
|
||||
}
|
||||
|
||||
if (globalObject == null) {
|
||||
return error.NotSupported;
|
||||
}
|
||||
|
||||
const bundle = try JSC.API.PageBundle.init(globalObject.?, path.text);
|
||||
return ResolvedSource{
|
||||
.allocator = &jsc_vm.allocator,
|
||||
.jsvalue_for_export = bundle.toJS(globalObject.?),
|
||||
.specifier = input_specifier,
|
||||
.source_url = input_specifier.createIfDifferent(path.text),
|
||||
.hash = 0,
|
||||
.tag = .export_default_object,
|
||||
};
|
||||
},
|
||||
|
||||
else => {
|
||||
if (flags.disableTranspiling()) {
|
||||
return ResolvedSource{
|
||||
@@ -2343,6 +2370,8 @@ pub const ModuleLoader = struct {
|
||||
loader = .tsx;
|
||||
} else if (attribute.eqlComptime("html")) {
|
||||
loader = .html;
|
||||
} else if (attribute.eqlComptime("page")) {
|
||||
loader = .page_reference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1556,6 +1556,52 @@ pub const BundleV2 = struct {
|
||||
return try this.linker.generateChunksInParallel(chunks, false);
|
||||
}
|
||||
|
||||
pub fn generateFromPageBundle(
|
||||
entry_points: bake.production.EntryPointMap,
|
||||
server_transpiler: *Transpiler,
|
||||
kit_options: BakeOptions,
|
||||
allocator: std.mem.Allocator,
|
||||
event_loop: EventLoop,
|
||||
) !std.ArrayList(options.OutputFile) {
|
||||
var this = try BundleV2.init(server_transpiler, kit_options, allocator, event_loop, false, null, null);
|
||||
this.unique_key = generateUniqueKey();
|
||||
|
||||
if (this.transpiler.log.hasErrors()) {
|
||||
return error.BuildFailed;
|
||||
}
|
||||
|
||||
this.graph.pool.pool.schedule(try this.enqueueEntryPoints(.bake_production, entry_points));
|
||||
|
||||
if (this.transpiler.log.hasErrors()) {
|
||||
return error.BuildFailed;
|
||||
}
|
||||
|
||||
this.waitForParse();
|
||||
|
||||
if (this.transpiler.log.hasErrors()) {
|
||||
return error.BuildFailed;
|
||||
}
|
||||
|
||||
try this.processServerComponentManifestFiles();
|
||||
|
||||
const reachable_files = try this.findReachableFiles();
|
||||
|
||||
try this.processFilesToCopy(reachable_files);
|
||||
|
||||
try this.addServerComponentBoundariesAsExtraEntryPoints();
|
||||
|
||||
try this.cloneAST();
|
||||
|
||||
const chunks = try this.linker.link(
|
||||
this,
|
||||
this.graph.entry_points.items,
|
||||
this.graph.server_component_boundaries,
|
||||
reachable_files,
|
||||
);
|
||||
|
||||
return try this.linker.generateChunksInParallel(chunks, false);
|
||||
}
|
||||
|
||||
pub fn addServerComponentBoundariesAsExtraEntryPoints(this: *BundleV2) !void {
|
||||
// Prepare server component boundaries. Each boundary turns into two
|
||||
// entry points, a client entrypoint and a server entrypoint.
|
||||
@@ -1722,7 +1768,7 @@ pub const BundleV2 = struct {
|
||||
log: Logger.Log,
|
||||
cancelled: bool = false,
|
||||
|
||||
html_build_task: ?*JSC.API.HTMLBundle.HTMLBundleRoute = null,
|
||||
build_task: BuildTask = .none,
|
||||
|
||||
result: Result = .{ .pending = {} },
|
||||
|
||||
@@ -1734,6 +1780,20 @@ pub const BundleV2 = struct {
|
||||
|
||||
pub usingnamespace bun.NewThreadSafeRefCounted(JSBundleCompletionTask, _deinit, null);
|
||||
|
||||
pub const BuildTask = union(enum) {
|
||||
none: void,
|
||||
html: *JSC.API.HTMLBundle.HTMLBundleRoute,
|
||||
page: *bun.JSC.API.Server.PageBundleRoute,
|
||||
|
||||
pub fn onComplete(this: BuildTask, completion: *JSBundleCompletionTask) void {
|
||||
switch (this) {
|
||||
.none => unreachable,
|
||||
.html => |html| html.onComplete(completion),
|
||||
.page => |page| page.onComplete(completion),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn configureBundler(
|
||||
completion: *JSBundleCompletionTask,
|
||||
transpiler: *Transpiler,
|
||||
@@ -1834,9 +1894,9 @@ pub const BundleV2 = struct {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.html_build_task) |html_build_task| {
|
||||
if (this.build_task != .none) {
|
||||
this.plugins = null;
|
||||
html_build_task.onComplete(this);
|
||||
this.build_task.onComplete(this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4043,7 +4103,7 @@ pub const ParseTask = struct {
|
||||
.dataurl, .base64, .bunsh => {
|
||||
return try getEmptyAST(log, transpiler, opts, allocator, source, E.String);
|
||||
},
|
||||
.file, .wasm => {
|
||||
.file, .wasm, .page_reference => {
|
||||
bun.assert(loader.shouldCopyForBundling());
|
||||
|
||||
// Put a unique key in the AST to implement the URL loader. At the end
|
||||
@@ -7634,13 +7694,13 @@ pub const LinkerContext = struct {
|
||||
.{@tagName(loader)},
|
||||
) catch bun.outOfMemory();
|
||||
},
|
||||
.sqlite_embedded => {
|
||||
.sqlite_embedded, .page_reference => {
|
||||
this.log.addErrorFmt(
|
||||
source,
|
||||
record.range.loc,
|
||||
this.allocator,
|
||||
"Cannot import a \"sqlite_embedded\" file into a CSS file",
|
||||
.{},
|
||||
"Cannot import a \"{s}\" file into a CSS file",
|
||||
.{@tagName(loader)},
|
||||
) catch bun.outOfMemory();
|
||||
},
|
||||
.css, .file, .toml, .wasm, .base64, .dataurl, .text, .bunsh => {},
|
||||
|
||||
@@ -53,6 +53,7 @@ pub const API = struct {
|
||||
pub const NativeZlib = @import("./bun.js/node/node_zlib_binding.zig").SNativeZlib;
|
||||
pub const NativeBrotli = @import("./bun.js/node/node_zlib_binding.zig").SNativeBrotli;
|
||||
pub const HTMLBundle = @import("./bun.js/api/server/HTMLBundle.zig");
|
||||
pub const PageBundle = @import("./bun.js/api/server/PageBundle.zig");
|
||||
};
|
||||
pub const Postgres = @import("./sql/postgres.zig");
|
||||
pub const DNS = @import("./bun.js/api/bun/dns_resolver.zig");
|
||||
|
||||
@@ -645,6 +645,8 @@ pub const Loader = enum(u8) {
|
||||
sqlite,
|
||||
sqlite_embedded,
|
||||
html,
|
||||
/// Returns
|
||||
page_reference,
|
||||
|
||||
pub fn disableHTML(this: Loader) Loader {
|
||||
return switch (this) {
|
||||
@@ -825,6 +827,7 @@ pub const Loader = enum(u8) {
|
||||
.dataurl => .dataurl,
|
||||
.text => .text,
|
||||
.sqlite_embedded, .sqlite => .sqlite,
|
||||
.page_reference => .file,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -981,7 +981,7 @@ pub const Transpiler = struct {
|
||||
output_file.value = .{ .buffer = .{ .allocator = alloc, .bytes = result.code } };
|
||||
},
|
||||
|
||||
.html, .bunsh, .sqlite_embedded, .sqlite, .wasm, .file, .napi => {
|
||||
else => {
|
||||
const hashed_name = try transpiler.linker.getHashedFilename(file_path, null);
|
||||
var pathname = try transpiler.allocator.alloc(u8, hashed_name.len + file_path.name.ext.len);
|
||||
bun.copy(u8, pathname, hashed_name);
|
||||
|
||||
Reference in New Issue
Block a user