diff options
author | 2022-03-12 02:59:10 -0800 | |
---|---|---|
committer | 2022-03-12 02:59:10 -0800 | |
commit | 7b9312313363e1d63caaef5ec1f98635f2eedd2d (patch) | |
tree | ad616afd80038e9dac049c94fc685344803fc12a | |
parent | d6831cf801a0c9e1e3f60d9c8182636cc6567d3a (diff) | |
download | bun-7b9312313363e1d63caaef5ec1f98635f2eedd2d.tar.gz bun-7b9312313363e1d63caaef5ec1f98635f2eedd2d.tar.zst bun-7b9312313363e1d63caaef5ec1f98635f2eedd2d.zip |
Implement iterator
-rw-r--r-- | integration/bunjs-only-snippets/html-rewriter.test.js | 28 | ||||
-rw-r--r-- | src/javascript/jsc/api/html_rewriter.zig | 153 |
2 files changed, 159 insertions, 22 deletions
diff --git a/integration/bunjs-only-snippets/html-rewriter.test.js b/integration/bunjs-only-snippets/html-rewriter.test.js index 77c14e479..8990bf959 100644 --- a/integration/bunjs-only-snippets/html-rewriter.test.js +++ b/integration/bunjs-only-snippets/html-rewriter.test.js @@ -17,6 +17,34 @@ describe("HTMLRewriter", () => { expect(await output.text()).toBe("<div><blink>it worked!</blink></div>"); }); + it("supports attribute iterator", async () => { + var rewriter = new HTMLRewriter(); + var expected = [ + ["first", ""], + ["second", "alrihgt"], + ["third", "123"], + ["fourth", "5"], + ["fifth", "helloooo"], + ]; + rewriter.on("div", { + element(element2) { + for (let attr of element2.attributes) { + const stack = expected.shift(); + expect(stack[0]).toBe(attr[0]); + expect(stack[1]).toBe(attr[1]); + } + }, + }); + var input = new Response( + '<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>' + ); + var output = rewriter.transform(input); + expect(await output.text()).toBe( + '<div first second="alrihgt" third="123" fourth=5 fifth=helloooo>hello</div>' + ); + expect(expected.length).toBe(0); + }); + it("handles element specific mutations", async () => { // prepend/append let res = new HTMLRewriter() diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig index 3a811b2c1..264b26cbf 100644 --- a/src/javascript/jsc/api/html_rewriter.zig +++ b/src/javascript/jsc/api/html_rewriter.zig @@ -706,7 +706,7 @@ pub fn getterWrap(comptime Container: type, comptime name: string) GetterType(Co exception: js.ExceptionRef, ) js.JSObjectRef { const result: JSValue = @call(.{}, @field(Container, name), .{ this, ctx.ptr() }); - if (result.isError()) { + if (!result.isUndefinedOrNull() and result.isError()) { exception.* = result.asObjectRef(); return null; } @@ -850,6 +850,11 @@ pub const TextChunk = struct { pub const DocType = struct { doctype: ?*LOLHTML.DocType = null, + pub fn finalize(this: *DocType) void { + this.doctype = null; + bun.default_allocator.destroy(this); + } + pub const Class = NewClass( DocType, .{ @@ -904,6 +909,11 @@ pub const DocType = struct { pub const DocEnd = struct { doc_end: ?*LOLHTML.DocEnd, + pub fn finalize(this: *DocEnd) void { + this.doc_end = null; + bun.default_allocator.destroy(this); + } + pub const Class = NewClass( DocEnd, .{ .name = "DocEnd" }, @@ -945,6 +955,11 @@ pub const DocEnd = struct { pub const Comment = struct { comment: ?*LOLHTML.Comment = null, + pub fn finalize(this: *Comment) void { + this.comment = null; + bun.default_allocator.destroy(this); + } + pub const Class = NewClass( Comment, .{ .name = "Comment" }, @@ -1058,6 +1073,11 @@ pub const Comment = struct { pub const EndTag = struct { end_tag: ?*LOLHTML.EndTag, + pub fn finalize(this: *EndTag) void { + this.end_tag = null; + bun.default_allocator.destroy(this); + } + pub const Handler = struct { callback: ?JSC.JSValue, global: *JSGlobalObject, @@ -1176,6 +1196,68 @@ pub const EndTag = struct { pub const AttributeIterator = struct { iterator: ?*LOLHTML.Attribute.Iterator = null, + const attribute_iterator_path: string = "file:///bun-vfs/lolhtml/AttributeIterator.js"; + const attribute_iterator_code: string = + \\"use strict"; + \\ + \\class AttributeIterator { + \\ constructor(internal) { + \\ this.#iterator = internal; + \\ } + \\ + \\ #iterator; + \\ + \\ [Symbol.iterator]() { + \\ return this; + \\ } + \\ + \\ next() { + \\ if (this.#iterator === null) + \\ return {done: true}; + \\ var value = this.#iterator.next(); + \\ if (!value) { + \\ this.#iterator = null; + \\ return {done: true}; + \\ } + \\ return {done: false, value: value}; + \\ } + \\} + \\ + \\return new AttributeIterator(internal1); + ; + threadlocal var attribute_iterator_class: JSC.C.JSObjectRef = undefined; + threadlocal var attribute_iterator_loaded: bool = false; + + pub fn getAttributeIteratorJSClass(global: *JSGlobalObject) JSValue { + if (attribute_iterator_loaded) + return JSC.JSValue.fromRef(attribute_iterator_class); + attribute_iterator_loaded = true; + var exception_ptr: ?[*]JSC.JSValueRef = null; + var name = JSC.C.JSStringCreateStatic("AttributeIteratorGetter", "AttributeIteratorGetter".len); + var param_name = JSC.C.JSStringCreateStatic("internal1", "internal1".len); + var attribute_iterator_class_ = JSC.C.JSObjectMakeFunction( + global.ref(), + name, + 1, + &[_]JSC.C.JSStringRef{param_name}, + JSC.C.JSStringCreateStatic(attribute_iterator_code.ptr, attribute_iterator_code.len), + JSC.C.JSStringCreateStatic(attribute_iterator_path.ptr, attribute_iterator_path.len), + 0, + exception_ptr, + ); + JSC.C.JSValueProtect(global.ref(), attribute_iterator_class_); + attribute_iterator_class = attribute_iterator_class_; + return JSC.JSValue.fromRef(attribute_iterator_class); + } + + pub fn finalize(this: *AttributeIterator) void { + if (this.iterator) |iter| { + iter.deinit(); + this.iterator = null; + } + bun.default_allocator.destroy(this); + } + pub const Class = NewClass( AttributeIterator, .{ .name = "AttributeIterator" }, @@ -1194,39 +1276,36 @@ pub const AttributeIterator = struct { globalObject: *JSGlobalObject, ) JSValue { if (this.iterator == null) { - return JSC.JSValue.createObject2( - globalObject, - &value_, - &done_, - JSC.JSValue.jsUndefined(), - JSC.JSValue.jsBoolean(true), - ); + return JSC.JSValue.jsNull(); } var attribute = this.iterator.?.next() orelse { this.iterator.?.deinit(); this.iterator = null; - return JSC.JSValue.createObject2( - globalObject, - &value_, - &done_, - JSC.JSValue.jsUndefined(), - JSC.JSValue.jsBoolean(true), - ); + return JSC.JSValue.jsNull(); }; + // TODO: don't clone here + const value = attribute.value(); + const name = attribute.name(); + defer name.deinit(); + defer value.deinit(); + var strs = [2]ZigString{ - htmlStringValue(attribute.name().slice(), globalObject), - htmlStringValue(attribute.value().slice(), globalObject), + ZigString.init(name.slice()), + ZigString.init(value.slice()), }; - return JSC.JSValue.createObject2( + var valid_strs: []ZigString = strs[0..2]; + + var array = JSC.JSValue.createStringArray( globalObject, - &value_, - &done_, - JSC.JSValue.createStringArray(globalObject, &strs, 2, false), - JSC.JSValue.jsBoolean(false), + valid_strs.ptr, + valid_strs.len, + true, ); + + return array; } }; pub const Element = struct { @@ -1287,9 +1366,17 @@ pub const Element = struct { .namespaceURI = .{ .get = getterWrap(Element, "getNamespaceURI"), }, + .attributes = .{ + .get = getterWrap(Element, "getAttributes"), + }, }, ); + pub fn finalize(this: *Element) void { + this.element = null; + bun.default_allocator.destroy(this); + } + pub fn onEndTag( this: *Element, globalObject: *JSGlobalObject, @@ -1513,4 +1600,26 @@ pub const Element = struct { return ZigString.init(std.mem.span(this.element.?.namespaceURI())).toValue(globalObject); } + + pub fn getAttributes(this: *Element, globalObject: *JSGlobalObject) JSValue { + if (this.element == null) + return JSValue.jsUndefined(); + + var iter = this.element.?.attributes() orelse return throwLOLHTMLError(globalObject); + var attr_iter = bun.default_allocator.create(AttributeIterator) catch unreachable; + attr_iter.* = .{ .iterator = iter }; + var attr = AttributeIterator.Class.make(globalObject.ref(), attr_iter); + JSC.C.JSValueProtect(globalObject.ref(), attr); + defer JSC.C.JSValueUnprotect(globalObject.ref(), attr); + return JSC.JSValue.fromRef( + JSC.C.JSObjectCallAsFunction( + globalObject.ref(), + AttributeIterator.getAttributeIteratorJSClass(globalObject).asObjectRef(), + null, + 1, + @ptrCast([*]JSC.C.JSObjectRef, &attr), + null, + ), + ); + } }; |