diff options
author | 2021-09-26 20:06:10 -0700 | |
---|---|---|
committer | 2021-09-26 20:06:10 -0700 | |
commit | e1306be4be79bf1602734ca4786c6b90d9ce5a5b (patch) | |
tree | 98288a4cdc37b65ebb91ba92bd11121392c7836b | |
parent | 002d46d0c415137fb4985fa4c2ce47f1a38a8f93 (diff) | |
download | bun-e1306be4be79bf1602734ca4786c6b90d9ce5a5b.tar.gz bun-e1306be4be79bf1602734ca4786c6b90d9ce5a5b.tar.zst bun-e1306be4be79bf1602734ca4786c6b90d9ce5a5b.zip |
Update `Bun.readFileAsBytes` and `Bun.readFile` to also accept an array of filepaths to join
For example:
```
Bun.readFileAsString([Bun.main, "../pages/hi.tsx"]);
```
-rw-r--r-- | src/javascript/jsc/javascript.zig | 234 |
1 files changed, 143 insertions, 91 deletions
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 4dfacffdd..9b1d22a05 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -179,6 +179,74 @@ pub const Bun = struct { return ZigString.init(VirtualMachine.vm.bundler.options.routes.dir).toValue(VirtualMachine.vm.global).asRef(); } + pub fn getFilePath(ctx: js.JSContextRef, arguments: []const js.JSValueRef, buf: []u8, exception: js.ExceptionRef) ?string { + if (arguments.len != 1) { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + + const value = arguments[0]; + if (js.JSValueIsString(ctx, value)) { + var out = ZigString.Empty; + JSValue.toZigString(JSValue.fromRef(value), &out, VirtualMachine.vm.global); + var out_slice = out.slice(); + + // The dots are kind of unnecessary. They'll be normalized. + if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + + var parts = [_]string{out_slice}; + // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice)) + var res = VirtualMachine.vm.bundler.fs.absBuf(&parts, buf); + + return res; + } else if (js.JSValueIsArray(ctx, value)) { + var temp_strings_list: [32]string = undefined; + var temp_strings_list_len: u8 = 0; + defer { + for (temp_strings_list[0..temp_strings_list_len]) |_, i| { + temp_strings_list[i] = ""; + } + } + + var iter = JSValue.fromRef(value).arrayIterator(VirtualMachine.vm.global); + while (iter.next()) |item| { + if (temp_strings_list_len >= temp_strings_list.len) { + break; + } + + if (!item.isString()) { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + + var out = ZigString.Empty; + JSValue.toZigString(item, &out, VirtualMachine.vm.global); + const out_slice = out.slice(); + + temp_strings_list[temp_strings_list_len] = out_slice; + // The dots are kind of unnecessary. They'll be normalized. + if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + temp_strings_list_len += 1; + } + + if (temp_strings_list_len == 0) { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + + return VirtualMachine.vm.bundler.fs.absBuf(temp_strings_list[0..temp_strings_list_len], buf); + } else { + JSError(getAllocator(ctx), "Expected a file path as a string or an array of strings to be part of a file path.", .{}, ctx, exception); + return null; + } + } + pub fn getImportedStyles( this: void, ctx: js.JSContextRef, @@ -196,59 +264,52 @@ pub const Bun = struct { return JSValue.createStringArray(VirtualMachine.vm.global, styles.ptr, styles.len).asRef(); } - pub fn getRouteFiles( - this: void, + pub fn readFileAsStringCallback( ctx: js.JSContextRef, - function: js.JSObjectRef, - thisObject: js.JSObjectRef, - arguments: []const js.JSValueRef, + buf_z: [:0]const u8, exception: js.ExceptionRef, ) js.JSValueRef { - if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); - - const router = &(VirtualMachine.vm.bundler.router orelse unreachable); - const list = router.getEntryPointsWithBuffer(VirtualMachine.vm.allocator, false) catch unreachable; - VirtualMachine.vm.flush_list.append(list.buffer) catch {}; - defer VirtualMachine.vm.allocator.free(list.entry_points); + const path = buf_z.ptr[0..buf_z.len]; + var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| { + JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + }; - for (routes_list_strings[0..std.math.min(list.entry_points.len, routes_list_strings.len)]) |_, i| { - routes_list_strings[i] = ZigString.init(list.entry_points[i]); - } + defer file.close(); - const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.entry_points.len).asRef(); - return ref; - } + const stat = file.stat() catch |err| { + JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + }; - pub fn readFileAsBytes( - this: void, - ctx: js.JSContextRef, - function: js.JSObjectRef, - thisObject: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - if (arguments.len != 1 or !JSValue.isString(JSValue.fromRef(arguments[0]))) { - JSError(getAllocator(ctx), "readFileBytes expects a file path as a string. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); + if (stat.kind != .File) { + JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception); return js.JSValueMakeUndefined(ctx); } - var out = ZigString.Empty; - JSValue.toZigString(JSValue.fromRef(arguments[0]), &out, VirtualMachine.vm.global); - var out_slice = out.slice(); - // The dots are kind of unnecessary. They'll be normalized. - if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { - JSError(getAllocator(ctx), "readFileBytes expects a valid file path. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); + var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM + defer VirtualMachine.vm.allocator.free(contents_buf); + const contents_len = file.readAll(contents_buf) catch |err| { + JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception); return js.JSValueMakeUndefined(ctx); - } + }; - var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + contents_buf[contents_len] = 0; - var parts = [_]string{out_slice}; - // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice)) - var path = VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf); - buf[path.len] = 0; + // Very slow to do it this way. We're copying the string twice. + // But it's important that this string is garbage collected instead of manually managed. + // We can't really recycle this one. + // TODO: use external string + return js.JSValueMakeString(ctx, js.JSStringCreateWithUTF8CString(contents_buf.ptr)); + } + + pub fn readFileAsBytesCallback( + ctx: js.JSContextRef, + buf_z: [:0]const u8, + exception: js.ExceptionRef, + ) js.JSValueRef { + const path = buf_z.ptr[0..buf_z.len]; - const buf_z: [:0]const u8 = buf[0..path.len :0]; var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| { JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); return js.JSValueMakeUndefined(ctx); @@ -285,7 +346,7 @@ pub const Bun = struct { return marked_array_buffer.toJSObjectRef(ctx, exception); } - pub fn readFileAsString( + pub fn getRouteFiles( this: void, ctx: js.JSContextRef, function: js.JSObjectRef, @@ -293,59 +354,53 @@ pub const Bun = struct { arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - if (arguments.len != 1 or !JSValue.isString(JSValue.fromRef(arguments[0]))) { - JSError(getAllocator(ctx), "readFile expects a file path as a string. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - var out = ZigString.Empty; - JSValue.toZigString(JSValue.fromRef(arguments[0]), &out, VirtualMachine.vm.global); - var out_slice = out.slice(); + if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); - // The dots are kind of unnecessary. They'll be normalized. - if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { - JSError(getAllocator(ctx), "readFile expects a valid file path. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); + const router = &(VirtualMachine.vm.bundler.router orelse unreachable); + const list = router.getEntryPointsWithBuffer(VirtualMachine.vm.allocator, false) catch unreachable; + VirtualMachine.vm.flush_list.append(list.buffer) catch {}; + defer VirtualMachine.vm.allocator.free(list.entry_points); + + for (routes_list_strings[0..std.math.min(list.entry_points.len, routes_list_strings.len)]) |_, i| { + routes_list_strings[i] = ZigString.init(list.entry_points[i]); } - var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const ref = JSValue.createStringArray(VirtualMachine.vm.global, &routes_list_strings, list.entry_points.len).asRef(); + return ref; + } - var parts = [_]string{out_slice}; - // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice)) - var path = VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf); + pub fn readFileAsBytes( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const path = getFilePath(ctx, arguments, &buf, exception) orelse return null; buf[path.len] = 0; const buf_z: [:0]const u8 = buf[0..path.len :0]; - var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| { - JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - defer file.close(); - - const stat = file.stat() catch |err| { - JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; - - if (stat.kind != .File) { - JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM - defer VirtualMachine.vm.allocator.free(contents_buf); - const contents_len = file.readAll(contents_buf) catch |err| { - JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception); - return js.JSValueMakeUndefined(ctx); - }; + const result = readFileAsBytesCallback(ctx, buf_z, exception); + return result; + } - contents_buf[contents_len] = 0; + pub fn readFileAsString( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const path = getFilePath(ctx, arguments, &buf, exception) orelse return null; + buf[path.len] = 0; - // Very slow to do it this way. We're copying the string twice. - // But it's important that this string is garbage collected instead of manually managed. - // We can't really recycle this one. - // TODO: use external string - return js.JSValueMakeString(ctx, js.JSStringCreateWithUTF8CString(contents_buf.ptr)); + const buf_z: [:0]const u8 = buf[0..path.len :0]; + const result = readFileAsStringCallback(ctx, buf_z, exception); + return result; } pub fn getPublicPath(to: string, comptime Writer: type, writer: Writer) void { @@ -650,17 +705,14 @@ pub const VirtualMachine = struct { &vm.bundler.fs.fs, ) orelse 0), }; - } else if (strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) { + } else if (vm.node_modules == null and strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(Runtime.Runtime.sourceContent()), .specifier = ZigString.init(Runtime.Runtime.Imports.Name), .source_url = ZigString.init(Runtime.Runtime.Imports.Name), .hash = Runtime.Runtime.versionHash(), - .bytecodecache_fd = std.math.lossyCast( - u64, - Runtime.Runtime.byteCodeCacheFile(&vm.bundler.fs.fs) orelse 0, - ), + .bytecodecache_fd = 0, }; // This is all complicated because the imports have to be linked and we want to run the printer on it // so it consistently handles bundled imports @@ -840,7 +892,7 @@ pub const VirtualMachine = struct { std.debug.assert(VirtualMachine.vm_loaded); std.debug.assert(VirtualMachine.vm.global == global); - if (vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { + if (vm.node_modules == null and strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) { ret.path = Runtime.Runtime.Imports.Name; return; } else if (vm.node_modules != null and strings.eql(specifier, bun_file_import_path)) { |