Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
d32ba76b46 fix: support Blob and BunFile inputs in HTMLRewriter.transform()
HTMLRewriter.transform() only accepted Response, string, and ArrayBuffer
inputs despite the type definitions and docs promising Blob and BunFile
support. Add Blob detection to the transform_ function so that in-memory
Blobs return a transformed Blob and file-backed Blobs (BunFile) return a
Response (since they require async I/O through the ValueBufferer path).

Closes #17259

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-20 02:40:05 +00:00
2 changed files with 116 additions and 1 deletions

View File

@@ -186,6 +186,47 @@ pub const HTMLRewriter = struct {
return out;
}
// Handle Blob and BunFile inputs by wrapping in a Response and transforming.
// BunFile (file-backed Blob) requires async I/O so we return a Response directly.
// In-memory Blobs return a Blob.
if (response_value.as(jsc.WebCore.Blob)) |blob| {
const body_value = try jsc.WebCore.Body.extract(global, response_value);
const resp = bun.new(Response, Response.init(
.{
.status_code = 200,
},
body_value,
bun.String.empty,
false,
));
if (blob.needsToReadFile()) {
// BunFile requires async I/O via ValueBufferer; return Response directly.
const out = try this.beginTransform(global, resp);
if (out.toError()) |err| {
return global.throwValue(err);
}
return out;
}
defer resp.finalize();
const out_response_value = try this.beginTransform(global, resp);
if (out_response_value.toError()) |err| {
return global.throwValue(err);
}
out_response_value.ensureStillAlive();
var out_response = out_response_value.as(Response) orelse return out_response_value;
var any_blob = out_response.getBodyValue().useAsAnyBlobAllowNonUTF8String();
defer {
_ = Response.js.dangerouslySetPtr(out_response_value, null);
out_response.finalize();
}
const result_blob = jsc.WebCore.Blob.new(any_blob.toBlob(global));
return result_blob.toJS(global);
}
const ResponseKind = enum { string, array_buffer, other };
const kind: ResponseKind = brk: {
if (response_value.isString())
@@ -235,7 +276,7 @@ pub const HTMLRewriter = struct {
}
}
return global.throwInvalidArguments("Expected Response or Body", .{});
return global.throwInvalidArguments("Expected Response, Blob, String, or ArrayBuffer", .{});
}
pub const on = host_fn.wrapInstanceMethod(HTMLRewriter, "on_", false);

View File

@@ -0,0 +1,74 @@
import { expect, test } from "bun:test";
import { tempDir } from "harness";
test("HTMLRewriter.transform supports Blob input", () => {
const html = '<div class="hello">Hello</div><script src="/main.js"></script>';
const blob = new Blob([html], { type: "text/html" });
const tags: string[] = [];
const result = new HTMLRewriter()
.on("*", {
element(element) {
tags.push(element.tagName);
},
})
.transform(blob);
expect(result).toBeInstanceOf(Blob);
expect(tags).toEqual(["div", "script"]);
});
test("HTMLRewriter.transform supports Blob input and modifies content", async () => {
const html = '<div class="old">content</div>';
const blob = new Blob([html], { type: "text/html" });
const result = new HTMLRewriter()
.on("div", {
element(element) {
element.setAttribute("class", "new");
},
})
.transform(blob);
expect(result).toBeInstanceOf(Blob);
const text = await result.text();
expect(text).toBe('<div class="new">content</div>');
});
test("HTMLRewriter.transform supports Bun.file() input", async () => {
using dir = tempDir("html-rewriter-bunfile", {
"index.html": '<h1>Hello</h1><p class="old">World</p>',
});
const file = Bun.file(`${dir}/index.html`);
const result = new HTMLRewriter()
.on("p", {
element(element) {
element.setAttribute("class", "new");
},
})
.transform(file);
// BunFile requires async I/O, so transform returns a Response
expect(result).toBeInstanceOf(Response);
const text = await result.text();
expect(text).toBe('<h1>Hello</h1><p class="new">World</p>');
});
test("HTMLRewriter.transform Blob with element handler reading attributes", () => {
const html = '<script src="/app.js"></script><script src="/vendor.js"></script>';
const blob = new Blob([html]);
const srcs: string[] = [];
new HTMLRewriter()
.on("script", {
element(element) {
const src = element.getAttribute("src");
if (src) srcs.push(src);
},
})
.transform(blob);
expect(srcs).toEqual(["/app.js", "/vendor.js"]);
});