allow template literals in bun macros

This commit is contained in:
RiskyMH
2025-08-06 01:27:54 +10:00
parent 806d6c156f
commit 9cc1bb4397
3 changed files with 86 additions and 2 deletions

View File

@@ -579,8 +579,55 @@ pub const Runner = struct {
out.* = value;
}
},
.e_template => {
@panic("TODO: support template literals in macros");
.e_template => |template| {
// For tagged template literals, the first argument is an array of template strings
// followed by the interpolated values as separate arguments
const parts_count = template.parts.len;
const total_args = 1 + parts_count + @as(usize, @intFromBool(javascript_object != .zero));
js_args = try allocator.alloc(jsc.JSValue, total_args);
js_processed_args_len = total_args;
var template_strings = try allocator.alloc(jsc.JSValue, parts_count + 1);
defer allocator.free(template_strings);
var head_str = switch (template.head) {
.cooked => |str| str,
.raw => |raw| E.String.init(raw),
};
template_strings[0] = try head_str.toJS(allocator, globalObject);
template_strings[0].protect();
for (template.parts, 1..) |part, i| {
var tail_str = switch (part.tail) {
.cooked => |str| str,
.raw => |raw| E.String.init(raw),
};
template_strings[i] = try tail_str.toJS(allocator, globalObject);
template_strings[i].protect();
}
js_args[0] = try jsc.JSArray.create(globalObject, template_strings);
js_args[0].protect();
for (template_strings) |str| {
str.unprotect();
}
for (template.parts, 0..) |part, i| {
const expr = part.value;
const value = expr.toJS(
allocator,
globalObject,
) catch |e| {
for (js_args[0 .. i + 1]) |arg| {
arg.unprotect();
}
js_processed_args_len = i + 1;
return e;
};
value.protect();
js_args[i + 1] = value;
}
},
else => {
@panic("Unexpected caller type");

View File

@@ -9,6 +9,9 @@ import defaultMacro, {
identity as identity1,
identity as identity2,
ireturnapromise,
simpleTag,
interpolateTag,
objectTag,
} from "./macro.ts" assert { type: "macro" };
import * as macros from "./macro.ts" assert { type: "macro" };
@@ -118,6 +121,15 @@ test("namespace import", () => {
expect(macros.escape()).toBe("\\\f\n\r\t\v\0'\"`$\x00\x0B\x0C");
});
test("varables", () => {
const a = "A";
expect(identity(a)).toBe("A");
});
test("template string basic", () => {
expect(identity(`A`)).toBe("A");
});
// test("template string ascii", () => {
// expect(identity(`A${""}`)).toBe("A");
// });
@@ -129,3 +141,15 @@ test("namespace import", () => {
test("ireturnapromise", async () => {
expect(await ireturnapromise()).toEqual("aaa");
});
test("template literals with macros", () => {
expect(simpleTag`World`).toBe("Hello World!");
expect(interpolateTag`Alice${100}`).toBe("User Alice has 100 points");
expect(objectTag`${{ name: "Bob", age: 25 }}`).toBe("Name: Bob, Age: 25");
});
test("template literals with namespace import", () => {
expect(macros.simpleTag`Test`).toBe("Hello Test!");
expect(macros.interpolateTag`Jane${50}`).toBe("User Jane has 50 points");
expect(macros.objectTag`${{ name: "Alice", age: 30 }}`).toBe("Name: Alice, Age: 30");
});

View File

@@ -23,3 +23,16 @@ export async function ireturnapromise() {
setTimeout(() => resolve("aaa"), 100);
return promise;
}
export function simpleTag(strings: TemplateStringsArray, ..._values: any[]) {
return `Hello ${strings[0]}!`;
}
export function interpolateTag(strings: TemplateStringsArray, ...values: any[]) {
return `User ${strings[0]} has ${values[0]} points`;
}
export function objectTag(_strings: TemplateStringsArray, ...values: any[]) {
const obj = values[0];
return `Name: ${obj.name}, Age: ${obj.age}`;
}