diff options
author | 2021-07-14 00:14:11 -0700 | |
---|---|---|
committer | 2021-07-14 00:14:11 -0700 | |
commit | f4381bb2979e7409e4809c490a5a080a2b3753c3 (patch) | |
tree | e47de3102684fc7e5820428f5d510b2939b58c79 | |
parent | ab73c7b323c222e5d1172c07036653ca98aa8e6b (diff) | |
download | bun-f4381bb2979e7409e4809c490a5a080a2b3753c3.tar.gz bun-f4381bb2979e7409e4809c490a5a080a2b3753c3.tar.zst bun-f4381bb2979e7409e4809c490a5a080a2b3753c3.zip |
ts
-rw-r--r-- | build.zig | 32 | ||||
-rw-r--r-- | src/deps/picohttp.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/api/router.zig | 244 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 499 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 60 | ||||
-rw-r--r-- | src/javascript/jsc/typescript.zig | 76 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 101 |
7 files changed, 835 insertions, 179 deletions
@@ -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( |