aboutsummaryrefslogtreecommitdiff
path: root/src/node_module_bundle.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_module_bundle.zig')
-rw-r--r--src/node_module_bundle.zig479
1 files changed, 0 insertions, 479 deletions
diff --git a/src/node_module_bundle.zig b/src/node_module_bundle.zig
deleted file mode 100644
index e785cb61d..000000000
--- a/src/node_module_bundle.zig
+++ /dev/null
@@ -1,479 +0,0 @@
-const schema = @import("./api/schema.zig");
-const Api = schema.Api;
-const std = @import("std");
-const Fs = @import("./fs.zig");
-const bun = @import("root").bun;
-const string = bun.string;
-const Output = bun.Output;
-const Global = bun.Global;
-const Environment = bun.Environment;
-const strings = bun.strings;
-const MutableString = bun.MutableString;
-const FileDescriptorType = bun.FileDescriptor;
-const StoredFileDescriptorType = bun.StoredFileDescriptorType;
-const stringZ = bun.stringZ;
-const default_allocator = bun.default_allocator;
-const C = bun.C;
-
-pub fn modulesIn(bundle: *const Api.JavascriptBundle, pkg: *const Api.JavascriptBundledPackage) []const Api.JavascriptBundledModule {
- return bundle.modules[pkg.modules_offset .. pkg.modules_offset + pkg.modules_length];
-}
-
-// This corresponds to Api.JavascriptBundledPackage.hash
-pub const BundledPackageHash = u32;
-// This is the offset in the array of packages
-pub const BundledPackageID = u32;
-
-const PackageIDMap = std.AutoHashMap(BundledPackageHash, BundledPackageID);
-
-const PackageNameMap = bun.StringHashMap([]BundledPackageID);
-
-pub const AllocatedString = struct {
- str: string,
- len: u32,
- allocator: std.mem.Allocator,
-};
-
-pub const NodeModuleBundle = struct {
- container: Api.JavascriptBundleContainer,
- bundle: Api.JavascriptBundle,
- allocator: std.mem.Allocator,
- bytes_ptr: []u8 = undefined,
- bytes: []u8 = &[_]u8{},
- fd: FileDescriptorType = 0,
- code_end_pos: u32 = 0,
-
- // Lookup packages by ID - hash(name@version)
- package_id_map: PackageIDMap,
-
- // Lookup packages by name. Remember that you can have multiple versions of the same package.
- package_name_map: PackageNameMap,
-
- // This is stored as a single pre-allocated, flat array so we can avoid dynamic allocations.
- package_name_ids_ptr: []BundledPackageID = &([_]BundledPackageID{}),
-
- code_string: ?AllocatedString = null,
-
- pub const magic_bytes = "#!/usr/bin/env bun\n\n";
- threadlocal var jsbundle_prefix: [magic_bytes.len + 5]u8 = undefined;
-
- // TODO: support preact-refresh, others by not hard coding
- pub fn hasFastRefresh(this: *const NodeModuleBundle) bool {
- return this.package_name_map.contains("react-refresh");
- }
-
- pub fn readCodeAsStringSlow(this: *NodeModuleBundle, allocator: std.mem.Allocator) !string {
- if (this.code_string) |code| {
- return code.str;
- }
-
- var file = std.fs.File{ .handle = this.fd };
-
- var buf = try allocator.alloc(u8, this.code_end_pos);
- const count = try file.preadAll(buf, this.codeStartOffset());
- this.code_string = AllocatedString{ .str = buf[0..count], .len = @as(u32, @truncate(buf.len)), .allocator = allocator };
- return this.code_string.?.str;
- }
-
- pub fn loadPackageMap(this: *NodeModuleBundle) !void {
- this.package_name_map = PackageNameMap.init(this.allocator);
- this.package_id_map = PackageIDMap.init(this.allocator);
-
- const package_count = @as(u32, @truncate(this.bundle.packages.len));
-
- // this.package_has_multiple_versions = try std.bit_set.DynamicBitSet.initFull(package_count, this.allocator);
-
- try this.package_id_map.ensureTotalCapacity(
- package_count,
- );
- this.package_name_ids_ptr = try this.allocator.alloc(BundledPackageID, this.bundle.packages.len);
- var remaining_names = this.package_name_ids_ptr;
- try this.package_name_map.ensureTotalCapacity(
- package_count,
- );
- var prev_package_ids_for_name: []u32 = &[_]u32{};
-
- for (this.bundle.packages, 0..) |package, _package_id| {
- const package_id = @as(u32, @truncate(_package_id));
- std.debug.assert(package.hash != 0);
- this.package_id_map.putAssumeCapacityNoClobber(package.hash, @as(u32, @truncate(package_id)));
-
- const package_name = this.str(package.name);
- var entry = this.package_name_map.getOrPutAssumeCapacity(package_name);
-
- if (entry.found_existing) {
- // this.package_has_multiple_versions.set(prev_package_ids_for_name[prev_package_ids_for_name.len - 1]);
- // Assert that multiple packages with the same name come immediately after another
- // This catches any issues with the sorting order, which would cause all sorts of weird bugs
- // This also allows us to simply extend the length of the previous slice to the new length
- // Saving us an allocation
- if (@intFromPtr(prev_package_ids_for_name.ptr) != @intFromPtr(entry.value_ptr.ptr)) {
- Output.prettyErrorln(
- \\<r><red>Fatal<r>: incorrect package sorting order detected in .bun file.\n
- \\This is a bug! Please create an issue.\n
- \\If this bug blocks you from doing work, for now
- \\please <b>avoid having multiple versions of <cyan>"{s}"<r> in the same bundle.\n
- \\\n
- \\- Jarred"
- ,
- .{
- package_name,
- },
- );
- Global.crash();
- }
-
- const end = prev_package_ids_for_name.len + 1;
- // Assert we have enough room to add another package
- std.debug.assert(end < remaining_names.len);
- entry.value_ptr.* = prev_package_ids_for_name.ptr[0..end];
- entry.value_ptr.*[end - 1] = package_id;
- } else {
- prev_package_ids_for_name = remaining_names[0..1];
- prev_package_ids_for_name[0] = package_id;
- entry.value_ptr.* = prev_package_ids_for_name;
- remaining_names = remaining_names[1..];
- }
- }
- }
-
- pub fn getPackageIDByHash(this: *const NodeModuleBundle, hash: BundledPackageID) ?u32 {
- return this.package_id_map.get(hash);
- }
-
- pub fn getPackageIDByName(this: *const NodeModuleBundle, name: string) ?[]u32 {
- return this.package_name_map.get(name);
- }
-
- pub fn getPackage(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage {
- const package_id = this.getPackageIDByName(name) orelse return null;
- return &this.bundle.packages[@as(usize, @intCast(package_id[0]))];
- }
-
- pub fn hasModule(this: *const NodeModuleBundle, name: string) ?*const Api.JavascriptBundledPackage {
- const package_id = this.getPackageID(name) orelse return null;
- return &this.bundle.packages[@as(usize, @intCast(package_id))];
- }
-
- pub const ModuleQuery = struct {
- package: *const Api.JavascriptBundledPackage,
- relative_path: string,
- extensions: []string,
- };
-
- pub fn allocModuleImport(
- this: *const NodeModuleBundle,
- to: *const Api.JavascriptBundledModule,
- allocator: std.mem.Allocator,
- ) !string {
- const fmt = bun.fmt.hexIntLower(this.bundle.packages[to.package_id].hash);
- return try std.fmt.allocPrint(
- allocator,
- "{any}/{s}",
- .{
- fmt,
- this.str(to.path),
- 123,
- },
- );
- }
-
- pub fn findModuleInPackage(
- this: *const NodeModuleBundle,
- package: *const Api.JavascriptBundledPackage,
- _query: string,
- ) ?*const Api.JavascriptBundledModule {
- if (this.findModuleIDInPackage(package, _query)) |id| {
- return &this.bundle.modules[id];
- }
-
- return null;
- }
-
- pub fn findModuleIDInPackageStupid(
- this: *const NodeModuleBundle,
- package: *const Api.JavascriptBundledPackage,
- _query: string,
- ) ?u32 {
- for (modulesIn(&this.bundle, package), 0..) |mod, i| {
- if (strings.eql(this.str(mod.path), _query)) {
- return @as(u32, @truncate(i + package.modules_offset));
- }
- }
-
- return null;
- }
-
- pub fn findModuleIDInPackage(
- this: *const NodeModuleBundle,
- package: *const Api.JavascriptBundledPackage,
- _query: string,
- ) ?u32 {
- const ModuleFinder = struct {
- const Self = @This();
- ctx: *const NodeModuleBundle,
- pkg: *const Api.JavascriptBundledPackage,
- query: string,
-
- // Since the module doesn't necessarily exist, we use an integer overflow as the module name
- pub fn moduleName(context: *const Self, module: *const Api.JavascriptBundledModule) string {
- return if (module.path.offset == context.ctx.bundle.manifest_string.len) context.query else context.ctx.str(module.path);
- }
-
- pub fn cmpAsc(context: Self, lhs: Api.JavascriptBundledModule, rhs: Api.JavascriptBundledModule) std.math.Order {
- // Comapre the module name
- const lhs_name = context.moduleName(&lhs);
- const rhs_name = context.moduleName(&rhs);
-
- const traversal_length = @min(lhs_name.len, rhs_name.len);
-
- for (lhs_name[0..traversal_length], 0..) |char, i| {
- switch (std.math.order(char, rhs_name[i])) {
- .lt, .gt => |order| {
- return order;
- },
- .eq => {},
- }
- }
-
- return std.math.order(lhs_name.len, rhs_name.len);
- }
- };
- var to_find = Api.JavascriptBundledModule{
- .package_id = 0,
- .code = .{},
- .path = .{
- .offset = @as(u32, @truncate(this.bundle.manifest_string.len)),
- },
- };
-
- var finder = ModuleFinder{ .ctx = this, .pkg = package, .query = _query };
-
- const modules = modulesIn(&this.bundle, package);
- return @as(u32, @intCast(std.sort.binarySearch(
- Api.JavascriptBundledModule,
- to_find,
- modules,
- finder,
- ModuleFinder.cmpAsc,
- ) orelse return null)) + package.modules_offset;
- }
-
- pub fn findModuleIDInPackageIgnoringExtension(
- this: *const NodeModuleBundle,
- package: *const Api.JavascriptBundledPackage,
- _query: string,
- ) ?u32 {
- const ModuleFinder = struct {
- const Self = @This();
- ctx: *const NodeModuleBundle,
- pkg: *const Api.JavascriptBundledPackage,
- query: string,
-
- // Since the module doesn't necessarily exist, we use an integer overflow as the module name
- pub fn moduleName(context: *const Self, module: *const Api.JavascriptBundledModule) string {
- return if (module.path.offset == context.ctx.bundle.manifest_string.len) context.query else context.ctx.str(.{
- .offset = module.path.offset,
- .length = module.path.length - @as(u32, module.path_extname_length),
- });
- }
-
- pub fn cmpAsc(context: Self, lhs: Api.JavascriptBundledModule, rhs: Api.JavascriptBundledModule) std.math.Order {
- // Comapre the module name
- const lhs_name = context.moduleName(&lhs);
- const rhs_name = context.moduleName(&rhs);
-
- const traversal_length = @min(lhs_name.len, rhs_name.len);
-
- for (lhs_name[0..traversal_length], 0..) |char, i| {
- switch (std.math.order(char, rhs_name[i])) {
- .lt, .gt => |order| {
- return order;
- },
- .eq => {},
- }
- }
-
- return std.math.order(lhs_name.len, rhs_name.len);
- }
- };
- var to_find = Api.JavascriptBundledModule{
- .package_id = 0,
- .code = .{},
- .path = .{
- .offset = @as(u32, @truncate(this.bundle.manifest_string.len)),
- },
- };
-
- var finder = ModuleFinder{ .ctx = this, .pkg = package, .query = _query[0 .. _query.len - std.fs.path.extension(_query).len] };
-
- const modules = modulesIn(&this.bundle, package);
- return @as(u32, @intCast(std.sort.binarySearch(
- Api.JavascriptBundledModule,
- to_find,
- modules,
- finder,
- ModuleFinder.cmpAsc,
- ) orelse return null)) + package.modules_offset;
- }
-
- pub fn init(container: Api.JavascriptBundleContainer, allocator: std.mem.Allocator) NodeModuleBundle {
- return NodeModuleBundle{
- .container = container,
- .bundle = container.bundle.?,
- .allocator = allocator,
- .package_id_map = undefined,
- .package_name_map = undefined,
- .package_name_ids_ptr = undefined,
- };
- }
-
- pub fn getCodeEndPosition(stream: anytype, comptime needs_seek: bool) !u32 {
- if (needs_seek) try stream.seekTo(0);
-
- const read_bytes = try stream.read(&jsbundle_prefix);
- if (read_bytes != jsbundle_prefix.len) {
- return error.JSBundleBadHeaderTooShort;
- }
-
- return std.mem.readIntNative(u32, jsbundle_prefix[magic_bytes.len .. magic_bytes.len + 4]);
- }
-
- pub fn loadBundle(allocator: std.mem.Allocator, stream: anytype) !NodeModuleBundle {
- const end = try getCodeEndPosition(stream, false);
- try stream.seekTo(end);
- const file_end = try stream.getEndPos();
- var file_bytes = try allocator.alloc(u8, file_end - end);
- var read_count = try stream.read(file_bytes);
- var read_bytes = file_bytes[0..read_count];
- var reader = schema.Reader.init(read_bytes, allocator);
- var container = try Api.JavascriptBundleContainer.decode(&reader);
- if (container.bundle == null) return error.InvalidBundle;
- var bundle = NodeModuleBundle{
- .allocator = allocator,
- .container = container,
- .bundle = container.bundle.?,
- .fd = stream.handle,
- // sorry you can't have 4 GB of node_modules
- .code_end_pos = end - @as(u32, @intCast(jsbundle_prefix.len)),
- .bytes = read_bytes,
- .bytes_ptr = file_bytes,
- .package_id_map = undefined,
- .package_name_map = undefined,
- .package_name_ids_ptr = undefined,
- };
- try bundle.loadPackageMap();
- return bundle;
- }
-
- pub fn str(bundle: *const NodeModuleBundle, pointer: Api.StringPointer) string {
- return bundle.bundle.manifest_string[pointer.offset .. pointer.offset + pointer.length];
- }
-
- pub fn printSummary(this: *const NodeModuleBundle) void {
- const indent = comptime " ";
- for (this.bundle.packages) |pkg| {
- const modules = this.bundle.modules[pkg.modules_offset .. pkg.modules_offset + pkg.modules_length];
-
- Output.prettyln(
- "<r><blue><b>{s}</r> v{s}",
- .{ this.str(pkg.name), this.str(pkg.version) },
- );
-
- for (modules, 0..) |module, module_i| {
- const size_level: SizeLevel =
- switch (module.code.length) {
- 0...5_000 => .good,
- 5_001...74_999 => .neutral,
- else => .bad,
- };
-
- Output.print(indent, .{});
- prettySize(module.code.length, size_level, ">");
- Output.prettyln(
- indent ++ "<d>{s}</r>" ++ std.fs.path.sep_str ++ "{s} <r><d>[{d}]<r>\n",
- .{
- this.str(pkg.name),
- this.str(module.path),
- module_i + pkg.modules_offset,
- },
- );
- }
-
- Output.print("\n", .{});
- }
- const source_code_size = this.container.code_length.? - @as(u32, @intCast(jsbundle_prefix.len));
-
- Output.pretty("<b>", .{});
- prettySize(source_code_size, .neutral, ">");
- Output.prettyln("<b> JavaScript<r>", .{});
- Output.prettyln(indent ++ "<b>{d:6} modules", .{this.bundle.modules.len});
- Output.prettyln(indent ++ "<b>{d:6} packages", .{this.bundle.packages.len});
- }
-
- pub inline fn codeStartOffset(_: *const NodeModuleBundle) u32 {
- return @as(u32, @intCast(jsbundle_prefix.len));
- }
-
- pub fn printSummaryFromDisk(
- comptime StreamType: type,
- input: StreamType,
- comptime DestinationStreamType: type,
- _: DestinationStreamType,
- allocator: std.mem.Allocator,
- ) !void {
- const this = try loadBundle(allocator, input);
- this.printSummary();
- }
-
- const SizeLevel = enum { good, neutral, bad };
- fn prettySize(size: u32, level: SizeLevel, comptime align_char: []const u8) void {
- switch (size) {
- 0...1024 * 1024 => {
- switch (level) {
- .bad => Output.pretty("<red>{d: " ++ align_char ++ "6.2} KB</r>", .{@as(f64, @floatFromInt(size)) / 1024.0}),
- .neutral => Output.pretty("{d: " ++ align_char ++ "6.2} KB</r>", .{@as(f64, @floatFromInt(size)) / 1024.0}),
- .good => Output.pretty("<green>{d: " ++ align_char ++ "6.2} KB</r>", .{@as(f64, @floatFromInt(size)) / 1024.0}),
- }
- },
- else => {
- switch (level) {
- .bad => Output.pretty("<red>{d: " ++ align_char ++ "6.2} MB</r>", .{@as(f64, @floatFromInt(size)) / (1024 * 1024.0)}),
- .neutral => Output.pretty("{d: " ++ align_char ++ "6.2} MB</r>", .{@as(f64, @floatFromInt(size)) / (1024 * 1024.0)}),
- .good => Output.pretty("<green>{d: " ++ align_char ++ "6.2} MB</r>", .{@as(f64, @floatFromInt(size)) / (1024 * 1024.0)}),
- }
- },
- }
- }
-
- pub fn printBundle(
- comptime StreamType: type,
- input: StreamType,
- comptime DestinationStreamType: type,
- output: DestinationStreamType,
- ) !void {
- const BufferStreamContext = struct {
- pub fn run(in: StreamType, out: DestinationStreamType, end_at: u32) !void {
- var buf: [4096]u8 = undefined;
- var remain = @as(i64, @intCast(end_at));
- var read_amount: i64 = 99999;
- while (remain > 0 and read_amount > 0) {
- read_amount = @as(i64, @intCast(in.read(&buf) catch 0));
- remain -= @as(i64, @intCast(try out.write(buf[0..@as(usize, @intCast(@min(read_amount, remain)))])));
- }
- }
- };
-
- if (comptime Environment.isMac) {
- // darwin only allows reading ahead on/off, not specific amount
- _ = std.os.fcntl(input.handle, std.os.F.RDAHEAD, 1) catch 0;
- }
- const end = (try getCodeEndPosition(input, false)) - @as(u32, @intCast(jsbundle_prefix.len));
-
- try BufferStreamContext.run(
- input,
- output,
- end,
- );
- }
-};