Files
bun.sh/src/javascript/jsc/api/bun.zig
2022-03-22 04:44:39 -07:00

1676 lines
54 KiB
Zig

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.JSObjectMakeArray(ctx, 0, null, null);
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.JSObjectMakeArray(ctx, 0, null, null);
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, false);
}
fn doResolveWithArgs(
ctx: js.JSContextRef,
specifier: ZigString,
from: ZigString,
exception: js.ExceptionRef,
comptime is_file_path: bool,
) ?JSC.JSValue {
var errorable: ErrorableZigString = undefined;
if (comptime is_file_path) {
VirtualMachine.resolve(
&errorable,
ctx.ptr(),
specifier,
from,
);
} else {
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, true) orelse {
return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0]));
};
return JSC.JSPromise.resolvedPromiseValue(global, value);
}
comptime {
if (!is_bindgen) {
_ = Bun__resolve;
}
}
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 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",
},
.enumerable = false,
},
.fs = .{
.rfn = Bun.createNodeFS,
.ts = d.ts{},
.enumerable = false,
},
.jest = .{
.rfn = @import("../test/jest.zig").Jest.call,
.ts = d.ts{},
.enumerable = false,
},
.gc = .{
.rfn = Bun.runGC,
.ts = d.ts{},
},
.allocUnsafe = .{
.rfn = Bun.allocUnsafe,
.ts = .{},
},
.generateHeapSnapshot = .{
.rfn = Bun.generateHeapSnapshot,
.ts = d.ts{},
},
.shrink = .{
.rfn = Bun.shrink,
.ts = d.ts{},
},
.readAllStdinSync = .{
.rfn = Bun.readAllStdinSync,
.ts = d.ts{},
},
.startServer = .{
.rfn = Bun.startServer,
.ts = d.ts{},
},
.file = .{
.rfn = JSC.WebCore.Blob.constructFile,
.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 startServer(
_: void,
ctx: js.JSContextRef,
_: js.JSObjectRef,
_: js.JSObjectRef,
arguments: []const js.JSValueRef,
_: js.ExceptionRef,
) js.JSValueRef {
var vm = JSC.VirtualMachine.vm;
const handler = if (arguments.len > 0) JSC.JSValue.fromRef(arguments[0]) else JSC.JSValue.zero;
if (handler.isEmpty() or handler.isUndefinedOrNull() or !handler.isCell() or !handler.isCallable(ctx.ptr().vm())) {
Output.prettyWarnln("\"serverless\" export should be a function", .{});
Output.flush();
return JSC.JSValue.jsUndefined().asObjectRef();
}
JSC.C.JSValueProtect(ctx.ptr().ref(), handler.asObjectRef());
var server = JSC.API.Server.init(vm.bundler.options.origin.getPortAuto(), handler, ctx.ptr());
server.listen();
return JSC.JSValue.jsUndefined().asObjectRef();
}
pub fn allocUnsafe(
_: void,
ctx: js.JSContextRef,
_: js.JSObjectRef,
_: js.JSObjectRef,
arguments: []const js.JSValueRef,
exception: js.ExceptionRef,
) js.JSValueRef {
var args = JSC.Node.ArgumentsSlice.from(arguments);
const length = @intCast(
usize,
@minimum(
@maximum(1, (args.nextEat() orelse JSC.JSValue.jsNumber(@as(i32, 1))).toInt32()),
std.math.maxInt(i32),
),
);
var bytes = bun.default_allocator.alloc(u8, length) catch {
JSC.JSError(bun.default_allocator, "OOM! Out of memory", .{}, ctx, exception);
return null;
};
return JSC.MarkedArrayBuffer.fromBytes(
bytes,
bun.default_allocator,
.Uint8Array,
).toJSObjectRef(ctx, null);
}
pub fn getTranspilerConstructor(
_: void,
ctx: js.JSContextRef,
_: js.JSValueRef,
_: js.JSStringRef,
_: js.ExceptionRef,
) js.JSValueRef {
return Transpiler.Constructor.constructor(ctx);
}
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 (comptime JSC.is_bindgen)
unreachable;
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 {
if (comptime JSC.is_bindgen)
unreachable;
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 {
if (!JSC.is_bindgen) {
@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));
}
}
};
export fn Bun__reportError(_: *JSGlobalObject, err: JSC.JSValue) void {
JSC.VirtualMachine.vm.defaultErrorHandler(err, null);
}
comptime {
if (!is_bindgen) {
_ = Bun__reportError;
}
}