diff options
-rw-r--r-- | integration/bunjs-only-snippets/macro-check.js | 3 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 31 | ||||
-rw-r--r-- | src/javascript/jsc/api/transpiler.zig | 45 | ||||
-rw-r--r-- | src/javascript/jsc/node/types.zig | 8 |
4 files changed, 67 insertions, 20 deletions
diff --git a/integration/bunjs-only-snippets/macro-check.js b/integration/bunjs-only-snippets/macro-check.js new file mode 100644 index 000000000..2ea3fb8bd --- /dev/null +++ b/integration/bunjs-only-snippets/macro-check.js @@ -0,0 +1,3 @@ +export function keepSecondArgument(args) { + return args.arguments[1]; +} diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index 8480dd091..34ec9f4aa 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -51,15 +51,42 @@ describe("Bun.Transpiler", () => { }); describe("transform", () => { + it("supports macros", async () => { + const out = await transpiler.transform(` + import {keepSecondArgument} from 'macro:${ + import.meta.dir + }/macro-check.js'; + + export default keepSecondArgument("Test failed", "Test passed"); + `); + expect(out.includes("Test failed")).toBe(false); + expect(out.includes("Test passed")).toBe(true); + + // ensure both the import and the macro function call are removed + expect(out.includes("keepSecondArgument")).toBe(false); + }); + + it("sync supports macros", async () => { + const out = transpiler.transformSync(` + import {keepSecondArgument} from 'macro:${ + import.meta.dir + }/macro-check.js'; + + export default keepSecondArgument("Test failed", "Test passed"); + `); + expect(out.includes("Test failed")).toBe(false); + expect(out.includes("Test passed")).toBe(true); + + expect(out.includes("keepSecondArgument")).toBe(false); + }); + it("removes types", () => { expect(code.includes("ActionFunction")).toBe(true); expect(code.includes("LoaderFunction")).toBe(true); - const out = transpiler.transformSync(code); expect(out.includes("ActionFunction")).toBe(false); expect(out.includes("LoaderFunction")).toBe(false); - const { exports } = transpiler.scan(out); expect(exports[0]).toBe("action"); diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig index 0390be154..10d7bf72a 100644 --- a/src/javascript/jsc/api/transpiler.zig +++ b/src/javascript/jsc/api/transpiler.zig @@ -39,7 +39,7 @@ const Transpiler = @This(); const JSParser = @import("../../../js_parser.zig"); const JSPrinter = @import("../../../js_printer.zig"); const ScanPassResult = JSParser.ScanPassResult; - +const Mimalloc = @import("../../../mimalloc_arena.zig"); bundler: Bundler.Bundler, arena: std.heap.ArenaAllocator, transpiler_options: TranspilerOptions, @@ -139,7 +139,6 @@ pub const TransformTask = struct { pub fn run(this: *TransformTask) void { const name = this.loader.stdinName(); const source = logger.Source.initPathString(name, this.input_code.slice()); - const Mimalloc = @import("../../../mimalloc_arena.zig"); JSAst.Stmt.Data.Store.create(_global.default_allocator); JSAst.Expr.Data.Store.create(_global.default_allocator); @@ -299,7 +298,8 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo 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; + // cannot be a temporary because it may be loaded on different threads. + var map_entries = allocator.alloc([]u8, count * 2) catch unreachable; var names = map_entries[0..count]; var values = map_entries[count..]; @@ -729,6 +729,18 @@ pub fn transformSync( }; const code = code_holder.slice(); + JSC.C.JSValueProtect(ctx, arguments[0]); + defer JSC.C.JSValueUnprotect(ctx, arguments[0]); + var arena = Mimalloc.Arena.init() catch unreachable; + this.bundler.setAllocator(arena.allocator()); + var log = logger.Log.init(arena.backingAllocator()); + this.bundler.setLog(&log); + defer { + this.bundler.setLog(&this.transpiler_options.log); + this.bundler.setAllocator(_global.default_allocator); + arena.deinit(); + } + args.eat(); const loader: ?Loader = brk: { if (args.next()) |arg| { @@ -746,7 +758,7 @@ pub fn transformSync( JSAst.Expr.Data.Store.reset(); } - const parse_result = getParseResult(this, args.arena.allocator(), code, loader) orelse { + const parse_result = getParseResult(this, 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(); @@ -767,26 +779,31 @@ pub fn transformSync( exception.* = out_exception.asObjectRef(); return null; } - if (this.buffer_writer == null) { - this.buffer_writer = JSPrinter.BufferWriter.init(_global.default_allocator) catch { + + var buffer_writer = this.buffer_writer orelse brk: { + var writer = JSPrinter.BufferWriter.init(arena.backingAllocator()) catch { JSC.throwInvalidArguments("Failed to create BufferWriter", .{}, ctx, exception); return null; }; - this.buffer_writer.?.buffer.growIfNeeded(code.len) catch unreachable; - this.buffer_writer.?.buffer.list.expandToCapacity(); + writer.buffer.growIfNeeded(code.len) catch unreachable; + writer.buffer.list.expandToCapacity(); + break :brk writer; + }; + + defer { + this.buffer_writer = buffer_writer; } - this.buffer_writer.?.reset(); - var printer = JSPrinter.BufferPrinter.init(this.buffer_writer.?); - const printed = this.bundler.print(parse_result, @TypeOf(printer), printer, .esm_ascii) catch |err| { + buffer_writer.reset(); + var printer = JSPrinter.BufferPrinter.init(buffer_writer); + _ = this.bundler.print(parse_result, @TypeOf(&printer), &printer, .esm_ascii) catch |err| { JSC.JSError(_global.default_allocator, "Failed to print code: {s}", .{@errorName(err)}, ctx, exception); return null; }; // TODO: benchmark if pooling this way is faster or moving is faster - this.buffer_writer = printer.ctx; - this.buffer_writer.?.buffer.list.expandToCapacity(); - var out = JSC.ZigString.init(this.buffer_writer.?.buffer.list.items[0..printed]); + buffer_writer = printer.ctx; + var out = JSC.ZigString.init(buffer_writer.written); out.mark(); return out.toValueGC(ctx.ptr()).asObjectRef(); diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig index 31b467cd4..7e4927e78 100644 --- a/src/javascript/jsc/node/types.zig +++ b/src/javascript/jsc/node/types.zig @@ -151,10 +151,10 @@ pub const StringOrBuffer = union(Tag) { JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject, JSC.JSValue.JSType.Object => { var zig_str = JSC.ZigString.init(""); value.toZigString(&zig_str, global); - if (zig_str.len == 0) { - JSC.throwInvalidArguments("Expected string to have length > 0", .{}, global.ref(), exception); - return null; - } + // if (zig_str.len == 0) { + // JSC.throwInvalidArguments("Expected string to have length > 0", .{}, global.ref(), exception); + // return null; + // } return StringOrBuffer{ .string = zig_str.slice(), |