diff options
author | 2022-03-17 18:28:24 -0700 | |
---|---|---|
committer | 2022-03-17 18:28:24 -0700 | |
commit | 86a4ab704d026b984fba049c67b2e9594d0ddf65 (patch) | |
tree | b7ad87633953a4e36334ae93270f8dded6c98e0c /src | |
parent | d5613308f9b629e4e28f3625ead38fb052f2db34 (diff) | |
download | bun-86a4ab704d026b984fba049c67b2e9594d0ddf65.tar.gz bun-86a4ab704d026b984fba049c67b2e9594d0ddf65.tar.zst bun-86a4ab704d026b984fba049c67b2e9594d0ddf65.zip |
Move `Bun` to JSC.API
Diffstat (limited to '')
-rw-r--r-- | src/http.zig | 4 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 1618 | ||||
-rw-r--r-- | src/javascript/jsc/api/router.zig | 4 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/exports.zig | 4 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 1441 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 9 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/url.zig | 5 | ||||
-rw-r--r-- | src/jsc.zig | 2 |
8 files changed, 1667 insertions, 1420 deletions
diff --git a/src/http.zig b/src/http.zig index 601caabf2..585df3756 100644 --- a/src/http.zig +++ b/src/http.zig @@ -1401,7 +1401,7 @@ pub const RequestContext = struct { js_ast.Stmt.Data.Store.reset(); js_ast.Expr.Data.Store.reset(); - JavaScript.Bun.flushCSSImports(); + JavaScript.API.Bun.flushCSSImports(); vm.flush(); Output.printElapsed(@intToFloat(f64, (handler.start_timer.read())) / std.time.ns_per_ms); @@ -1530,7 +1530,7 @@ pub const RequestContext = struct { ); js_ast.Stmt.Data.Store.reset(); js_ast.Expr.Data.Store.reset(); - JavaScript.Bun.flushCSSImports(); + JavaScript.API.Bun.flushCSSImports(); Output.flush(); JavaScript.VirtualMachine.vm.arena.deinit(); JavaScript.VirtualMachine.vm.has_loaded = false; diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig new file mode 100644 index 000000000..e2af7ed95 --- /dev/null +++ b/src/javascript/jsc/api/bun.zig @@ -0,0 +1,1618 @@ +const Bun = @This(); +const default_allocator = @import("../../../global.zig").default_allocator; +const bun = @import("../../../global.zig"); +const Environment = bun.Environment; +const NetworkThread = @import("http").NetworkThread; +const Global = bun.Global; +const strings = bun.strings; +const string = bun.string; +const Output = @import("../../../global.zig").Output; +const MutableString = @import("../../../global.zig").MutableString; +const std = @import("std"); +const Allocator = std.mem.Allocator; +const IdentityContext = @import("../../../identity_context.zig").IdentityContext; +const Fs = @import("../../../fs.zig"); +const Resolver = @import("../../../resolver/resolver.zig"); +const ast = @import("../../../import_record.zig"); +const NodeModuleBundle = @import("../../../node_module_bundle.zig").NodeModuleBundle; +const MacroEntryPoint = @import("../../../bundler.zig").MacroEntryPoint; +const logger = @import("../../../logger.zig"); +const Api = @import("../../../api/schema.zig").Api; +const options = @import("../../../options.zig"); +const Bundler = @import("../../../bundler.zig").Bundler; +const ServerEntryPoint = @import("../../../bundler.zig").ServerEntryPoint; +const js_printer = @import("../../../js_printer.zig"); +const js_parser = @import("../../../js_parser.zig"); +const js_ast = @import("../../../js_ast.zig"); +const hash_map = @import("../../../hash_map.zig"); +const http = @import("../../../http.zig"); +const NodeFallbackModules = @import("../../../node_fallbacks.zig"); +const ImportKind = ast.ImportKind; +const Analytics = @import("../../../analytics/analytics_thread.zig"); +const ZigString = @import("../../../jsc.zig").ZigString; +const Runtime = @import("../../../runtime.zig"); +const Router = @import("./router.zig"); +const ImportRecord = ast.ImportRecord; +const DotEnv = @import("../../../env_loader.zig"); +const ParseResult = @import("../../../bundler.zig").ParseResult; +const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; +const MacroRemap = @import("../../../resolver/package_json.zig").MacroMap; +const WebCore = @import("../../../jsc.zig").WebCore; +const Request = WebCore.Request; +const Response = WebCore.Response; +const Headers = WebCore.Headers; +const Fetch = WebCore.Fetch; +const FetchEvent = WebCore.FetchEvent; +const js = @import("../../../jsc.zig").C; +const JSC = @import("../../../jsc.zig"); +const JSError = @import("../base.zig").JSError; +const d = @import("../base.zig").d; +const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; +const getAllocator = @import("../base.zig").getAllocator; +const JSValue = @import("../../../jsc.zig").JSValue; +const NewClass = @import("../base.zig").NewClass; +const Microtask = @import("../../../jsc.zig").Microtask; +const JSGlobalObject = @import("../../../jsc.zig").JSGlobalObject; +const ExceptionValueRef = @import("../../../jsc.zig").ExceptionValueRef; +const JSPrivateDataPtr = @import("../../../jsc.zig").JSPrivateDataPtr; +const ZigConsoleClient = @import("../../../jsc.zig").ZigConsoleClient; +const Node = @import("../../../jsc.zig").Node; +const ZigException = @import("../../../jsc.zig").ZigException; +const ZigStackTrace = @import("../../../jsc.zig").ZigStackTrace; +const ErrorableResolvedSource = @import("../../../jsc.zig").ErrorableResolvedSource; +const ResolvedSource = @import("../../../jsc.zig").ResolvedSource; +const JSPromise = @import("../../../jsc.zig").JSPromise; +const JSInternalPromise = @import("../../../jsc.zig").JSInternalPromise; +const JSModuleLoader = @import("../../../jsc.zig").JSModuleLoader; +const JSPromiseRejectionOperation = @import("../../../jsc.zig").JSPromiseRejectionOperation; +const Exception = @import("../../../jsc.zig").Exception; +const ErrorableZigString = @import("../../../jsc.zig").ErrorableZigString; +const ZigGlobalObject = @import("../../../jsc.zig").ZigGlobalObject; +const VM = @import("../../../jsc.zig").VM; +const JSFunction = @import("../../../jsc.zig").JSFunction; +const Config = @import("../config.zig"); +const URL = @import("../../../url.zig").URL; +const Transpiler = @import("./transpiler.zig"); +const VirtualMachine = @import("../javascript.zig").VirtualMachine; +const IOTask = JSC.IOTask; +const is_bindgen = JSC.is_bindgen; + +threadlocal var css_imports_list_strings: [512]ZigString = undefined; +threadlocal var css_imports_list: [512]Api.StringPointer = undefined; +threadlocal var css_imports_list_tail: u16 = 0; +threadlocal var css_imports_buf: std.ArrayList(u8) = undefined; +threadlocal var css_imports_buf_loaded: bool = false; + +threadlocal var routes_list_strings: [1024]ZigString = undefined; + +pub fn onImportCSS( + resolve_result: *const Resolver.Result, + import_record: *ImportRecord, + origin: URL, +) void { + if (!css_imports_buf_loaded) { + css_imports_buf = std.ArrayList(u8).initCapacity( + VirtualMachine.vm.allocator, + import_record.path.text.len, + ) catch unreachable; + css_imports_buf_loaded = true; + } + + var writer = css_imports_buf.writer(); + const offset = css_imports_buf.items.len; + css_imports_list[css_imports_list_tail] = .{ + .offset = @truncate(u32, offset), + .length = 0, + }; + getPublicPath(resolve_result.path_pair.primary.text, origin, @TypeOf(writer), writer); + const length = css_imports_buf.items.len - offset; + css_imports_list[css_imports_list_tail].length = @truncate(u32, length); + css_imports_list_tail += 1; +} + +pub fn flushCSSImports() void { + if (css_imports_buf_loaded) { + css_imports_buf.clearRetainingCapacity(); + css_imports_list_tail = 0; + } +} + +pub fn getCSSImports() []ZigString { + var i: u16 = 0; + const tail = css_imports_list_tail; + while (i < tail) : (i += 1) { + ZigString.fromStringPointer(css_imports_list[i], css_imports_buf.items, &css_imports_list_strings[i]); + } + return css_imports_list_strings[0..tail]; +} + +pub fn inspect( + // this + _: void, + ctx: js.JSContextRef, + // function + _: js.JSObjectRef, + // thisObject + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (arguments.len == 0) + return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); + + for (arguments) |arg| { + JSC.C.JSValueProtect(ctx, arg); + } + defer { + for (arguments) |arg| { + JSC.C.JSValueUnprotect(ctx, arg); + } + } + + // very stable memory address + var array = MutableString.init(getAllocator(ctx), 0) catch unreachable; + var buffered_writer_ = MutableString.BufferedWriter{ .context = &array }; + var buffered_writer = &buffered_writer_; + + var writer = buffered_writer.writer(); + const Writer = @TypeOf(writer); + // we buffer this because it'll almost always be < 4096 + // when it's under 4096, we want to avoid the dynamic allocation + ZigConsoleClient.format( + .Debug, + ctx.ptr(), + @ptrCast([*]const JSValue, arguments.ptr), + arguments.len, + Writer, + Writer, + writer, + false, + false, + false, + ); + + // when it's a small thing, rely on GC to manage the memory + if (writer.context.pos < 2048 and array.list.items.len == 0) { + var slice = writer.context.buffer[0..writer.context.pos]; + if (slice.len == 0) { + return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); + } + + var zig_str = ZigString.init(slice).withEncoding(); + return zig_str.toValueGC(ctx.ptr()).asObjectRef(); + } + + // when it's a big thing, we will manage it + { + writer.context.flush() catch {}; + var slice = writer.context.context.toOwnedSlice(); + + var zig_str = ZigString.init(slice).withEncoding(); + if (!zig_str.isUTF8()) { + return zig_str.toExternalValue(ctx.ptr()).asObjectRef(); + } else { + return zig_str.toValueGC(ctx.ptr()).asObjectRef(); + } + } +} + +pub fn registerMacro( + // this + _: void, + ctx: js.JSContextRef, + // function + _: js.JSObjectRef, + // thisObject + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + if (arguments.len != 2 or !js.JSValueIsNumber(ctx, arguments[0])) { + JSError(getAllocator(ctx), "Internal error registering macros: invalid args", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + // TODO: make this faster + const id = @truncate(i32, @floatToInt(i64, js.JSValueToNumber(ctx, arguments[0], exception))); + if (id == -1 or id == 0) { + JSError(getAllocator(ctx), "Internal error registering macros: invalid id", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + if (!js.JSValueIsObject(ctx, arguments[1]) or !js.JSObjectIsFunction(ctx, arguments[1])) { + JSError(getAllocator(ctx), "Macro must be a function. Received: {s}", .{@tagName(js.JSValueGetType(ctx, arguments[1]))}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + var get_or_put_result = VirtualMachine.vm.macros.getOrPut(id) catch unreachable; + if (get_or_put_result.found_existing) { + js.JSValueUnprotect(ctx, get_or_put_result.value_ptr.*); + } + + js.JSValueProtect(ctx, arguments[1]); + get_or_put_result.value_ptr.* = arguments[1]; + + return js.JSValueMakeUndefined(ctx); +} + +pub fn getCWD( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return ZigString.init(VirtualMachine.vm.bundler.fs.top_level_dir).toValue(ctx.ptr()).asRef(); +} + +pub fn getOrigin( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return ZigString.init(VirtualMachine.vm.origin.origin).toValue(ctx.ptr()).asRef(); +} + +pub fn enableANSIColors( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return js.JSValueMakeBoolean(ctx, Output.enable_ansi_colors); +} +pub fn getMain( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return ZigString.init(VirtualMachine.vm.main).toValue(ctx.ptr()).asRef(); +} + +pub fn getAssetPrefix( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return ZigString.init(VirtualMachine.vm.bundler.options.routes.asset_prefix_path).toValue(ctx.ptr()).asRef(); +} + +pub fn getArgv( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (comptime Environment.isWindows) { + @compileError("argv not supported on windows"); + } + + var argv_list = std.heap.stackFallback(128, getAllocator(ctx)); + var allocator = argv_list.get(); + var argv = allocator.alloc(ZigString, std.os.argv.len) catch unreachable; + defer if (argv.len > 128) allocator.free(argv); + for (std.os.argv) |arg, i| { + argv[i] = ZigString.init(std.mem.span(arg)); + } + + return JSValue.createStringArray(ctx.ptr(), argv.ptr, argv.len, true).asObjectRef(); +} + +pub fn getRoutesDir( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (!VirtualMachine.vm.bundler.options.routes.routes_enabled or VirtualMachine.vm.bundler.options.routes.dir.len == 0) { + return js.JSValueMakeUndefined(ctx); + } + + return ZigString.init(VirtualMachine.vm.bundler.options.routes.dir).toValue(ctx.ptr()).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, ctx.ptr()); + 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(ctx.ptr()); + 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, ctx.ptr()); + 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( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + defer flushCSSImports(); + const styles = getCSSImports(); + if (styles.len == 0) { + return js.JSObjectMakeArray(ctx, 0, null, null); + } + + return JSValue.createStringArray(ctx.ptr(), styles.ptr, styles.len, true).asRef(); +} + +pub fn newPath( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + args: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + const is_windows = args.len == 1 and JSValue.fromRef(args[0]).toBoolean(); + return Node.Path.create(ctx.ptr(), is_windows).asObjectRef(); +} + +pub fn readFileAsStringCallback( + ctx: js.JSContextRef, + buf_z: [:0]const u8, + exception: js.ExceptionRef, +) js.JSValueRef { + const path = buf_z.ptr[0..buf_z.len]; + var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) 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); + }; + + contents_buf[contents_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]; + + var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) 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 + errdefer 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); + }; + + contents_buf[contents_len] = 0; + + var marked_array_buffer = VirtualMachine.vm.allocator.create(MarkedArrayBuffer) catch unreachable; + marked_array_buffer.* = MarkedArrayBuffer.fromBytes( + contents_buf[0..contents_len], + VirtualMachine.vm.allocator, + .Uint8Array, + ); + + return marked_array_buffer.toJSObjectRef(ctx, exception); +} + +pub fn getRouteFiles( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); + + const router = &VirtualMachine.vm.bundler.router.?; + const list = router.getPublicPaths() catch unreachable; + + for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { + routes_list_strings[i] = ZigString.init(list[i]); + } + + const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); + return ref; +} + +pub fn getRouteNames( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); + + const router = &VirtualMachine.vm.bundler.router.?; + const list = router.getNames() catch unreachable; + + for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { + routes_list_strings[i] = ZigString.init(list[i]); + } + + const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); + return ref; +} + +pub fn readFileAsBytes( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + var buf: [bun.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]; + const result = readFileAsBytesCallback(ctx, buf_z, exception); + return result; +} + +pub fn readFileAsString( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + var buf: [bun.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]; + const result = readFileAsStringCallback(ctx, buf_z, exception); + return result; +} + +pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void { + const relative_path = VirtualMachine.vm.bundler.fs.relativeTo(to); + if (origin.isAbsolute()) { + if (strings.hasPrefix(relative_path, "..") or strings.hasPrefix(relative_path, "./")) { + writer.writeAll(origin.origin) catch return; + writer.writeAll("/abs:") catch return; + if (std.fs.path.isAbsolute(to)) { + writer.writeAll(to) catch return; + } else { + writer.writeAll(VirtualMachine.vm.bundler.fs.abs(&[_]string{to})) catch return; + } + } else { + origin.joinWrite( + Writer, + writer, + VirtualMachine.vm.bundler.options.routes.asset_prefix_path, + "", + relative_path, + "", + ) catch return; + } + } else { + writer.writeAll(std.mem.trimLeft(u8, relative_path, "/")) catch unreachable; + } +} + +pub fn sleepSync( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + if (js.JSValueIsNumber(ctx, arguments[0])) { + const seconds = JSValue.fromRef(arguments[0]).asNumber(); + if (seconds > 0 and std.math.isFinite(seconds)) std.time.sleep(@floatToInt(u64, seconds * 1000) * std.time.ns_per_ms); + } + + return js.JSValueMakeUndefined(ctx); +} + +pub fn createNodeFS( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + return Node.NodeFSBindings.make( + ctx, + VirtualMachine.vm.node_fs orelse brk: { + VirtualMachine.vm.node_fs = bun.default_allocator.create(Node.NodeFS) catch unreachable; + VirtualMachine.vm.node_fs.?.* = Node.NodeFS{ .async_io = undefined }; + break :brk VirtualMachine.vm.node_fs.?; + }, + ); +} + +pub fn generateHeapSnapshot( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + return ctx.ptr().generateHeapSnapshot().asObjectRef(); +} + +pub fn runGC( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + // it should only force cleanup on thread exit + + Global.mimalloc_cleanup(false); + + return ctx.ptr().vm().runGC(arguments.len > 0 and JSValue.fromRef(arguments[0]).toBoolean()).asRef(); +} + +pub fn shrink( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + ctx.ptr().vm().shrinkFootprint(); + return JSValue.jsUndefined().asRef(); +} + +fn doResolve( + ctx: js.JSContextRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) ?JSC.JSValue { + var args = JSC.Node.ArgumentsSlice.from(arguments); + const specifier = args.nextEat() orelse { + JSC.throwInvalidArguments("Expected a specifier and a from path", .{}, ctx, exception); + return null; + }; + + if (specifier.isUndefinedOrNull()) { + JSC.throwInvalidArguments("specifier must be a string", .{}, ctx, exception); + return null; + } + + const from = args.nextEat() orelse { + JSC.throwInvalidArguments("Expected a from path", .{}, ctx, exception); + return null; + }; + + if (from.isUndefinedOrNull()) { + JSC.throwInvalidArguments("from must be a string", .{}, ctx, exception); + return null; + } + + return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception); +} + +fn doResolveWithArgs( + ctx: js.JSContextRef, + specifier: ZigString, + from: ZigString, + exception: js.ExceptionRef, +) ?JSC.JSValue { + var errorable: ErrorableZigString = undefined; + + VirtualMachine.resolveForAPI( + &errorable, + ctx.ptr(), + specifier, + from, + ); + + if (!errorable.success) { + exception.* = bun.cast(JSC.JSValueRef, errorable.result.err.ptr.?); + return null; + } + + return errorable.result.value.toValue(ctx.ptr()); +} + +pub fn resolveSync( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + const value = doResolve(ctx, arguments, exception) orelse return null; + return value.asObjectRef(); +} + +pub fn resolve( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + const value = doResolve(ctx, arguments, exception) orelse { + var exception_value = exception.*.?; + exception.* = null; + return JSC.JSPromise.rejectedPromiseValue(ctx.ptr(), JSC.JSValue.fromRef(exception_value)).asObjectRef(); + }; + return JSC.JSPromise.resolvedPromiseValue(ctx.ptr(), value).asObjectRef(); +} + +export fn Bun__resolve( + global: *JSGlobalObject, + specifier: JSValue, + source: JSValue, +) JSC.JSValue { + var exception_ = [1]JSC.JSValueRef{null}; + var exception = &exception_; + const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception) orelse { + return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0])); + }; + return JSC.JSPromise.resolvedPromiseValue(global, value); +} + +export fn Bun__resolveSync( + global: *JSGlobalObject, + specifier: JSValue, + source: JSValue, + exception_: ?*JSValue, +) JSC.JSValue { + var exception_ = [1]JSC.JSValueRef{null}; + var exception = &exception_; + exception_.* = exception[0]; + const value = doResolveWithArgs(global.ref(), specifier.getZigString(global), source.getZigString(global), exception) orelse { + return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0])); + }; + return JSC.JSPromise.resolvedPromiseValue(global, value); +} + +comptime { + if (!is_bindgen) { + _ = Bun__resolve; + _ = Bun__resolveSync; + } +} + +pub fn readAllStdinSync( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + exception: js.ExceptionRef, +) js.JSValueRef { + var stack = std.heap.stackFallback(2048, getAllocator(ctx)); + var allocator = stack.get(); + + var stdin = std.io.getStdIn(); + var result = stdin.readToEndAlloc(allocator, std.math.maxInt(u32)) catch |err| { + JSError(undefined, "{s} reading stdin", .{@errorName(err)}, ctx, exception); + return null; + }; + var out = ZigString.init(result); + out.detectEncoding(); + return out.toValueGC(ctx.ptr()).asObjectRef(); +} + +var public_path_temp_str: [bun.MAX_PATH_BYTES]u8 = undefined; + +pub fn getPublicPathJS( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + _: js.ExceptionRef, +) js.JSValueRef { + var zig_str: ZigString = ZigString.Empty; + JSValue.toZigString(JSValue.fromRef(arguments[0]), &zig_str, ctx.ptr()); + + const to = zig_str.slice(); + + var stream = std.io.fixedBufferStream(&public_path_temp_str); + var writer = stream.writer(); + getPublicPath(to, VirtualMachine.vm.origin, @TypeOf(&writer), &writer); + return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(ctx.ptr()).asObjectRef(); +} + +// pub fn resolvePath( +// _: void, +// ctx: js.JSContextRef, +// _: js.JSObjectRef, +// _: js.JSObjectRef, +// arguments: []const js.JSValueRef, +// _: js.ExceptionRef, +// ) js.JSValueRef { +// if (arguments.len == 0) return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); +// var zig_str: ZigString = ZigString.Empty; +// JSValue.toZigString(JSValue.fromRef(arguments[0]), &zig_str, ctx.ptr()); +// var buf: [bun.MAX_PATH_BYTES]u8 = undefined; +// var stack = std.heap.stackFallback(32 * @sizeOf(string), VirtualMachine.vm.allocator); +// var allocator = stack.get(); +// var parts = allocator.alloc(string, arguments.len) catch {}; +// defer allocator.free(parts); + +// const to = zig_str.slice(); +// var parts = .{to}; +// const value = ZigString.init(VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf)).toValueGC(ctx.ptr()); +// return value.asObjectRef(); +// } + +pub const Class = NewClass( + void, + .{ + .name = "Bun", + .read_only = true, + .ts = .{ + .module = .{ + .path = "bun.js/router", + .tsdoc = "Filesystem Router supporting dynamic routes, exact routes, catch-all routes, and optional catch-all routes. Implemented in native code and only available with Bun.js.", + }, + }, + }, + .{ + .match = .{ + .rfn = Router.match, + .ts = Router.match_type_definition, + }, + + .sleepSync = .{ + .rfn = sleepSync, + }, + .fetch = .{ + .rfn = Fetch.call, + .ts = d.ts{}, + }, + .getImportedStyles = .{ + .rfn = Bun.getImportedStyles, + .ts = d.ts{ + .name = "getImportedStyles", + .@"return" = "string[]", + }, + }, + .inspect = .{ + .rfn = Bun.inspect, + .ts = d.ts{ + .name = "inspect", + .@"return" = "string", + }, + }, + .getRouteFiles = .{ + .rfn = Bun.getRouteFiles, + .ts = d.ts{ + .name = "getRouteFiles", + .@"return" = "string[]", + }, + }, + ._Path = .{ + .rfn = Bun.newPath, + .ts = d.ts{}, + }, + .getRouteNames = .{ + .rfn = Bun.getRouteNames, + .ts = d.ts{ + .name = "getRouteNames", + .@"return" = "string[]", + }, + }, + .readFile = .{ + .rfn = Bun.readFileAsString, + .ts = d.ts{ + .name = "readFile", + .@"return" = "string", + }, + }, + .resolveSync = .{ + .rfn = Bun.resolveSync, + .ts = d.ts{ + .name = "resolveSync", + .@"return" = "string", + }, + }, + .resolve = .{ + .rfn = Bun.resolve, + .ts = d.ts{ + .name = "resolve", + .@"return" = "string", + }, + }, + .readFileBytes = .{ + .rfn = Bun.readFileAsBytes, + .ts = d.ts{ + .name = "readFile", + .@"return" = "Uint8Array", + }, + }, + .getPublicPath = .{ + .rfn = Bun.getPublicPathJS, + .ts = d.ts{ + .name = "getPublicPath", + .@"return" = "string", + }, + }, + .registerMacro = .{ + .rfn = Bun.registerMacro, + .ts = d.ts{ + .name = "registerMacro", + .@"return" = "undefined", + }, + }, + .fs = .{ + .rfn = Bun.createNodeFS, + .ts = d.ts{}, + }, + .jest = .{ + .rfn = @import("../test/jest.zig").Jest.call, + .ts = d.ts{}, + }, + .gc = .{ + .rfn = Bun.runGC, + .ts = d.ts{}, + }, + .generateHeapSnapshot = .{ + .rfn = Bun.generateHeapSnapshot, + .ts = d.ts{}, + }, + .shrink = .{ + .rfn = Bun.shrink, + .ts = d.ts{}, + }, + .readAllStdinSync = .{ + .rfn = Bun.readAllStdinSync, + .ts = d.ts{}, + }, + }, + .{ + .main = .{ + .get = getMain, + .ts = d.ts{ .name = "main", .@"return" = "string" }, + }, + .cwd = .{ + .get = getCWD, + .ts = d.ts{ .name = "cwd", .@"return" = "string" }, + }, + .origin = .{ + .get = getOrigin, + .ts = d.ts{ .name = "origin", .@"return" = "string" }, + }, + .routesDir = .{ + .get = getRoutesDir, + .ts = d.ts{ .name = "routesDir", .@"return" = "string" }, + }, + .assetPrefix = .{ + .get = getAssetPrefix, + .ts = d.ts{ .name = "assetPrefix", .@"return" = "string" }, + }, + .argv = .{ + .get = getArgv, + .ts = d.ts{ .name = "argv", .@"return" = "string[]" }, + }, + .env = .{ + .get = EnvironmentVariables.getter, + }, + .enableANSIColors = .{ + .get = enableANSIColors, + }, + .Transpiler = .{ + .get = getTranspilerConstructor, + .ts = d.ts{ .name = "Transpiler", .@"return" = "Transpiler.prototype" }, + }, + .TOML = .{ + .get = getTOMLObject, + .ts = d.ts{ .name = "TOML", .@"return" = "TOML.prototype" }, + }, + .unsafe = .{ + .get = getUnsafe, + }, + }, +); + +pub fn getTranspilerConstructor( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return js.JSObjectMake(ctx, Transpiler.TranspilerConstructor.get().?[0], null); +} + +pub fn getTOMLObject( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return js.JSObjectMake(ctx, TOML.Class.get().?[0], null); +} + +pub fn getUnsafe( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, +) js.JSValueRef { + return js.JSObjectMake(ctx, Unsafe.Class.get().?[0], null); +} + +pub const Unsafe = struct { + pub const Class = NewClass( + void, + .{ .name = "Unsafe", .read_only = true }, + .{ + .segfault = .{ + .rfn = __debug__doSegfault, + }, + .arrayBufferToString = .{ + .rfn = arrayBufferToString, + }, + }, + .{}, + ); + + // For testing the segfault handler + pub fn __debug__doSegfault( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, + ) js.JSValueRef { + _ = ctx; + const Reporter = @import("../../../report.zig"); + Reporter.globalError(error.SegfaultTest); + } + + pub fn arrayBufferToString( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + args: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + const array_buffer = JSC.ArrayBuffer.fromTypedArray(ctx, JSC.JSValue.fromRef(args[0]), exception); + switch (array_buffer.typed_array_type) { + .Uint16Array, .Int16Array => { + var zig_str = ZigString.init(""); + zig_str.ptr = @ptrCast([*]const u8, @alignCast(@alignOf([*]align(1) const u16), array_buffer.ptr)); + zig_str.len = array_buffer.len; + zig_str.markUTF16(); + return ZigString.toValue(&zig_str, ctx.ptr()).asObjectRef(); + }, + else => { + return ZigString.init(array_buffer.slice()).toValue(ctx.ptr()).asObjectRef(); + }, + } + } +}; + +// pub const Lockfile = struct { +// const BunLockfile = @import("../../../install/install.zig").Lockfile; +// lockfile: *BunLockfile, + +// pub const RefCountedLockfile = bun.RefCount(Lockfile, true); + +// pub const StaticClass = NewClass( +// void, +// .{ +// .name = "Lockfile", +// .read_only = true, +// }, +// .{ +// .load = .{ +// .rfn = BunLockfile.load, +// }, +// }, +// .{}, +// ); + +// pub const Class = NewClass( +// RefCountedLockfile, +// .{ +// .name = "Lockfile", +// .read_only = true, +// }, +// .{ +// .findPackagesByName = .{ +// .rfn = BunLockfile.load, +// }, +// .dependencies = .{ +// .rfn = BunLockfile.load, +// }, +// }, +// .{}, +// ); + +// pub fn deinit(this: *Lockfile) void { +// this.lockfile.deinit(); +// } + +// pub fn load( +// // this +// _: void, +// ctx: js.JSContextRef, +// // function +// _: js.JSObjectRef, +// // thisObject +// _: js.JSObjectRef, +// arguments: []const js.JSValueRef, +// exception: js.ExceptionRef, +// ) js.JSValueRef { +// if (arguments.len == 0) { +// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); +// return null; +// } + +// var lockfile: *BunLockfile = getAllocator(ctx).create(BunLockfile) catch return JSValue.jsUndefined().asRef(); + +// var log = logger.Log.init(default_allocator); +// var args_slice = @ptrCast([*]const JSValue, arguments.ptr)[0..arguments.len]; + +// var arguments_slice = Node.ArgumentsSlice.init(args_slice); +// var path_or_buffer = Node.PathLike.fromJS(ctx, &arguments_slice, exception) orelse { +// getAllocator(ctx).destroy(lockfile); +// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); +// return null; +// }; + +// const load_from_disk_result = switch (path_or_buffer) { +// Node.PathLike.Tag.string => lockfile.loadFromDisk(getAllocator(ctx), &log, path_or_buffer.string), +// Node.PathLike.Tag.buffer => lockfile.loadFromBytes(getAllocator(ctx), path_or_buffer.buffer.slice(), &log), +// else => { +// getAllocator(ctx).destroy(lockfile); +// JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); +// return null; +// }, +// }; + +// switch (load_from_disk_result) { +// .err => |cause| { +// defer getAllocator(ctx).destroy(lockfile); +// switch (cause.step) { +// .open_file => { +// JSError(undefined, "error opening lockfile: {s}", .{ +// @errorName(cause.value), +// }, ctx, exception); +// return null; +// }, +// .parse_file => { +// JSError(undefined, "error parsing lockfile: {s}", .{ +// @errorName(cause.value), +// }, ctx, exception); +// return null; +// }, +// .read_file => { +// JSError(undefined, "error reading lockfile: {s}", .{ +// @errorName(cause.value), +// }, ctx, exception); +// return null; +// }, +// } +// }, +// .ok => {}, +// } +// } +// }; + +pub const TOML = struct { + const TOMLParser = @import("../../../toml/toml_parser.zig").TOML; + pub const Class = NewClass( + void, + .{ + .name = "TOML", + .read_only = true, + }, + .{ + .parse = .{ + .rfn = TOML.parse, + }, + }, + .{}, + ); + + pub fn parse( + // this + _: void, + ctx: js.JSContextRef, + // function + _: js.JSObjectRef, + // thisObject + _: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + var arena = std.heap.ArenaAllocator.init(getAllocator(ctx)); + var allocator = arena.allocator(); + defer arena.deinit(); + var log = logger.Log.init(default_allocator); + var input_str = ZigString.init(""); + JSValue.fromRef(arguments[0]).toZigString(&input_str, ctx.ptr()); + var needs_deinit = false; + var input = input_str.slice(); + if (input_str.is16Bit()) { + input = std.fmt.allocPrint(allocator, "{}", .{input_str}) catch unreachable; + needs_deinit = true; + } + var source = logger.Source.initPathString("input.toml", input); + var parse_result = TOMLParser.parse(&source, &log, allocator) catch { + exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to parse toml").asObjectRef(); + return null; + }; + + // for now... + var buffer_writer = try js_printer.BufferWriter.init(allocator); + var writer = js_printer.BufferPrinter.init(buffer_writer); + _ = js_printer.printJSON(*js_printer.BufferPrinter, &writer, parse_result, &source) catch { + exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to print toml").asObjectRef(); + return null; + }; + + var slice = writer.ctx.buffer.toOwnedSliceLeaky(); + var out = ZigString.init(slice); + + const out_value = js.JSValueMakeFromJSONString(ctx, out.toJSStringRef()); + return out_value; + } +}; + +pub const Timer = struct { + last_id: i32 = 0, + warned: bool = false, + active: u32 = 0, + timeouts: TimeoutMap = TimeoutMap{}, + + const TimeoutMap = std.AutoArrayHashMapUnmanaged(i32, *Timeout); + + pub fn getNextID() callconv(.C) i32 { + VirtualMachine.vm.timer.last_id += 1; + return VirtualMachine.vm.timer.last_id; + } + + pub const Timeout = struct { + id: i32 = 0, + callback: JSValue, + interval: i32 = 0, + completion: NetworkThread.Completion = undefined, + repeat: bool = false, + io_task: ?*TimeoutTask = null, + cancelled: bool = false, + + pub const TimeoutTask = IOTask(Timeout); + + pub fn run(this: *Timeout, _task: *TimeoutTask) void { + this.io_task = _task; + NetworkThread.global.pool.io.?.timeout( + *Timeout, + this, + onCallback, + &this.completion, + std.time.ns_per_ms * @intCast( + u63, + @maximum( + this.interval, + 1, + ), + ), + ); + } + + pub fn onCallback(this: *Timeout, _: *NetworkThread.Completion, _: NetworkThread.AsyncIO.TimeoutError!void) void { + this.io_task.?.onFinish(); + } + + pub fn then(this: *Timeout, global: *JSGlobalObject) void { + if (!this.cancelled) { + if (this.repeat) { + this.io_task.?.deinit(); + var task = Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, global, this) catch unreachable; + this.io_task = task; + task.schedule(); + } + + _ = JSC.C.JSObjectCallAsFunction(global.ref(), this.callback.asObjectRef(), null, 0, null, null); + + if (this.repeat) + return; + } + + this.clear(global); + } + + pub fn clear(this: *Timeout, global: *JSGlobalObject) void { + this.cancelled = true; + JSC.C.JSValueUnprotect(global.ref(), this.callback.asObjectRef()); + _ = VirtualMachine.vm.timer.timeouts.swapRemove(this.id); + if (this.io_task) |task| { + task.deinit(); + } + VirtualMachine.vm.allocator.destroy(this); + VirtualMachine.vm.timer.active -|= 1; + VirtualMachine.vm.active_tasks -|= 1; + } + }; + + fn set( + id: i32, + globalThis: *JSGlobalObject, + callback: JSValue, + countdown: JSValue, + repeat: bool, + ) !void { + if (comptime is_bindgen) unreachable; + var timeout = try VirtualMachine.vm.allocator.create(Timeout); + js.JSValueProtect(globalThis.ref(), callback.asObjectRef()); + timeout.* = Timeout{ .id = id, .callback = callback, .interval = countdown.toInt32(), .repeat = repeat }; + var task = try Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, globalThis, timeout); + VirtualMachine.vm.timer.timeouts.put(VirtualMachine.vm.allocator, id, timeout) catch unreachable; + VirtualMachine.vm.timer.active +|= 1; + VirtualMachine.vm.active_tasks +|= 1; + task.schedule(); + } + + pub fn setTimeout( + globalThis: *JSGlobalObject, + callback: JSValue, + countdown: JSValue, + ) callconv(.C) JSValue { + if (comptime is_bindgen) unreachable; + const id = VirtualMachine.vm.timer.last_id; + VirtualMachine.vm.timer.last_id +%= 1; + + Timer.set(id, globalThis, callback, countdown, false) catch + return JSValue.jsUndefined(); + + return JSValue.jsNumberWithType(i32, id); + } + pub fn setInterval( + globalThis: *JSGlobalObject, + callback: JSValue, + countdown: JSValue, + ) callconv(.C) JSValue { + if (comptime is_bindgen) unreachable; + const id = VirtualMachine.vm.timer.last_id; + VirtualMachine.vm.timer.last_id +%= 1; + + Timer.set(id, globalThis, callback, countdown, true) catch + return JSValue.jsUndefined(); + + return JSValue.jsNumberWithType(i32, id); + } + + pub fn clearTimer(id: JSValue, _: *JSGlobalObject) void { + if (comptime is_bindgen) unreachable; + var timer: *Timeout = VirtualMachine.vm.timer.timeouts.get(id.toInt32()) orelse return; + timer.cancelled = true; + } + + pub fn clearTimeout( + globalThis: *JSGlobalObject, + id: JSValue, + ) callconv(.C) JSValue { + if (comptime is_bindgen) unreachable; + Timer.clearTimer(id, globalThis); + return JSValue.jsUndefined(); + } + pub fn clearInterval( + globalThis: *JSGlobalObject, + id: JSValue, + ) callconv(.C) JSValue { + if (comptime is_bindgen) unreachable; + Timer.clearTimer(id, globalThis); + return JSValue.jsUndefined(); + } + + const Shimmer = @import("../bindings/shimmer.zig").Shimmer; + + pub const shim = Shimmer("Bun", "Timer", @This()); + pub const name = "Bun__Timer"; + pub const include = ""; + pub const namespace = shim.namespace; + + pub const Export = shim.exportFunctions(.{ + .@"setTimeout" = setTimeout, + .@"setInterval" = setInterval, + .@"clearTimeout" = clearTimeout, + .@"clearInterval" = clearInterval, + .@"getNextID" = getNextID, + }); + + comptime { + @export(setTimeout, .{ .name = Export[0].symbol_name }); + @export(setInterval, .{ .name = Export[1].symbol_name }); + @export(clearTimeout, .{ .name = Export[2].symbol_name }); + @export(clearInterval, .{ .name = Export[3].symbol_name }); + @export(getNextID, .{ .name = Export[4].symbol_name }); + } +}; + +/// EnvironmentVariables is runtime defined. +/// Also, you can't iterate over process.env normally since it only exists at build-time otherwise +// This is aliased to Bun.env +pub const EnvironmentVariables = struct { + pub const Class = NewClass( + void, + .{ + .name = "DotEnv", + .read_only = true, + }, + .{ + .getProperty = .{ + .rfn = getProperty, + }, + .setProperty = .{ + .rfn = setProperty, + }, + .deleteProperty = .{ + .rfn = deleteProperty, + }, + .convertToType = .{ .rfn = convertToType }, + .hasProperty = .{ + .rfn = hasProperty, + }, + .getPropertyNames = .{ + .rfn = getPropertyNames, + }, + .toJSON = .{ + .rfn = toJSON, + .name = "toJSON", + }, + }, + .{}, + ); + + pub fn getter( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return js.JSObjectMake(ctx, EnvironmentVariables.Class.get().*, null); + } + + pub const BooleanString = struct { + pub const @"true": string = "true"; + pub const @"false": string = "false"; + }; + + pub fn getProperty( + ctx: js.JSContextRef, + _: js.JSObjectRef, + propertyName: js.JSStringRef, + _: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { + const len = js.JSStringGetLength(propertyName); + var ptr = js.JSStringGetCharacters8Ptr(propertyName); + var name = ptr[0..len]; + if (VirtualMachine.vm.bundler.env.map.get(name)) |value| { + return ZigString.toRef(value, ctx.ptr()); + } + + if (Output.enable_ansi_colors) { + // https://github.com/chalk/supports-color/blob/main/index.js + if (strings.eqlComptime(name, "FORCE_COLOR")) { + return ZigString.toRef(BooleanString.@"true", ctx.ptr()); + } + } + + return js.JSValueMakeUndefined(ctx); + } + + pub fn toJSON( + _: void, + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, + ) js.JSValueRef { + var map = VirtualMachine.vm.bundler.env.map.map; + var keys = map.keys(); + var values = map.values(); + const StackFallback = std.heap.StackFallbackAllocator(32 * 2 * @sizeOf(ZigString)); + var stack = StackFallback{ + .buffer = undefined, + .fallback_allocator = bun.default_allocator, + .fixed_buffer_allocator = undefined, + }; + var allocator = stack.get(); + var key_strings_ = allocator.alloc(ZigString, keys.len * 2) catch unreachable; + var key_strings = key_strings_[0..keys.len]; + var value_strings = key_strings_[keys.len..]; + + for (keys) |key, i| { + key_strings[i] = ZigString.init(key); + key_strings[i].detectEncoding(); + value_strings[i] = ZigString.init(values[i]); + value_strings[i].detectEncoding(); + } + + var result = JSValue.fromEntries(ctx.ptr(), key_strings.ptr, value_strings.ptr, keys.len, false).asObjectRef(); + allocator.free(key_strings_); + return result; + // } + // ZigConsoleClient.Formatter.format(this: *Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) + } + + pub fn deleteProperty( + _: js.JSContextRef, + _: js.JSObjectRef, + propertyName: js.JSStringRef, + _: js.ExceptionRef, + ) callconv(.C) bool { + const len = js.JSStringGetLength(propertyName); + var ptr = js.JSStringGetCharacters8Ptr(propertyName); + var name = ptr[0..len]; + _ = VirtualMachine.vm.bundler.env.map.map.swapRemove(name); + return true; + } + + pub fn setProperty( + ctx: js.JSContextRef, + _: js.JSObjectRef, + propertyName: js.JSStringRef, + value: js.JSValueRef, + exception: js.ExceptionRef, + ) callconv(.C) bool { + const len = js.JSStringGetLength(propertyName); + var ptr = js.JSStringGetCharacters8Ptr(propertyName); + var name = ptr[0..len]; + var val = ZigString.init(""); + JSValue.fromRef(value).toZigString(&val, ctx.ptr()); + if (exception.* != null) return false; + var result = std.fmt.allocPrint(VirtualMachine.vm.allocator, "{}", .{val}) catch unreachable; + VirtualMachine.vm.bundler.env.map.put(name, result) catch unreachable; + + return true; + } + + pub fn hasProperty( + _: js.JSContextRef, + _: js.JSObjectRef, + propertyName: js.JSStringRef, + ) callconv(.C) bool { + const len = js.JSStringGetLength(propertyName); + const ptr = js.JSStringGetCharacters8Ptr(propertyName); + const name = ptr[0..len]; + return VirtualMachine.vm.bundler.env.map.get(name) != null or (Output.enable_ansi_colors and strings.eqlComptime(name, "FORCE_COLOR")); + } + + pub fn convertToType(ctx: js.JSContextRef, obj: js.JSObjectRef, kind: js.JSType, exception: js.ExceptionRef) callconv(.C) js.JSValueRef { + _ = ctx; + _ = obj; + _ = kind; + _ = exception; + return obj; + } + + pub fn getPropertyNames( + _: js.JSContextRef, + _: js.JSObjectRef, + props: js.JSPropertyNameAccumulatorRef, + ) callconv(.C) void { + var iter = VirtualMachine.vm.bundler.env.map.iter(); + + while (iter.next()) |item| { + const str = item.key_ptr.*; + js.JSPropertyNameAccumulatorAddName(props, js.JSStringCreateStatic(str.ptr, str.len)); + } + } +}; diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig index b6f96b082..da0731efa 100644 --- a/src/javascript/jsc/api/router.zig +++ b/src/javascript/jsc/api/router.zig @@ -450,7 +450,7 @@ pub fn getScriptSrcString( // instead, we just store a boolean saying whether we should generate this whenever the script is requested // this is kind of bad. we should consider instead a way to inline the contents of the script. if (client_framework_enabled) { - JavaScript.Bun.getPublicPath( + JSC.API.Bun.getPublicPath( Bundler.ClientEntryPoint.generateEntryPointPath( &entry_point_tempbuf, Fs.PathName.init(file_path), @@ -460,7 +460,7 @@ pub fn getScriptSrcString( writer, ); } else { - JavaScript.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, Writer, writer); + JSC.API.Bun.getPublicPath(file_path, VirtualMachine.vm.origin, Writer, writer); } } diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index ccdded85c..7a4c1eb0a 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -1252,7 +1252,7 @@ pub const ZigConsoleClient = struct { // Temporary workaround // console.log(process.env) shows up as [class JSCallbackObject] // We want to print it like an object - if (CAPI.JSValueIsObjectOfClass(globalThis.ref(), value.asObjectRef(), JSC.Bun.EnvironmentVariables.Class.get().?[0])) { + if (CAPI.JSValueIsObjectOfClass(globalThis.ref(), value.asObjectRef(), JSC.API.Bun.EnvironmentVariables.Class.get().?[0])) { return .{ .tag = .Object, .cell = js_type, @@ -2472,7 +2472,7 @@ comptime { @export(ErrorCode.JSErrorObject, .{ .name = "Zig_ErrorCodeJSErrorObject" }); } -const Bun = @import("../javascript.zig").Bun; +const Bun = @import("../api/bun.zig"); pub const BunTimer = Bun.Timer; pub const Formatter = ZigConsoleClient.Formatter; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 594525cc3..072f685b3 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -83,6 +83,7 @@ const JSFunction = @import("../../jsc.zig").JSFunction; const Config = @import("./config.zig"); const URL = @import("../../url.zig").URL; const Transpiler = @import("./api/transpiler.zig"); +const Bun = JSC.API.Bun; pub const GlobalClasses = [_]type{ Request.Class, Response.Class, @@ -111,1410 +112,6 @@ const Blob = @import("../../blob.zig"); pub const Buffer = MarkedArrayBuffer; const Lock = @import("../../lock.zig").Lock; -pub const Bun = struct { - threadlocal var css_imports_list_strings: [512]ZigString = undefined; - threadlocal var css_imports_list: [512]Api.StringPointer = undefined; - threadlocal var css_imports_list_tail: u16 = 0; - threadlocal var css_imports_buf: std.ArrayList(u8) = undefined; - threadlocal var css_imports_buf_loaded: bool = false; - - threadlocal var routes_list_strings: [1024]ZigString = undefined; - - pub fn onImportCSS( - resolve_result: *const Resolver.Result, - import_record: *ImportRecord, - origin: URL, - ) void { - if (!css_imports_buf_loaded) { - css_imports_buf = std.ArrayList(u8).initCapacity( - VirtualMachine.vm.allocator, - import_record.path.text.len, - ) catch unreachable; - css_imports_buf_loaded = true; - } - - var writer = css_imports_buf.writer(); - const offset = css_imports_buf.items.len; - css_imports_list[css_imports_list_tail] = .{ - .offset = @truncate(u32, offset), - .length = 0, - }; - getPublicPath(resolve_result.path_pair.primary.text, origin, @TypeOf(writer), writer); - const length = css_imports_buf.items.len - offset; - css_imports_list[css_imports_list_tail].length = @truncate(u32, length); - css_imports_list_tail += 1; - } - - pub fn flushCSSImports() void { - if (css_imports_buf_loaded) { - css_imports_buf.clearRetainingCapacity(); - css_imports_list_tail = 0; - } - } - - pub fn getCSSImports() []ZigString { - var i: u16 = 0; - const tail = css_imports_list_tail; - while (i < tail) : (i += 1) { - ZigString.fromStringPointer(css_imports_list[i], css_imports_buf.items, &css_imports_list_strings[i]); - } - return css_imports_list_strings[0..tail]; - } - - pub fn inspect( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (arguments.len == 0) - return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); - - for (arguments) |arg| { - JSC.C.JSValueProtect(ctx, arg); - } - defer { - for (arguments) |arg| { - JSC.C.JSValueUnprotect(ctx, arg); - } - } - - // very stable memory address - var array = MutableString.init(getAllocator(ctx), 0) catch unreachable; - var buffered_writer_ = MutableString.BufferedWriter{ .context = &array }; - var buffered_writer = &buffered_writer_; - - var writer = buffered_writer.writer(); - const Writer = @TypeOf(writer); - // we buffer this because it'll almost always be < 4096 - // when it's under 4096, we want to avoid the dynamic allocation - ZigConsoleClient.format( - .Debug, - ctx.ptr(), - @ptrCast([*]const JSValue, arguments.ptr), - arguments.len, - Writer, - Writer, - writer, - false, - false, - false, - ); - - // when it's a small thing, rely on GC to manage the memory - if (writer.context.pos < 2048 and array.list.items.len == 0) { - var slice = writer.context.buffer[0..writer.context.pos]; - if (slice.len == 0) { - return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); - } - - var zig_str = ZigString.init(slice).withEncoding(); - return zig_str.toValueGC(ctx.ptr()).asObjectRef(); - } - - // when it's a big thing, we will manage it - { - writer.context.flush() catch {}; - var slice = writer.context.context.toOwnedSlice(); - - var zig_str = ZigString.init(slice).withEncoding(); - if (!zig_str.isUTF8()) { - return zig_str.toExternalValue(ctx.ptr()).asObjectRef(); - } else { - return zig_str.toValueGC(ctx.ptr()).asObjectRef(); - } - } - } - - pub fn registerMacro( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - if (arguments.len != 2 or !js.JSValueIsNumber(ctx, arguments[0])) { - JSError(getAllocator(ctx), "Internal error registering macros: invalid args", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - // TODO: make this faster - const id = @truncate(i32, @floatToInt(i64, js.JSValueToNumber(ctx, arguments[0], exception))); - if (id == -1 or id == 0) { - JSError(getAllocator(ctx), "Internal error registering macros: invalid id", .{}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - if (!js.JSValueIsObject(ctx, arguments[1]) or !js.JSObjectIsFunction(ctx, arguments[1])) { - JSError(getAllocator(ctx), "Macro must be a function. Received: {s}", .{@tagName(js.JSValueGetType(ctx, arguments[1]))}, ctx, exception); - return js.JSValueMakeUndefined(ctx); - } - - var get_or_put_result = VirtualMachine.vm.macros.getOrPut(id) catch unreachable; - if (get_or_put_result.found_existing) { - js.JSValueUnprotect(ctx, get_or_put_result.value_ptr.*); - } - - js.JSValueProtect(ctx, arguments[1]); - get_or_put_result.value_ptr.* = arguments[1]; - - return js.JSValueMakeUndefined(ctx); - } - - pub fn getCWD( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.bundler.fs.top_level_dir).toValue(ctx.ptr()).asRef(); - } - - pub fn getOrigin( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.origin.origin).toValue(ctx.ptr()).asRef(); - } - - pub fn enableANSIColors( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return js.JSValueMakeBoolean(ctx, Output.enable_ansi_colors); - } - pub fn getMain( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.main).toValue(ctx.ptr()).asRef(); - } - - pub fn getAssetPrefix( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return ZigString.init(VirtualMachine.vm.bundler.options.routes.asset_prefix_path).toValue(ctx.ptr()).asRef(); - } - - pub fn getArgv( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (comptime Environment.isWindows) { - @compileError("argv not supported on windows"); - } - - var argv_list = std.heap.stackFallback(128, getAllocator(ctx)); - var allocator = argv_list.get(); - var argv = allocator.alloc(ZigString, std.os.argv.len) catch unreachable; - defer if (argv.len > 128) allocator.free(argv); - for (std.os.argv) |arg, i| { - argv[i] = ZigString.init(std.mem.span(arg)); - } - - return JSValue.createStringArray(ctx.ptr(), argv.ptr, argv.len, true).asObjectRef(); - } - - pub fn getRoutesDir( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (!VirtualMachine.vm.bundler.options.routes.routes_enabled or VirtualMachine.vm.bundler.options.routes.dir.len == 0) { - return js.JSValueMakeUndefined(ctx); - } - - return ZigString.init(VirtualMachine.vm.bundler.options.routes.dir).toValue(ctx.ptr()).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, ctx.ptr()); - 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(ctx.ptr()); - 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, ctx.ptr()); - 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( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - defer flushCSSImports(); - const styles = getCSSImports(); - if (styles.len == 0) { - return js.JSObjectMakeArray(ctx, 0, null, null); - } - - return JSValue.createStringArray(ctx.ptr(), styles.ptr, styles.len, true).asRef(); - } - - pub fn newPath( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - args: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - const is_windows = args.len == 1 and JSValue.fromRef(args[0]).toBoolean(); - return Node.Path.create(ctx.ptr(), is_windows).asObjectRef(); - } - - pub fn readFileAsStringCallback( - ctx: js.JSContextRef, - buf_z: [:0]const u8, - exception: js.ExceptionRef, - ) js.JSValueRef { - const path = buf_z.ptr[0..buf_z.len]; - var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) 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); - }; - - contents_buf[contents_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]; - - var file = std.fs.cwd().openFileZ(buf_z, .{ .mode = .read_only }) 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 - errdefer 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); - }; - - contents_buf[contents_len] = 0; - - var marked_array_buffer = VirtualMachine.vm.allocator.create(MarkedArrayBuffer) catch unreachable; - marked_array_buffer.* = MarkedArrayBuffer.fromBytes( - contents_buf[0..contents_len], - VirtualMachine.vm.allocator, - .Uint8Array, - ); - - return marked_array_buffer.toJSObjectRef(ctx, exception); - } - - pub fn getRouteFiles( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); - - const router = &VirtualMachine.vm.bundler.router.?; - const list = router.getPublicPaths() catch unreachable; - - for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { - routes_list_strings[i] = ZigString.init(list[i]); - } - - const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); - return ref; - } - - pub fn getRouteNames( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (VirtualMachine.vm.bundler.router == null) return js.JSValueMakeNull(ctx); - - const router = &VirtualMachine.vm.bundler.router.?; - const list = router.getNames() catch unreachable; - - for (routes_list_strings[0..@minimum(list.len, routes_list_strings.len)]) |_, i| { - routes_list_strings[i] = ZigString.init(list[i]); - } - - const ref = JSValue.createStringArray(ctx.ptr(), &routes_list_strings, list.len, true).asRef(); - return ref; - } - - pub fn readFileAsBytes( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var buf: [bun.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]; - const result = readFileAsBytesCallback(ctx, buf_z, exception); - return result; - } - - pub fn readFileAsString( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var buf: [bun.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]; - const result = readFileAsStringCallback(ctx, buf_z, exception); - return result; - } - - pub fn getPublicPath(to: string, origin: URL, comptime Writer: type, writer: Writer) void { - const relative_path = VirtualMachine.vm.bundler.fs.relativeTo(to); - if (origin.isAbsolute()) { - if (strings.hasPrefix(relative_path, "..") or strings.hasPrefix(relative_path, "./")) { - writer.writeAll(origin.origin) catch return; - writer.writeAll("/abs:") catch return; - if (std.fs.path.isAbsolute(to)) { - writer.writeAll(to) catch return; - } else { - writer.writeAll(VirtualMachine.vm.bundler.fs.abs(&[_]string{to})) catch return; - } - } else { - origin.joinWrite( - Writer, - writer, - VirtualMachine.vm.bundler.options.routes.asset_prefix_path, - "", - relative_path, - "", - ) catch return; - } - } else { - writer.writeAll(std.mem.trimLeft(u8, relative_path, "/")) catch unreachable; - } - } - - pub fn sleepSync( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - if (js.JSValueIsNumber(ctx, arguments[0])) { - const seconds = JSValue.fromRef(arguments[0]).asNumber(); - if (seconds > 0 and std.math.isFinite(seconds)) std.time.sleep(@floatToInt(u64, seconds * 1000) * std.time.ns_per_ms); - } - - return js.JSValueMakeUndefined(ctx); - } - - pub fn createNodeFS( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return Node.NodeFSBindings.make( - ctx, - VirtualMachine.vm.node_fs orelse brk: { - VirtualMachine.vm.node_fs = bun.default_allocator.create(Node.NodeFS) catch unreachable; - VirtualMachine.vm.node_fs.?.* = Node.NodeFS{ .async_io = undefined }; - break :brk VirtualMachine.vm.node_fs.?; - }, - ); - } - - pub fn generateHeapSnapshot( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return ctx.ptr().generateHeapSnapshot().asObjectRef(); - } - - pub fn runGC( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - // it should only force cleanup on thread exit - - Global.mimalloc_cleanup(false); - - return ctx.ptr().vm().runGC(arguments.len > 0 and JSValue.fromRef(arguments[0]).toBoolean()).asRef(); - } - - pub fn shrink( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - ctx.ptr().vm().shrinkFootprint(); - return JSValue.jsUndefined().asRef(); - } - - pub fn readAllStdinSync( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var stack = std.heap.stackFallback(2048, getAllocator(ctx)); - var allocator = stack.get(); - - var stdin = std.io.getStdIn(); - var result = stdin.readToEndAlloc(allocator, std.math.maxInt(u32)) catch |err| { - JSError(undefined, "{s} reading stdin", .{@errorName(err)}, ctx, exception); - return null; - }; - var out = ZigString.init(result); - out.detectEncoding(); - return out.toValueGC(ctx.ptr()).asObjectRef(); - } - - var public_path_temp_str: [bun.MAX_PATH_BYTES]u8 = undefined; - - pub fn getPublicPathJS( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var zig_str: ZigString = ZigString.Empty; - JSValue.toZigString(JSValue.fromRef(arguments[0]), &zig_str, ctx.ptr()); - - const to = zig_str.slice(); - - var stream = std.io.fixedBufferStream(&public_path_temp_str); - var writer = stream.writer(); - getPublicPath(to, VirtualMachine.vm.origin, @TypeOf(&writer), &writer); - return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(ctx.ptr()).asObjectRef(); - } - - // pub fn resolvePath( - // _: void, - // ctx: js.JSContextRef, - // _: js.JSObjectRef, - // _: js.JSObjectRef, - // arguments: []const js.JSValueRef, - // _: js.ExceptionRef, - // ) js.JSValueRef { - // if (arguments.len == 0) return ZigString.Empty.toValue(ctx.ptr()).asObjectRef(); - // var zig_str: ZigString = ZigString.Empty; - // JSValue.toZigString(JSValue.fromRef(arguments[0]), &zig_str, ctx.ptr()); - // var buf: [bun.MAX_PATH_BYTES]u8 = undefined; - // var stack = std.heap.stackFallback(32 * @sizeOf(string), VirtualMachine.vm.allocator); - // var allocator = stack.get(); - // var parts = allocator.alloc(string, arguments.len) catch {}; - // defer allocator.free(parts); - - // const to = zig_str.slice(); - // var parts = .{to}; - // const value = ZigString.init(VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf)).toValueGC(ctx.ptr()); - // return value.asObjectRef(); - // } - - pub const Class = NewClass( - void, - .{ - .name = "Bun", - .read_only = true, - .ts = .{ - .module = .{ - .path = "bun.js/router", - .tsdoc = "Filesystem Router supporting dynamic routes, exact routes, catch-all routes, and optional catch-all routes. Implemented in native code and only available with Bun.js.", - }, - }, - }, - .{ - .match = .{ - .rfn = Router.match, - .ts = Router.match_type_definition, - }, - - .sleepSync = .{ - .rfn = sleepSync, - }, - .fetch = .{ - .rfn = Fetch.call, - .ts = d.ts{}, - }, - .getImportedStyles = .{ - .rfn = Bun.getImportedStyles, - .ts = d.ts{ - .name = "getImportedStyles", - .@"return" = "string[]", - }, - }, - .inspect = .{ - .rfn = Bun.inspect, - .ts = d.ts{ - .name = "inspect", - .@"return" = "string", - }, - }, - .getRouteFiles = .{ - .rfn = Bun.getRouteFiles, - .ts = d.ts{ - .name = "getRouteFiles", - .@"return" = "string[]", - }, - }, - ._Path = .{ - .rfn = Bun.newPath, - .ts = d.ts{}, - }, - .getRouteNames = .{ - .rfn = Bun.getRouteNames, - .ts = d.ts{ - .name = "getRouteNames", - .@"return" = "string[]", - }, - }, - .readFile = .{ - .rfn = Bun.readFileAsString, - .ts = d.ts{ - .name = "readFile", - .@"return" = "string", - }, - }, - .readFileBytes = .{ - .rfn = Bun.readFileAsBytes, - .ts = d.ts{ - .name = "readFile", - .@"return" = "Uint8Array", - }, - }, - .getPublicPath = .{ - .rfn = Bun.getPublicPathJS, - .ts = d.ts{ - .name = "getPublicPath", - .@"return" = "string", - }, - }, - .registerMacro = .{ - .rfn = Bun.registerMacro, - .ts = d.ts{ - .name = "registerMacro", - .@"return" = "undefined", - }, - }, - .fs = .{ - .rfn = Bun.createNodeFS, - .ts = d.ts{}, - }, - .jest = .{ - .rfn = @import("./test/jest.zig").Jest.call, - .ts = d.ts{}, - }, - .gc = .{ - .rfn = Bun.runGC, - .ts = d.ts{}, - }, - .generateHeapSnapshot = .{ - .rfn = Bun.generateHeapSnapshot, - .ts = d.ts{}, - }, - .shrink = .{ - .rfn = Bun.shrink, - .ts = d.ts{}, - }, - .readAllStdinSync = .{ - .rfn = Bun.readAllStdinSync, - .ts = d.ts{}, - }, - }, - .{ - .main = .{ - .get = getMain, - .ts = d.ts{ .name = "main", .@"return" = "string" }, - }, - .cwd = .{ - .get = getCWD, - .ts = d.ts{ .name = "cwd", .@"return" = "string" }, - }, - .origin = .{ - .get = getOrigin, - .ts = d.ts{ .name = "origin", .@"return" = "string" }, - }, - .routesDir = .{ - .get = getRoutesDir, - .ts = d.ts{ .name = "routesDir", .@"return" = "string" }, - }, - .assetPrefix = .{ - .get = getAssetPrefix, - .ts = d.ts{ .name = "assetPrefix", .@"return" = "string" }, - }, - .argv = .{ - .get = getArgv, - .ts = d.ts{ .name = "argv", .@"return" = "string[]" }, - }, - .env = .{ - .get = EnvironmentVariables.getter, - }, - .enableANSIColors = .{ - .get = enableANSIColors, - }, - .Transpiler = .{ - .get = getTranspilerConstructor, - .ts = d.ts{ .name = "Transpiler", .@"return" = "Transpiler.prototype" }, - }, - .TOML = .{ - .get = getTOMLObject, - .ts = d.ts{ .name = "TOML", .@"return" = "TOML.prototype" }, - }, - .unsafe = .{ - .get = getUnsafe, - }, - }, - ); - - pub fn getTranspilerConstructor( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return js.JSObjectMake(ctx, Transpiler.TranspilerConstructor.get().?[0], null); - } - - pub fn getTOMLObject( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return js.JSObjectMake(ctx, TOML.Class.get().?[0], null); - } - - pub fn getUnsafe( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return js.JSObjectMake(ctx, Unsafe.Class.get().?[0], null); - } - - pub const Unsafe = struct { - pub const Class = NewClass( - void, - .{ .name = "Unsafe", .read_only = true }, - .{ - .segfault = .{ - .rfn = __debug__doSegfault, - }, - .arrayBufferToString = .{ - .rfn = arrayBufferToString, - }, - }, - .{}, - ); - - // For testing the segfault handler - pub fn __debug__doSegfault( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - _ = ctx; - const Reporter = @import("../../report.zig"); - Reporter.globalError(error.SegfaultTest); - } - - pub fn arrayBufferToString( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - args: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - const array_buffer = JSC.ArrayBuffer.fromTypedArray(ctx, JSC.JSValue.fromRef(args[0]), exception); - switch (array_buffer.typed_array_type) { - .Uint16Array, .Int16Array => { - var zig_str = ZigString.init(""); - zig_str.ptr = @ptrCast([*]const u8, @alignCast(@alignOf([*]align(1) const u16), array_buffer.ptr)); - zig_str.len = array_buffer.len; - zig_str.markUTF16(); - return ZigString.toValue(&zig_str, ctx.ptr()).asObjectRef(); - }, - else => { - return ZigString.init(array_buffer.slice()).toValue(ctx.ptr()).asObjectRef(); - }, - } - } - }; - - // pub const Lockfile = struct { - // const BunLockfile = @import("../../install/install.zig").Lockfile; - // pub const Class = NewClass( - // void, - // .{ - // .name = "Lockfile", - // .read_only = true, - // }, - // .{ - // . = .{ - // .rfn = BunLockfile.load, - // }, - // }, - // .{}, - // ); - - // pub const StaticClass = NewClass( - // void, - // .{ - // .name = "Lockfile", - // .read_only = true, - // }, - // .{ - // .load = .{ - // .rfn = BunLockfile.load, - // }, - // }, - // .{}, - // ); - - // pub fn load( - // // this - // _: void, - // ctx: js.JSContextRef, - // // function - // _: js.JSObjectRef, - // // thisObject - // _: js.JSObjectRef, - // arguments: []const js.JSValueRef, - // exception: js.ExceptionRef, - // ) js.JSValueRef { - // if (arguments.len == 0) { - // JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); - // return null; - // } - - // var lockfile: *BunLockfile = getAllocator(ctx).create(BunLockfile) catch return JSValue.jsUndefined().asRef(); - - // var log = logger.Log.init(default_allocator); - // var args_slice = @ptrCast([*]const JSValue, arguments.ptr)[0..arguments.len]; - - // var arguments_slice = Node.ArgumentsSlice.init(args_slice); - // var path_or_buffer = Node.PathLike.fromJS(ctx, &arguments_slice, exception) orelse { - // getAllocator(ctx).destroy(lockfile); - // JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); - // return null; - // }; - - // const load_from_disk_result = switch (path_or_buffer) { - // Node.PathLike.Tag.string => lockfile.loadFromDisk(getAllocator(ctx), &log, path_or_buffer.string), - // Node.PathLike.Tag.buffer => lockfile.loadFromBytes(getAllocator(ctx), path_or_buffer.buffer.slice(), &log), - // else => { - // getAllocator(ctx).destroy(lockfile); - // JSError(undefined, "Expected file path string or buffer", .{}, ctx, exception); - // return null; - // } - // }; - - // switch (load_from_disk_result) { - // .err => |cause| { - // defer getAllocator(ctx).destroy(lockfile); - // switch (cause.step) { - // .open_file => { - // JSError(undefined, "error opening lockfile: {s}", .{ - // @errorName(cause.value), - // }, ctx, exception); - // return null; - // }, - // .parse_file => { - // JSError(undefined, "error parsing lockfile: {s}", .{ - // @errorName(cause.value), - // }, ctx, exception); - // return null; - // }, - // .read_file => { - // JSError(undefined, "error reading lockfile: {s}", .{ - // @errorName(cause.value), - // }, ctx, exception); - // return null; - // }, - // } - // }, - // .ok => { - - // }, - // } - // } - // }; - - pub const TOML = struct { - const TOMLParser = @import("../../toml/toml_parser.zig").TOML; - pub const Class = NewClass( - void, - .{ - .name = "TOML", - .read_only = true, - }, - .{ - .parse = .{ - .rfn = TOML.parse, - }, - }, - .{}, - ); - - pub fn parse( - // this - _: void, - ctx: js.JSContextRef, - // function - _: js.JSObjectRef, - // thisObject - _: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef { - var arena = std.heap.ArenaAllocator.init(getAllocator(ctx)); - var allocator = arena.allocator(); - defer arena.deinit(); - var log = logger.Log.init(default_allocator); - var input_str = ZigString.init(""); - JSValue.fromRef(arguments[0]).toZigString(&input_str, ctx.ptr()); - var needs_deinit = false; - var input = input_str.slice(); - if (input_str.is16Bit()) { - input = std.fmt.allocPrint(allocator, "{}", .{input_str}) catch unreachable; - needs_deinit = true; - } - var source = logger.Source.initPathString("input.toml", input); - var parse_result = TOMLParser.parse(&source, &log, allocator) catch { - exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to parse toml").asObjectRef(); - return null; - }; - - // for now... - var buffer_writer = try js_printer.BufferWriter.init(allocator); - var writer = js_printer.BufferPrinter.init(buffer_writer); - _ = js_printer.printJSON(*js_printer.BufferPrinter, &writer, parse_result, &source) catch { - exception.* = log.toJS(ctx.ptr(), default_allocator, "Failed to print toml").asObjectRef(); - return null; - }; - - var slice = writer.ctx.buffer.toOwnedSliceLeaky(); - var out = ZigString.init(slice); - - const out_value = js.JSValueMakeFromJSONString(ctx, out.toJSStringRef()); - return out_value; - } - }; - - pub const Timer = struct { - last_id: i32 = 0, - warned: bool = false, - active: u32 = 0, - timeouts: TimeoutMap = TimeoutMap{}, - - const TimeoutMap = std.AutoArrayHashMapUnmanaged(i32, *Timeout); - - pub fn getNextID() callconv(.C) i32 { - VirtualMachine.vm.timer.last_id += 1; - return VirtualMachine.vm.timer.last_id; - } - - pub const Timeout = struct { - id: i32 = 0, - callback: JSValue, - interval: i32 = 0, - completion: NetworkThread.Completion = undefined, - repeat: bool = false, - io_task: ?*TimeoutTask = null, - cancelled: bool = false, - - pub const TimeoutTask = IOTask(Timeout); - - pub fn run(this: *Timeout, _task: *TimeoutTask) void { - this.io_task = _task; - NetworkThread.global.pool.io.?.timeout( - *Timeout, - this, - onCallback, - &this.completion, - std.time.ns_per_ms * @intCast( - u63, - @maximum( - this.interval, - 1, - ), - ), - ); - } - - pub fn onCallback(this: *Timeout, _: *NetworkThread.Completion, _: NetworkThread.AsyncIO.TimeoutError!void) void { - this.io_task.?.onFinish(); - } - - pub fn then(this: *Timeout, global: *JSGlobalObject) void { - if (!this.cancelled) { - if (this.repeat) { - this.io_task.?.deinit(); - var task = Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, global, this) catch unreachable; - this.io_task = task; - task.schedule(); - } - - _ = JSC.C.JSObjectCallAsFunction(global.ref(), this.callback.asObjectRef(), null, 0, null, null); - - if (this.repeat) - return; - } - - this.clear(global); - } - - pub fn clear(this: *Timeout, global: *JSGlobalObject) void { - this.cancelled = true; - JSC.C.JSValueUnprotect(global.ref(), this.callback.asObjectRef()); - _ = VirtualMachine.vm.timer.timeouts.swapRemove(this.id); - if (this.io_task) |task| { - task.deinit(); - } - VirtualMachine.vm.allocator.destroy(this); - VirtualMachine.vm.timer.active -|= 1; - VirtualMachine.vm.active_tasks -|= 1; - } - }; - - fn set( - id: i32, - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - repeat: bool, - ) !void { - if (comptime is_bindgen) unreachable; - var timeout = try VirtualMachine.vm.allocator.create(Timeout); - js.JSValueProtect(globalThis.ref(), callback.asObjectRef()); - timeout.* = Timeout{ .id = id, .callback = callback, .interval = countdown.toInt32(), .repeat = repeat }; - var task = try Timeout.TimeoutTask.createOnJSThread(VirtualMachine.vm.allocator, globalThis, timeout); - VirtualMachine.vm.timer.timeouts.put(VirtualMachine.vm.allocator, id, timeout) catch unreachable; - VirtualMachine.vm.timer.active +|= 1; - VirtualMachine.vm.active_tasks +|= 1; - task.schedule(); - } - - pub fn setTimeout( - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - const id = VirtualMachine.vm.timer.last_id; - VirtualMachine.vm.timer.last_id +%= 1; - - Timer.set(id, globalThis, callback, countdown, false) catch - return JSValue.jsUndefined(); - - return JSValue.jsNumberWithType(i32, id); - } - pub fn setInterval( - globalThis: *JSGlobalObject, - callback: JSValue, - countdown: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - const id = VirtualMachine.vm.timer.last_id; - VirtualMachine.vm.timer.last_id +%= 1; - - Timer.set(id, globalThis, callback, countdown, true) catch - return JSValue.jsUndefined(); - - return JSValue.jsNumberWithType(i32, id); - } - - pub fn clearTimer(id: JSValue, _: *JSGlobalObject) void { - if (comptime is_bindgen) unreachable; - var timer: *Timeout = VirtualMachine.vm.timer.timeouts.get(id.toInt32()) orelse return; - timer.cancelled = true; - } - - pub fn clearTimeout( - globalThis: *JSGlobalObject, - id: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - Timer.clearTimer(id, globalThis); - return JSValue.jsUndefined(); - } - pub fn clearInterval( - globalThis: *JSGlobalObject, - id: JSValue, - ) callconv(.C) JSValue { - if (comptime is_bindgen) unreachable; - Timer.clearTimer(id, globalThis); - return JSValue.jsUndefined(); - } - - const Shimmer = @import("./bindings/shimmer.zig").Shimmer; - - pub const shim = Shimmer("Bun", "Timer", @This()); - pub const name = "Bun__Timer"; - pub const include = ""; - pub const namespace = shim.namespace; - - pub const Export = shim.exportFunctions(.{ - .@"setTimeout" = setTimeout, - .@"setInterval" = setInterval, - .@"clearTimeout" = clearTimeout, - .@"clearInterval" = clearInterval, - .@"getNextID" = getNextID, - }); - - comptime { - @export(setTimeout, .{ .name = Export[0].symbol_name }); - @export(setInterval, .{ .name = Export[1].symbol_name }); - @export(clearTimeout, .{ .name = Export[2].symbol_name }); - @export(clearInterval, .{ .name = Export[3].symbol_name }); - @export(getNextID, .{ .name = Export[4].symbol_name }); - } - }; - - /// EnvironmentVariables is runtime defined. - /// Also, you can't iterate over process.env normally since it only exists at build-time otherwise - // This is aliased to Bun.env - pub const EnvironmentVariables = struct { - pub const Class = NewClass( - void, - .{ - .name = "DotEnv", - .read_only = true, - }, - .{ - .getProperty = .{ - .rfn = getProperty, - }, - .setProperty = .{ - .rfn = setProperty, - }, - .deleteProperty = .{ - .rfn = deleteProperty, - }, - .convertToType = .{ .rfn = convertToType }, - .hasProperty = .{ - .rfn = hasProperty, - }, - .getPropertyNames = .{ - .rfn = getPropertyNames, - }, - .toJSON = .{ - .rfn = toJSON, - .name = "toJSON", - }, - }, - .{}, - ); - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - return js.JSObjectMake(ctx, EnvironmentVariables.Class.get().*, null); - } - - pub const BooleanString = struct { - pub const @"true": string = "true"; - pub const @"false": string = "false"; - }; - - pub fn getProperty( - ctx: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - if (VirtualMachine.vm.bundler.env.map.get(name)) |value| { - return ZigString.toRef(value, ctx.ptr()); - } - - if (Output.enable_ansi_colors) { - // https://github.com/chalk/supports-color/blob/main/index.js - if (strings.eqlComptime(name, "FORCE_COLOR")) { - return ZigString.toRef(BooleanString.@"true", ctx.ptr()); - } - } - - return js.JSValueMakeUndefined(ctx); - } - - pub fn toJSON( - _: void, - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var map = VirtualMachine.vm.bundler.env.map.map; - var keys = map.keys(); - var values = map.values(); - const StackFallback = std.heap.StackFallbackAllocator(32 * 2 * @sizeOf(ZigString)); - var stack = StackFallback{ - .buffer = undefined, - .fallback_allocator = bun.default_allocator, - .fixed_buffer_allocator = undefined, - }; - var allocator = stack.get(); - var key_strings_ = allocator.alloc(ZigString, keys.len * 2) catch unreachable; - var key_strings = key_strings_[0..keys.len]; - var value_strings = key_strings_[keys.len..]; - - for (keys) |key, i| { - key_strings[i] = ZigString.init(key); - key_strings[i].detectEncoding(); - value_strings[i] = ZigString.init(values[i]); - value_strings[i].detectEncoding(); - } - - var result = JSValue.fromEntries(ctx.ptr(), key_strings.ptr, value_strings.ptr, keys.len, false).asObjectRef(); - allocator.free(key_strings_); - return result; - // } - // ZigConsoleClient.Formatter.format(this: *Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) - } - - pub fn deleteProperty( - _: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - _ = VirtualMachine.vm.bundler.env.map.map.swapRemove(name); - return true; - } - - pub fn setProperty( - ctx: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - value: js.JSValueRef, - exception: js.ExceptionRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - var ptr = js.JSStringGetCharacters8Ptr(propertyName); - var name = ptr[0..len]; - var val = ZigString.init(""); - JSValue.fromRef(value).toZigString(&val, ctx.ptr()); - if (exception.* != null) return false; - var result = std.fmt.allocPrint(VirtualMachine.vm.allocator, "{}", .{val}) catch unreachable; - VirtualMachine.vm.bundler.env.map.put(name, result) catch unreachable; - - return true; - } - - pub fn hasProperty( - _: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - ) callconv(.C) bool { - const len = js.JSStringGetLength(propertyName); - const ptr = js.JSStringGetCharacters8Ptr(propertyName); - const name = ptr[0..len]; - return VirtualMachine.vm.bundler.env.map.get(name) != null or (Output.enable_ansi_colors and strings.eqlComptime(name, "FORCE_COLOR")); - } - - pub fn convertToType(ctx: js.JSContextRef, obj: js.JSObjectRef, kind: js.JSType, exception: js.ExceptionRef) callconv(.C) js.JSValueRef { - _ = ctx; - _ = obj; - _ = kind; - _ = exception; - return obj; - } - - pub fn getPropertyNames( - _: js.JSContextRef, - _: js.JSObjectRef, - props: js.JSPropertyNameAccumulatorRef, - ) callconv(.C) void { - var iter = VirtualMachine.vm.bundler.env.map.iter(); - - while (iter.next()) |item| { - const str = item.key_ptr.*; - js.JSPropertyNameAccumulatorAddName(props, js.JSStringCreateStatic(str.ptr, str.len)); - } - } - }; -}; - pub const OpaqueCallback = fn (current: ?*anyopaque) callconv(.C) void; pub fn OpaqueWrap(comptime Context: type, comptime Function: fn (this: *Context) void) OpaqueCallback { return struct { @@ -2166,7 +763,7 @@ pub const VirtualMachine = struct { }; } - pub fn refCountedString(this: *VirtualMachine, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString { + pub fn refCountedStringWithWasNew(this: *VirtualMachine, new: *bool, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString { const hash = hash_ orelse JSC.RefString.computeHash(input_); var entry = this.ref_strings.getOrPut(hash) catch unreachable; @@ -2187,10 +784,15 @@ pub const VirtualMachine = struct { }; entry.value_ptr.* = ref; } - + new.* = !entry.found_existing; return entry.value_ptr.*; } + pub fn refCountedString(this: *VirtualMachine, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString { + var _was_new = false; + return this.refCountedStringWithWasNew(&_was_new, input_, hash_, comptime dupe); + } + pub fn preflush(this: *VirtualMachine) void { // We flush on the next tick so that if there were any errors you can still see them this.blobs.?.temporary.reset() catch {}; @@ -2534,7 +1136,13 @@ pub const VirtualMachine = struct { path: string, }; - fn _resolve(ret: *ResolveFunctionResult, _: *JSGlobalObject, specifier: string, source: string) !void { + fn _resolve( + ret: *ResolveFunctionResult, + _: *JSGlobalObject, + specifier: string, + source: string, + comptime is_a_file_path: bool, + ) !void { std.debug.assert(VirtualMachine.vm_loaded); // macOS threadlocal vars are very slow // we won't change threads in this function @@ -2573,7 +1181,13 @@ pub const VirtualMachine = struct { const is_special_source = strings.eqlComptime(source, main_file_name) or js_ast.Macro.isMacroPath(source); const result = try jsc_vm.bundler.resolver.resolve( - if (!is_special_source) Fs.PathName.init(source).dirWithTrailingSlash() else jsc_vm.bundler.fs.top_level_dir, + if (!is_special_source) + if (is_a_file_path) + Fs.PathName.init(source).dirWithTrailingSlash() + else + source + else + jsc_vm.bundler.fs.top_level_dir, specifier, .stmt, ); @@ -2637,10 +1251,19 @@ pub const VirtualMachine = struct { vm.enqueueTask(Task.init(microtask)); } + + pub fn resolveForAPI(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { + resolveMaybeNeedsTrailingSlash(res, global, specifier, source, false); + } + pub fn resolve(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString) void { + resolveMaybeNeedsTrailingSlash(res, global, specifier, source, true); + } + + pub fn resolveMaybeNeedsTrailingSlash(res: *ErrorableZigString, global: *JSGlobalObject, specifier: ZigString, source: ZigString, comptime is_a_file_path: bool) void { var result = ResolveFunctionResult{ .path = "", .result = null }; - _resolve(&result, global, specifier.slice(), source.slice()) catch |err| { + _resolve(&result, global, specifier.slice(), source.slice(), is_a_file_path) catch |err| { // This should almost always just apply to dynamic imports const printed = ResolveError.fmt( diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index d45ed107e..52dce05ef 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -1670,9 +1670,12 @@ pub const Blob = struct { return true; const prev_content_type = this.content_type; - defer if (this.content_type_allocated) bun.default_allocator.free(prev_content_type); - var content_type_buf = getAllocator(ctx).alloc(u8, slice.len) catch unreachable; - this.content_type = strings.copyLowercase(slice, content_type_buf); + { + defer if (this.content_type_allocated) bun.default_allocator.free(prev_content_type); + var content_type_buf = getAllocator(ctx).alloc(u8, slice.len) catch unreachable; + this.content_type = strings.copyLowercase(slice, content_type_buf); + } + this.content_type_allocated = true; return true; } diff --git a/src/javascript/jsc/webcore/url.zig b/src/javascript/jsc/webcore/url.zig index e3251e65f..0d767fcd3 100644 --- a/src/javascript/jsc/webcore/url.zig +++ b/src/javascript/jsc/webcore/url.zig @@ -6,7 +6,7 @@ const MimeType = @import("../../../http.zig").MimeType; const ZigURL = @import("../../../url.zig").URL; const HTTPClient = @import("http"); const NetworkThread = HTTPClient.NetworkThread; - +const bun = @import("../../global.zig"); const JSC = @import("../../../jsc.zig"); const js = JSC.C; @@ -131,8 +131,9 @@ pub const DOMURL = struct { var input = value.toSlice(globalThis, bun.default_allocator); defer input.deinit(); const buf = input.slice(); - const host_len = copy.parseHost(buf) orelse return false; + _ = copy.parseHost(buf) orelse return false; var temp_clone = std.fmt.allocPrint("{}://{s}/{s}", .{ this.url.displayProtocol(), copy.displayHost(), strings.trimLeadingChar(this.url.pathname, '/') }) catch return false; + this.url = URL.parse(temp_clone); this.m_string = JSC.VirtualMachine.vm.refCountedString(temp_clone, null, false); if (this.m_string.ptr != temp_clone.ptr) { bun.default_allocator.free(temp_clone); diff --git a/src/jsc.zig b/src/jsc.zig index ff18d5818..cd1744f21 100644 --- a/src/jsc.zig +++ b/src/jsc.zig @@ -21,6 +21,8 @@ pub const Cloudflare = struct { pub const Jest = @import("./javascript/jsc/test/jest.zig"); pub const API = struct { pub const Transpiler = @import("./javascript/jsc/api/transpiler.zig"); + pub const Bun = @import("./javascript/jsc/api/bun.zig"); + pub const Router = @import("./javascript/jsc/api/router.zig"); }; pub const Node = struct { pub usingnamespace @import("./javascript/jsc/node/types.zig"); |