mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Compare commits
1 Commits
jarred/bro
...
jarred/hmm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dfbeb9067 |
@@ -338,7 +338,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
for (dev.framework.file_system_router_types) |fsr| {
|
||||
bun.writeAnyToHasher(&hash, fsr.allow_layouts);
|
||||
bun.writeAnyToHasher(&hash, fsr.ignore_underscores);
|
||||
hash.update(fsr.entry_server);
|
||||
hash.update(fsr.entry_server orelse "");
|
||||
hash.update(&.{0});
|
||||
hash.update(fsr.entry_client orelse "");
|
||||
hash.update(&.{0});
|
||||
@@ -402,18 +402,20 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
|
||||
dev.initServerRuntime();
|
||||
|
||||
var has_client_routes = false;
|
||||
var has_server_routes = false;
|
||||
|
||||
// Initialize FrameworkRouter
|
||||
dev.router = router: {
|
||||
var types = try std.ArrayListUnmanaged(FrameworkRouter.Type).initCapacity(allocator, options.framework.file_system_router_types.len);
|
||||
errdefer types.deinit(allocator);
|
||||
|
||||
for (options.framework.file_system_router_types, 0..) |fsr, i| {
|
||||
_ = i; // autofix
|
||||
const joined_root = bun.path.joinAbs(dev.root, .auto, fsr.root);
|
||||
const entry = dev.server_bundler.resolver.readDirInfoIgnoreError(joined_root) orelse
|
||||
continue;
|
||||
|
||||
const server_file = try dev.server_graph.insertStaleExtra(fsr.entry_server, false, true);
|
||||
|
||||
try types.append(allocator, .{
|
||||
.abs_root = bun.strings.withoutTrailingSlash(entry.abs_path),
|
||||
.prefix = fsr.prefix,
|
||||
@@ -422,17 +424,28 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
.extensions = fsr.extensions,
|
||||
.style = fsr.style,
|
||||
.allow_layouts = fsr.allow_layouts,
|
||||
.server_file = toOpaqueFileId(.server, server_file),
|
||||
.client_file = if (fsr.entry_client) |client|
|
||||
toOpaqueFileId(.client, try dev.client_graph.insertStale(client, false)).toOptional()
|
||||
else
|
||||
.none,
|
||||
.server_file_string = .{},
|
||||
});
|
||||
.server_file = brk: {
|
||||
if (fsr.entry_server) |server| {
|
||||
const server_entry = try dev.server_graph.insertStaleExtra(server, false, true);
|
||||
const server_file = toOpaqueFileId(.server, server_entry).toOptional();
|
||||
has_server_routes = true;
|
||||
break :brk server_file;
|
||||
}
|
||||
|
||||
try dev.route_lookup.put(allocator, server_file, .{
|
||||
.route_index = FrameworkRouter.Route.Index.init(@intCast(i)),
|
||||
.should_recurse_when_visiting = true,
|
||||
break :brk .none;
|
||||
},
|
||||
.client_file = brk: {
|
||||
const is_route = fsr.entry_server == null;
|
||||
if (fsr.entry_client) |client| {
|
||||
const client_entry = try dev.client_graph.insertStaleExtra(client, false, is_route);
|
||||
const client_file = toOpaqueFileId(.client, client_entry).toOptional();
|
||||
has_client_routes = true;
|
||||
break :brk client_file;
|
||||
}
|
||||
|
||||
break :brk .none;
|
||||
},
|
||||
.server_file_string = .{},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -442,7 +455,7 @@ pub fn init(options: Options) bun.JSOOM!*DevServer {
|
||||
// TODO: move scanning to be one tick after server startup. this way the
|
||||
// line saying the server is ready shows quicker, and route errors show up
|
||||
// after that line.
|
||||
try dev.scanInitialRoutes();
|
||||
try dev.scanInitialRoutes(if (has_server_routes) .server else .client);
|
||||
|
||||
if (bun.FeatureFlags.bake_debugging_features and dev.has_pre_crash_handler)
|
||||
try bun.crash_handler.appendPreCrashHandler(DevServer, dev, dumpStateDueToCrash);
|
||||
@@ -476,10 +489,13 @@ fn initServerRuntime(dev: *DevServer) void {
|
||||
}
|
||||
|
||||
/// Deferred one tick so that the server can be up faster
|
||||
fn scanInitialRoutes(dev: *DevServer) !void {
|
||||
fn scanInitialRoutes(dev: *DevServer, side: bake.Side) !void {
|
||||
try dev.router.scanAll(
|
||||
dev.allocator,
|
||||
&dev.server_bundler.resolver,
|
||||
switch (side) {
|
||||
.server => &dev.server_bundler.resolver,
|
||||
.client => &dev.client_bundler.resolver,
|
||||
},
|
||||
FrameworkRouter.InsertionContext.wrap(DevServer, dev),
|
||||
);
|
||||
|
||||
@@ -760,12 +776,15 @@ fn onRequestWithBundle(
|
||||
.{
|
||||
// routerTypeMain
|
||||
router_type.server_file_string.get() orelse str: {
|
||||
const name = dev.server_graph.bundled_files.keys()[fromOpaqueFileId(.server, router_type.server_file).get()];
|
||||
const str = bun.String.createUTF8(dev.relativePath(name));
|
||||
defer str.deref();
|
||||
const js = str.toJS(dev.vm.global);
|
||||
router_type.server_file_string = JSC.Strong.create(js, dev.vm.global);
|
||||
break :str js;
|
||||
if (router_type.server_file.unwrap()) |id| {
|
||||
const name = dev.server_graph.bundled_files.keys()[fromOpaqueFileId(.server, id).get()];
|
||||
const str = bun.String.createUTF8(dev.relativePath(name));
|
||||
defer str.deref();
|
||||
const js = str.toJS(dev.vm.global);
|
||||
router_type.server_file_string = JSC.Strong.create(js, dev.vm.global);
|
||||
break :str js;
|
||||
}
|
||||
break :str bun.String.empty.toJS(dev.vm.global);
|
||||
},
|
||||
// routeModules
|
||||
route_bundle.cached_module_list.get() orelse arr: {
|
||||
@@ -1064,7 +1083,10 @@ fn traceAllRouteImports(dev: *DevServer, route_bundle: *RouteBundle, gts: *Graph
|
||||
const router_type = dev.router.typePtr(route.type);
|
||||
|
||||
// Both framework entry points are considered
|
||||
try dev.server_graph.traceImports(fromOpaqueFileId(.server, router_type.server_file), gts, .{ .find_css = true });
|
||||
if (router_type.server_file.unwrap()) |id| {
|
||||
try dev.server_graph.traceImports(fromOpaqueFileId(.server, id), gts, .{ .find_css = true });
|
||||
}
|
||||
|
||||
if (router_type.client_file.unwrap()) |id| {
|
||||
try dev.client_graph.traceImports(fromOpaqueFileId(.client, id), gts, goal);
|
||||
}
|
||||
@@ -1950,6 +1972,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
fn fileKind(file: @This()) FileKind {
|
||||
return file.kind;
|
||||
}
|
||||
|
||||
pub fn isRoute(file: @This()) bool {
|
||||
return file.is_route;
|
||||
}
|
||||
},
|
||||
.client => struct {
|
||||
/// Allocated by default_allocator. Access with `.code()`
|
||||
@@ -1959,7 +1985,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
code_len: u32,
|
||||
flags: Flags,
|
||||
|
||||
const Flags = struct {
|
||||
const Flags = packed struct {
|
||||
/// If the file has an error, the failure can be looked up
|
||||
/// in the `.failures` map.
|
||||
failed: bool,
|
||||
@@ -1971,6 +1997,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
is_special_framework_file: bool,
|
||||
/// CSS and Asset files get special handling
|
||||
kind: FileKind,
|
||||
|
||||
/// If this file is a route root, the route can be looked up in
|
||||
/// the route list. This also stops dependency propagation.
|
||||
is_route: bool,
|
||||
};
|
||||
|
||||
comptime {
|
||||
@@ -1986,6 +2016,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn isRoute(file: *const @This()) bool {
|
||||
return file.flags.is_route;
|
||||
}
|
||||
|
||||
fn code(file: @This()) []const u8 {
|
||||
return file.code_ptr[0..file.code_len];
|
||||
}
|
||||
@@ -2123,6 +2157,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
.is_hmr_root = ctx.server_to_client_bitset.isSet(index.get()),
|
||||
.is_special_framework_file = false,
|
||||
.kind = kind,
|
||||
.is_route = false,
|
||||
};
|
||||
if (kind == .css) {
|
||||
if (!gop.found_existing or gop.value_ptr.code_len == 0) {
|
||||
@@ -2397,7 +2432,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
switch (side) {
|
||||
.server => {
|
||||
const dev = g.owner();
|
||||
if (file.is_route) {
|
||||
if (file.isRoute()) {
|
||||
const route_index = dev.route_lookup.get(file_index) orelse
|
||||
Output.panic("Route not in lookup index: {d} {}", .{ file_index.get(), bun.fmt.quote(g.bundled_files.keys()[file_index.get()]) });
|
||||
igLog("\\<- Route", .{});
|
||||
@@ -2409,7 +2444,13 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
}
|
||||
},
|
||||
.client => {
|
||||
if (file.flags.is_hmr_root or (file.flags.kind == .css and trace_kind == .css_to_route)) {
|
||||
if (file.isRoute()) {
|
||||
const dev = g.owner();
|
||||
const key = g.bundled_files.keys()[file_index.get()];
|
||||
const index = dev.client_graph.getFileIndex(key) orelse
|
||||
Output.panic("Client Incremental Graph is missing component for {}", .{bun.fmt.quote(key)});
|
||||
try dev.client_graph.traceDependencies(index, gts, trace_kind);
|
||||
} else if (file.flags.is_hmr_root or (file.flags.kind == .css and trace_kind == .css_to_route)) {
|
||||
const dev = g.owner();
|
||||
const key = g.bundled_files.keys()[file_index.get()];
|
||||
const index = dev.server_graph.getFileIndex(key) orelse
|
||||
@@ -2517,6 +2558,10 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
} else {
|
||||
if (side == .server) {
|
||||
if (is_route) gop.value_ptr.*.is_route = is_route;
|
||||
} else if (side == .client) {
|
||||
if (is_route) {
|
||||
gop.value_ptr.*.flags.is_route = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2531,6 +2576,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
.is_hmr_root = false,
|
||||
.is_special_framework_file = false,
|
||||
.kind = .unknown,
|
||||
.is_route = is_route,
|
||||
});
|
||||
},
|
||||
.server => {
|
||||
@@ -2614,6 +2660,7 @@ pub fn IncrementalGraph(side: bake.Side) type {
|
||||
.is_hmr_root = false,
|
||||
.is_special_framework_file = false,
|
||||
.kind = .unknown,
|
||||
.is_route = false,
|
||||
});
|
||||
},
|
||||
.server => {
|
||||
|
||||
@@ -89,7 +89,7 @@ pub const Type = struct {
|
||||
/// `FrameworkRouter` itself does not use this value.
|
||||
client_file: OpaqueFileId.Optional,
|
||||
/// `FrameworkRouter` itself does not use this value.
|
||||
server_file: OpaqueFileId,
|
||||
server_file: OpaqueFileId.Optional,
|
||||
/// `FrameworkRouter` itself does not use this value.
|
||||
server_file_string: JSC.Strong,
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ pub const Framework = struct {
|
||||
pub const FileSystemRouterType = struct {
|
||||
root: []const u8,
|
||||
prefix: []const u8,
|
||||
entry_server: []const u8,
|
||||
entry_server: ?[]const u8,
|
||||
entry_client: ?[]const u8,
|
||||
ignore_underscores: bool,
|
||||
ignore_dirs: []const []const u8,
|
||||
@@ -281,7 +281,7 @@ pub const Framework = struct {
|
||||
for (clone.file_system_router_types) |*fsr| {
|
||||
fsr.root = try arena.dupe(u8, bun.path.joinAbs(server.fs.top_level_dir, .auto, fsr.root));
|
||||
if (fsr.entry_client) |*entry_client| f.resolveHelper(client, entry_client, &had_errors, "client side entrypoint");
|
||||
f.resolveHelper(client, &fsr.entry_server, &had_errors, "server side entrypoint");
|
||||
if (fsr.entry_server) |*entry_server| f.resolveHelper(client, entry_server, &had_errors, "server side entrypoint");
|
||||
}
|
||||
|
||||
if (had_errors) return error.ModuleNotFound;
|
||||
@@ -443,9 +443,7 @@ pub const Framework = struct {
|
||||
const root = try getOptionalString(fsr_opts, global, "root", refs, arena) orelse {
|
||||
return global.throwInvalidArguments("'fileSystemRouterTypes[{d}]' is missing 'root'", .{i});
|
||||
};
|
||||
const server_entry_point = try getOptionalString(fsr_opts, global, "serverEntryPoint", refs, arena) orelse {
|
||||
return global.throwInvalidArguments("'fileSystemRouterTypes[{d}]' is missing 'serverEntryPoint'", .{i});
|
||||
};
|
||||
const server_entry_point = try getOptionalString(fsr_opts, global, "serverEntryPoint", refs, arena);
|
||||
const client_entry_point = try getOptionalString(fsr_opts, global, "clientEntryPoint", refs, arena);
|
||||
const prefix = try getOptionalString(fsr_opts, global, "prefix", refs, arena) orelse "/";
|
||||
const ignore_underscores = try fsr_opts.getBooleanStrict(global, "ignoreUnderscores") orelse false;
|
||||
|
||||
@@ -36,17 +36,12 @@ pub fn buildCommand(ctx: bun.CLI.Command.Context) !void {
|
||||
.smol = ctx.runtime_options.smol,
|
||||
});
|
||||
defer vm.deinit();
|
||||
// A special global object is used to allow registering virtual modules
|
||||
// that bypass Bun's normal module resolver and plugin system.
|
||||
vm.global = BakeCreateProdGlobal(vm.console);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
vm.event_loop.ensureWaker();
|
||||
const b = &vm.transpiler;
|
||||
vm.preload = ctx.preloads;
|
||||
vm.argv = ctx.passthrough;
|
||||
vm.arena = &arena;
|
||||
vm.allocator = arena.allocator();
|
||||
const b = &vm.transpiler;
|
||||
|
||||
b.options.install = ctx.install;
|
||||
b.resolver.opts.install = ctx.install;
|
||||
b.resolver.opts.global_cache = ctx.debug.global_cache;
|
||||
@@ -62,7 +57,24 @@ pub fn buildCommand(ctx: bun.CLI.Command.Context) !void {
|
||||
b.resolver.opts.minify_identifiers = ctx.bundler_options.minify_identifiers;
|
||||
b.resolver.opts.minify_whitespace = ctx.bundler_options.minify_whitespace;
|
||||
b.options.env.behavior = .load_all_without_inlining;
|
||||
b.configureDefines() catch {
|
||||
bun.bun_js.failWithBuildError(vm);
|
||||
};
|
||||
JSC.VirtualMachine.is_main_thread_vm = true;
|
||||
|
||||
vm.event_loop.ensureWaker();
|
||||
|
||||
bun.http.AsyncHTTP.loadEnv(vm.allocator, vm.log, b.env);
|
||||
vm.loadExtraEnvAndSourceCodePrinter();
|
||||
vm.is_main_thread = true;
|
||||
|
||||
// A special global object is used to allow registering virtual modules
|
||||
// that bypass Bun's normal module resolver and plugin system.
|
||||
vm.global = BakeCreateProdGlobal(vm.console);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.jsc = vm.global.vm();
|
||||
vm.event_loop.ensureWaker();
|
||||
|
||||
switch (ctx.debug.macros) {
|
||||
.disable => {
|
||||
b.options.no_macros = true;
|
||||
@@ -72,13 +84,6 @@ pub fn buildCommand(ctx: bun.CLI.Command.Context) !void {
|
||||
},
|
||||
.unspecified => {},
|
||||
}
|
||||
b.configureDefines() catch {
|
||||
bun.bun_js.failWithBuildError(vm);
|
||||
};
|
||||
bun.http.AsyncHTTP.loadEnv(vm.allocator, vm.log, b.env);
|
||||
vm.loadExtraEnvAndSourceCodePrinter();
|
||||
vm.is_main_thread = true;
|
||||
JSC.VirtualMachine.is_main_thread_vm = true;
|
||||
|
||||
const api_lock = vm.jsc.getAPILock();
|
||||
defer api_lock.release();
|
||||
@@ -232,7 +237,10 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
.extensions = fsr.extensions,
|
||||
.style = fsr.style,
|
||||
.allow_layouts = fsr.allow_layouts,
|
||||
.server_file = try entry_points.getOrPutEntryPoint(fsr.entry_server, .server),
|
||||
.server_file = if (fsr.entry_server) |server|
|
||||
(try entry_points.getOrPutEntryPoint(server, .server)).toOptional()
|
||||
else
|
||||
.none,
|
||||
.client_file = if (fsr.entry_client) |client|
|
||||
(try entry_points.getOrPutEntryPoint(client, .client)).toOptional()
|
||||
else
|
||||
@@ -371,25 +379,10 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
client_entry_urls.putIndex(global, @intCast(i), .null);
|
||||
}
|
||||
|
||||
const server_entry_point = try pt.loadBundledModule(router_type.server_file);
|
||||
const server_render_func = brk: {
|
||||
const raw = BakeGetOnModuleNamespace(global, server_entry_point, "prerender") orelse
|
||||
break :brk null;
|
||||
if (!raw.isCallable(vm.jsc)) {
|
||||
break :brk null;
|
||||
}
|
||||
break :brk raw;
|
||||
} orelse {
|
||||
Output.errGeneric("Framework does not support static site generation", .{});
|
||||
Output.note("The file {s} is missing the \"prerender\" export, which defines how to generate static files.", .{
|
||||
bun.fmt.quote(bun.path.relative(cwd, entry_points.files.keys()[router_type.server_file.get()].absPath())),
|
||||
});
|
||||
bun.Global.crash();
|
||||
};
|
||||
|
||||
const server_param_func = if (router.dynamic_routes.count() > 0)
|
||||
brk: {
|
||||
const raw = BakeGetOnModuleNamespace(global, server_entry_point, "getParams") orelse
|
||||
if (router_type.server_file.unwrap()) |server_file| {
|
||||
const server_entry_point = try pt.loadBundledModule(server_file);
|
||||
const server_render_func = brk: {
|
||||
const raw = BakeGetOnModuleNamespace(global, server_entry_point, "prerender") orelse
|
||||
break :brk null;
|
||||
if (!raw.isCallable(vm.jsc)) {
|
||||
break :brk null;
|
||||
@@ -397,15 +390,32 @@ pub fn buildWithVm(ctx: bun.CLI.Command.Context, cwd: []const u8, vm: *VirtualMa
|
||||
break :brk raw;
|
||||
} orelse {
|
||||
Output.errGeneric("Framework does not support static site generation", .{});
|
||||
Output.note("The file {s} is missing the \"getParams\" export, which defines how to generate static files.", .{
|
||||
bun.fmt.quote(bun.path.relative(cwd, entry_points.files.keys()[router_type.server_file.get()].absPath())),
|
||||
Output.note("The file {s} is missing the \"prerender\" export, which defines how to generate static files.", .{
|
||||
bun.fmt.quote(bun.path.relative(cwd, entry_points.files.keys()[server_file.get()].absPath())),
|
||||
});
|
||||
bun.Global.crash();
|
||||
}
|
||||
else
|
||||
JSValue.null;
|
||||
server_render_funcs.putIndex(global, @intCast(i), server_render_func);
|
||||
server_param_funcs.putIndex(global, @intCast(i), server_param_func);
|
||||
};
|
||||
|
||||
const server_param_func = if (router.dynamic_routes.count() > 0)
|
||||
brk: {
|
||||
const raw = BakeGetOnModuleNamespace(global, server_entry_point, "getParams") orelse
|
||||
break :brk null;
|
||||
if (!raw.isCallable(vm.jsc)) {
|
||||
break :brk null;
|
||||
}
|
||||
break :brk raw;
|
||||
} orelse {
|
||||
Output.errGeneric("Framework does not support static site generation", .{});
|
||||
Output.note("The file {s} is missing the \"getParams\" export, which defines how to generate static files.", .{
|
||||
bun.fmt.quote(bun.path.relative(cwd, entry_points.files.keys()[server_file.get()].absPath())),
|
||||
});
|
||||
bun.Global.crash();
|
||||
}
|
||||
else
|
||||
JSValue.null;
|
||||
server_render_funcs.putIndex(global, @intCast(i), server_render_func);
|
||||
server_param_funcs.putIndex(global, @intCast(i), server_param_func);
|
||||
}
|
||||
}
|
||||
|
||||
var navigatable_routes = std.ArrayList(FrameworkRouter.Route.Index).init(allocator);
|
||||
|
||||
Reference in New Issue
Block a user