aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-07-14 00:14:11 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-07-14 00:14:11 -0700
commitf4381bb2979e7409e4809c490a5a080a2b3753c3 (patch)
treee47de3102684fc7e5820428f5d510b2939b58c79
parentab73c7b323c222e5d1172c07036653ca98aa8e6b (diff)
downloadbun-f4381bb2979e7409e4809c490a5a080a2b3753c3.tar.gz
bun-f4381bb2979e7409e4809c490a5a080a2b3753c3.tar.zst
bun-f4381bb2979e7409e4809c490a5a080a2b3753c3.zip
ts
-rw-r--r--build.zig32
-rw-r--r--src/deps/picohttp.zig2
-rw-r--r--src/javascript/jsc/api/router.zig244
-rw-r--r--src/javascript/jsc/base.zig499
-rw-r--r--src/javascript/jsc/javascript.zig60
-rw-r--r--src/javascript/jsc/typescript.zig76
-rw-r--r--src/javascript/jsc/webcore/response.zig101
7 files changed, 835 insertions, 179 deletions
diff --git a/build.zig b/build.zig
index 889d88ecf..cdfba8795 100644
--- a/build.zig
+++ b/build.zig
@@ -1,7 +1,7 @@
const std = @import("std");
const resolve_path = @import("./src/resolver/resolve_path.zig");
-pub fn addPicoHTTP(step: *std.build.LibExeObjStep, dir: []const u8) void {
+pub fn addPicoHTTP(step: *std.build.LibExeObjStep) void {
const picohttp = step.addPackage(.{
.name = "picohttp",
.path = .{ .path = "src/deps/picohttp.zig" },
@@ -119,10 +119,14 @@ pub fn build(b: *std.build.Builder) void {
var javascript: @TypeOf(exe) = undefined;
// exe.want_lto = true;
if (!target.getCpuArch().isWasm()) {
- addPicoHTTP(exe, cwd);
+ addPicoHTTP(
+ exe,
+ );
if (ENABLE_JAVASCRIPT_BUILD) {
javascript = b.addExecutable("spjs", "src/main_javascript.zig");
- addPicoHTTP(javascript, cwd);
+ addPicoHTTP(
+ javascript,
+ );
javascript.packages = std.ArrayList(std.build.Pkg).fromOwnedSlice(std.heap.page_allocator, std.heap.page_allocator.dupe(std.build.Pkg, exe.packages.items) catch unreachable);
javascript.setOutputDir(output_dir);
javascript.setBuildMode(mode);
@@ -142,7 +146,7 @@ pub fn build(b: *std.build.Builder) void {
}
}
- exe.install();
+ b.default_step.dependOn(&exe.step);
if (!target.getCpuArch().isWasm()) {
if (ENABLE_JAVASCRIPT_BUILD) {
@@ -159,5 +163,23 @@ pub fn build(b: *std.build.Builder) void {
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
- std.debug.print("Build: ./{s}/{s}\n", .{ output_dir, "esdev" });
+ var log_step = b.addLog("Destination: {s}/{s}\n", .{ output_dir, "esdev" });
+ log_step.step.dependOn(&exe.step);
+
+ var typings_exe = b.addExecutable("typescript-decls", "src/javascript/jsc/typescript.zig");
+ var typings_cmd = typings_exe.run();
+ typings_cmd.cwd = b.build_root;
+
+ typings_cmd.step.dependOn(&typings_exe.step);
+
+ typings_exe.linkLibC();
+ typings_exe.setMainPkgPath(cwd);
+ if (target.getOsTag() == .macos) {
+ typings_exe.linkFramework("JavascriptCore");
+ }
+ addPicoHTTP(
+ typings_exe,
+ );
+ const typings_step = b.step("types", "Build TypeScript types");
+ typings_step.dependOn(&typings_cmd.step);
}
diff --git a/src/deps/picohttp.zig b/src/deps/picohttp.zig
index 58edb4708..7b081b59f 100644
--- a/src/deps/picohttp.zig
+++ b/src/deps/picohttp.zig
@@ -55,7 +55,7 @@ pub const Request = struct {
);
// Leave a sentinel value, for JavaScriptCore support.
- path.ptr[path.len] = 0;
+ @intToPtr([*]u8, @ptrToInt(path.ptr))[path.len] = 0;
return switch (rc) {
-1 => error.BadRequest,
diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig
index f9c46b3e4..1a88098a4 100644
--- a/src/javascript/jsc/api/router.zig
+++ b/src/javascript/jsc/api/router.zig
@@ -4,131 +4,155 @@ const Api = @import("../../../api/schema.zig").Api;
const FilesystemRouter = @import("../../../router.zig");
const JavaScript = @import("../javascript.zig");
-pub const Router = struct {
- match: FilesystemRouter.RouteMap.MatchedRoute,
- file_path_str: js.JSStringRef = null,
- pathname_str: js.JSStringRef = null,
+const Router = @This();
- pub const Class = NewClass(
- Router,
- "Router",
- .{
- .finalize = finalize,
+match: FilesystemRouter.RouteMap.MatchedRoute,
+file_path_str: js.JSStringRef = null,
+pathname_str: js.JSStringRef = null,
+
+pub fn constructor(
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+) js.JSObjectRef {
+ return null;
+}
+
+pub const Class = NewClass(
+ Router,
+ .{
+ .name = "Router",
+ .read_only = true,
+ .ts = d.ts.class{ .path = "speedy.js/router", .tsdoc =
+ \\Filesystem Router supporting dynamic routes, exact routes, catch-all routes, and optional catch-all routes. Implemented in native code and only available with Speedy.js.
},
- .{
- .@"pathname" = .{
- .get = getPathname,
- .ro = true,
- .ts = .{
- .@"return" = "string",
- .@"tsdoc" = "URL path as appears in a web browser's address bar",
+ },
+ .{
+ .finalize = .{
+ .rfn = finalize,
+ },
+ .constructor = .{
+ .rfn = constructor,
+ .ts = d.ts{
+ .tsdoc =
+ \\Native filesystem router. Use with {@link https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent FetchEvent}.
+ ,
+ .@"return" = "Router",
+ .args = &[_]d.ts.arg{
+ d.ts.arg{ .name = "event", .@"return" = "FetchEvent" },
},
},
- .@"filepath" = .{
- .get = getPageFilePath,
- .ro = true,
- .ts = .{
- .@"return" = "string",
- .@"tsdoc" =
- \\Project-relative filesystem path to the route file
- \\
- \\@example
- \\
- \\```tsx
- \\const PageComponent = (await import(route.filepath)).default;
- \\ReactDOMServer.renderToString(<PageComponent query={route.query} />);
- \\```
- ,
- },
+ },
+ },
+ .{
+ .@"pathname" = .{
+ .get = getPathname,
+ .ro = true,
+ .ts = d.ts{
+ .@"return" = "string",
+ .@"tsdoc" = "URL path as appears in a web browser's address bar",
},
- .@"route" = .{
- .@"get" = getRoute,
- .ro = true,
+ },
+ .filepath = .{
+ .get = getFilePath,
+ .ro = true,
+ .ts = d.ts{
+ .@"return" = "string",
+ .tsdoc =
+ \\Project-relative filesystem path to the route file. Useful for importing.
+ \\
+ \\@example ```tsx
+ \\await import(route.filepath);
+ \\```
+ ,
},
- .query = .{
- .@"get" = getQuery,
- .ro = true,
+ },
+ .@"route" = .{
+ .@"get" = getRoute,
+ .ro = true,
+ .ts = d.ts{
+ .@"return" = "string",
+ .tsdoc =
+ \\Route name
+ \\@example
+ \\`"blog/posts/[id]"`
+ \\`"blog/posts/[id]/[[...slug]]"`
+ \\`"blog"`
+ ,
},
- .pageFilePath = .{
- .@"get" = getPageFilePath,
- .ro = true,
+ },
+ .query = .{
+ .@"get" = getQuery,
+ .ro = true,
+ .ts = d.ts{
+ .@"return" = "Record<string, string | string[]>",
+ .tsdoc =
+ \\Route parameters as a key-value object
+ \\
+ \\@example
+ \\```js
+ \\console.assert(router.query.id === "123");
+ \\console.assert(router.pathname === "/blog/posts/123");
+ \\console.assert(router.route === "blog/posts/[id]");
+ \\```
+ ,
},
},
- false,
- false,
- );
-
- pub fn getPageFilePath(
- this: *Router,
- ctx: js.JSContextRef,
- thisObject: js.JSObjectRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- if (this.file_path_str == null) {
- this.file_path_str = js.JSStringCreateWithUTF8CString(this.match.file_path[0.. :0]);
- }
-
- return js.JSValueMakeString(ctx, this.file_path_str);
- }
+ },
+);
- pub fn finalize(
- this: *Router,
- ctx: js.JSObjectRef,
- ) void {
- // this.deinit();
+pub fn getFilePath(
+ this: *Router,
+ ctx: js.JSContextRef,
+ thisObject: js.JSObjectRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+) js.JSValueRef {
+ if (this.file_path_str == null) {
+ this.file_path_str = js.JSStringCreateWithUTF8CString(this.match.file_path.ptr);
}
- pub fn requirePage(
- this: *Router,
- ctx: js.JSContextRef,
- function: js.JSObjectRef,
- thisObject: js.JSObjectRef,
- arguments: []const js.JSValueRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {}
+ return js.JSValueMakeString(ctx, this.file_path_str);
+}
- pub fn getPathname(
- this: *Router,
- ctx: js.JSContextRef,
- thisObject: js.JSObjectRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- if (this.pathname_str == null) {
- this.pathname_str = js.JSStringCreateWithUTF8CString(this.match.pathname[0.. :0]);
- }
+pub fn finalize(
+ this: *Router,
+ ctx: js.JSObjectRef,
+) void {
+ // this.deinit();
+}
- return js.JSValueMakeString(ctx, this.pathname_str);
+pub fn getPathname(
+ this: *Router,
+ ctx: js.JSContextRef,
+ thisObject: js.JSObjectRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+) js.JSValueRef {
+ if (this.pathname_str == null) {
+ this.pathname_str = js.JSStringCreateWithUTF8CString(this.match.pathname.ptr);
}
- pub fn getAsPath(
- this: *Router,
- ctx: js.JSContextRef,
- thisObject: js.JSObjectRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- return js.JSValueMakeString(ctx, Properties.Refs.default);
- }
+ return js.JSValueMakeString(ctx, this.pathname_str);
+}
- pub fn getRoute(
- this: *Router,
- ctx: js.JSContextRef,
- thisObject: js.JSObjectRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- return js.JSValueMakeString(ctx, Properties.Refs.default);
- }
+pub fn getRoute(
+ this: *Router,
+ ctx: js.JSContextRef,
+ thisObject: js.JSObjectRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+) js.JSValueRef {
+ return js.JSValueMakeString(ctx, Properties.Refs.default);
+}
- pub fn getQuery(
- this: *Router,
- ctx: js.JSContextRef,
- thisObject: js.JSObjectRef,
- prop: js.JSStringRef,
- exception: js.ExceptionRef,
- ) js.JSValueRef {
- return js.JSValueMakeString(ctx, Properties.Refs.default);
- }
-};
+pub fn getQuery(
+ this: *Router,
+ ctx: js.JSContextRef,
+ thisObject: js.JSObjectRef,
+ prop: js.JSStringRef,
+ exception: js.ExceptionRef,
+) js.JSValueRef {
+ return js.JSValueMakeString(ctx, Properties.Refs.default);
+}
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig
index 307bf2da9..9bdd36afb 100644
--- a/src/javascript/jsc/base.zig
+++ b/src/javascript/jsc/base.zig
@@ -163,6 +163,32 @@ pub const To = struct {
};
};
+pub const RuntimeImports = struct {
+ pub fn resolve(ctx: js.JSContextRef, str: string) ?js.JSObjectRef {
+ switch (ModuleList.Map.get(str) orelse ModuleList.none) {
+ .Router => {
+ const Router = @import("./api/router.zig");
+ return js.JSObjectMake(ctx, Router.Class.get().*, null);
+ },
+ else => {
+ return null;
+ },
+ }
+ }
+
+ pub const ModuleList = enum(u8) {
+ Router = 0,
+ none = std.math.maxInt(u8),
+
+ pub const Map = std.ComptimeStringMap(
+ ModuleList,
+ .{
+ .{ "speedy.js/router", ModuleList.Router },
+ },
+ );
+ };
+};
+
pub const Properties = struct {
pub const UTF8 = struct {
pub const module = "module";
@@ -277,16 +303,331 @@ pub const Properties = struct {
};
const hasSetter = std.meta.trait.hasField("set");
+const hasReadOnly = std.meta.trait.hasField("ro");
const hasFinalize = std.meta.trait.hasField("finalize");
+const hasTypeScript = std.meta.trait.hasField("ts");
+
+pub const d = struct {
+ pub const ts = struct {
+ @"return": string = "",
+ tsdoc: string = "",
+ name: string = "",
+ read_only: ?bool = null,
+ args: []const arg = &[_]arg{},
+ splat_args: bool = false,
+
+ pub const builder = struct {
+ classes: []class,
+ };
+
+ pub const class = struct {
+ path: string = "",
+ name: string = "",
+ tsdoc: string = "",
+ @"return": string = "",
+ read_only: ?bool = null,
+ interface: bool = true,
+ global: bool = false,
+
+ properties: []ts = &[_]ts{},
+ functions: []ts = &[_]ts{},
+
+ pub const Printer = struct {
+ const indent_level = 2;
+ pub fn printIndented(comptime fmt: string, args: anytype, comptime indent: usize) string {
+ comptime var buf: string = "";
+ comptime buf = buf ++ " " ** indent;
+
+ return comptime buf ++ std.fmt.comptimePrint(fmt, args);
+ }
+
+ pub fn printVar(comptime property: d.ts, comptime indent: usize) string {
+ comptime var buf: string = "";
+ comptime buf = buf ++ " " ** indent;
+
+ comptime {
+ if (property.read_only orelse false) {
+ buf = buf ++ "readonly ";
+ }
+
+ buf = buf ++ "var ";
+ buf = buf ++ property.name;
+ buf = buf ++ ": ";
+
+ if (property.@"return".len > 0) {
+ buf = buf ++ property.@"return";
+ } else {
+ buf = buf ++ "any";
+ }
+
+ buf = buf ++ ";\n";
+ }
+
+ comptime {
+ if (property.tsdoc.len > 0) {
+ buf = printTSDoc(property.tsdoc, indent) ++ buf;
+ }
+ }
+
+ return buf;
+ }
+
+ pub fn printProperty(comptime property: d.ts, comptime indent: usize) string {
+ comptime var buf: string = "";
+ comptime buf = buf ++ " " ** indent;
+
+ comptime {
+ if (property.read_only orelse false) {
+ buf = buf ++ "readonly ";
+ }
+
+ buf = buf ++ property.name;
+ buf = buf ++ ": ";
+
+ if (property.@"return".len > 0) {
+ buf = buf ++ property.@"return";
+ } else {
+ buf = buf ++ "any";
+ }
+
+ buf = buf ++ ";\n";
+ }
+
+ comptime {
+ if (property.tsdoc.len > 0) {
+ buf = printTSDoc(property.tsdoc, indent) ++ buf;
+ }
+ }
+
+ return buf;
+ }
+ pub fn printInstanceFunction(comptime func: d.ts, comptime _indent: usize, comptime no_type: bool) string {
+ comptime var indent = _indent;
+ comptime var buf: string = "";
+
+ comptime {
+ var args: string = "";
+ for (func.args) |a, i| {
+ if (i > 0) {
+ args = args ++ ", ";
+ }
+ args = args ++ printArg(a);
+ }
+
+ if (no_type) {
+ buf = buf ++ printIndented("{s}({s});\n", .{
+ func.name,
+ args,
+ }, indent);
+ } else {
+ buf = buf ++ printIndented("{s}({s}): {s};\n", .{
+ func.name,
+ args,
+ func.@"return",
+ }, indent);
+ }
+ }
+
+ comptime {
+ if (func.tsdoc.len > 0) {
+ buf = printTSDoc(func.tsdoc, indent) ++ buf;
+ }
+ }
+
+ return buf;
+ }
+ pub fn printFunction(comptime func: d.ts, comptime _indent: usize, comptime no_type: bool) string {
+ comptime var indent = _indent;
+ comptime var buf: string = "";
+
+ comptime {
+ var args: string = "";
+ for (func.args) |a, i| {
+ if (i > 0) {
+ args = args ++ ", ";
+ }
+ args = args ++ printArg(a);
+ }
+
+ if (no_type) {
+ buf = buf ++ printIndented("function {s}({s});\n", .{
+ func.name,
+ args,
+ }, indent);
+ } else {
+ buf = buf ++ printIndented("function {s}({s}): {s};\n", .{
+ func.name,
+ args,
+ func.@"return",
+ }, indent);
+ }
+ }
+
+ comptime {
+ if (func.tsdoc.len > 0) {
+ buf = printTSDoc(func.tsdoc, indent) ++ buf;
+ }
+ }
+
+ return buf;
+ }
+ pub fn printArg(
+ comptime _arg: d.ts.arg,
+ ) string {
+ comptime var buf: string = "";
+ comptime {
+ buf = buf ++ _arg.name;
+ buf = buf ++ ": ";
+
+ if (_arg.@"return".len == 0) {
+ buf = buf ++ "any";
+ } else {
+ buf = buf ++ _arg.@"return";
+ }
+ }
+
+ return buf;
+ }
+ pub fn printClass(comptime klass: d.ts.class, comptime _indent: usize) string {
+ comptime var indent = _indent;
+ comptime var buf: string = "";
+ comptime brk: {
+ if (klass.path.len > 0) {
+ buf = buf ++ printIndented("declare module \"{s}\" {{\n\n", .{klass.path}, indent);
+ indent += indent_level;
+ }
+ if (klass.tsdoc.len > 0) {
+ buf = buf ++ printTSDoc(klass.tsdoc, indent);
+ }
+
+ const qualifier = if (klass.path.len == 0 and !klass.interface) "declare " else "";
+ var stmt: string = qualifier;
+
+ if (!klass.global) {
+ if (klass.interface) {
+ buf = buf ++ printIndented("export interface {s} {{\n", .{klass.name}, indent);
+ } else {
+ buf = buf ++ printIndented("{s}class {s} {{\n", .{ qualifier, klass.name }, indent);
+ }
+
+ indent += indent_level;
+ }
+
+ var did_print_constructor = false;
+ for (klass.functions) |func, i| {
+ if (!strings.eqlComptime(func.name, "constructor")) continue;
+ did_print_constructor = true;
+ buf = buf ++ printInstanceFunction(
+ func,
+ indent,
+ !klass.interface,
+ );
+ }
+
+ for (klass.properties) |property, i| {
+ if (i > 0 or did_print_constructor) {
+ buf = buf ++ "\n";
+ }
+
+ if (!klass.global) {
+ buf = buf ++ printProperty(property, indent);
+ } else {
+ buf = buf ++ printVar(property, indent);
+ }
+ }
+
+ buf = buf ++ "\n";
+
+ for (klass.functions) |func, i| {
+ if (i > 0) {
+ buf = buf ++ "\n";
+ }
+
+ if (strings.eqlComptime(func.name, "constructor")) continue;
+
+ if (!klass.global) {
+ buf = buf ++ printInstanceFunction(
+ func,
+ indent,
+ false,
+ );
+ } else {
+ buf = buf ++ printFunction(
+ func,
+ indent,
+ false,
+ );
+ }
+ }
+
+ indent -= indent_level;
+
+ buf = buf ++ printIndented("}}\n", .{}, indent);
+
+ if (klass.path.len > 0) {
+ buf = buf ++ printIndented("export = {s};\n", .{klass.name}, indent);
+ indent -= indent_level;
+ buf = buf ++ printIndented("\n}}\n", .{}, indent);
+ }
+
+ break :brk;
+ }
+ return comptime buf;
+ }
+
+ pub fn printTSDoc(comptime str: string, comptime indent: usize) string {
+ comptime var buf: string = "";
+
+ comptime brk: {
+ var splitter = std.mem.split(str, "\n");
+
+ const first = splitter.next() orelse break :brk;
+ const second = splitter.next() orelse {
+ buf = buf ++ printIndented("/** {s} */\n", .{std.mem.trim(u8, first, " ")}, indent);
+ break :brk;
+ };
+ buf = buf ++ printIndented("/**\n", .{}, indent);
+ buf = buf ++ printIndented(" * {s}\n", .{std.mem.trim(u8, first, " ")}, indent);
+ buf = buf ++ printIndented(" * {s}\n", .{std.mem.trim(u8, second, " ")}, indent);
+ while (splitter.next()) |line| {
+ buf = buf ++ printIndented(" * {s}\n", .{std.mem.trim(u8, line, " ")}, indent);
+ }
+ buf = buf ++ printIndented("*/\n", .{}, indent);
+ }
+
+ return buf;
+ }
+ };
+ };
+
+ pub const arg = struct {
+ name: string = "",
+ @"return": string = "any",
+ optional: bool = false,
+ };
+ };
+};
+
+// This should only exist at compile-time.
+pub const ClassOptions = struct {
+ name: string,
+
+ read_only: bool = false,
+ singleton: bool = false,
+ ts: d.ts.class = d.ts.class{},
+};
+
pub fn NewClass(
comptime ZigType: type,
- comptime name: string,
+ comptime options: ClassOptions,
comptime staticFunctions: anytype,
comptime properties: anytype,
- comptime read_only: bool,
- comptime singleton: bool,
) type {
+ const read_only = options.read_only;
+ const singleton = options.singleton;
+
return struct {
+ const name = options.name;
const ClassDefinitionCreator = @This();
const function_names = std.meta.fieldNames(@TypeOf(staticFunctions));
const names_buf = brk: {
@@ -538,6 +879,107 @@ pub fn NewClass(
};
}
+ // This should only be run at comptime
+ pub fn typescriptDeclaration() d.ts.class {
+ comptime var class = options.ts;
+
+ comptime {
+ if (class.name.len == 0) {
+ class.name = options.name;
+ }
+
+ if (class.read_only == null) {
+ class.read_only = options.read_only;
+ }
+
+ if (static_functions.len > 0) {
+ var count: usize = 0;
+ inline for (function_name_literals) |function_name, i| {
+ const func = @field(staticFunctions, function_names[i]);
+ const Func = @TypeOf(func);
+
+ switch (@typeInfo(Func)) {
+ .Struct => {
+ if (hasTypeScript(Func)) {
+ count += 1;
+ }
+ },
+ else => continue,
+ }
+ }
+
+ var funcs = std.mem.zeroes([count]d.ts);
+ class.functions = std.mem.span(&funcs);
+ var func_i: usize = 0;
+
+ inline for (function_name_literals) |function_name, i| {
+ const func = @field(staticFunctions, function_names[i]);
+ const Func = @TypeOf(func);
+
+ switch (@typeInfo(Func)) {
+ .Struct => {
+ if (hasTypeScript(Func)) {
+ var ts_function: d.ts = func.ts;
+ if (ts_function.name.len == 0) {
+ ts_function.name = function_names[i];
+ }
+
+ if (ts_function.read_only == null) {
+ ts_function.read_only = class.read_only;
+ }
+
+ class.functions[func_i] = ts_function;
+
+ func_i += 1;
+ }
+ },
+ else => continue,
+ }
+ }
+ }
+
+ if (property_names.len > 0) {
+ var count: usize = 0;
+ inline for (property_names) |property_name, i| {
+ const field = @field(properties, property_names[i]);
+
+ if (hasTypeScript(@TypeOf(field))) {
+ count += 1;
+ }
+ }
+
+ var props = std.mem.zeroes([count]d.ts);
+ class.properties = std.mem.span(&props);
+ var property_i: usize = 0;
+
+ inline for (property_names) |property_name, i| {
+ const field = @field(properties, property_names[i]);
+
+ if (hasTypeScript(@TypeOf(field))) {
+ var ts_field: d.ts = field.ts;
+ if (ts_field.name.len == 0) {
+ ts_field.name = property_name;
+ }
+
+ if (ts_field.read_only == null) {
+ if (hasReadOnly(@TypeOf(field))) {
+ ts_field.read_only = field.ro;
+ } else {
+ ts_field.read_only = class.read_only;
+ }
+ }
+
+ class.properties[property_i] = ts_field;
+
+ property_i += 1;
+ }
+ }
+ }
+ }
+
+ return comptime class;
+ }
+
pub fn define() js.JSClassDefinition {
var def = js.kJSClassDefinitionEmpty;
@@ -545,19 +987,46 @@ pub fn NewClass(
std.mem.set(js.JSStaticFunction, &static_functions, std.mem.zeroes(js.JSStaticFunction));
var count: usize = 0;
inline for (function_name_literals) |function_name, i| {
- if (comptime strings.eqlComptime(function_names[i], "constructor")) {
- def.callAsConstructor = To.JS.Constructor(@field(staticFunctions, function_names[i])).rfn;
- } else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
- def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn;
- } else {
- var callback = To.JS.Callback(ZigType, @field(staticFunctions, function_names[i])).rfn;
- static_functions[count] = js.JSStaticFunction{
- .name = (function_names[i][0.. :0]).ptr,
- .callAsFunction = callback,
- .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone,
- };
+ switch (comptime @typeInfo(@TypeOf(@field(staticFunctions, function_names[i])))) {
+ .Struct => {
+ if (comptime strings.eqlComptime(function_names[i], "constructor")) {
+ def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn;
+ } else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
+ def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn;
+ } else {
+ var callback = To.JS.Callback(
+ ZigType,
+ @field(staticFunctions, function_names[i]).rfn,
+ ).rfn;
+ static_functions[count] = js.JSStaticFunction{
+ .name = (function_names[i][0.. :0]).ptr,
+ .callAsFunction = callback,
+ .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone,
+ };
- count += 1;
+ count += 1;
+ }
+ },
+ .Fn => {
+ if (comptime strings.eqlComptime(function_names[i], "constructor")) {
+ def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor).rfn;
+ } else if (comptime strings.eqlComptime(function_names[i], "finalize")) {
+ def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn;
+ } else {
+ var callback = To.JS.Callback(
+ ZigType,
+ @field(staticFunctions, function_names[i]),
+ ).rfn;
+ static_functions[count] = js.JSStaticFunction{
+ .name = (function_names[i][0.. :0]).ptr,
+ .callAsFunction = callback,
+ .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone,
+ };
+
+ count += 1;
+ }
+ },
+ else => unreachable,
}
// if (singleton) {
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 102abf925..a35186721 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -261,11 +261,6 @@ pub const Module = struct {
// reload_pending should not be applied to bundled modules
reload_pending: bool = false,
- pub var module_class: js.JSClassRef = undefined;
- pub var module_global_class: js.JSClassRef = undefined;
- pub var module_global_class_def: js.JSClassDefinition = undefined;
- pub var module_class_def: js.JSClassDefinition = undefined;
-
pub const NodeModuleList = struct {
tempbuf: []u8,
property_names: [*]u8,
@@ -709,6 +704,10 @@ pub const Module = struct {
var import_path = require_buf.list.items[0 .. end - 1];
var module = this;
+ if (RuntimeImports.resolve(ctx, import_path)) |ref| {
+ return ref;
+ }
+
if (this.vm.bundler.linker.resolver.resolve(module.path.name.dirWithTrailingSlash(), import_path, .require)) |resolved| {
var load_result = Module.loadFromResolveResult(this.vm, ctx, resolved, exception) catch |err| {
return null;
@@ -856,7 +855,7 @@ pub const Module = struct {
const ModuleClass = NewClass(
Module,
- "Module",
+ .{ .name = "Module" },
.{
.@"require" = require,
.@"requireFirst" = requireFirst,
@@ -876,8 +875,6 @@ pub const Module = struct {
.ro = false,
},
},
- false,
- false,
);
const ExportsClassName = "module.exports";
@@ -891,9 +888,6 @@ pub const Module = struct {
// ExportsClass.callAsConstructor = To.JS.Callback(Module, callExportsAsConstructor);
exports_class_ref = js.JSClassRetain(js.JSClassCreate(&ExportsClass));
-
- module_class_def = ModuleClass.define();
- module_class = js.JSClassRetain(js.JSClassCreate(&module_class_def));
}
pub const LoadResult = union(Tag) {
@@ -932,7 +926,7 @@ pub const Module = struct {
.ref = undefined,
.vm = vm,
};
- module.ref = js.JSObjectMake(global_ctx, Module.module_class, module);
+ module.ref = js.JSObjectMake(global_ctx, Module.ModuleClass.get(), module);
js.JSValueProtect(global_ctx, module.ref);
} else {
js.JSValueUnprotect(global_ctx, module.exports.?);
@@ -1489,7 +1483,7 @@ pub const GlobalObject = struct {
pub const ConsoleClass = NewClass(
GlobalObject,
- "Console",
+ .{ .name = "Console", .singleton = true },
.{
.@"log" = stdout,
.@"info" = stdout,
@@ -1500,25 +1494,43 @@ pub const GlobalObject = struct {
.@"warn" = stderr,
},
.{},
- // people sometimes modify console.log, let them.
- false,
- true,
);
pub const GlobalClass = NewClass(
GlobalObject,
- "Global",
.{
- .@"addEventListener" = EventListenerMixin.addEventListener(GlobalObject).addListener,
+ .name = "Global",
+ .ts = d.ts.class{
+ .global = true,
+ },
},
.{
- .@"console" = getConsole,
- .@"Request" = Request.Class.GetClass(GlobalObject).getter,
- .@"Response" = Response.Class.GetClass(GlobalObject).getter,
- .@"Headers" = Headers.Class.GetClass(GlobalObject).getter,
+ .@"addEventListener" = .{
+ .rfn = EventListenerMixin.addEventListener(GlobalObject).addListener,
+ .ts = d.ts{
+ .args = &[_]d.ts.arg{
+ .{ .name = "name", .@"return" = "\"fetch\"" },
+ .{ .name = "callback", .@"return" = "(event: FetchEvent) => void" },
+ },
+ .@"return" = "void",
+ },
+ },
+ },
+ .{
+ .@"console" = d.ts{ .@"return" = "console" },
+ .@"Request" = .{
+ .get = Request.Class.GetClass(GlobalObject).getter,
+ // .ts = d.ts{ .@"return" = "typeof Request" },
+ },
+ .@"Response" = .{
+ .get = Response.Class.GetClass(GlobalObject).getter,
+ // .ts = d.ts{ .@"return" = "typeof Response" },
+ },
+ .@"Headers" = .{
+ .get = Headers.Class.GetClass(GlobalObject).getter,
+ // .ts = d.ts{ .@"return" = "typeof Headers" },
+ },
},
- false,
- false,
);
pub fn getConsole(
diff --git a/src/javascript/jsc/typescript.zig b/src/javascript/jsc/typescript.zig
new file mode 100644
index 000000000..3c9d1595d
--- /dev/null
+++ b/src/javascript/jsc/typescript.zig
@@ -0,0 +1,76 @@
+usingnamespace @import("./base.zig");
+const std = @import("std");
+const Api = @import("../../api/schema.zig").Api;
+const Router = @import("./api/router.zig");
+const JavaScript = @import("./javascript.zig");
+const builtin = std.builtin;
+const io = std.io;
+const fs = std.fs;
+const process = std.process;
+const ChildProcess = std.ChildProcess;
+const Progress = std.Progress;
+const print = std.debug.print;
+const mem = std.mem;
+const testing = std.testing;
+const Allocator = std.mem.Allocator;
+const resolve_path = @import("../../resolver/resolve_path.zig");
+usingnamespace @import("./webcore/response.zig");
+
+pub fn main() anyerror!void {
+ var args_it = process.args();
+ var allocator = std.heap.c_allocator;
+
+ _ = args_it.skip();
+ var stdout = io.getStdOut();
+
+ const modules = comptime [_]d.ts.class{
+ Router.Class.typescriptDeclaration(),
+ };
+ inline for (modules) |class, i| {
+ if (i > 0) {
+ try stdout.writeAll("\n\n");
+ }
+
+ try stdout.writeAll(comptime d.ts.class.Printer.printClass(class, 0));
+ }
+
+ try stdout.writeAll("\n\n");
+
+ try stdout.writeAll("declare global {\n");
+
+ const global = comptime JavaScript.GlobalObject.GlobalClass.typescriptDeclaration();
+
+ inline for (global.properties) |property, i| {
+ if (i > 0) {
+ try stdout.writeAll("\n\n");
+ }
+
+ try stdout.writeAll(comptime d.ts.class.Printer.printVar(property, 2));
+ }
+
+ try stdout.writeAll("\n");
+
+ inline for (global.functions) |property, i| {
+ if (i > 0) {
+ try stdout.writeAll("\n\n");
+ }
+
+ try stdout.writeAll(comptime d.ts.class.Printer.printFunction(property, 2, false));
+ }
+
+ try stdout.writeAll("\n");
+
+ const globals = comptime [_]d.ts.class{
+ FetchEvent.Class.typescriptDeclaration(),
+ };
+
+ inline for (globals) |class, i| {
+ if (i > 0) {
+ try stdout.writeAll("\n\n");
+ }
+
+ try stdout.writeAll(comptime d.ts.class.Printer.printClass(class, 2));
+ }
+
+ try stdout.writeAll("}\n");
+}
diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig
index 9e73d3113..2c231e97c 100644
--- a/src/javascript/jsc/webcore/response.zig
+++ b/src/javascript/jsc/webcore/response.zig
@@ -6,7 +6,7 @@ const JavaScript = @import("../javascript.zig");
pub const Response = struct {
pub const Class = NewClass(
Response,
- "Response",
+ .{ .name = "Response" },
.{
.@"constructor" = constructor,
},
@@ -24,8 +24,6 @@ pub const Response = struct {
.ro = true,
},
},
- false,
- false,
);
allocator: *std.mem.Allocator,
@@ -304,21 +302,47 @@ pub const Headers = struct {
};
pub const Class = NewClass(
Headers,
- "Headers",
.{
- .@"get" = JS.get,
- .@"set" = JS.set,
- .@"append" = JS.append,
- .@"delete" = JS.delete,
- .@"entries" = JS.entries,
- .@"keys" = JS.keys,
- .@"values" = JS.values,
- .@"constructor" = JS.constructor,
- .@"finalize" = JS.finalize,
+ .name = "Headers",
+ .read_only = true,
+ },
+ .{
+ .@"get" = .{
+ .rfn = JS.get,
+ },
+ .@"set" = .{
+ .rfn = JS.set,
+ .ts = d.ts{},
+ },
+ .@"append" = .{
+ .rfn = JS.append,
+ .ts = d.ts{},
+ },
+ .@"delete" = .{
+ .rfn = JS.delete,
+ .ts = d.ts{},
+ },
+ .@"entries" = .{
+ .rfn = JS.entries,
+ .ts = d.ts{},
+ },
+ .@"keys" = .{
+ .rfn = JS.keys,
+ .ts = d.ts{},
+ },
+ .@"values" = .{
+ .rfn = JS.values,
+ .ts = d.ts{},
+ },
+ .@"constructor" = .{
+ .rfn = JS.constructor,
+ .ts = d.ts{},
+ },
+ .@"finalize" = .{
+ .rfn = JS.finalize,
+ },
},
.{},
- true,
- false,
);
// https://developer.mozilla.org/en-US/docs/Glossary/Guard
@@ -807,7 +831,10 @@ pub const Request = struct {
pub const Class = NewClass(
Request,
- "Request",
+ .{
+ .name = "Request",
+ .read_only = true,
+ },
.{},
.{
.@"cache" = .{
@@ -855,8 +882,6 @@ pub const Request = struct {
.@"ro" = true,
},
},
- true,
- false,
);
pub fn getCache(
@@ -987,14 +1012,42 @@ pub const FetchEvent = struct {
pub const Class = NewClass(
FetchEvent,
- "FetchEvent",
- .{ .@"respondWith" = respondWith, .@"waitUntil" = waitUntil },
.{
- .@"client" = .{ .@"get" = getClient, .ro = true },
- .@"request" = .{ .@"get" = getRequest, .ro = true },
+ .name = "FetchEvent",
+ .read_only = true,
+ .ts = d.ts.class{ .interface = true },
+ },
+ .{
+ .@"respondWith" = .{
+ .rfn = respondWith,
+ .ts = d.ts{
+ .tsdoc = "Render the response in the active HTTP request",
+ .@"return" = "void",
+ .args = &[_]d.ts.arg{
+ .{ .name = "response", .@"return" = "Response" },
+ },
+ },
+ },
+ .@"waitUntil" = waitUntil,
+ },
+ .{
+ .@"client" = .{
+ .@"get" = getClient,
+ .ro = true,
+ .ts = d.ts{
+ .tsdoc = "HTTP client metadata. This is not implemented yet, do not use.",
+ .@"return" = "undefined",
+ },
+ },
+ .@"request" = .{
+ .@"get" = getRequest,
+ .ro = true,
+ .ts = d.ts{
+ .tsdoc = "HTTP request",
+ .@"return" = "InstanceType<Request>",
+ },
+ },
},
- true,
- false,
);
pub fn getClient(