aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-11 04:49:25 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-03-11 04:49:25 -0800
commit64f1af0aa6cdc536339b17887cd6cbcfa8e740a7 (patch)
treea2400bc4b36099ff80cd01baedc9732c907a6073
parent313ad01e4213f0297221fef0c2ba454244b40c55 (diff)
downloadbun-64f1af0aa6cdc536339b17887cd6cbcfa8e740a7.tar.gz
bun-64f1af0aa6cdc536339b17887cd6cbcfa8e740a7.tar.zst
bun-64f1af0aa6cdc536339b17887cd6cbcfa8e740a7.zip
skeleton
-rw-r--r--integration/bunjs-only-snippets/html-rewriter.test.js8
-rw-r--r--src/javascript/jsc/api/html_rewriter.zig410
-rw-r--r--src/javascript/jsc/base.zig5
-rw-r--r--src/javascript/jsc/bindings/bindings.zig5
-rw-r--r--src/javascript/jsc/javascript.zig2
-rw-r--r--src/javascript/jsc/node/types.zig3
-rw-r--r--src/jsc.zig4
7 files changed, 437 insertions, 0 deletions
diff --git a/integration/bunjs-only-snippets/html-rewriter.test.js b/integration/bunjs-only-snippets/html-rewriter.test.js
new file mode 100644
index 000000000..cd4fad746
--- /dev/null
+++ b/integration/bunjs-only-snippets/html-rewriter.test.js
@@ -0,0 +1,8 @@
+import { describe, it, expect } from "bun:test";
+
+describe("HTMLRewriter", () => {
+ it("exists globally", () => {
+ expect(typeof HTMLRewriter).toBe("function");
+ console.log(HTMLRewriter.name);
+ });
+});
diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig
new file mode 100644
index 000000000..3a2bc1cc3
--- /dev/null
+++ b/src/javascript/jsc/api/html_rewriter.zig
@@ -0,0 +1,410 @@
+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 bun = @import("../../../global.zig");
+const string = bun.string;
+const JSC = @import("../../../jsc.zig");
+const js = JSC.C;
+const WebCore = @import("../webcore/response.zig");
+const Router = @This();
+const Bundler = @import("../../../bundler.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 Response = WebCore.Response;
+
+pub const HTMLRewriter = struct {
+ listeners: *anyopaque,
+ built: bool = false,
+
+ pub const Class = NewClass(
+ HTMLRewriter,
+ .{ .name = "HTMLRewriter" },
+ .{
+ .constructor = constructor,
+ .on = .{
+ .rfn = wrap(HTMLRewriter, "on"),
+ },
+ .onDocument = .{
+ .rfn = wrap(HTMLRewriter, "onDocument"),
+ },
+ .transform = .{
+ .rfn = wrap(HTMLRewriter, "transform"),
+ },
+ },
+ .{},
+ );
+
+ pub fn constructor(
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: []const js.JSValueRef,
+ _: js.ExceptionRef,
+ ) js.JSObjectRef {
+ var rewriter = bun.default_allocator.create(HTMLRewriter) catch unreachable;
+ rewriter.* = HTMLRewriter{ .listeners = undefined };
+ return HTMLRewriter.Class.make(ctx, rewriter);
+ }
+
+ pub fn on(
+ this: *HTMLRewriter,
+ global: *JSGlobalObject,
+ event: ZigString,
+ listener: JSValue,
+ ) JSValue {
+ _ = this;
+ _ = global;
+ _ = event;
+ _ = listener;
+ return undefined;
+ }
+
+ pub fn onDocument(
+ this: *HTMLRewriter,
+ global: *JSGlobalObject,
+ event: ZigString,
+ listener: JSValue,
+ ) JSValue {
+ _ = this;
+ _ = global;
+ _ = event;
+ _ = listener;
+ return undefined;
+ }
+
+ pub fn transform(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue {
+ _ = this;
+ _ = global;
+ _ = response;
+ return undefined;
+ }
+};
+
+const ContentOptions = struct {
+ html: bool = false,
+};
+
+fn MethodType(comptime Container: type) type {
+ return fn (
+ this: *Container,
+ ctx: js.JSContextRef,
+ thisObject: js.JSObjectRef,
+ target: js.JSObjectRef,
+ args: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef;
+}
+
+fn GetterType(comptime Container: type) type {
+ return fn (
+ this: *Container,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef;
+}
+
+pub fn wrap(comptime Container: type, comptime name: string) MethodType(Container) {
+ return struct {
+ const FunctionType = @TypeOf(@field(Container, name));
+ const FunctionTypeInfo: std.builtin.TypeInfo.Fn = @typeInfo(FunctionType).Fn;
+
+ pub fn callback(
+ this: *Container,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ var iter = JSC.Node.ArgumentsSlice.from(arguments);
+ var args: std.meta.ArgsTuple(FunctionType) = undefined;
+
+ comptime var i: usize = 0;
+ inline while (i < FunctionTypeInfo.args.len) : (i += 1) {
+ const ArgType = FunctionTypeInfo.args[i].arg_type.?;
+
+ switch (ArgType) {
+ *Container => {
+ args[i] = this;
+ },
+ *JSGlobalObject => {
+ args[i] = ctx.ptr();
+ },
+ ZigString => {
+ var arg: ?JSC.JSValue = iter.next();
+ args[i] = (arg orelse {
+ JSC.throwInvalidArguments("Expected string", .{}, ctx, exception);
+ return null;
+ }).getZigString(ctx.ptr());
+ },
+ ?ContentOptions => {
+ var arg: ?JSC.JSValue = iter.next();
+
+ if (arg) |content_arg| {
+ if (content_arg.get("html")) |html_val| {
+ args[i] = .{ .html = html_val.toBoolean() };
+ }
+ } else {
+ args[i] = null;
+ }
+ },
+ *Response => {
+ var arg: ?JSC.JSValue = iter.next();
+
+ args[i] = (arg orelse {
+ JSC.throwInvalidArguments("Missing Response object", .{}, ctx, exception);
+ return null;
+ }).as(Response) orelse {
+ JSC.throwInvalidArguments("Expected Response object", .{}, ctx, exception);
+ return null;
+ };
+ },
+ JSValue => {
+ var arg: ?JSC.JSValue = iter.next();
+
+ args[i] = arg orelse {
+ JSC.throwInvalidArguments("Missing argument", .{}, ctx, exception);
+ return null;
+ };
+ },
+ else => @compileError("Unexpected Type" ++ @typeName(ArgType)),
+ }
+ }
+
+ const result: JSValue = @call(.{}, @field(Container, name), args);
+ if (result.isError()) {
+ exception.* = result.asObjectRef();
+ return null;
+ }
+
+ return result.asObjectRef();
+ }
+ }.callback;
+}
+
+pub fn getterWrap(comptime Container: type, comptime name: string) GetterType(Container) {
+ return struct {
+ const FunctionType = @TypeOf(@field(Container, name));
+ const FunctionTypeInfo: std.builtin.TypeInfo.Fn = @typeInfo(FunctionType).Fn;
+
+ pub fn callback(
+ this: *Container,
+ ctx: js.JSContextRef,
+ _: js.JSObjectRef,
+ _: js.JSStringRef,
+ exception: js.ExceptionRef,
+ ) js.JSObjectRef {
+ const result: JSValue = @call(.{}, @field(Container, name), .{ this, ctx.ptr() });
+ if (result.isError()) {
+ exception.* = result.asObjectRef();
+ return null;
+ }
+
+ return result.asObjectRef();
+ }
+ }.callback;
+}
+
+pub const Element = struct {
+ global: *JSGlobalObject,
+ removed: bool = false,
+
+ pub const Class = NewClass(
+ Element,
+ .{ .name = "Element" },
+ .{
+ .getAttribute = .{
+ .rfn = wrap(Element, "getAttribute"),
+ },
+ .hasAttribute = .{
+ .rfn = wrap(Element, "hasAttribute"),
+ },
+ .setAttribute = .{
+ .rfn = wrap(Element, "setAttribute"),
+ },
+ .removeAttribute = .{
+ .rfn = wrap(Element, "removeAttribute"),
+ },
+ .before = .{
+ .rfn = wrap(Element, "before"),
+ },
+ .after = .{
+ .rfn = wrap(Element, "after"),
+ },
+ .prepend = .{
+ .rfn = wrap(Element, "prepend"),
+ },
+ .append = .{
+ .rfn = wrap(Element, "append"),
+ },
+ .replace = .{
+ .rfn = wrap(Element, "replace"),
+ },
+ .setInnerContent = .{
+ .rfn = wrap(Element, "setInnerContent"),
+ },
+ .remove = .{
+ .rfn = wrap(Element, "remove"),
+ },
+ .removeAndKeepContent = .{
+ .rfn = wrap(Element, "removeAndKeepContent"),
+ },
+ },
+ .{
+ .tagName = .{
+ .get = getterWrap(Element, "getTagName"),
+ },
+ .removed = .{
+ .get = getterWrap(Element, "getRemoved"),
+ },
+ .namespaceUri = .{
+ .get = getterWrap(Element, "getNamespaceURI"),
+ },
+ },
+ );
+
+ // // fn wrap(comptime name: string)
+
+ /// Returns the value for a given attribute name: ZigString on the element, or null if it is not found.
+ pub fn getAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString) JSValue {
+ _ = this;
+ _ = name;
+ _ = globalObject;
+ return undefined;
+ }
+
+ /// Returns a boolean indicating whether an attribute exists on the element.
+ pub fn hasAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString) JSValue {
+ _ = this;
+ _ = name;
+ _ = globalObject;
+ return undefined;
+ }
+
+ /// Sets an attribute to a provided value, creating the attribute if it does not exist.
+ pub fn setAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString, value: ZigString) JSValue {
+ _ = this;
+ _ = name;
+ _ = globalObject;
+ _ = value;
+ return undefined;
+ }
+
+ /// Removes the attribute.
+ pub fn removeAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString) JSValue {
+ _ = this;
+ _ = name;
+ _ = globalObject;
+ return undefined;
+ }
+
+ /// Inserts content before the element.
+ pub fn before(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// Inserts content right after the element.
+ pub fn after(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// nserts content right after the start tag of the element.
+ pub fn prepend(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// Inserts content right before the end tag of the element.
+ pub fn append(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// Removes the element and inserts content in place of it.
+ pub fn replace(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// Replaces content of the element.
+ pub fn setInnerContent(this: *Element, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ _ = this;
+ _ = content;
+ _ = globalObject;
+ _ = contentOptions;
+ return undefined;
+ }
+
+ /// Removes the element with all its content.
+ pub fn remove(this: *Element, globalObject: *JSGlobalObject) JSValue {
+ _ = this;
+
+ _ = globalObject;
+ return undefined;
+ }
+
+ /// Removes the start tag and end tag of the element but keeps its inner content intact.
+ pub fn removeAndKeepContent(this: *Element, globalObject: *JSGlobalObject) JSValue {
+ _ = this;
+
+ _ = globalObject;
+ return undefined;
+ }
+
+ pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) JSValue {
+ _ = this;
+
+ _ = globalObject;
+ return undefined;
+ }
+
+ pub fn getRemoved(this: *Element, globalObject: *JSGlobalObject) JSValue {
+ _ = this;
+
+ _ = globalObject;
+ return undefined;
+ }
+
+ pub fn getNamespaceURI(this: *Element, globalObject: *JSGlobalObject) JSValue {
+ _ = this;
+
+ _ = globalObject;
+ return undefined;
+ }
+};
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index c1ea196e1..a39058e45 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -1857,6 +1857,9 @@ const Transpiler = @import("./api/transpiler.zig");
const TextEncoder = WebCore.TextEncoder;
const TextDecoder = WebCore.TextDecoder;
const TimeoutTask = JSC.BunTimer.Timeout.TimeoutTask;
+const HTMLRewriter = JSC.Cloudflare.HTMLRewriter;
+const Element = JSC.Cloudflare.Element;
+
pub const JSPrivateDataPtr = TaggedPointerUnion(.{
ResolveError,
BuildError,
@@ -1881,6 +1884,8 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{
TextEncoder,
TextDecoder,
TimeoutTask,
+ HTMLRewriter,
+ Element,
});
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 ab6407e76..f93589183 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -14,6 +14,7 @@ const ZigException = Exports.ZigException;
const ZigStackTrace = Exports.ZigStackTrace;
const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false;
const ArrayBuffer = @import("../base.zig").ArrayBuffer;
+const JSC = @import("../../../jsc.zig");
pub const JSObject = extern struct {
pub const shim = Shimmer("JSC", "JSObject", @This());
bytes: shim.Bytes,
@@ -1822,6 +1823,10 @@ pub const JSValue = enum(i64) {
return cppFn("putRecord", .{ value, global, key, values, values_len });
}
+ pub fn as(value: JSValue, comptime ZigType: type) ?*ZigType {
+ return JSC.GetJSPrivateData(ZigType, value.asObjectRef());
+ }
+
/// Create an object with exactly two properties
pub fn createObject2(global: *JSGlobalObject, key1: *const ZigString, key2: *const ZigString, value1: JSValue, value2: JSValue) JSValue {
return cppFn("createObject2", .{ global, key1, key2, value1, value2 });
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 622364a6c..7f35ef8b7 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -101,6 +101,8 @@ pub const GlobalClasses = [_]type{
WebCore.TextEncoder.Constructor.Class,
WebCore.TextDecoder.Constructor.Class,
+ JSC.Cloudflare.HTMLRewriter.Class,
+
// The last item in this array becomes "process.env"
Bun.EnvironmentVariables.Class,
};
diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig
index c663c4695..f9cf5a97d 100644
--- a/src/javascript/jsc/node/types.zig
+++ b/src/javascript/jsc/node/types.zig
@@ -392,6 +392,9 @@ pub const ArgumentsSlice = struct {
arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator),
all: []const JSC.JSValue,
+ pub fn from(arguments: []const JSC.JSValueRef) ArgumentsSlice {
+ return init(@ptrCast([*]const JSC.JSValue, arguments.ptr)[0..arguments.len]);
+ }
pub fn init(arguments: []const JSC.JSValue) ArgumentsSlice {
return ArgumentsSlice{
.remaining = arguments,
diff --git a/src/jsc.zig b/src/jsc.zig
index 8702202e0..e75b0fb29 100644
--- a/src/jsc.zig
+++ b/src/jsc.zig
@@ -7,6 +7,10 @@ pub usingnamespace @import("./javascript/jsc/base.zig");
pub usingnamespace @import("./javascript/jsc/javascript.zig");
pub const C = @import("./javascript/jsc/javascript_core_c_api.zig");
pub const WebCore = @import("./javascript/jsc/webcore.zig");
+pub const Cloudflare = struct {
+ pub const HTMLRewriter = @import("./javascript/jsc/api/html_rewriter.zig").HTMLRewriter;
+ pub const Element = @import("./javascript/jsc/api/html_rewriter.zig").Element;
+};
pub const Jest = @import("./javascript/jsc/test/jest.zig");
pub const API = struct {
pub const Transpiler = @import("./javascript/jsc/api/transpiler.zig");