//! Represents a boundary between client and server code. Every boundary //! gets bundled twice, once for the desired target, and once to generate //! a module of "references". Specifically, the generated file takes the //! canonical Ast as input to derive a wrapper. See `Framework.ServerComponents` //! for more details about this generated file. //! //! This is sometimes abbreviated as SCB use_directive: UseDirective, /// The index of the original file. source_index: Index.Int, /// Index to the file imported on the opposite platform, which is /// generated by the bundler. For client components, this is the /// server's code. For server actions, this is the client's code. reference_source_index: Index.Int, /// When `bake.Framework.ServerComponents.separate_ssr_graph` is enabled this /// points to the separated module. When the SSR graph is not separate, this is /// equal to `reference_source_index` // // TODO: Is this used for server actions. ssr_source_index: Index.Int, /// The requirements for this data structure is to have reasonable lookup /// speed, but also being able to pull a `[]const Index.Int` of all /// boundaries for iteration. pub const List = struct { list: std.MultiArrayList(ServerComponentBoundary) = .{}, /// Used to facilitate fast lookups into `items` by `.source_index` map: Map = .{}, const Map = std.ArrayHashMapUnmanaged(void, void, struct {}, true); /// Can only be called on the bundler thread. pub fn put( m: *List, allocator: std.mem.Allocator, source_index: Index.Int, use_directive: UseDirective, reference_source_index: Index.Int, ssr_source_index: Index.Int, ) !void { try m.list.append(allocator, .{ .source_index = source_index, .use_directive = use_directive, .reference_source_index = reference_source_index, .ssr_source_index = ssr_source_index, }); const gop = try m.map.getOrPutAdapted( allocator, source_index, Adapter{ .list = m.list.slice() }, ); bun.assert(!gop.found_existing); } /// Can only be called on the bundler thread. pub fn getIndex(l: *const List, real_source_index: Index.Int) ?usize { return l.map.getIndexAdapted( real_source_index, Adapter{ .list = l.list.slice() }, ); } /// Use this to improve speed of accessing fields at the cost of /// storing more pointers. Invalidated when input is mutated. pub fn slice(l: List) Slice { return .{ .list = l.list.slice(), .map = l.map }; } pub const Slice = struct { list: std.MultiArrayList(ServerComponentBoundary).Slice, map: Map, pub fn getIndex(l: *const Slice, real_source_index: Index.Int) ?usize { return l.map.getIndexAdapted( real_source_index, Adapter{ .list = l.list }, ) orelse return null; } pub fn getReferenceSourceIndex(l: *const Slice, real_source_index: Index.Int) ?u32 { const i = l.map.getIndexAdapted( real_source_index, Adapter{ .list = l.list }, ) orelse return null; bun.unsafeAssert(l.list.capacity > 0); // optimize MultiArrayList.Slice.items return l.list.items(.reference_source_index)[i]; } pub fn bitSet(scbs: Slice, alloc: std.mem.Allocator, input_file_count: usize) !bun.bit_set.DynamicBitSetUnmanaged { var scb_bitset = try bun.bit_set.DynamicBitSetUnmanaged.initEmpty(alloc, input_file_count); for (scbs.list.items(.source_index)) |source_index| { scb_bitset.set(source_index); } return scb_bitset; } }; pub const Adapter = struct { list: std.MultiArrayList(ServerComponentBoundary).Slice, pub fn hash(_: Adapter, key: Index.Int) u32 { return std.hash.uint32(key); } pub fn eql(adapt: Adapter, a: Index.Int, _: void, b_index: usize) bool { bun.unsafeAssert(adapt.list.capacity > 0); // optimize MultiArrayList.Slice.items return a == adapt.list.items(.source_index)[b_index]; } }; }; const bun = @import("bun"); const std = @import("std"); const js_ast = bun.ast; const Index = js_ast.Index; const ServerComponentBoundary = js_ast.ServerComponentBoundary; const UseDirective = js_ast.UseDirective;