diff options
author | 2023-06-25 16:32:27 -0700 | |
---|---|---|
committer | 2023-06-25 16:32:27 -0700 | |
commit | fdfbb18531828fc5dec329d5d9e5c828a3c83921 (patch) | |
tree | e337f5505396dcdaee1b5c9785c810a2a83e298c /src | |
parent | 3ed28f2828a29129a1791b7a4f6935d842d6493c (diff) | |
download | bun-fdfbb18531828fc5dec329d5d9e5c828a3c83921.tar.gz bun-fdfbb18531828fc5dec329d5d9e5c828a3c83921.tar.zst bun-fdfbb18531828fc5dec329d5d9e5c828a3c83921.zip |
Support reading embedded files in compiled executables (#3405)
* Support reading embedded files in compiled executables
* :nail_care:
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/api/bun.zig | 3 | ||||
-rw-r--r-- | src/bun.js/api/server.zig | 22 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 5 | ||||
-rw-r--r-- | src/bun.js/node/node_fs.zig | 30 | ||||
-rw-r--r-- | src/bun.js/webcore/blob.zig | 36 | ||||
-rw-r--r-- | src/cli/build_command.zig | 1 | ||||
-rw-r--r-- | src/standalone_bun.zig | 44 |
7 files changed, 124 insertions, 17 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 034aaa81f..2e6381c74 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -896,6 +896,9 @@ pub fn createNodeFS( ) js.JSValueRef { var module = ctx.allocator().create(JSC.Node.NodeJSFS) catch unreachable; module.* = .{}; + var vm = ctx.bunVM(); + if (vm.standalone_module_graph != null) + module.node_fs.vm = vm; return module.toJS(ctx).asObjectRef(); } diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index a56ff971f..ebfacdcc9 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2744,19 +2744,15 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // 1. Bun.file("foo") // 2. The content-disposition header is not present if (!has_content_disposition and content_type.category.autosetFilename()) { - if (this.blob.store()) |store| { - if (store.data == .file) { - if (store.data.file.pathlike == .path) { - const basename = std.fs.path.basename(store.data.file.pathlike.path.slice()); - if (basename.len > 0) { - var filename_buf: [1024]u8 = undefined; - - resp.writeHeader( - "content-disposition", - std.fmt.bufPrint(&filename_buf, "filename=\"{s}\"", .{basename[0..@min(basename.len, 1024 - 32)]}) catch "", - ); - } - } + if (this.blob.getFileName()) |filename| { + const basename = std.fs.path.basename(filename); + if (basename.len > 0) { + var filename_buf: [1024]u8 = undefined; + + resp.writeHeader( + "content-disposition", + std.fmt.bufPrint(&filename_buf, "filename=\"{s}\"", .{basename[0..@min(basename.len, 1024 - 32)]}) catch "", + ); } } } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 3baa25e22..cb1a50f1d 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -593,7 +593,10 @@ pub const VirtualMachine = struct { pub inline fn nodeFS(this: *VirtualMachine) *Node.NodeFS { return this.node_fs orelse brk: { this.node_fs = bun.default_allocator.create(Node.NodeFS) catch unreachable; - this.node_fs.?.* = Node.NodeFS{}; + this.node_fs.?.* = Node.NodeFS{ + // only used when standalone module graph is enabled + .vm = if (this.standalone_module_graph != null) this else null, + }; break :brk this.node_fs.?; }; } diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 21a65251a..35c616a89 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -2492,6 +2492,7 @@ pub const NodeFS = struct { /// That means a stack-allocated buffer won't suffice. Instead, we re-use /// the heap allocated buffer on the NodefS struct sync_error_buf: [bun.MAX_PATH_BYTES]u8 = undefined, + vm: ?*JSC.VirtualMachine = null, pub const ReturnType = Return; @@ -3442,6 +3443,35 @@ pub const NodeFS = struct { const fd = switch (args.path) { .path => brk: { path = args.path.path.sliceZ(&this.sync_error_buf); + if (this.vm) |vm| { + if (vm.standalone_module_graph) |graph| { + if (graph.find(path)) |file| { + if (args.encoding == .buffer) { + return .{ + .result = .{ + .buffer = Buffer.fromBytes( + bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"), + bun.default_allocator, + .Uint8Array, + ), + }, + }; + } else if (comptime string_type == .default) + .{ + .result = .{ + .string = bun.default_allocator.dupe(u8, file.contents) catch @panic("out of memory"), + }, + } + else + .{ + .result = .{ + .null_terminated = bun.default_allocator.dupeZ(u8, file.contents) catch @panic("out of memory"), + }, + }; + } + } + } + break :brk switch (Syscall.open( path, os.O.RDONLY | os.O.NOCTTY, diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 1e63ea3a2..868acbb80 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -952,6 +952,13 @@ pub const Blob = struct { switch (path_) { .path => { const slice = path_.path.slice(); + + if (vm.standalone_module_graph) |graph| { + if (graph.find(slice)) |file| { + return file.blob(globalThis).dupe(); + } + } + var cloned = (allocator.dupeZ(u8, slice) catch unreachable)[0..slice.len]; break :brk .{ @@ -2195,6 +2202,9 @@ pub const Blob = struct { cap: SizeType = 0, allocator: std.mem.Allocator, + /// Used by standalone module graph + stored_name: bun.PathString = bun.PathString.empty, + pub fn init(bytes: []u8, allocator: std.mem.Allocator) ByteStore { return .{ .ptr = bytes.ptr, @@ -2528,17 +2538,31 @@ pub const Blob = struct { this: *Blob, globalThis: *JSC.JSGlobalObject, ) callconv(.C) JSValue { + if (this.getFileName()) |path| { + var str = bun.String.create(path); + return str.toJS(globalThis); + } + + return JSValue.undefined; + } + + pub fn getFileName( + this: *const Blob, + ) ?[]const u8 { if (this.store) |store| { if (store.data == .file) { if (store.data.file.pathlike == .path) { - return ZigString.fromUTF8(store.data.file.pathlike.path.slice()).toValueGC(globalThis); + return store.data.file.pathlike.path.slice(); } // we shouldn't return Number here. + } else if (store.data == .bytes) { + if (store.data.bytes.stored_name.slice().len > 0) + return store.data.bytes.stored_name.slice(); } } - return JSC.JSValue.jsUndefined(); + return null; } // TODO: Move this to a separate `File` object or BunFile @@ -3469,6 +3493,14 @@ pub const AnyBlob = union(enum) { InternalBlob: InternalBlob, WTFStringImpl: bun.WTF.StringImpl, + pub fn getFileName(this: *const AnyBlob) ?[]const u8 { + return switch (this.*) { + .Blob => this.Blob.getFileName(), + .WTFStringImpl => null, + .InternalBlob => null, + }; + } + pub inline fn fastSize(this: *const AnyBlob) Blob.SizeType { return switch (this.*) { .Blob => this.Blob.size, diff --git a/src/cli/build_command.zig b/src/cli/build_command.zig index 44e512996..ef99f7765 100644 --- a/src/cli/build_command.zig +++ b/src/cli/build_command.zig @@ -107,6 +107,7 @@ pub const BuildCommand = struct { // We never want to hit the filesystem for these files // This "compiled" protocol is specially handled by the module resolver. this_bundler.options.public_path = "compiled://root/"; + this_bundler.resolver.opts.public_path = "compiled://root/"; if (outfile.len == 0) { outfile = std.fs.path.basename(this_bundler.options.entry_points[0]); diff --git a/src/standalone_bun.zig b/src/standalone_bun.zig index e7363fb58..b18fe384e 100644 --- a/src/standalone_bun.zig +++ b/src/standalone_bun.zig @@ -18,6 +18,14 @@ pub const StandaloneModuleGraph = struct { return &this.files.values()[this.entry_point_id]; } + pub fn find(this: *const StandaloneModuleGraph, name: []const u8) ?*File { + if (!bun.strings.hasPrefixComptime(name, "compiled://root/")) { + return null; + } + + return this.files.getPtr(name); + } + pub const CompiledModuleGraphFile = struct { name: Schema.StringPointer = .{}, loader: bun.options.Loader = .file, @@ -30,6 +38,32 @@ pub const StandaloneModuleGraph = struct { loader: bun.options.Loader, contents: []const u8 = "", sourcemap: LazySourceMap, + blob_: ?*bun.JSC.WebCore.Blob = null, + + pub fn blob(this: *File, globalObject: *bun.JSC.JSGlobalObject) *bun.JSC.WebCore.Blob { + if (this.blob_ == null) { + var store = bun.JSC.WebCore.Blob.Store.init(@constCast(this.contents), bun.default_allocator) catch @panic("out of memory"); + // make it never free + store.ref(); + + var blob_ = bun.default_allocator.create(bun.JSC.WebCore.Blob) catch @panic("out of memory"); + blob_.* = bun.JSC.WebCore.Blob.initWithStore(store, globalObject); + blob_.allocator = bun.default_allocator; + + if (bun.HTTP.MimeType.byExtensionNoDefault(bun.strings.trimLeadingChar(std.fs.path.extension(this.name), '.'))) |mime| { + store.mime_type = mime; + blob_.content_type = mime.value; + blob_.content_type_was_set = true; + blob_.content_type_allocated = false; + } + + store.data.bytes.stored_name = bun.PathString.init(this.name); + + this.blob_ = blob_; + } + + return this.blob_.?; + } }; pub const LazySourceMap = union(enum) { @@ -152,8 +186,16 @@ pub const StandaloneModuleGraph = struct { continue; } + var dest_path = output_file.dest_path; + if (bun.strings.hasPrefixComptime(dest_path, "./")) { + dest_path = dest_path[2..]; + } + var module = CompiledModuleGraphFile{ - .name = string_builder.fmtAppendCount("{s}{s}", .{ prefix, output_file.dest_path }), + .name = string_builder.fmtAppendCount("{s}{s}", .{ + prefix, + dest_path, + }), .loader = output_file.loader, .contents = string_builder.appendCount(output_file.value.buffer.bytes), }; |