mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
ts
This commit is contained in:
32
build.zig
32
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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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} />);
|
||||
\\```
|
||||
,
|
||||
},
|
||||
},
|
||||
.@"route" = .{
|
||||
.@"get" = getRoute,
|
||||
.ro = true,
|
||||
},
|
||||
.query = .{
|
||||
.@"get" = getQuery,
|
||||
.ro = true,
|
||||
},
|
||||
.pageFilePath = .{
|
||||
.@"get" = getPageFilePath,
|
||||
.ro = true,
|
||||
},
|
||||
},
|
||||
.{
|
||||
.@"pathname" = .{
|
||||
.get = getPathname,
|
||||
.ro = true,
|
||||
.ts = d.ts{
|
||||
.@"return" = "string",
|
||||
.@"tsdoc" = "URL path as appears in a web browser's address bar",
|
||||
},
|
||||
},
|
||||
false,
|
||||
false,
|
||||
);
|
||||
.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);
|
||||
\\```
|
||||
,
|
||||
},
|
||||
},
|
||||
.@"route" = .{
|
||||
.@"get" = getRoute,
|
||||
.ro = true,
|
||||
.ts = d.ts{
|
||||
.@"return" = "string",
|
||||
.tsdoc =
|
||||
\\Route name
|
||||
\\@example
|
||||
\\`"blog/posts/[id]"`
|
||||
\\`"blog/posts/[id]/[[...slug]]"`
|
||||
\\`"blog"`
|
||||
,
|
||||
},
|
||||
},
|
||||
.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]");
|
||||
\\```
|
||||
,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
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 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 finalize(
|
||||
this: *Router,
|
||||
ctx: js.JSObjectRef,
|
||||
) void {
|
||||
// this.deinit();
|
||||
return js.JSValueMakeString(ctx, this.file_path_str);
|
||||
}
|
||||
|
||||
pub fn finalize(
|
||||
this: *Router,
|
||||
ctx: js.JSObjectRef,
|
||||
) void {
|
||||
// this.deinit();
|
||||
}
|
||||
|
||||
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 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.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[0.. :0]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return js.JSValueMakeString(ctx, this.pathname_str);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
76
src/javascript/jsc/typescript.zig
Normal file
76
src/javascript/jsc/typescript.zig
Normal file
@@ -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");
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user