aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc')
-rw-r--r--src/javascript/jsc/api/transpiler.zig616
-rw-r--r--src/javascript/jsc/base.zig10
-rw-r--r--src/javascript/jsc/bindings/bindings.zig28
-rw-r--r--src/javascript/jsc/javascript.zig17
-rw-r--r--src/javascript/jsc/test/jest.zig42
5 files changed, 707 insertions, 6 deletions
diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig
new file mode 100644
index 000000000..07d07763e
--- /dev/null
+++ b/src/javascript/jsc/api/transpiler.zig
@@ -0,0 +1,616 @@
+const std = @import("std");
+const Api = @import("../../../api/schema.zig").Api;
+const FilesystemRouter = @import("../../../router.zig");
+const http = @import("../../../http.zig");
+const JavaScript = @import("../javascript.zig");
+const QueryStringMap = @import("../../../query_string_map.zig").QueryStringMap;
+const CombinedScanner = @import("../../../query_string_map.zig").CombinedScanner;
+const _global = @import("../../../global.zig");
+const string = _global.string;
+const JSC = @import("javascript_core");
+const js = JSC.C;
+const WebCore = @import("../webcore/response.zig");
+const Bundler = @import("../../../bundler.zig");
+const options = @import("../../../options.zig");
+const VirtualMachine = JavaScript.VirtualMachine;
+const ScriptSrcStream = std.io.FixedBufferStream([]u8);
+const ZigString = JSC.ZigString;
+const Fs = @import("../../../fs.zig");
+const Base = @import("../base.zig");
+const getAllocator = Base.getAllocator;
+const JSObject = JSC.JSObject;
+const JSError = Base.JSError;
+const JSValue = JSC.JSValue;
+const JSGlobalObject = JSC.JSGlobalObject;
+const strings = @import("strings");
+const NewClass = Base.NewClass;
+const To = Base.To;
+const Request = WebCore.Request;
+const d = Base.d;
+const FetchEvent = WebCore.FetchEvent;
+const MacroMap = @import("../../../resolver/package_json.zig").MacroMap;
+const TSConfigJSON = @import("../../../resolver/tsconfig_json.zig").TSConfigJSON;
+const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON;
+const logger = @import("../../../logger.zig");
+const Loader = options.Loader;
+const Platform = options.Platform;
+const JSAst = @import("../../../js_ast.zig");
+const Transpiler = @This();
+const JSParser = @import("../../../js_parser.zig");
+const ScanPassResult = JSParser.ScanPassResult;
+
+bundler: Bundler.Bundler,
+arena: std.heap.ArenaAllocator,
+transpiler_options: TranspilerOptions,
+scan_pass_result: ScanPassResult,
+
+pub const Class = NewClass(
+ Transpiler,
+ .{ .name = "Transpiler" },
+ .{
+ .scanImports = .{
+ .rfn = scanImports,
+ },
+ .scan = .{
+ .rfn = scan,
+ },
+ .finalize = finalize,
+ },
+ .{},
+);
+
+pub const TranspilerConstructor = NewClass(
+ void,
+ .{ .name = "Transpiler" },
+ .{
+ .constructor = .{ .rfn = constructor },
+ },
+ .{},
+);
+
+const default_transform_options: Api.TransformOptions = brk: {
+ var opts = std.mem.zeroes(Api.TransformOptions);
+ opts.disable_hmr = true;
+ opts.platform = Api.Platform.browser;
+ opts.serve = false;
+
+ break :brk opts;
+};
+
+const TranspilerOptions = struct {
+ transform: Api.TransformOptions = default_transform_options,
+ default_loader: options.Loader = options.Loader.jsx,
+ macro_map: MacroMap = MacroMap{},
+ tsconfig: ?*TSConfigJSON = null,
+ tsconfig_buf: []const u8 = "",
+ macros_buf: []const u8 = "",
+ log: logger.Log,
+};
+
+fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allocator, args: *JSC.Node.ArgumentsSlice, exception: JSC.C.ExceptionRef) TranspilerOptions {
+ var globalThis = ctx.ptr();
+ const object = args.next() orelse return TranspilerOptions{ .log = logger.Log.init(temp_allocator) };
+ if (object.isUndefinedOrNull()) return TranspilerOptions{ .log = logger.Log.init(temp_allocator) };
+
+ args.eat();
+ var allocator = args.arena.allocator();
+
+ var transpiler = TranspilerOptions{
+ .default_loader = .jsx,
+ .transform = default_transform_options,
+ .log = logger.Log.init(allocator),
+ };
+ transpiler.log.level = .warn;
+
+ if (!object.isObject()) {
+ JSC.throwInvalidArguments("Expected an object", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ if (object.getIfPropertyExists(ctx.ptr(), "define")) |define| {
+ define: {
+ if (define.isUndefinedOrNull()) {
+ break :define;
+ }
+
+ if (!define.isObject()) {
+ JSC.throwInvalidArguments("define must be an object", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ var array = JSC.C.JSObjectCopyPropertyNames(globalThis.ref(), define.asObjectRef());
+ defer JSC.C.JSPropertyNameArrayRelease(array);
+ const count = JSC.C.JSPropertyNameArrayGetCount(array);
+ var map_entries = temp_allocator.alloc([]u8, count * 2) catch unreachable;
+ var names = map_entries[0..count];
+
+ var values = map_entries[count..];
+
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(
+ array,
+ i,
+ );
+ defer JSC.C.JSStringRelease(property_name_ref);
+ const prop: []const u8 = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)];
+ const property_value: JSC.JSValue = JSC.JSValue.fromRef(
+ JSC.C.JSObjectGetProperty(
+ globalThis.ref(),
+ define.asObjectRef(),
+ property_name_ref,
+ null,
+ ),
+ );
+ const value_type = property_value.jsType();
+
+ if (!value_type.isStringLike()) {
+ JSC.throwInvalidArguments("define \"{s}\" must be a JSON string", .{prop}, ctx, exception);
+ return transpiler;
+ }
+ names[i] = allocator.dupe(u8, prop) catch unreachable;
+ var val = JSC.ZigString.init("");
+ property_value.toZigString(&val, globalThis);
+ if (val.len == 0) {
+ val = JSC.ZigString.init("\"\"");
+ }
+ values[i] = std.fmt.allocPrint(allocator, "{}", .{val}) catch unreachable;
+ }
+ transpiler.transform.define = Api.StringMap{
+ .keys = names,
+ .values = values,
+ };
+ }
+ }
+
+ if (object.get(globalThis, "external")) |external| {
+ external: {
+ if (external.isUndefinedOrNull()) break :external;
+
+ const toplevel_type = external.jsType();
+ if (toplevel_type.isStringLike()) {
+ var zig_str = JSC.ZigString.init("");
+ external.toZigString(&zig_str, globalThis);
+ if (zig_str.len == 0) break :external;
+ var single_external = allocator.alloc(string, 1) catch unreachable;
+ single_external[0] = std.fmt.allocPrint(allocator, "{}", .{external}) catch unreachable;
+ transpiler.transform.external = single_external;
+ } else if (toplevel_type.isArray()) {
+ const count = external.getLengthOfArray(globalThis);
+ if (count == 0) break :external;
+
+ var externals = allocator.alloc(string, count) catch unreachable;
+ var iter = external.arrayIterator(globalThis);
+ var i: usize = 0;
+ while (iter.next()) |entry| {
+ if (!entry.jsType().isStringLike()) {
+ JSC.throwInvalidArguments("external must be a string or string[]", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ var zig_str = JSC.ZigString.init("");
+ entry.toZigString(&zig_str, globalThis);
+ if (zig_str.len == 0) continue;
+ externals[i] = std.fmt.allocPrint(allocator, "{}", .{external}) catch unreachable;
+ i += 1;
+ }
+
+ transpiler.transform.external = externals[0..i];
+ } else {
+ JSC.throwInvalidArguments("external must be a string or string[]", .{}, ctx, exception);
+ return transpiler;
+ }
+ }
+ }
+
+ if (object.get(globalThis, "loader")) |loader| {
+ if (Loader.fromJS(globalThis, loader, exception)) |resolved| {
+ if (!resolved.isJavaScriptLike()) {
+ JSC.throwInvalidArguments("only JavaScript-like loaders supported for now", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ transpiler.default_loader = resolved;
+ }
+
+ if (exception.* != null) {
+ return transpiler;
+ }
+ }
+
+ if (object.get(globalThis, "platform")) |platform| {
+ if (Platform.fromJS(globalThis, platform, exception)) |resolved| {
+ transpiler.transform.platform = resolved.toAPI();
+ }
+
+ if (exception.* != null) {
+ return transpiler;
+ }
+ }
+
+ if (object.get(globalThis, "tsconfig")) |tsconfig| {
+ tsconfig: {
+ if (tsconfig.isUndefinedOrNull()) break :tsconfig;
+ const kind = tsconfig.jsType();
+ var out = JSC.ZigString.init("");
+
+ if (kind.isArray()) {
+ JSC.throwInvalidArguments("tsconfig must be a string or object", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ if (!kind.isStringLike()) {
+ tsconfig.jsonStringify(globalThis, 0, &out);
+ } else {
+ tsconfig.toZigString(&out, globalThis);
+ }
+
+ if (out.len == 0) break :tsconfig;
+ transpiler.tsconfig_buf = std.fmt.allocPrint(allocator, "{}", .{out}) catch unreachable;
+
+ // TODO: JSC -> Ast conversion
+ if (TSConfigJSON.parse(
+ allocator,
+ &transpiler.log,
+ logger.Source.initPathString("tsconfig.json", transpiler.tsconfig_buf),
+ &VirtualMachine.vm.bundler.resolver.caches.json,
+ true,
+ ) catch null) |parsed_tsconfig| {
+ transpiler.tsconfig = parsed_tsconfig;
+ }
+ }
+ }
+
+ if (object.getIfPropertyExists(globalThis, "macro")) |macros| {
+ macros: {
+ if (macros.isUndefinedOrNull()) break :macros;
+ const kind = macros.jsType();
+ const is_object = kind == JSC.JSValue.JSType.Object;
+ if (!(kind.isStringLike() or is_object)) {
+ JSC.throwInvalidArguments("macro must be an object", .{}, ctx, exception);
+ return transpiler;
+ }
+
+ var out: ZigString = ZigString.init("");
+ // TODO: write a converter between JSC types and Bun AST types
+ if (is_object) {
+ macros.jsonStringify(globalThis, 0, &out);
+ } else {
+ macros.toZigString(&out, globalThis);
+ }
+
+ if (out.len == 0) break :macros;
+ transpiler.macros_buf = std.fmt.allocPrint(allocator, "{}", .{out}) catch unreachable;
+ const source = logger.Source.initPathString("macros.json", transpiler.macros_buf);
+ const json = (VirtualMachine.vm.bundler.resolver.caches.json.parseJSON(
+ &transpiler.log,
+ source,
+ allocator,
+ ) catch null) orelse break :macros;
+ transpiler.macro_map = PackageJSON.parseMacrosJSON(allocator, json, &transpiler.log, &source);
+ }
+ }
+
+ return transpiler;
+}
+
+pub fn constructor(
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+) js.JSObjectRef {
+ var temp = std.heap.ArenaAllocator.init(getAllocator(ctx));
+ var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]);
+ defer temp.deinit();
+ const transpiler_options: TranspilerOptions = if (arguments.len > 0)
+ transformOptionsFromJSC(ctx, temp.allocator(), &args, exception)
+ else
+ TranspilerOptions{ .log = logger.Log.init(getAllocator(ctx)) };
+
+ if (exception.* != null) {
+ return null;
+ }
+
+ if ((transpiler_options.log.warnings + transpiler_options.log.errors) > 0) {
+ var out_exception = transpiler_options.log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to create transpiler");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ var log = getAllocator(ctx).create(logger.Log) catch unreachable;
+ log.* = transpiler_options.log;
+ var bundler = Bundler.Bundler.init(
+ getAllocator(ctx),
+ log,
+ transpiler_options.transform,
+ null,
+ JavaScript.VirtualMachine.vm.bundler.env,
+ ) catch |err| {
+ if ((log.warnings + log.errors) > 0) {
+ var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to create transpiler");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ JSC.throwInvalidArguments("Error creating transpiler: {s}", .{@errorName(err)}, ctx, exception);
+ return null;
+ };
+
+ bundler.configureLinkerWithAutoJSX(false);
+ bundler.configureDefines() catch |err| {
+ if ((log.warnings + log.errors) > 0) {
+ var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to load define");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ JSC.throwInvalidArguments("Failed to load define: {s}", .{@errorName(err)}, ctx, exception);
+ return null;
+ };
+
+ var transpiler = getAllocator(ctx).create(Transpiler) catch unreachable;
+ transpiler.* = Transpiler{
+ .transpiler_options = transpiler_options,
+ .bundler = bundler,
+ .arena = args.arena,
+ .scan_pass_result = ScanPassResult.init(getAllocator(ctx)),
+ };
+
+ transpiler.bundler.macro_context = JSAst.Macro.MacroContext.init(&transpiler.bundler);
+ if (transpiler_options.macro_map.count() > 0) {
+ transpiler.bundler.macro_context.?.remap = transpiler_options.macro_map;
+ }
+
+ return Class.make(ctx, transpiler);
+}
+
+pub fn finalize(
+ this: *Transpiler,
+) void {
+ this.bundler.log.deinit();
+ this.scan_pass_result.named_imports.deinit();
+ this.scan_pass_result.import_records.deinit();
+ this.scan_pass_result.used_symbols.deinit();
+
+ // _global.default_allocator.free(this.transpiler_options.tsconfig_buf);
+ // _global.default_allocator.free(this.transpiler_options.macros_buf);
+ this.arena.deinit();
+}
+
+fn getParseResult(this: *Transpiler, allocator: std.mem.Allocator, code: []const u8, loader: ?Loader) ?Bundler.ParseResult {
+ const name = this.transpiler_options.default_loader.stdinName();
+ const source = logger.Source.initPathString(name, code);
+
+ const jsx = if (this.transpiler_options.tsconfig != null)
+ this.transpiler_options.tsconfig.?.mergeJSX(this.bundler.options.jsx)
+ else
+ this.bundler.options.jsx;
+
+ const parse_options = Bundler.Bundler.ParseOptions{
+ .allocator = allocator,
+ .macro_remappings = this.transpiler_options.macro_map,
+ .dirname_fd = 0,
+ .file_descriptor = null,
+ .loader = loader orelse this.transpiler_options.default_loader,
+ .jsx = jsx,
+ .path = source.path,
+ .virtual_source = &source,
+ // .allocator = this.
+ };
+
+ return this.bundler.parse(parse_options, null);
+}
+
+pub fn scan(
+ this: *Transpiler,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+) JSC.C.JSObjectRef {
+ var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]);
+ defer args.arena.deinit();
+ const code_arg = args.next() orelse {
+ JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception);
+ return null;
+ };
+
+ const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), code_arg, exception) orelse {
+ if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception);
+ return null;
+ };
+
+ const code = code_holder.slice();
+ args.eat();
+ const loader: ?Loader = brk: {
+ if (args.next()) |arg| {
+ args.eat();
+ break :brk Loader.fromJS(ctx.ptr(), arg, exception);
+ }
+
+ break :brk null;
+ };
+
+ if (exception.* != null) return null;
+
+ defer {
+ JSAst.Stmt.Data.Store.reset();
+ JSAst.Expr.Data.Store.reset();
+ }
+
+ const parse_result = getParseResult(this, args.arena.allocator(), code, loader) orelse {
+ if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) {
+ var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ JSC.throwInvalidArguments("Failed to parse", .{}, ctx, exception);
+ return null;
+ };
+ defer {
+ if (parse_result.ast.symbol_pool) |symbols| {
+ symbols.release();
+ }
+ }
+
+ if ((this.bundler.log.warnings + this.bundler.log.errors) > 0) {
+ var out_exception = this.bundler.log.toJS(ctx.ptr(), getAllocator(ctx), "Parse error");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ const exports_label = JSC.ZigString.init("exports");
+ const imports_label = JSC.ZigString.init("imports");
+ const named_imports_value = namedImportsToJS(
+ ctx.ptr(),
+ parse_result.ast.import_records,
+ exception,
+ );
+ if (exception.* != null) return null;
+ var named_exports_value = namedExportsToJS(
+ ctx.ptr(),
+ parse_result.ast.named_exports,
+ );
+ return JSC.JSValue.createObject2(ctx.ptr(), &imports_label, &exports_label, named_imports_value, named_exports_value).asObjectRef();
+}
+
+fn namedExportsToJS(global: *JSGlobalObject, named_exports: JSAst.Ast.NamedExports) JSC.JSValue {
+ var named_exports_iter = named_exports.iterator();
+ var stack_fallback = std.heap.stackFallback(@sizeOf(JSC.ZigString) * 32, getAllocator(global.ref()));
+ var allocator = stack_fallback.get();
+ var names = allocator.alloc(
+ JSC.ZigString,
+ named_exports.count(),
+ ) catch unreachable;
+ defer allocator.free(names);
+ var i: usize = 0;
+ while (named_exports_iter.next()) |entry| {
+ names[i] = JSC.ZigString.init(entry.key_ptr.*);
+ i += 1;
+ }
+ JSC.ZigString.sortAsc(names[0..i]);
+ return JSC.JSValue.createStringArray(global, names.ptr, names.len, true);
+}
+
+const ImportRecord = @import("../../../import_record.zig").ImportRecord;
+
+fn namedImportsToJS(
+ global: *JSGlobalObject,
+ import_records: []const ImportRecord,
+ exception: JSC.C.ExceptionRef,
+) JSC.JSValue {
+ var stack_fallback = std.heap.stackFallback(@sizeOf(JSC.C.JSObjectRef) * 32, getAllocator(global.ref()));
+ var allocator = stack_fallback.get();
+
+ var i: usize = 0;
+ const path_label = JSC.ZigString.init("path");
+ const kind_label = JSC.ZigString.init("kind");
+ var array_items = allocator.alloc(
+ JSC.C.JSValueRef,
+ import_records.len,
+ ) catch unreachable;
+ defer allocator.free(array_items);
+
+ for (import_records) |record| {
+ if (record.is_internal) continue;
+
+ const path = JSC.ZigString.init(record.path.text).toValueGC(global);
+ const kind = JSC.ZigString.init(record.kind.label()).toValue(global);
+ array_items[i] = JSC.JSValue.createObject2(global, &path_label, &kind_label, path, kind).asObjectRef();
+ i += 1;
+ }
+
+ return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArray(global.ref(), i, array_items.ptr, exception));
+}
+
+pub fn scanImports(
+ this: *Transpiler,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+) JSC.C.JSObjectRef {
+ var args = JSC.Node.ArgumentsSlice.init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]);
+ const code_arg = args.next() orelse {
+ JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception);
+ return null;
+ };
+
+ const code_holder = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), code_arg, exception) orelse {
+ if (exception.* == null) JSC.throwInvalidArguments("Expected a string or Uint8Array", .{}, ctx, exception);
+ return null;
+ };
+ args.eat();
+ const code = code_holder.slice();
+
+ var loader: Loader = this.transpiler_options.default_loader;
+ if (args.next()) |arg| {
+ if (Loader.fromJS(ctx.ptr(), arg, exception)) |_loader| {
+ loader = _loader;
+ }
+ args.eat();
+ }
+
+ if (!loader.isJavaScriptLike()) {
+ JSC.throwInvalidArguments("Only JavaScript-like files support this fast path", .{}, ctx, exception);
+ return null;
+ }
+
+ if (exception.* != null) return null;
+
+ const source = logger.Source.initPathString(loader.stdinName(), code);
+ var bundler = &this.bundler;
+ const jsx = if (this.transpiler_options.tsconfig != null)
+ this.transpiler_options.tsconfig.?.mergeJSX(this.bundler.options.jsx)
+ else
+ this.bundler.options.jsx;
+
+ var opts = JSParser.Parser.Options.init(jsx, loader);
+ opts.macro_context = &this.bundler.macro_context.?;
+ var log = logger.Log.init(getAllocator(ctx));
+ defer log.deinit();
+
+ defer {
+ JSAst.Stmt.Data.Store.reset();
+ JSAst.Expr.Data.Store.reset();
+ }
+
+ bundler.resolver.caches.js.scan(
+ bundler.allocator,
+ &this.scan_pass_result,
+ opts,
+ bundler.options.define,
+ &log,
+ &source,
+ ) catch |err| {
+ defer this.scan_pass_result.reset();
+ if ((log.warnings + log.errors) > 0) {
+ var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to scan imports");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ JSC.throwInvalidArguments("Failed to scan imports: {s}", .{@errorName(err)}, ctx, exception);
+ return null;
+ };
+
+ defer this.scan_pass_result.reset();
+
+ if ((log.warnings + log.errors) > 0) {
+ var out_exception = log.toJS(ctx.ptr(), getAllocator(ctx), "Failed to scan imports");
+ exception.* = out_exception.asObjectRef();
+ return null;
+ }
+
+ const named_imports_value = namedImportsToJS(
+ ctx.ptr(),
+ this.scan_pass_result.import_records.items,
+ exception,
+ );
+ if (exception.* != null) return null;
+ return named_imports_value.asObjectRef();
+}
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index cf2ed1bad..4d61fb33a 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -1263,7 +1263,7 @@ pub fn NewClass(
if (comptime property_name_refs.len > 0) {
comptime var i: usize = 0;
if (!property_name_refs_set) {
- property_name_refs_set =true;
+ property_name_refs_set = true;
inline while (i < property_name_refs.len) : (i += 1) {
property_name_refs[i] = js.JSStringCreateStatic(property_names[i].ptr, property_names[i].len);
}
@@ -1687,7 +1687,8 @@ pub const ArrayBuffer = struct {
return ArrayBuffer{
.byte_len = @truncate(u32, JSC.C.JSObjectGetTypedArrayByteLength(ctx, value.asObjectRef(), exception)),
.offset = @truncate(u32, JSC.C.JSObjectGetTypedArrayByteOffset(ctx, value.asObjectRef(), exception)),
- .ptr = @ptrCast([*]u8, JSC.C.JSObjectGetTypedArrayBytesPtr(ctx, value.asObjectRef(), exception).?),
+ .ptr = @ptrCast([*]u8, JSC.C.JSObjectGetArrayBufferBytesPtr(ctx, value.asObjectRef(), exception) orelse
+ JSC.C.JSObjectGetTypedArrayBytesPtr(ctx, value.asObjectRef(), exception).?),
// TODO
.typed_array_type = js.JSTypedArrayType.kJSTypedArrayTypeUint8Array,
.len = @truncate(u32, JSC.C.JSObjectGetTypedArrayLength(ctx, value.asObjectRef(), exception)),
@@ -1697,7 +1698,8 @@ pub const ArrayBuffer = struct {
pub fn fromArrayBuffer(ctx: JSC.C.JSContextRef, value: JSC.JSValue, exception: JSC.C.ExceptionRef) ArrayBuffer {
var buffer = ArrayBuffer{
.byte_len = @truncate(u32, JSC.C.JSObjectGetArrayBufferByteLength(ctx, value.asObjectRef(), exception)),
- .ptr = @ptrCast([*]u8, JSC.C.JSObjectGetArrayBufferBytesPtr(ctx, value.asObjectRef(), exception).?),
+ .ptr = @ptrCast([*]u8, JSC.C.JSObjectGetArrayBufferBytesPtr(ctx, value.asObjectRef(), exception) orelse
+ JSC.C.JSObjectGetTypedArrayBytesPtr(ctx, value.asObjectRef(), exception).?),
// TODO
.typed_array_type = js.JSTypedArrayType.kJSTypedArrayTypeUint8Array,
.len = 0,
@@ -1804,6 +1806,7 @@ const NodeFS = JSC.Node.NodeFS;
const DirEnt = JSC.Node.DirEnt;
const Stats = JSC.Node.Stats;
const BigIntStats = JSC.Node.BigIntStats;
+const Transpiler = @import("./api/transpiler.zig");
pub const JSPrivateDataPtr = TaggedPointerUnion(.{
ResolveError,
BuildError,
@@ -1824,6 +1827,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{
Stats,
BigIntStats,
DirEnt,
+ Transpiler,
});
pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type {
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index a28717400..4dad9a926 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -107,6 +107,22 @@ pub const ZigString = extern struct {
};
}
+ pub fn sortDesc(slice_: []ZigString) void {
+ std.sort.sort(ZigString, slice_, {}, cmpDesc);
+ }
+
+ pub fn cmpDesc(_: void, a: ZigString, b: ZigString) bool {
+ return strings.cmpStringsDesc(void{}, a.slice(), b.slice());
+ }
+
+ pub fn sortAsc(slice_: []ZigString) void {
+ std.sort.sort(ZigString, slice_, {}, cmpAsc);
+ }
+
+ pub fn cmpAsc(_: void, a: ZigString, b: ZigString) bool {
+ return strings.cmpStringsAsc(void{}, a.slice(), b.slice());
+ }
+
pub fn init(slice_: []const u8) ZigString {
return ZigString{ .ptr = slice_.ptr, .len = slice_.len };
}
@@ -1446,6 +1462,13 @@ pub const JSValue = enum(i64) {
else => false,
};
}
+
+ pub inline fn isArray(this: JSType) bool {
+ return switch (this) {
+ .Array, .DerivedArray => true,
+ else => false,
+ };
+ }
};
pub inline fn cast(ptr: anytype) JSValue {
@@ -1735,11 +1758,14 @@ pub const JSValue = enum(i64) {
return cppFn("getIfPropertyExistsImpl", .{ this, global, ptr, len });
}
- pub fn getIfPropertyExists(this: JSValue, global: *JSGlobalObject, property: []const u8) ?JSValue {
+ pub fn get(this: JSValue, global: *JSGlobalObject, property: []const u8) ?JSValue {
const value = getIfPropertyExistsImpl(this, global, property.ptr, @intCast(u32, property.len));
return if (@enumToInt(value) != 0) value else return null;
}
+ /// Alias for getIfPropertyExists
+ pub const getIfPropertyExists = get;
+
pub fn createTypeError(message: *const ZigString, code: *const ZigString, global: *JSGlobalObject) JSValue {
return cppFn("createTypeError", .{ message, code, global });
}
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 64147bc5c..2299e9339 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -77,6 +77,7 @@ const ZigGlobalObject = @import("../../jsc.zig").ZigGlobalObject;
const VM = @import("../../jsc.zig").VM;
const Config = @import("./config.zig");
const URL = @import("../../query_string_map.zig").URL;
+const Transpiler = @import("./api/transpiler.zig");
pub const GlobalClasses = [_]type{
Request.Class,
Response.Class,
@@ -746,9 +747,23 @@ pub const Bun = struct {
.enableANSIColors = .{
.get = enableANSIColors,
},
+ .Transpiler = .{
+ .get = getTranspilerConstructor,
+ .ts = d.ts{ .name = "Transpiler", .@"return" = "Transpiler.prototype" },
+ },
},
);
+ pub fn getTranspilerConstructor(
+ _: void,
+ ctx: js.JSContextRef,
+ _: js.JSValueRef,
+ _: js.JSStringRef,
+ _: js.ExceptionRef,
+ ) js.JSValueRef {
+ return js.JSObjectMake(ctx, Transpiler.TranspilerConstructor.get().?[0], null);
+ }
+
// For testing the segfault handler
pub fn __debug__doSegfault(
_: void,
@@ -1702,7 +1717,7 @@ pub const VirtualMachine = struct {
}
const main_file_name: string = "bun:main";
- threadlocal var errors_stack: [256]*anyopaque = undefined;
+ pub threadlocal var errors_stack: [256]*anyopaque = undefined;
pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void {
var log = logger.Log.init(vm.bundler.allocator);
const spec = specifier.slice();
diff --git a/src/javascript/jsc/test/jest.zig b/src/javascript/jsc/test/jest.zig
index 1398e9058..2ffc8bd01 100644
--- a/src/javascript/jsc/test/jest.zig
+++ b/src/javascript/jsc/test/jest.zig
@@ -365,6 +365,47 @@ pub const Expect = struct {
}
return thisObject;
}
+
+ pub fn toHaveLength(
+ this: *Expect,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ if (arguments.len != 1) {
+ JSC.JSError(
+ getAllocator(ctx),
+ ".toHaveLength() takes 1 argument",
+ .{},
+ ctx,
+ exception,
+ );
+ return js.JSValueMakeUndefined(ctx);
+ }
+ this.scope.tests.items[this.test_id].counter.actual += 1;
+
+ const expected = JSC.JSValue.fromRef(arguments[0]).toU32();
+ const actual = JSC.JSValue.fromRef(this.value).getLengthOfArray(ctx.ptr());
+ if (expected != actual) {
+ JSC.JSError(
+ getAllocator(ctx),
+ "Expected length to equal {d} but received {d}\n Expected: {d}\n Actual: {d}\n",
+ .{
+ expected,
+ actual,
+ expected,
+ actual,
+ },
+ ctx,
+ exception,
+ );
+ return null;
+ }
+ return thisObject;
+ }
+
pub const toHaveBeenCalledTimes = notImplementedFn;
pub const toHaveBeenCalledWith = notImplementedFn;
pub const toHaveBeenLastCalledWith = notImplementedFn;
@@ -373,7 +414,6 @@ pub const Expect = struct {
pub const toHaveReturnedWith = notImplementedFn;
pub const toHaveLastReturnedWith = notImplementedFn;
pub const toHaveNthReturnedWith = notImplementedFn;
- pub const toHaveLength = notImplementedFn;
pub const toHaveProperty = notImplementedFn;
pub const toBeCloseTo = notImplementedFn;
pub const toBeGreaterThan = notImplementedFn;