diff options
author | 2021-06-26 23:12:57 -0700 | |
---|---|---|
committer | 2021-06-26 23:12:57 -0700 | |
commit | 506d9b81a7c9dac5dd870f6735c39df105e72fd4 (patch) | |
tree | 4dc6e2f51867acf1cf5d601aa5444a99a9f95142 | |
parent | 3a95a74b7feadb59a215ff06446ccebff4a4008e (diff) | |
download | bun-506d9b81a7c9dac5dd870f6735c39df105e72fd4.tar.gz bun-506d9b81a7c9dac5dd870f6735c39df105e72fd4.tar.zst bun-506d9b81a7c9dac5dd870f6735c39df105e72fd4.zip |
wip
-rw-r--r-- | .vscode/launch.json | 14 | ||||
-rw-r--r-- | build.zig | 17 | ||||
-rw-r--r-- | src/api/schema.d.ts | 7 | ||||
-rw-r--r-- | src/api/schema.js | 8 | ||||
-rw-r--r-- | src/api/schema.peechy | 1 | ||||
-rw-r--r-- | src/api/schema.zig | 2288 | ||||
-rw-r--r-- | src/bundler.zig | 17 | ||||
-rw-r--r-- | src/cli.zig | 2 | ||||
-rw-r--r-- | src/hash_map.zig | 13 | ||||
-rw-r--r-- | src/javascript/jsc/JavascriptCore.zig | 122 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 1264 | ||||
-rw-r--r-- | src/main_javascript.zig | 374 | ||||
-rw-r--r-- | src/options.zig | 20 | ||||
-rw-r--r-- | src/string_immutable.zig | 7 | ||||
-rw-r--r-- | src/wtf_string_mutable.zig | 14 |
15 files changed, 2803 insertions, 1365 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ed5db09e..3f9b8d7be 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,6 +11,20 @@ // "cwd": "${workspaceFolder}", // "console": "internalConsole" // }, + { + "type": "lldb", + "request": "launch", + "name": "Eval", + "program": "${workspaceFolder}/build/debug/macos-x86_64/spjs", + "args": [ + "./simple.css", + "--resolve=dev", + "--outdir=outcss", + "--public-url=https://localhost:9000/" + ], + "cwd": "${workspaceFolder}/src/test/fixtures", + "console": "internalConsole" + }, { "type": "lldb", @@ -27,7 +27,6 @@ pub fn build(b: *std.build.Builder) void { var cwd_buf = [_]u8{0} ** 4096; var cwd = std.os.getcwd(&cwd_buf) catch unreachable; var exe: *std.build.LibExeObjStep = undefined; - var javascript: *std.build.LibExeObjStep = undefined; var output_dir_buf = std.mem.zeroes([4096]u8); var bin_label = if (mode == std.builtin.Mode.Debug) "/debug/" else "/"; @@ -90,7 +89,7 @@ pub fn build(b: *std.build.Builder) void { } // exe.setLibCFile("libc.txt"); exe.linkLibC(); - + exe.linkLibCpp(); exe.addPackage(.{ .name = "clap", .path = .{ .path = "src/deps/zig-clap/clap.zig" }, @@ -130,6 +129,20 @@ pub fn build(b: *std.build.Builder) void { // exe.addSystemIncludeDir(sys); // } addPicoHTTP(exe, cwd); + var javascript = b.addExecutable("spjs", "src/main_javascript.zig"); + javascript.packages = exe.packages; + javascript.setOutputDir(output_dir); + javascript.setBuildMode(mode); + javascript.linkLibC(); + javascript.linkLibCpp(); + + if (target.getOsTag() == .macos) { + javascript.linkFramework("JavaScriptCore"); + exe.linkFramework("JavascriptCore"); + } + + javascript.strip = false; + javascript.install(); } exe.install(); diff --git a/src/api/schema.d.ts b/src/api/schema.d.ts index a15f7b878..a78156e10 100644 --- a/src/api/schema.d.ts +++ b/src/api/schema.d.ts @@ -55,13 +55,16 @@ type uint32 = number; } export enum Platform { browser = 1, - node = 2 + node = 2, + speedy = 3 } export const PlatformKeys = { 1: "browser", browser: "browser", 2: "node", - node: "node" + node: "node", + 3: "speedy", + speedy: "speedy" } export enum JSXRuntime { automatic = 1, diff --git a/src/api/schema.js b/src/api/schema.js index c4341c1d4..e48d857a6 100644 --- a/src/api/schema.js +++ b/src/api/schema.js @@ -53,14 +53,18 @@ const ResolveModeKeys = { const Platform = { "1": 1, "2": 2, + "3": 3, "browser": 1, - "node": 2 + "node": 2, + "speedy": 3 }; const PlatformKeys = { "1": "browser", "2": "node", + "3": "speedy", "browser": "browser", - "node": "node" + "node": "node", + "speedy": "speedy" }; const JSXRuntime = { "1": 1, diff --git a/src/api/schema.peechy b/src/api/schema.peechy index 74050e90f..1da6c8c67 100644 --- a/src/api/schema.peechy +++ b/src/api/schema.peechy @@ -20,6 +20,7 @@ smol ResolveMode { smol Platform { browser = 1; node = 2; + speedy = 3; } smol JSXRuntime { diff --git a/src/api/schema.zig b/src/api/schema.zig index 8227f5e88..0805ddfc1 100644 --- a/src/api/schema.zig +++ b/src/api/schema.zig @@ -1,3 +1,4 @@ + const std = @import("std"); pub const Reader = struct { @@ -281,1386 +282,1487 @@ pub fn Writer(comptime WritableStream: type) type { pub const ByteWriter = Writer(*std.io.FixedBufferStream([]u8)); pub const FileWriter = Writer(std.fs.File); -pub const Api = struct { - pub const Loader = enum(u8) { - _none, - /// jsx - jsx, - /// js - js, - /// ts - ts, - /// tsx - tsx, +pub const Api = struct { - /// css - css, +pub const Loader = enum(u8) { - /// file - file, +_none, + /// jsx + jsx, - /// json - json, + /// js + js, - _, + /// ts + ts, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; + /// tsx + tsx, - pub const ResolveMode = enum(u8) { - _none, - /// disable - disable, + /// css + css, - /// lazy - lazy, + /// file + file, - /// dev - dev, + /// json + json, - /// bundle - bundle, +_, - _, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; + +}; - pub const Platform = enum(u8) { - _none, - /// browser - browser, +pub const ResolveMode = enum(u8) { - /// node - node, +_none, + /// disable + disable, - _, + /// lazy + lazy, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; + /// dev + dev, - pub const JsxRuntime = enum(u8) { - _none, - /// automatic - automatic, + /// bundle + bundle, - /// classic - classic, +_, - _, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; + +}; - pub const Jsx = struct { - /// factory - factory: []const u8, +pub const Platform = enum(u8) { - /// runtime - runtime: JsxRuntime, +_none, + /// browser + browser, - /// fragment - fragment: []const u8, + /// node + node, - /// development - development: bool = false, + /// speedy + speedy, - /// import_source - import_source: []const u8, +_, - /// react_fast_refresh - react_fast_refresh: bool = false, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - pub fn decode(reader: anytype) anyerror!Jsx { - var this = std.mem.zeroes(Jsx); + +}; - this.factory = try reader.readValue([]const u8); - this.runtime = try reader.readValue(JsxRuntime); - this.fragment = try reader.readValue([]const u8); - this.development = try reader.readValue(bool); - this.import_source = try reader.readValue([]const u8); - this.react_fast_refresh = try reader.readValue(bool); - return this; - } +pub const JsxRuntime = enum(u8) { - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.factory); - try writer.writeEnum(this.runtime); - try writer.writeValue(this.fragment); - try writer.writeInt(@intCast(u8, @boolToInt(this.development))); - try writer.writeValue(this.import_source); - try writer.writeInt(@intCast(u8, @boolToInt(this.react_fast_refresh))); - } - }; +_none, + /// automatic + automatic, - pub const StringPointer = packed struct { - /// offset - offset: u32 = 0, + /// classic + classic, - /// length - length: u32 = 0, +_, - pub fn decode(reader: anytype) anyerror!StringPointer { - var this = std.mem.zeroes(StringPointer); + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - this.offset = try reader.readValue(u32); - this.length = try reader.readValue(u32); - return this; - } + +}; - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.offset); - try writer.writeInt(this.length); - } - }; +pub const Jsx = struct { +/// factory +factory: []const u8, - pub const JavascriptBundledModule = struct { - /// path - path: StringPointer, +/// runtime +runtime: JsxRuntime, - /// code - code: StringPointer, +/// fragment +fragment: []const u8, - /// package_id - package_id: u32 = 0, +/// development +development: bool = false, - /// id - id: u32 = 0, +/// import_source +import_source: []const u8, - /// path_extname_length - path_extname_length: u8 = 0, +/// react_fast_refresh +react_fast_refresh: bool = false, - pub fn decode(reader: anytype) anyerror!JavascriptBundledModule { - var this = std.mem.zeroes(JavascriptBundledModule); - this.path = try reader.readValue(StringPointer); - this.code = try reader.readValue(StringPointer); - this.package_id = try reader.readValue(u32); - this.id = try reader.readValue(u32); - this.path_extname_length = try reader.readValue(u8); - return this; - } +pub fn decode(reader: anytype) anyerror!Jsx { + var this = std.mem.zeroes(Jsx); - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.path); - try writer.writeValue(this.code); - try writer.writeInt(this.package_id); - try writer.writeInt(this.id); - try writer.writeInt(this.path_extname_length); - } - }; + this.factory = try reader.readValue([]const u8); + this.runtime = try reader.readValue(JsxRuntime); + this.fragment = try reader.readValue([]const u8); + this.development = try reader.readValue(bool); + this.import_source = try reader.readValue([]const u8); + this.react_fast_refresh = try reader.readValue(bool); + return this; +} - pub const JavascriptBundledPackage = struct { - /// name - name: StringPointer, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.factory); + try writer.writeEnum(this.runtime); + try writer.writeValue(this.fragment); + try writer.writeInt(@intCast(u8, @boolToInt(this.development))); + try writer.writeValue(this.import_source); + try writer.writeInt(@intCast(u8, @boolToInt(this.react_fast_refresh))); +} - /// version - version: StringPointer, +}; - /// hash - hash: u32 = 0, +pub const StringPointer = packed struct { +/// offset +offset: u32 = 0, - /// modules_offset - modules_offset: u32 = 0, +/// length +length: u32 = 0, - /// modules_length - modules_length: u32 = 0, - pub fn decode(reader: anytype) anyerror!JavascriptBundledPackage { - var this = std.mem.zeroes(JavascriptBundledPackage); +pub fn decode(reader: anytype) anyerror!StringPointer { + var this = std.mem.zeroes(StringPointer); - this.name = try reader.readValue(StringPointer); - this.version = try reader.readValue(StringPointer); - this.hash = try reader.readValue(u32); - this.modules_offset = try reader.readValue(u32); - this.modules_length = try reader.readValue(u32); - return this; - } + this.offset = try reader.readValue(u32); + this.length = try reader.readValue(u32); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.name); - try writer.writeValue(this.version); - try writer.writeInt(this.hash); - try writer.writeInt(this.modules_offset); - try writer.writeInt(this.modules_length); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.offset); + try writer.writeInt(this.length); +} - pub const JavascriptBundle = struct { - /// modules - modules: []const JavascriptBundledModule, +}; - /// packages - packages: []const JavascriptBundledPackage, +pub const JavascriptBundledModule = struct { +/// path +path: StringPointer, - /// etag - etag: []const u8, +/// code +code: StringPointer, - /// generated_at - generated_at: u32 = 0, +/// package_id +package_id: u32 = 0, - /// app_package_json_dependencies_hash - app_package_json_dependencies_hash: []const u8, +/// id +id: u32 = 0, - /// import_from_name - import_from_name: []const u8, +/// path_extname_length +path_extname_length: u8 = 0, - /// manifest_string - manifest_string: []const u8, - pub fn decode(reader: anytype) anyerror!JavascriptBundle { - var this = std.mem.zeroes(JavascriptBundle); +pub fn decode(reader: anytype) anyerror!JavascriptBundledModule { + var this = std.mem.zeroes(JavascriptBundledModule); - this.modules = try reader.readArray(JavascriptBundledModule); - this.packages = try reader.readArray(JavascriptBundledPackage); - this.etag = try reader.readArray(u8); - this.generated_at = try reader.readValue(u32); - this.app_package_json_dependencies_hash = try reader.readArray(u8); - this.import_from_name = try reader.readArray(u8); - this.manifest_string = try reader.readArray(u8); - return this; - } + this.path = try reader.readValue(StringPointer); + this.code = try reader.readValue(StringPointer); + this.package_id = try reader.readValue(u32); + this.id = try reader.readValue(u32); + this.path_extname_length = try reader.readValue(u8); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(JavascriptBundledModule, this.modules); - try writer.writeArray(JavascriptBundledPackage, this.packages); - try writer.writeArray(u8, this.etag); - try writer.writeInt(this.generated_at); - try writer.writeArray(u8, this.app_package_json_dependencies_hash); - try writer.writeArray(u8, this.import_from_name); - try writer.writeArray(u8, this.manifest_string); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.path); + try writer.writeValue(this.code); + try writer.writeInt(this.package_id); + try writer.writeInt(this.id); + try writer.writeInt(this.path_extname_length); +} - pub const JavascriptBundleContainer = struct { - /// bundle_format_version - bundle_format_version: ?u32 = null, +}; - /// bundle - bundle: ?JavascriptBundle = null, +pub const JavascriptBundledPackage = struct { +/// name +name: StringPointer, - /// code_length - code_length: ?u32 = null, +/// version +version: StringPointer, - pub fn decode(reader: anytype) anyerror!JavascriptBundleContainer { - var this = std.mem.zeroes(JavascriptBundleContainer); +/// hash +hash: u32 = 0, - while (true) { - switch (try reader.readByte()) { - 0 => { - return this; - }, +/// modules_offset +modules_offset: u32 = 0, - 1 => { - this.bundle_format_version = try reader.readValue(u32); - }, - 2 => { - this.bundle = try reader.readValue(JavascriptBundle); - }, - 3 => { - this.code_length = try reader.readValue(u32); - }, - else => { - return error.InvalidMessage; - }, +/// modules_length +modules_length: u32 = 0, + + +pub fn decode(reader: anytype) anyerror!JavascriptBundledPackage { + var this = std.mem.zeroes(JavascriptBundledPackage); + + this.name = try reader.readValue(StringPointer); + this.version = try reader.readValue(StringPointer); + this.hash = try reader.readValue(u32); + this.modules_offset = try reader.readValue(u32); + this.modules_length = try reader.readValue(u32); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.name); + try writer.writeValue(this.version); + try writer.writeInt(this.hash); + try writer.writeInt(this.modules_offset); + try writer.writeInt(this.modules_length); +} + +}; + +pub const JavascriptBundle = struct { +/// modules +modules: []const JavascriptBundledModule, + +/// packages +packages: []const JavascriptBundledPackage, + +/// etag +etag: []const u8, + +/// generated_at +generated_at: u32 = 0, + +/// app_package_json_dependencies_hash +app_package_json_dependencies_hash: []const u8, + +/// import_from_name +import_from_name: []const u8, + +/// manifest_string +manifest_string: []const u8, + + +pub fn decode(reader: anytype) anyerror!JavascriptBundle { + var this = std.mem.zeroes(JavascriptBundle); + + this.modules = try reader.readArray(JavascriptBundledModule); + this.packages = try reader.readArray(JavascriptBundledPackage); + this.etag = try reader.readArray(u8); + this.generated_at = try reader.readValue(u32); + this.app_package_json_dependencies_hash = try reader.readArray(u8); + this.import_from_name = try reader.readArray(u8); + this.manifest_string = try reader.readArray(u8); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(JavascriptBundledModule, this.modules); + try writer.writeArray(JavascriptBundledPackage, this.packages); + try writer.writeArray(u8, this.etag); + try writer.writeInt(this.generated_at); + try writer.writeArray(u8, this.app_package_json_dependencies_hash); + try writer.writeArray(u8, this.import_from_name); + try writer.writeArray(u8, this.manifest_string); +} + +}; + +pub const JavascriptBundleContainer = struct { +/// bundle_format_version +bundle_format_version: ?u32 = null, + +/// bundle +bundle: ?JavascriptBundle = null, + +/// code_length +code_length: ?u32 = null, + + +pub fn decode(reader: anytype) anyerror!JavascriptBundleContainer { + var this = std.mem.zeroes(JavascriptBundleContainer); + + while(true) { + switch (try reader.readByte()) { + 0 => { return this; }, + + 1 => { + this.bundle_format_version = try reader.readValue(u32); +}, + 2 => { + this.bundle = try reader.readValue(JavascriptBundle); +}, + 3 => { + this.code_length = try reader.readValue(u32); +}, + else => { + return error.InvalidMessage; + }, + } + } +unreachable; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { +if (this.bundle_format_version) |bundle_format_version| { + try writer.writeFieldID(1); + try writer.writeInt(bundle_format_version); +} +if (this.bundle) |bundle| { + try writer.writeFieldID(2); + try writer.writeValue(bundle); +} +if (this.code_length) |code_length| { + try writer.writeFieldID(3); + try writer.writeInt(code_length); +} +try writer.endMessage(); +} + +}; + +pub const ScanDependencyMode = enum(u8) { + +_none, + /// app + app, + + /// all + all, + +_, + + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); } - } - unreachable; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - if (this.bundle_format_version) |bundle_format_version| { - try writer.writeFieldID(1); - try writer.writeInt(bundle_format_version); - } - if (this.bundle) |bundle| { - try writer.writeFieldID(2); - try writer.writeValue(bundle); - } - if (this.code_length) |code_length| { - try writer.writeFieldID(3); - try writer.writeInt(code_length); - } - try writer.endMessage(); - } - }; + +}; - pub const ScanDependencyMode = enum(u8) { - _none, - /// app - app, +pub const ModuleImportType = enum(u8) { - /// all - all, +_none, + /// import + import, - _, + /// require + require, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +_, - pub const ModuleImportType = enum(u8) { - _none, - /// import - import, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - /// require - require, + +}; - _, +pub const ModuleImportRecord = struct { +/// kind +kind: ModuleImportType, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +/// path +path: []const u8, - pub const ModuleImportRecord = struct { - /// kind - kind: ModuleImportType, +/// dynamic +dynamic: bool = false, - /// path - path: []const u8, - /// dynamic - dynamic: bool = false, +pub fn decode(reader: anytype) anyerror!ModuleImportRecord { + var this = std.mem.zeroes(ModuleImportRecord); - pub fn decode(reader: anytype) anyerror!ModuleImportRecord { - var this = std.mem.zeroes(ModuleImportRecord); + this.kind = try reader.readValue(ModuleImportType); + this.path = try reader.readValue([]const u8); + this.dynamic = try reader.readValue(bool); + return this; +} - this.kind = try reader.readValue(ModuleImportType); - this.path = try reader.readValue([]const u8); - this.dynamic = try reader.readValue(bool); - return this; - } +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.kind); + try writer.writeValue(this.path); + try writer.writeInt(@intCast(u8, @boolToInt(this.dynamic))); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeEnum(this.kind); - try writer.writeValue(this.path); - try writer.writeInt(@intCast(u8, @boolToInt(this.dynamic))); - } - }; +}; - pub const Module = struct { - /// path - path: []const u8, +pub const Module = struct { +/// path +path: []const u8, - /// imports - imports: []const ModuleImportRecord, +/// imports +imports: []const ModuleImportRecord, - pub fn decode(reader: anytype) anyerror!Module { - var this = std.mem.zeroes(Module); - this.path = try reader.readValue([]const u8); - this.imports = try reader.readArray(ModuleImportRecord); - return this; - } +pub fn decode(reader: anytype) anyerror!Module { + var this = std.mem.zeroes(Module); - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.path); - try writer.writeArray(ModuleImportRecord, this.imports); - } - }; + this.path = try reader.readValue([]const u8); + this.imports = try reader.readArray(ModuleImportRecord); + return this; +} - pub const StringMap = struct { - /// keys - keys: []const []const u8, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.path); + try writer.writeArray(ModuleImportRecord, this.imports); +} - /// values - values: []const []const u8, +}; - pub fn decode(reader: anytype) anyerror!StringMap { - var this = std.mem.zeroes(StringMap); +pub const StringMap = struct { +/// keys +keys: []const []const u8, - this.keys = try reader.readArray([]const u8); - this.values = try reader.readArray([]const u8); - return this; - } +/// values +values: []const []const u8, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray([]const u8, this.keys); - try writer.writeArray([]const u8, this.values); - } - }; - pub const LoaderMap = struct { - /// extensions - extensions: []const []const u8, +pub fn decode(reader: anytype) anyerror!StringMap { + var this = std.mem.zeroes(StringMap); - /// loaders - loaders: []const Loader, + this.keys = try reader.readArray([]const u8); + this.values = try reader.readArray([]const u8); + return this; +} - pub fn decode(reader: anytype) anyerror!LoaderMap { - var this = std.mem.zeroes(LoaderMap); +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray([]const u8, this.keys); + try writer.writeArray([]const u8, this.values); +} - this.extensions = try reader.readArray([]const u8); - this.loaders = try reader.readArray(Loader); - return this; - } +}; - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray([]const u8, this.extensions); - try writer.writeArray(Loader, this.loaders); - } - }; +pub const LoaderMap = struct { +/// extensions +extensions: []const []const u8, - pub const TransformOptions = struct { - /// jsx - jsx: ?Jsx = null, +/// loaders +loaders: []const Loader, - /// tsconfig_override - tsconfig_override: ?[]const u8 = null, - /// resolve - resolve: ?ResolveMode = null, +pub fn decode(reader: anytype) anyerror!LoaderMap { + var this = std.mem.zeroes(LoaderMap); - /// public_url - public_url: ?[]const u8 = null, + this.extensions = try reader.readArray([]const u8); + this.loaders = try reader.readArray(Loader); + return this; +} - /// absolute_working_dir - absolute_working_dir: ?[]const u8 = null, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray([]const u8, this.extensions); + try writer.writeArray(Loader, this.loaders); +} - /// define - define: ?StringMap = null, +}; - /// preserve_symlinks - preserve_symlinks: ?bool = null, +pub const TransformOptions = struct { +/// jsx +jsx: ?Jsx = null, + +/// tsconfig_override +tsconfig_override: ?[]const u8 = null, + +/// resolve +resolve: ?ResolveMode = null, + +/// public_url +public_url: ?[]const u8 = null, + +/// absolute_working_dir +absolute_working_dir: ?[]const u8 = null, + +/// define +define: ?StringMap = null, + +/// preserve_symlinks +preserve_symlinks: ?bool = null, + +/// entry_points +entry_points: []const []const u8, + +/// write +write: ?bool = null, + +/// inject +inject: []const []const u8, + +/// output_dir +output_dir: ?[]const u8 = null, + +/// external +external: []const []const u8, + +/// loaders +loaders: ?LoaderMap = null, + +/// main_fields +main_fields: []const []const u8, + +/// platform +platform: ?Platform = null, + +/// serve +serve: ?bool = null, + +/// extension_order +extension_order: []const []const u8, + +/// public_dir +public_dir: ?[]const u8 = null, + +/// only_scan_dependencies +only_scan_dependencies: ?ScanDependencyMode = null, + +/// generate_node_module_bundle +generate_node_module_bundle: ?bool = null, + +/// node_modules_bundle_path +node_modules_bundle_path: ?[]const u8 = null, + + +pub fn decode(reader: anytype) anyerror!TransformOptions { + var this = std.mem.zeroes(TransformOptions); + + while(true) { + switch (try reader.readByte()) { + 0 => { return this; }, + + 1 => { + this.jsx = try reader.readValue(Jsx); +}, + 2 => { + this.tsconfig_override = try reader.readValue([]const u8); +}, + 3 => { + this.resolve = try reader.readValue(ResolveMode); +}, + 4 => { + this.public_url = try reader.readValue([]const u8); +}, + 5 => { + this.absolute_working_dir = try reader.readValue([]const u8); +}, + 6 => { + this.define = try reader.readValue(StringMap); +}, + 7 => { + this.preserve_symlinks = try reader.readValue(bool); +}, + 8 => { + this.entry_points = try reader.readArray([]const u8); +}, + 9 => { + this.write = try reader.readValue(bool); +}, + 10 => { + this.inject = try reader.readArray([]const u8); +}, + 11 => { + this.output_dir = try reader.readValue([]const u8); +}, + 12 => { + this.external = try reader.readArray([]const u8); +}, + 13 => { + this.loaders = try reader.readValue(LoaderMap); +}, + 14 => { + this.main_fields = try reader.readArray([]const u8); +}, + 15 => { + this.platform = try reader.readValue(Platform); +}, + 16 => { + this.serve = try reader.readValue(bool); +}, + 17 => { + this.extension_order = try reader.readArray([]const u8); +}, + 18 => { + this.public_dir = try reader.readValue([]const u8); +}, + 19 => { + this.only_scan_dependencies = try reader.readValue(ScanDependencyMode); +}, + 20 => { + this.generate_node_module_bundle = try reader.readValue(bool); +}, + 21 => { + this.node_modules_bundle_path = try reader.readValue([]const u8); +}, + else => { + return error.InvalidMessage; + }, + } + } +unreachable; +} - /// entry_points - entry_points: []const []const u8, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { +if (this.jsx) |jsx| { + try writer.writeFieldID(1); + try writer.writeValue(jsx); +} +if (this.tsconfig_override) |tsconfig_override| { + try writer.writeFieldID(2); + try writer.writeValue(tsconfig_override); +} +if (this.resolve) |resolve| { + try writer.writeFieldID(3); + try writer.writeEnum(resolve); +} +if (this.public_url) |public_url| { + try writer.writeFieldID(4); + try writer.writeValue(public_url); +} +if (this.absolute_working_dir) |absolute_working_dir| { + try writer.writeFieldID(5); + try writer.writeValue(absolute_working_dir); +} +if (this.define) |define| { + try writer.writeFieldID(6); + try writer.writeValue(define); +} +if (this.preserve_symlinks) |preserve_symlinks| { + try writer.writeFieldID(7); + try writer.writeInt(@intCast(u8, @boolToInt(preserve_symlinks))); +} +if (this.entry_points) |entry_points| { + try writer.writeFieldID(8); + try writer.writeArray([]const u8, entry_points); +} +if (this.write) |write| { + try writer.writeFieldID(9); + try writer.writeInt(@intCast(u8, @boolToInt(write))); +} +if (this.inject) |inject| { + try writer.writeFieldID(10); + try writer.writeArray([]const u8, inject); +} +if (this.output_dir) |output_dir| { + try writer.writeFieldID(11); + try writer.writeValue(output_dir); +} +if (this.external) |external| { + try writer.writeFieldID(12); + try writer.writeArray([]const u8, external); +} +if (this.loaders) |loaders| { + try writer.writeFieldID(13); + try writer.writeValue(loaders); +} +if (this.main_fields) |main_fields| { + try writer.writeFieldID(14); + try writer.writeArray([]const u8, main_fields); +} +if (this.platform) |platform| { + try writer.writeFieldID(15); + try writer.writeEnum(platform); +} +if (this.serve) |serve| { + try writer.writeFieldID(16); + try writer.writeInt(@intCast(u8, @boolToInt(serve))); +} +if (this.extension_order) |extension_order| { + try writer.writeFieldID(17); + try writer.writeArray([]const u8, extension_order); +} +if (this.public_dir) |public_dir| { + try writer.writeFieldID(18); + try writer.writeValue(public_dir); +} +if (this.only_scan_dependencies) |only_scan_dependencies| { + try writer.writeFieldID(19); + try writer.writeEnum(only_scan_dependencies); +} +if (this.generate_node_module_bundle) |generate_node_module_bundle| { + try writer.writeFieldID(20); + try writer.writeInt(@intCast(u8, @boolToInt(generate_node_module_bundle))); +} +if (this.node_modules_bundle_path) |node_modules_bundle_path| { + try writer.writeFieldID(21); + try writer.writeValue(node_modules_bundle_path); +} +try writer.endMessage(); +} - /// write - write: ?bool = null, +}; - /// inject - inject: []const []const u8, +pub const FileHandle = struct { +/// path +path: []const u8, - /// output_dir - output_dir: ?[]const u8 = null, +/// size +size: u32 = 0, - /// external - external: []const []const u8, +/// fd +fd: u32 = 0, - /// loaders - loaders: ?LoaderMap = null, - /// main_fields - main_fields: []const []const u8, +pub fn decode(reader: anytype) anyerror!FileHandle { + var this = std.mem.zeroes(FileHandle); - /// platform - platform: ?Platform = null, + this.path = try reader.readValue([]const u8); + this.size = try reader.readValue(u32); + this.fd = try reader.readValue(u32); + return this; +} - /// serve - serve: ?bool = null, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.path); + try writer.writeInt(this.size); + try writer.writeInt(this.fd); +} - /// extension_order - extension_order: []const []const u8, +}; - /// public_dir - public_dir: ?[]const u8 = null, +pub const Transform = struct { +/// handle +handle: ?FileHandle = null, + +/// path +path: ?[]const u8 = null, + +/// contents +contents: []const u8, + +/// loader +loader: ?Loader = null, + +/// options +options: ?TransformOptions = null, + + +pub fn decode(reader: anytype) anyerror!Transform { + var this = std.mem.zeroes(Transform); + + while(true) { + switch (try reader.readByte()) { + 0 => { return this; }, + + 1 => { + this.handle = try reader.readValue(FileHandle); +}, + 2 => { + this.path = try reader.readValue([]const u8); +}, + 3 => { + this.contents = try reader.readArray(u8); +}, + 4 => { + this.loader = try reader.readValue(Loader); +}, + 5 => { + this.options = try reader.readValue(TransformOptions); +}, + else => { + return error.InvalidMessage; + }, + } + } +unreachable; +} - /// only_scan_dependencies - only_scan_dependencies: ?ScanDependencyMode = null, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { +if (this.handle) |handle| { + try writer.writeFieldID(1); + try writer.writeValue(handle); +} +if (this.path) |path| { + try writer.writeFieldID(2); + try writer.writeValue(path); +} +if (this.contents) |contents| { + try writer.writeFieldID(3); + try writer.writeArray(u8, contents); +} +if (this.loader) |loader| { + try writer.writeFieldID(4); + try writer.writeEnum(loader); +} +if (this.options) |options| { + try writer.writeFieldID(5); + try writer.writeValue(options); +} +try writer.endMessage(); +} - /// generate_node_module_bundle - generate_node_module_bundle: ?bool = null, +}; - /// node_modules_bundle_path - node_modules_bundle_path: ?[]const u8 = null, +pub const TransformResponseStatus = enum(u32) { - pub fn decode(reader: anytype) anyerror!TransformOptions { - var this = std.mem.zeroes(TransformOptions); +_none, + /// success + success, - while (true) { - switch (try reader.readByte()) { - 0 => { - return this; - }, + /// fail + fail, - 1 => { - this.jsx = try reader.readValue(Jsx); - }, - 2 => { - this.tsconfig_override = try reader.readValue([]const u8); - }, - 3 => { - this.resolve = try reader.readValue(ResolveMode); - }, - 4 => { - this.public_url = try reader.readValue([]const u8); - }, - 5 => { - this.absolute_working_dir = try reader.readValue([]const u8); - }, - 6 => { - this.define = try reader.readValue(StringMap); - }, - 7 => { - this.preserve_symlinks = try reader.readValue(bool); - }, - 8 => { - this.entry_points = try reader.readArray([]const u8); - }, - 9 => { - this.write = try reader.readValue(bool); - }, - 10 => { - this.inject = try reader.readArray([]const u8); - }, - 11 => { - this.output_dir = try reader.readValue([]const u8); - }, - 12 => { - this.external = try reader.readArray([]const u8); - }, - 13 => { - this.loaders = try reader.readValue(LoaderMap); - }, - 14 => { - this.main_fields = try reader.readArray([]const u8); - }, - 15 => { - this.platform = try reader.readValue(Platform); - }, - 16 => { - this.serve = try reader.readValue(bool); - }, - 17 => { - this.extension_order = try reader.readArray([]const u8); - }, - 18 => { - this.public_dir = try reader.readValue([]const u8); - }, - 19 => { - this.only_scan_dependencies = try reader.readValue(ScanDependencyMode); - }, - 20 => { - this.generate_node_module_bundle = try reader.readValue(bool); - }, - 21 => { - this.node_modules_bundle_path = try reader.readValue([]const u8); - }, - else => { - return error.InvalidMessage; - }, +_, + + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); } - } - unreachable; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - if (this.jsx) |jsx| { - try writer.writeFieldID(1); - try writer.writeValue(jsx); - } - if (this.tsconfig_override) |tsconfig_override| { - try writer.writeFieldID(2); - try writer.writeValue(tsconfig_override); - } - if (this.resolve) |resolve| { - try writer.writeFieldID(3); - try writer.writeEnum(resolve); - } - if (this.public_url) |public_url| { - try writer.writeFieldID(4); - try writer.writeValue(public_url); - } - if (this.absolute_working_dir) |absolute_working_dir| { - try writer.writeFieldID(5); - try writer.writeValue(absolute_working_dir); - } - if (this.define) |define| { - try writer.writeFieldID(6); - try writer.writeValue(define); - } - if (this.preserve_symlinks) |preserve_symlinks| { - try writer.writeFieldID(7); - try writer.writeInt(@intCast(u8, @boolToInt(preserve_symlinks))); - } - if (this.entry_points) |entry_points| { - try writer.writeFieldID(8); - try writer.writeArray([]const u8, entry_points); - } - if (this.write) |write| { - try writer.writeFieldID(9); - try writer.writeInt(@intCast(u8, @boolToInt(write))); - } - if (this.inject) |inject| { - try writer.writeFieldID(10); - try writer.writeArray([]const u8, inject); - } - if (this.output_dir) |output_dir| { - try writer.writeFieldID(11); - try writer.writeValue(output_dir); - } - if (this.external) |external| { - try writer.writeFieldID(12); - try writer.writeArray([]const u8, external); - } - if (this.loaders) |loaders| { - try writer.writeFieldID(13); - try writer.writeValue(loaders); - } - if (this.main_fields) |main_fields| { - try writer.writeFieldID(14); - try writer.writeArray([]const u8, main_fields); - } - if (this.platform) |platform| { - try writer.writeFieldID(15); - try writer.writeEnum(platform); - } - if (this.serve) |serve| { - try writer.writeFieldID(16); - try writer.writeInt(@intCast(u8, @boolToInt(serve))); - } - if (this.extension_order) |extension_order| { - try writer.writeFieldID(17); - try writer.writeArray([]const u8, extension_order); - } - if (this.public_dir) |public_dir| { - try writer.writeFieldID(18); - try writer.writeValue(public_dir); - } - if (this.only_scan_dependencies) |only_scan_dependencies| { - try writer.writeFieldID(19); - try writer.writeEnum(only_scan_dependencies); - } - if (this.generate_node_module_bundle) |generate_node_module_bundle| { - try writer.writeFieldID(20); - try writer.writeInt(@intCast(u8, @boolToInt(generate_node_module_bundle))); - } - if (this.node_modules_bundle_path) |node_modules_bundle_path| { - try writer.writeFieldID(21); - try writer.writeValue(node_modules_bundle_path); - } - try writer.endMessage(); - } - }; + +}; - pub const FileHandle = struct { - /// path - path: []const u8, +pub const OutputFile = struct { +/// data +data: []const u8, - /// size - size: u32 = 0, +/// path +path: []const u8, - /// fd - fd: u32 = 0, - pub fn decode(reader: anytype) anyerror!FileHandle { - var this = std.mem.zeroes(FileHandle); +pub fn decode(reader: anytype) anyerror!OutputFile { + var this = std.mem.zeroes(OutputFile); - this.path = try reader.readValue([]const u8); - this.size = try reader.readValue(u32); - this.fd = try reader.readValue(u32); - return this; - } + this.data = try reader.readArray(u8); + this.path = try reader.readValue([]const u8); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.path); - try writer.writeInt(this.size); - try writer.writeInt(this.fd); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(u8, this.data); + try writer.writeValue(this.path); +} - pub const Transform = struct { - /// handle - handle: ?FileHandle = null, +}; - /// path - path: ?[]const u8 = null, +pub const TransformResponse = struct { +/// status +status: TransformResponseStatus, - /// contents - contents: []const u8, +/// files +files: []const OutputFile, - /// loader - loader: ?Loader = null, +/// errors +errors: []const Message, - /// options - options: ?TransformOptions = null, - pub fn decode(reader: anytype) anyerror!Transform { - var this = std.mem.zeroes(Transform); +pub fn decode(reader: anytype) anyerror!TransformResponse { + var this = std.mem.zeroes(TransformResponse); - while (true) { - switch (try reader.readByte()) { - 0 => { - return this; - }, + this.status = try reader.readValue(TransformResponseStatus); + this.files = try reader.readArray(OutputFile); + this.errors = try reader.readArray(Message); + return this; +} - 1 => { - this.handle = try reader.readValue(FileHandle); - }, - 2 => { - this.path = try reader.readValue([]const u8); - }, - 3 => { - this.contents = try reader.readArray(u8); - }, - 4 => { - this.loader = try reader.readValue(Loader); - }, - 5 => { - this.options = try reader.readValue(TransformOptions); - }, - else => { - return error.InvalidMessage; - }, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.status); + try writer.writeArray(OutputFile, this.files); + try writer.writeArray(Message, this.errors); +} + +}; + +pub const MessageKind = enum(u32) { + +_none, + /// err + err, + + /// warn + warn, + + /// note + note, + + /// debug + debug, + +_, + + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); } - } - unreachable; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - if (this.handle) |handle| { - try writer.writeFieldID(1); - try writer.writeValue(handle); - } - if (this.path) |path| { - try writer.writeFieldID(2); - try writer.writeValue(path); - } - if (this.contents) |contents| { - try writer.writeFieldID(3); - try writer.writeArray(u8, contents); - } - if (this.loader) |loader| { - try writer.writeFieldID(4); - try writer.writeEnum(loader); - } - if (this.options) |options| { - try writer.writeFieldID(5); - try writer.writeValue(options); - } - try writer.endMessage(); - } - }; + +}; - pub const TransformResponseStatus = enum(u32) { - _none, - /// success - success, +pub const Location = struct { +/// file +file: []const u8, - /// fail - fail, +/// namespace +namespace: []const u8, - _, +/// line +line: i32 = 0, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +/// column +column: i32 = 0, - pub const OutputFile = struct { - /// data - data: []const u8, +/// line_text +line_text: []const u8, - /// path - path: []const u8, +/// suggestion +suggestion: []const u8, - pub fn decode(reader: anytype) anyerror!OutputFile { - var this = std.mem.zeroes(OutputFile); +/// offset +offset: u32 = 0, - this.data = try reader.readArray(u8); - this.path = try reader.readValue([]const u8); - return this; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(u8, this.data); - try writer.writeValue(this.path); - } - }; +pub fn decode(reader: anytype) anyerror!Location { + var this = std.mem.zeroes(Location); - pub const TransformResponse = struct { - /// status - status: TransformResponseStatus, + this.file = try reader.readValue([]const u8); + this.namespace = try reader.readValue([]const u8); + this.line = try reader.readValue(i32); + this.column = try reader.readValue(i32); + this.line_text = try reader.readValue([]const u8); + this.suggestion = try reader.readValue([]const u8); + this.offset = try reader.readValue(u32); + return this; +} - /// files - files: []const OutputFile, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeValue(this.file); + try writer.writeValue(this.namespace); + try writer.writeInt(this.line); + try writer.writeInt(this.column); + try writer.writeValue(this.line_text); + try writer.writeValue(this.suggestion); + try writer.writeInt(this.offset); +} - /// errors - errors: []const Message, +}; - pub fn decode(reader: anytype) anyerror!TransformResponse { - var this = std.mem.zeroes(TransformResponse); +pub const MessageData = struct { +/// text +text: ?[]const u8 = null, + +/// location +location: ?Location = null, + + +pub fn decode(reader: anytype) anyerror!MessageData { + var this = std.mem.zeroes(MessageData); + + while(true) { + switch (try reader.readByte()) { + 0 => { return this; }, + + 1 => { + this.text = try reader.readValue([]const u8); +}, + 2 => { + this.location = try reader.readValue(Location); +}, + else => { + return error.InvalidMessage; + }, + } + } +unreachable; +} - this.status = try reader.readValue(TransformResponseStatus); - this.files = try reader.readArray(OutputFile); - this.errors = try reader.readArray(Message); - return this; - } +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { +if (this.text) |text| { + try writer.writeFieldID(1); + try writer.writeValue(text); +} +if (this.location) |location| { + try writer.writeFieldID(2); + try writer.writeValue(location); +} +try writer.endMessage(); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeEnum(this.status); - try writer.writeArray(OutputFile, this.files); - try writer.writeArray(Message, this.errors); - } - }; +}; - pub const MessageKind = enum(u32) { - _none, - /// err - err, +pub const Message = struct { +/// kind +kind: MessageKind, - /// warn - warn, +/// data +data: MessageData, - /// note - note, +/// notes +notes: []const MessageData, - /// debug - debug, - _, +pub fn decode(reader: anytype) anyerror!Message { + var this = std.mem.zeroes(Message); - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; + this.kind = try reader.readValue(MessageKind); + this.data = try reader.readValue(MessageData); + this.notes = try reader.readArray(MessageData); + return this; +} - pub const Location = struct { - /// file - file: []const u8, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.kind); + try writer.writeValue(this.data); + try writer.writeArray(MessageData, this.notes); +} - /// namespace - namespace: []const u8, +}; - /// line - line: i32 = 0, +pub const Log = struct { +/// warnings +warnings: u32 = 0, - /// column - column: i32 = 0, +/// errors +errors: u32 = 0, - /// line_text - line_text: []const u8, +/// msgs +msgs: []const Message, - /// suggestion - suggestion: []const u8, - /// offset - offset: u32 = 0, +pub fn decode(reader: anytype) anyerror!Log { + var this = std.mem.zeroes(Log); - pub fn decode(reader: anytype) anyerror!Location { - var this = std.mem.zeroes(Location); + this.warnings = try reader.readValue(u32); + this.errors = try reader.readValue(u32); + this.msgs = try reader.readArray(Message); + return this; +} - this.file = try reader.readValue([]const u8); - this.namespace = try reader.readValue([]const u8); - this.line = try reader.readValue(i32); - this.column = try reader.readValue(i32); - this.line_text = try reader.readValue([]const u8); - this.suggestion = try reader.readValue([]const u8); - this.offset = try reader.readValue(u32); - return this; - } +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.warnings); + try writer.writeInt(this.errors); + try writer.writeArray(Message, this.msgs); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeValue(this.file); - try writer.writeValue(this.namespace); - try writer.writeInt(this.line); - try writer.writeInt(this.column); - try writer.writeValue(this.line_text); - try writer.writeValue(this.suggestion); - try writer.writeInt(this.offset); - } - }; +}; - pub const MessageData = struct { - /// text - text: ?[]const u8 = null, +pub const Reloader = enum(u8) { - /// location - location: ?Location = null, +_none, + /// disable + disable, - pub fn decode(reader: anytype) anyerror!MessageData { - var this = std.mem.zeroes(MessageData); + /// live + live, - while (true) { - switch (try reader.readByte()) { - 0 => { - return this; - }, + /// fast_refresh + fast_refresh, - 1 => { - this.text = try reader.readValue([]const u8); - }, - 2 => { - this.location = try reader.readValue(Location); - }, - else => { - return error.InvalidMessage; - }, +_, + + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); } - } - unreachable; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - if (this.text) |text| { - try writer.writeFieldID(1); - try writer.writeValue(text); - } - if (this.location) |location| { - try writer.writeFieldID(2); - try writer.writeValue(location); - } - try writer.endMessage(); - } - }; + +}; - pub const Message = struct { - /// kind - kind: MessageKind, +pub const WebsocketMessageKind = enum(u8) { - /// data - data: MessageData, +_none, + /// welcome + welcome, - /// notes - notes: []const MessageData, + /// file_change_notification + file_change_notification, - pub fn decode(reader: anytype) anyerror!Message { - var this = std.mem.zeroes(Message); + /// build_success + build_success, - this.kind = try reader.readValue(MessageKind); - this.data = try reader.readValue(MessageData); - this.notes = try reader.readArray(MessageData); - return this; - } + /// build_fail + build_fail, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeEnum(this.kind); - try writer.writeValue(this.data); - try writer.writeArray(MessageData, this.notes); - } - }; + /// manifest_success + manifest_success, - pub const Log = struct { - /// warnings - warnings: u32 = 0, + /// manifest_fail + manifest_fail, - /// errors - errors: u32 = 0, +_, - /// msgs - msgs: []const Message, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - pub fn decode(reader: anytype) anyerror!Log { - var this = std.mem.zeroes(Log); + +}; - this.warnings = try reader.readValue(u32); - this.errors = try reader.readValue(u32); - this.msgs = try reader.readArray(Message); - return this; - } +pub const WebsocketCommandKind = enum(u8) { - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.warnings); - try writer.writeInt(this.errors); - try writer.writeArray(Message, this.msgs); - } - }; +_none, + /// build + build, - pub const Reloader = enum(u8) { - _none, - /// disable - disable, + /// manifest + manifest, - /// live - live, +_, - /// fast_refresh - fast_refresh, + pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { + return try std.json.stringify(@tagName(self), opts, o); + } - _, + +}; - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +pub const WebsocketMessage = struct { +/// timestamp +timestamp: u32 = 0, - pub const WebsocketMessageKind = enum(u8) { - _none, - /// welcome - welcome, +/// kind +kind: WebsocketMessageKind, - /// file_change_notification - file_change_notification, - /// build_success - build_success, +pub fn decode(reader: anytype) anyerror!WebsocketMessage { + var this = std.mem.zeroes(WebsocketMessage); - /// build_fail - build_fail, + this.timestamp = try reader.readValue(u32); + this.kind = try reader.readValue(WebsocketMessageKind); + return this; +} - /// manifest_success - manifest_success, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.timestamp); + try writer.writeEnum(this.kind); +} - /// manifest_fail - manifest_fail, +}; - _, +pub const WebsocketMessageWelcome = struct { +/// epoch +epoch: u32 = 0, - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +/// javascriptReloader +javascript_reloader: Reloader, - pub const WebsocketCommandKind = enum(u8) { - _none, - /// build - build, - /// manifest - manifest, +pub fn decode(reader: anytype) anyerror!WebsocketMessageWelcome { + var this = std.mem.zeroes(WebsocketMessageWelcome); - _, + this.epoch = try reader.readValue(u32); + this.javascript_reloader = try reader.readValue(Reloader); + return this; +} - pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void { - return try std.json.stringify(@tagName(self), opts, o); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.epoch); + try writer.writeEnum(this.javascript_reloader); +} - pub const WebsocketMessage = struct { - /// timestamp - timestamp: u32 = 0, +}; - /// kind - kind: WebsocketMessageKind, +pub const WebsocketMessageFileChangeNotification = struct { +/// id +id: u32 = 0, - pub fn decode(reader: anytype) anyerror!WebsocketMessage { - var this = std.mem.zeroes(WebsocketMessage); +/// loader +loader: Loader, - this.timestamp = try reader.readValue(u32); - this.kind = try reader.readValue(WebsocketMessageKind); - return this; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.timestamp); - try writer.writeEnum(this.kind); - } - }; +pub fn decode(reader: anytype) anyerror!WebsocketMessageFileChangeNotification { + var this = std.mem.zeroes(WebsocketMessageFileChangeNotification); - pub const WebsocketMessageWelcome = struct { - /// epoch - epoch: u32 = 0, + this.id = try reader.readValue(u32); + this.loader = try reader.readValue(Loader); + return this; +} - /// javascriptReloader - javascript_reloader: Reloader, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeEnum(this.loader); +} - pub fn decode(reader: anytype) anyerror!WebsocketMessageWelcome { - var this = std.mem.zeroes(WebsocketMessageWelcome); +}; - this.epoch = try reader.readValue(u32); - this.javascript_reloader = try reader.readValue(Reloader); - return this; - } +pub const WebsocketCommand = struct { +/// kind +kind: WebsocketCommandKind, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.epoch); - try writer.writeEnum(this.javascript_reloader); - } - }; +/// timestamp +timestamp: u32 = 0, - pub const WebsocketMessageFileChangeNotification = struct { - /// id - id: u32 = 0, - /// loader - loader: Loader, +pub fn decode(reader: anytype) anyerror!WebsocketCommand { + var this = std.mem.zeroes(WebsocketCommand); - pub fn decode(reader: anytype) anyerror!WebsocketMessageFileChangeNotification { - var this = std.mem.zeroes(WebsocketMessageFileChangeNotification); + this.kind = try reader.readValue(WebsocketCommandKind); + this.timestamp = try reader.readValue(u32); + return this; +} - this.id = try reader.readValue(u32); - this.loader = try reader.readValue(Loader); - return this; - } +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeEnum(this.kind); + try writer.writeInt(this.timestamp); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - try writer.writeEnum(this.loader); - } - }; +}; - pub const WebsocketCommand = struct { - /// kind - kind: WebsocketCommandKind, +pub const WebsocketCommandBuild = packed struct { +/// id +id: u32 = 0, - /// timestamp - timestamp: u32 = 0, - pub fn decode(reader: anytype) anyerror!WebsocketCommand { - var this = std.mem.zeroes(WebsocketCommand); +pub fn decode(reader: anytype) anyerror!WebsocketCommandBuild { + var this = std.mem.zeroes(WebsocketCommandBuild); - this.kind = try reader.readValue(WebsocketCommandKind); - this.timestamp = try reader.readValue(u32); - return this; - } + this.id = try reader.readValue(u32); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeEnum(this.kind); - try writer.writeInt(this.timestamp); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); +} - pub const WebsocketCommandBuild = packed struct { - /// id - id: u32 = 0, +}; - pub fn decode(reader: anytype) anyerror!WebsocketCommandBuild { - var this = std.mem.zeroes(WebsocketCommandBuild); +pub const WebsocketCommandManifest = packed struct { +/// id +id: u32 = 0, - this.id = try reader.readValue(u32); - return this; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - } - }; +pub fn decode(reader: anytype) anyerror!WebsocketCommandManifest { + var this = std.mem.zeroes(WebsocketCommandManifest); - pub const WebsocketCommandManifest = packed struct { - /// id - id: u32 = 0, + this.id = try reader.readValue(u32); + return this; +} - pub fn decode(reader: anytype) anyerror!WebsocketCommandManifest { - var this = std.mem.zeroes(WebsocketCommandManifest); +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); +} - this.id = try reader.readValue(u32); - return this; - } +}; - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - } - }; +pub const WebsocketMessageBuildSuccess = struct { +/// id +id: u32 = 0, - pub const WebsocketMessageBuildSuccess = struct { - /// id - id: u32 = 0, +/// from_timestamp +from_timestamp: u32 = 0, - /// from_timestamp - from_timestamp: u32 = 0, +/// loader +loader: Loader, - /// loader - loader: Loader, +/// module_path +module_path: []const u8, - /// module_path - module_path: []const u8, +/// blob_length +blob_length: u32 = 0, - /// blob_length - blob_length: u32 = 0, - pub fn decode(reader: anytype) anyerror!WebsocketMessageBuildSuccess { - var this = std.mem.zeroes(WebsocketMessageBuildSuccess); +pub fn decode(reader: anytype) anyerror!WebsocketMessageBuildSuccess { + var this = std.mem.zeroes(WebsocketMessageBuildSuccess); - this.id = try reader.readValue(u32); - this.from_timestamp = try reader.readValue(u32); - this.loader = try reader.readValue(Loader); - this.module_path = try reader.readValue([]const u8); - this.blob_length = try reader.readValue(u32); - return this; - } + this.id = try reader.readValue(u32); + this.from_timestamp = try reader.readValue(u32); + this.loader = try reader.readValue(Loader); + this.module_path = try reader.readValue([]const u8); + this.blob_length = try reader.readValue(u32); + return this; +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - try writer.writeInt(this.from_timestamp); - try writer.writeEnum(this.loader); - try writer.writeValue(this.module_path); - try writer.writeInt(this.blob_length); - } - }; +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeInt(this.from_timestamp); + try writer.writeEnum(this.loader); + try writer.writeValue(this.module_path); + try writer.writeInt(this.blob_length); +} - pub const WebsocketMessageBuildFailure = struct { - /// id - id: u32 = 0, +}; - /// from_timestamp - from_timestamp: u32 = 0, +pub const WebsocketMessageBuildFailure = struct { +/// id +id: u32 = 0, - /// loader - loader: Loader, +/// from_timestamp +from_timestamp: u32 = 0, - /// module_path - module_path: []const u8, +/// loader +loader: Loader, - /// log - log: Log, +/// module_path +module_path: []const u8, - pub fn decode(reader: anytype) anyerror!WebsocketMessageBuildFailure { - var this = std.mem.zeroes(WebsocketMessageBuildFailure); +/// log +log: Log, - this.id = try reader.readValue(u32); - this.from_timestamp = try reader.readValue(u32); - this.loader = try reader.readValue(Loader); - this.module_path = try reader.readValue([]const u8); - this.log = try reader.readValue(Log); - return this; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - try writer.writeInt(this.from_timestamp); - try writer.writeEnum(this.loader); - try writer.writeValue(this.module_path); - try writer.writeValue(this.log); - } - }; +pub fn decode(reader: anytype) anyerror!WebsocketMessageBuildFailure { + var this = std.mem.zeroes(WebsocketMessageBuildFailure); - pub const DependencyManifest = struct { - /// ids - ids: []const u32, + this.id = try reader.readValue(u32); + this.from_timestamp = try reader.readValue(u32); + this.loader = try reader.readValue(Loader); + this.module_path = try reader.readValue([]const u8); + this.log = try reader.readValue(Log); + return this; +} - pub fn decode(reader: anytype) anyerror!DependencyManifest { - var this = std.mem.zeroes(DependencyManifest); +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeInt(this.from_timestamp); + try writer.writeEnum(this.loader); + try writer.writeValue(this.module_path); + try writer.writeValue(this.log); +} - this.ids = try reader.readArray(u32); - return this; - } +}; - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(u32, this.ids); - } - }; +pub const DependencyManifest = struct { +/// ids +ids: []const u32, - pub const FileList = struct { - /// ptrs - ptrs: []const StringPointer, - /// files - files: []const u8, +pub fn decode(reader: anytype) anyerror!DependencyManifest { + var this = std.mem.zeroes(DependencyManifest); - pub fn decode(reader: anytype) anyerror!FileList { - var this = std.mem.zeroes(FileList); + this.ids = try reader.readArray(u32); + return this; +} - this.ptrs = try reader.readArray(StringPointer); - this.files = try reader.readValue([]const u8); - return this; - } +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(u32, this.ids); +} - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(StringPointer, this.ptrs); - try writer.writeValue(this.files); - } - }; +}; - pub const WebsocketMessageResolveIDs = struct { - /// id - id: []const u32, +pub const FileList = struct { +/// ptrs +ptrs: []const StringPointer, - /// list - list: FileList, +/// files +files: []const u8, - pub fn decode(reader: anytype) anyerror!WebsocketMessageResolveIDs { - var this = std.mem.zeroes(WebsocketMessageResolveIDs); - this.id = try reader.readArray(u32); - this.list = try reader.readValue(FileList); - return this; - } +pub fn decode(reader: anytype) anyerror!FileList { + var this = std.mem.zeroes(FileList); - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(u32, this.id); - try writer.writeValue(this.list); - } - }; + this.ptrs = try reader.readArray(StringPointer); + this.files = try reader.readValue([]const u8); + return this; +} - pub const WebsocketCommandResolveIDs = struct { - /// ptrs - ptrs: []const StringPointer, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(StringPointer, this.ptrs); + try writer.writeValue(this.files); +} - /// files - files: []const u8, +}; - pub fn decode(reader: anytype) anyerror!WebsocketCommandResolveIDs { - var this = std.mem.zeroes(WebsocketCommandResolveIDs); +pub const WebsocketMessageResolveIDs = struct { +/// id +id: []const u32, - this.ptrs = try reader.readArray(StringPointer); - this.files = try reader.readValue([]const u8); - return this; - } +/// list +list: FileList, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeArray(StringPointer, this.ptrs); - try writer.writeValue(this.files); - } - }; - pub const WebsocketMessageManifestSuccess = struct { - /// id - id: u32 = 0, +pub fn decode(reader: anytype) anyerror!WebsocketMessageResolveIDs { + var this = std.mem.zeroes(WebsocketMessageResolveIDs); - /// module_path - module_path: []const u8, + this.id = try reader.readArray(u32); + this.list = try reader.readValue(FileList); + return this; +} - /// loader - loader: Loader, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(u32, this.id); + try writer.writeValue(this.list); +} - /// manifest - manifest: DependencyManifest, +}; - pub fn decode(reader: anytype) anyerror!WebsocketMessageManifestSuccess { - var this = std.mem.zeroes(WebsocketMessageManifestSuccess); +pub const WebsocketCommandResolveIDs = struct { +/// ptrs +ptrs: []const StringPointer, - this.id = try reader.readValue(u32); - this.module_path = try reader.readValue([]const u8); - this.loader = try reader.readValue(Loader); - this.manifest = try reader.readValue(DependencyManifest); - return this; - } +/// files +files: []const u8, - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - try writer.writeValue(this.module_path); - try writer.writeEnum(this.loader); - try writer.writeValue(this.manifest); - } - }; - pub const WebsocketMessageManifestFailure = struct { - /// id - id: u32 = 0, +pub fn decode(reader: anytype) anyerror!WebsocketCommandResolveIDs { + var this = std.mem.zeroes(WebsocketCommandResolveIDs); - /// from_timestamp - from_timestamp: u32 = 0, + this.ptrs = try reader.readArray(StringPointer); + this.files = try reader.readValue([]const u8); + return this; +} - /// loader - loader: Loader, +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeArray(StringPointer, this.ptrs); + try writer.writeValue(this.files); +} - /// log - log: Log, +}; - pub fn decode(reader: anytype) anyerror!WebsocketMessageManifestFailure { - var this = std.mem.zeroes(WebsocketMessageManifestFailure); +pub const WebsocketMessageManifestSuccess = struct { +/// id +id: u32 = 0, + +/// module_path +module_path: []const u8, + +/// loader +loader: Loader, + +/// manifest +manifest: DependencyManifest, + + +pub fn decode(reader: anytype) anyerror!WebsocketMessageManifestSuccess { + var this = std.mem.zeroes(WebsocketMessageManifestSuccess); + + this.id = try reader.readValue(u32); + this.module_path = try reader.readValue([]const u8); + this.loader = try reader.readValue(Loader); + this.manifest = try reader.readValue(DependencyManifest); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeValue(this.module_path); + try writer.writeEnum(this.loader); + try writer.writeValue(this.manifest); +} + +}; + +pub const WebsocketMessageManifestFailure = struct { +/// id +id: u32 = 0, + +/// from_timestamp +from_timestamp: u32 = 0, + +/// loader +loader: Loader, + +/// log +log: Log, + + +pub fn decode(reader: anytype) anyerror!WebsocketMessageManifestFailure { + var this = std.mem.zeroes(WebsocketMessageManifestFailure); + + this.id = try reader.readValue(u32); + this.from_timestamp = try reader.readValue(u32); + this.loader = try reader.readValue(Loader); + this.log = try reader.readValue(Log); + return this; +} + +pub fn encode(this: *const @This(), writer: anytype) anyerror!void { + try writer.writeInt(this.id); + try writer.writeInt(this.from_timestamp); + try writer.writeEnum(this.loader); + try writer.writeValue(this.log); +} + +}; - this.id = try reader.readValue(u32); - this.from_timestamp = try reader.readValue(u32); - this.loader = try reader.readValue(Loader); - this.log = try reader.readValue(Log); - return this; - } - pub fn encode(this: *const @This(), writer: anytype) anyerror!void { - try writer.writeInt(this.id); - try writer.writeInt(this.from_timestamp); - try writer.writeEnum(this.loader); - try writer.writeValue(this.log); - } - }; }; + const ExamplePackedStruct = packed struct { len: u32 = 0, offset: u32 = 0, diff --git a/src/bundler.zig b/src/bundler.zig index 87c34a5ff..143c57c50 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -131,7 +131,7 @@ pub const ScanResult = struct { pub fn NewBundler(cache_files: bool) type { return struct { - const Linker = if (cache_files) linker.Linker else linker.ServeLinker; + pub const Linker = if (cache_files) linker.Linker else linker.ServeLinker; pub const Resolver = if (cache_files) _resolver.Resolver else _resolver.ResolverUncached; const ThisBundler = @This(); @@ -164,8 +164,6 @@ pub fn NewBundler(cache_files: bool) type { ) !ThisBundler { js_ast.Expr.Data.Store.create(allocator); js_ast.Stmt.Data.Store.create(allocator); - js_ast.Expr.Data.Store.reset(); - js_ast.Stmt.Data.Store.reset(); var fs = try Fs.FileSystem.init1(allocator, opts.absolute_working_dir, opts.serve orelse false); const bundle_options = try options.BundleOptions.fromApi( allocator, @@ -941,7 +939,14 @@ pub fn NewBundler(cache_files: bool) type { }; }, else => { - var result = bundler.parse(allocator, file_path, loader, resolve_result.dirname_fd, file_descriptor, filepath_hash) orelse { + var result = bundler.parse( + allocator, + file_path, + loader, + resolve_result.dirname_fd, + file_descriptor, + filepath_hash, + ) orelse { bundler.resetStore(); return BuildResolveResultPair{ .written = 0, @@ -1241,9 +1246,9 @@ pub fn NewBundler(cache_files: bool) type { jsx.parse = loader.isJSX(); var opts = js_parser.Parser.Options.init(jsx, loader); opts.enable_bundling = false; - opts.transform_require_to_import = true; + opts.transform_require_to_import = bundler.options.platform != .speedy; opts.can_import_from_bundle = bundler.options.node_modules_bundle != null; - opts.features.hot_module_reloading = bundler.options.hot_module_reloading; + opts.features.hot_module_reloading = bundler.options.hot_module_reloading and bundler.options.platform != .speedy; opts.features.react_fast_refresh = opts.features.hot_module_reloading and jsx.parse and bundler.options.jsx.supports_fast_refresh; opts.filepath_hash_for_hmr = file_hash orelse 0; const value = (bundler.resolver.caches.js.parse(allocator, opts, bundler.options.define, bundler.log, &source) catch null) orelse return null; diff --git a/src/cli.zig b/src/cli.zig index f5d3ed4c0..0471b4e07 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -1,4 +1,4 @@ -usingnamespace @import("global.zig"); + usingnamespace @import("global.zig"); usingnamespace @import("./http.zig"); const std = @import("std"); diff --git a/src/hash_map.zig b/src/hash_map.zig index e625ff83c..853ad39be 100644 --- a/src/hash_map.zig +++ b/src/hash_map.zig @@ -234,6 +234,13 @@ pub fn HashMap( return self.unmanaged.putAssumeCapacityNoClobber(key, value); } + /// Asserts there is enough capacity to store the new key-value pair. + /// Asserts that it does not clobber any existing data. + /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`. + pub fn putAssumeCapacityNoClobberWithHash(self: *Self, key: K, hash: u64, value: V) void { + return self.unmanaged.putAssumeCapacityNoClobberWithHash(key, hash, value); + } + /// Inserts a new `Entry` into the hash map, returning the previous one, if any. pub fn fetchPut(self: *Self, key: K, value: V) !?Entry { return self.unmanaged.fetchPut(self.allocator, key, value); @@ -537,6 +544,12 @@ pub fn HashMapUnmanaged( assert(!self.contains(key)); const hash = hashFn(key); + putAssumeCapacityNoClobberWithHash(self, key, hash, value); + } + + /// Insert an entry in the map. Assumes it is not already present, + /// and that no allocation is needed. + pub fn putAssumeCapacityNoClobberWithHash(self: *Self, key: K, hash: u64, value: V) void { const mask = self.capacity() - 1; var idx = @truncate(usize, hash & mask); diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig index 1d5806fce..e1bab65b8 100644 --- a/src/javascript/jsc/JavascriptCore.zig +++ b/src/javascript/jsc/JavascriptCore.zig @@ -1,19 +1,21 @@ +const generic = opaque {}; +pub const Private = c_void; pub const struct_OpaqueJSContextGroup = opaque {}; pub const JSContextGroupRef = ?*const struct_OpaqueJSContextGroup; pub const struct_OpaqueJSContext = opaque {}; pub const JSContextRef = ?*const struct_OpaqueJSContext; pub const JSGlobalContextRef = ?*struct_OpaqueJSContext; -pub const struct_OpaqueJSString = opaque {}; +pub const struct_OpaqueJSString = generic; pub const JSStringRef = ?*struct_OpaqueJSString; -pub const struct_OpaqueJSClass = opaque {}; +pub const struct_OpaqueJSClass = generic; pub const JSClassRef = ?*struct_OpaqueJSClass; -pub const struct_OpaqueJSPropertyNameArray = opaque {}; +pub const struct_OpaqueJSPropertyNameArray = generic; pub const JSPropertyNameArrayRef = ?*struct_OpaqueJSPropertyNameArray; -pub const struct_OpaqueJSPropertyNameAccumulator = opaque {}; +pub const struct_OpaqueJSPropertyNameAccumulator = generic; pub const JSPropertyNameAccumulatorRef = ?*struct_OpaqueJSPropertyNameAccumulator; pub const JSTypedArrayBytesDeallocator = ?fn (?*c_void, ?*c_void) callconv(.C) void; -pub const struct_OpaqueJSValue = opaque {}; -pub const JSValueRef = ?*const struct_OpaqueJSValue; +pub const struct_OpaqueJSValue = generic; +pub const JSValueRef = ?*struct_OpaqueJSValue; pub const JSObjectRef = ?*struct_OpaqueJSValue; pub extern fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) JSValueRef; pub extern fn JSCheckScriptSyntax(ctx: JSContextRef, script: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) bool; @@ -115,7 +117,15 @@ pub const JSObjectGetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSString pub const JSObjectSetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, JSValueRef, [*c]JSValueRef) callconv(.C) bool; pub const JSObjectDeletePropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, [*c]JSValueRef) callconv(.C) bool; pub const JSObjectGetPropertyNamesCallback = ?fn (JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef) callconv(.C) void; -pub const JSObjectCallAsFunctionCallback = ?fn (ctx: JSContextRef, function: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) callconv(.C) JSValueRef; + +pub const JSObjectCallAsFunctionCallback = ?fn ( + ctx: JSContextRef, + function: JSObjectRef, + thisObject: JSObjectRef, + argumentCount: usize, + arguments: [*c]const JSValueRef, + exception: [*c]JSValueRef, +) callconv(.C) JSValueRef; pub const JSObjectCallAsConstructorCallback = ?fn (JSContextRef, JSObjectRef, usize, [*c]const JSValueRef, [*c]JSValueRef) callconv(.C) JSObjectRef; pub const JSObjectHasInstanceCallback = ?fn (JSContextRef, JSObjectRef, JSValueRef, [*c]JSValueRef) callconv(.C) bool; pub const JSObjectConvertToTypeCallback = ?fn (JSContextRef, JSObjectRef, JSType, [*c]JSValueRef) callconv(.C) JSValueRef; @@ -150,54 +160,54 @@ pub const JSClassDefinition = extern struct { convertToType: JSObjectConvertToTypeCallback, }; pub extern const kJSClassDefinitionEmpty: JSClassDefinition; -pub extern fn JSClassCreate(definition: [*c]const JSClassDefinition) JSClassRef; -pub extern fn JSClassRetain(jsClass: JSClassRef) JSClassRef; -pub extern fn JSClassRelease(jsClass: JSClassRef) void; -pub extern fn JSObjectMake(ctx: JSContextRef, jsClass: JSClassRef, data: ?*c_void) JSObjectRef; -pub extern fn JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) JSObjectRef; -pub extern fn JSObjectMakeConstructor(ctx: JSContextRef, jsClass: JSClassRef, callAsConstructor: JSObjectCallAsConstructorCallback) JSObjectRef; -pub extern fn JSObjectMakeArray(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeDate(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeError(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeRegExp(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: [*c]JSObjectRef, reject: [*c]JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeFunction(ctx: JSContextRef, name: JSStringRef, parameterCount: c_uint, parameterNames: [*c]const JSStringRef, body: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectGetPrototype(ctx: JSContextRef, object: JSObjectRef) JSValueRef; -pub extern fn JSObjectSetPrototype(ctx: JSContextRef, object: JSObjectRef, value: JSValueRef) void; -pub extern fn JSObjectHasProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef) bool; -pub extern fn JSObjectGetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) JSValueRef; -pub extern fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: [*c]JSValueRef) void; -pub extern fn JSObjectDeleteProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) bool; -pub extern fn JSObjectHasPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; -pub extern fn JSObjectGetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) JSValueRef; -pub extern fn JSObjectSetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: [*c]JSValueRef) void; -pub extern fn JSObjectDeletePropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; -pub extern fn JSObjectGetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, exception: [*c]JSValueRef) JSValueRef; -pub extern fn JSObjectSetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, value: JSValueRef, exception: [*c]JSValueRef) void; -pub extern fn JSObjectGetPrivate(object: JSObjectRef) ?*c_void; -pub extern fn JSObjectSetPrivate(object: JSObjectRef, data: ?*c_void) bool; -pub extern fn JSObjectIsFunction(ctx: JSContextRef, object: JSObjectRef) bool; -pub extern fn JSObjectCallAsFunction(ctx: JSContextRef, object: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSValueRef; -pub extern fn JSObjectIsConstructor(ctx: JSContextRef, object: JSObjectRef) bool; -pub extern fn JSObjectCallAsConstructor(ctx: JSContextRef, object: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectCopyPropertyNames(ctx: JSContextRef, object: JSObjectRef) JSPropertyNameArrayRef; -pub extern fn JSPropertyNameArrayRetain(array: JSPropertyNameArrayRef) JSPropertyNameArrayRef; -pub extern fn JSPropertyNameArrayRelease(array: JSPropertyNameArrayRef) void; -pub extern fn JSPropertyNameArrayGetCount(array: JSPropertyNameArrayRef) usize; -pub extern fn JSPropertyNameArrayGetNameAtIndex(array: JSPropertyNameArrayRef, index: usize) JSStringRef; -pub extern fn JSPropertyNameAccumulatorAddName(accumulator: JSPropertyNameAccumulatorRef, propertyName: JSStringRef) void; -pub extern fn JSContextGroupCreate() JSContextGroupRef; -pub extern fn JSContextGroupRetain(group: JSContextGroupRef) JSContextGroupRef; -pub extern fn JSContextGroupRelease(group: JSContextGroupRef) void; -pub extern fn JSGlobalContextCreate(globalObjectClass: JSClassRef) JSGlobalContextRef; -pub extern fn JSGlobalContextCreateInGroup(group: JSContextGroupRef, globalObjectClass: JSClassRef) JSGlobalContextRef; -pub extern fn JSGlobalContextRetain(ctx: JSGlobalContextRef) JSGlobalContextRef; -pub extern fn JSGlobalContextRelease(ctx: JSGlobalContextRef) void; -pub extern fn JSContextGetGlobalObject(ctx: JSContextRef) JSObjectRef; -pub extern fn JSContextGetGroup(ctx: JSContextRef) JSContextGroupRef; -pub extern fn JSContextGetGlobalContext(ctx: JSContextRef) JSGlobalContextRef; -pub extern fn JSGlobalContextCopyName(ctx: JSGlobalContextRef) JSStringRef; -pub extern fn JSGlobalContextSetName(ctx: JSGlobalContextRef, name: JSStringRef) void; +pub extern "c" fn JSClassCreate(definition: [*c]const JSClassDefinition) JSClassRef; +pub extern "c" fn JSClassRetain(jsClass: JSClassRef) JSClassRef; +pub extern "c" fn JSClassRelease(jsClass: JSClassRef) void; +pub extern "c" fn JSObjectMake(ctx: JSContextRef, jsClass: JSClassRef, data: ?*c_void) JSObjectRef; +pub extern "c" fn JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) JSObjectRef; +pub extern "c" fn JSObjectMakeConstructor(ctx: JSContextRef, jsClass: JSClassRef, callAsConstructor: JSObjectCallAsConstructorCallback) JSObjectRef; +pub extern "c" fn JSObjectMakeArray(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectMakeDate(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectMakeError(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectMakeRegExp(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: [*c]JSObjectRef, reject: [*c]JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectMakeFunction(ctx: JSContextRef, name: JSStringRef, parameterCount: c_uint, parameterNames: [*c]const JSStringRef, body: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectGetPrototype(ctx: JSContextRef, object: JSObjectRef) JSValueRef; +pub extern "c" fn JSObjectSetPrototype(ctx: JSContextRef, object: JSObjectRef, value: JSValueRef) void; +pub extern "c" fn JSObjectHasProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef) bool; +pub extern "c" fn JSObjectGetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) JSValueRef; +pub extern "c" fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: c_uint, exception: [*c]JSValueRef) void; +pub extern "c" fn JSObjectDeleteProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: [*c]JSValueRef) bool; +pub extern "c" fn JSObjectHasPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern "c" fn JSObjectGetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) JSValueRef; +pub extern "c" fn JSObjectSetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: [*c]JSValueRef) void; +pub extern "c" fn JSObjectDeletePropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern "c" fn JSObjectGetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, exception: [*c]JSValueRef) JSValueRef; +pub extern "c" fn JSObjectSetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, value: JSValueRef, exception: [*c]JSValueRef) void; +pub extern "c" fn JSObjectGetPrivate(object: JSObjectRef) ?*c_void; +pub extern "c" fn JSObjectSetPrivate(object: JSObjectRef, data: ?*c_void) bool; +pub extern "c" fn JSObjectIsFunction(ctx: JSContextRef, object: JSObjectRef) bool; +pub extern "c" fn JSObjectCallAsFunction(ctx: JSContextRef, object: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSValueRef; +pub extern "c" fn JSObjectIsConstructor(ctx: JSContextRef, object: JSObjectRef) bool; +pub extern "c" fn JSObjectCallAsConstructor(ctx: JSContextRef, object: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern "c" fn JSObjectCopyPropertyNames(ctx: JSContextRef, object: JSObjectRef) JSPropertyNameArrayRef; +pub extern "c" fn JSPropertyNameArrayRetain(array: JSPropertyNameArrayRef) JSPropertyNameArrayRef; +pub extern "c" fn JSPropertyNameArrayRelease(array: JSPropertyNameArrayRef) void; +pub extern "c" fn JSPropertyNameArrayGetCount(array: JSPropertyNameArrayRef) usize; +pub extern "c" fn JSPropertyNameArrayGetNameAtIndex(array: JSPropertyNameArrayRef, index: usize) JSStringRef; +pub extern "c" fn JSPropertyNameAccumulatorAddName(accumulator: JSPropertyNameAccumulatorRef, propertyName: JSStringRef) void; +pub extern "c" fn JSContextGroupCreate() JSContextGroupRef; +pub extern "c" fn JSContextGroupRetain(group: JSContextGroupRef) JSContextGroupRef; +pub extern "c" fn JSContextGroupRelease(group: JSContextGroupRef) void; +pub extern "c" fn JSGlobalContextCreate(globalObjectClass: JSClassRef) JSGlobalContextRef; +pub extern "c" fn JSGlobalContextCreateInGroup(group: JSContextGroupRef, globalObjectClass: JSClassRef) JSGlobalContextRef; +pub extern "c" fn JSGlobalContextRetain(ctx: JSGlobalContextRef) JSGlobalContextRef; +pub extern "c" fn JSGlobalContextRelease(ctx: JSGlobalContextRef) void; +pub extern "c" fn JSContextGetGlobalObject(ctx: JSContextRef) JSObjectRef; +pub extern "c" fn JSContextGetGroup(ctx: JSContextRef) JSContextGroupRef; +pub extern "c" fn JSContextGetGlobalContext(ctx: JSContextRef) JSGlobalContextRef; +pub extern "c" fn JSGlobalContextCopyName(ctx: JSGlobalContextRef) JSStringRef; +pub extern "c" fn JSGlobalContextSetName(ctx: JSGlobalContextRef, name: JSStringRef) void; pub const JSChar = c_ushort; pub extern fn JSStringCreateWithCharacters(chars: [*c]const JSChar, numChars: usize) JSStringRef; pub extern fn JSStringCreateWithUTF8CString(string: [*c]const u8) JSStringRef; @@ -235,4 +245,4 @@ pub const OpaqueJSValue = struct_OpaqueJSValue; // StringImpl::createWithoutCopying // https://github.com/WebKit/webkit/blob/main/Source/JavaScriptCore/API/JSStringRef.cpp#L62 pub extern fn JSStringCreateWithCharactersNoCopy(string: [*c]const JSChar, numChars: size_t) JSStringRef; - +const size_t = usize; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index aff505f22..1b71c0197 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -3,10 +3,16 @@ const std = @import("std"); usingnamespace @import("../../global.zig"); const Fs = @import("../../fs.zig"); const resolver = @import("../../resolver/resolver.zig"); -const ast = @import("../../ast/base.zig"); +const ast = @import("../../import_record.zig"); const NodeModuleBundle = @import("../../node_module_bundle.zig").NodeModuleBundle; const WTFString = @import("../../wtf_string_mutable.zig").WTFStringMutable; const logger = @import("../../logger.zig"); +const Api = @import("../../api/schema.zig").Api; +const options = @import("../../options.zig"); +const Bundler = @import("../../bundler.zig").ServeBundler; + +const hash_map = @import("../../hash_map.zig"); + pub const ExportJavaScript = union(Tag) { Module: *Module, String: *String, @@ -21,7 +27,7 @@ pub const ExportJavaScript = union(Tag) { pub const ResolveFunctionType = fn (ctx: anytype, source_dir: string, import_path: string, import_kind: ast.ImportKind) anyerror!resolver.Result; pub const TranspileFunctionType = fn (ctx: anytype, resolve_result: resolver.Result) anyerror![:0]const u8; - +const ExceptionValueRef = [*c]js.JSValueRef; const JSStringMapContext = struct { pub fn hash(self: @This(), s: js.JSStringRef) u64 { return hashString(s); @@ -35,61 +41,128 @@ pub fn JSStringMap(comptime V: type) type { return std.HashMap(js.JSStringRef, V, JSStringMapContext, 60); } +pub fn configureTransformOptionsForSpeedy(allocator: *std.mem.Allocator, _args: Api.TransformOptions) !Api.TransformOptions { + var args = _args; + + args.platform = Api.Platform.speedy; + args.serve = false; + args.write = false; + args.resolve = Api.ResolveMode.lazy; + args.generate_node_module_bundle = false; + + // We inline process.env.* at bundle time but process.env is a proxy object which will otherwise return undefined. + + var env_map = try getNodeEnvMap(allocator); + var env_count = env_map.count(); + + if (args.define) |def| { + for (def.keys) |key| { + env_count += @boolToInt((env_map.get(key) == null)); + } + } + var needs_node_env = env._map.get("NODE_ENV") == null; + + var needs_regenerate = args.define == null and env_count > 0; + if (args.define) |def| { + if (def.keys.len != env_count) { + needs_regenerate = true; + } + for (def.keys) |key| { + if (strings.eql(key, "process.env.NODE_ENV")) { + needs_node_env = false; + } + } + } + + if (needs_regenerate) { + var new_list = try allocator.alloc([]u8, env_count * 2 + @boolToInt(needs_node_env) * 2); + var new_map = Api.StringMap{ + .keys = new_list[0..env_count], + .values = new_list[env_count..], + }; + var iter = env_map.iterator(); + + var last: usize = 0; + while (iter.next()) |entry| { + new_map.keys[last] = entry.key_ptr.*; + var value = entry.value_ptr.*; + if (value.len == 0 or value.len[0] != '"' or value.len[value.len - 1] != '"') { + value = try std.fmt.allocPrint(allocator, "\"{s}\"", .{value}); + } + new_map.values[last] = value; + last += 1; + } + + if (args.define) |def| { + var from_env = new_map.keys[0..last]; + + for (def.keys) |pre, i| { + if (env_map.get(pre) != null) { + for (from_env) |key, i| { + if (srings.eql(key, pre)) { + new_map.values[i] = def.values[i]; + } + } + } else { + new_map.keys[last] = pre; + new_map.values[last] = def.values[i]; + last += 1; + } + } + } + + if (needs_node_env) { + new_map.keys[last] = options.DefaultUserDefines.NodeEnv.Key; + new_map.values[last] = options.DefaultUserDefines.NodeEnv.Value; + } + } + + return args; +} + // If you read JavascriptCore/API/JSVirtualMachine.mm - https://github.com/WebKit/WebKit/blob/acff93fb303baa670c055cb24c2bad08691a01a0/Source/JavaScriptCore/API/JSVirtualMachine.mm#L101 // We can see that it's sort of like std.mem.Allocator but for JSGlobalContextRef, to support Automatic Reference Counting // Its unavailable on Linux pub const VirtualMachine = struct { - const RequireCacheType = std.AutoHashMap(u64, Module); - ctx: js.JSGlobalContextRef, + const RequireCacheType = std.AutoHashMap(u32, Module); + root: js.JSGlobalContextRef, + ctx: js.JSGlobalContextRef = undefined, group: js.JSContextGroupRef, allocator: *std.mem.Allocator, - transpile_ctx: *c_void = undefined, - transpile: *TranspileFunctionType = undefined, require_cache: RequireCacheType, - resolve_: *ResolveFunctionType = undefined, - resolve_ctx: *c_void = undefined, node_modules: ?*NodeModuleBundle = null, node_modules_ref: js.JSObjectRef = null, - global: GlobalObject, + global: *GlobalObject, + bundler: Bundler, + log: *logger.Log, - pub fn init(allocator: *std.mem.Allocator) !*VirtualMachine { + pub fn init( + allocator: *std.mem.Allocator, + _args: Api.TransformOptions, + ) !*VirtualMachine { var group = js.JSContextGroupCreate(); var ctx = js.JSGlobalContextCreateInGroup(group, null); - - Properties.init(); + var log = try allocator.create(logger.Log); var vm = try allocator.create(VirtualMachine); + var global = try allocator.create(GlobalObject); vm.* = .{ .allocator = allocator, + .bundler = try Bundler.init(allocator, log, try configureTransformOptionsForSpeedy(allocator, _args)), .group = group, - .ctx = ctx, + .root = ctx, .require_cache = RequireCacheType.init(allocator), - .global = undefined, + .global = global, }; - vm.global = GlobalObject{ .vm = undefined }; - return vm; - } - - pub fn setupGlobals(this: *VirtualMachine) void {} + Properties.init(); + vm.bundler.configureLinker(); - pub fn resolve( - this: *VirtualMachine, - from: *const Module, - to: string, - ) !js.JSValueRef { - return (try this.resolve_(this.resolve_ctx, from.path.dir, to, .require)) orelse return error.ModuleNotFound; - } - threadlocal var require_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + global.* = GlobalObject{ .vm = vm }; + try vm.global.boot(); + vm.ctx = vm.global.ctx; - // Assume bundle is already transpiled, so we skip transpiling in here. - pub fn requireFromBundle(this: *VirtualMachine, import_path: string) !js.JSValueRef {} + Module.boot(vm); - pub fn require( - this: *VirtualMachine, - module: *const Module, - path_value: js.JSValueRef, - ) !js.JSValueRef { - var import_path = To.Zig.str(path_value, &require_buf); - var resolve_result = try this.resolve(module, import_path); + return vm; } threadlocal var eval_buf: WTFString = undefined; @@ -98,61 +171,46 @@ pub const VirtualMachine = struct { pub fn evalUtf8( this: *VirtualMachine, path_text: string, - contents: string, + contents: [:0]const u8, ) !js.JSValueRef { - if (!eval_buf_loaded) { - eval_buf = try WTFString.init(this.allocator, contents.len + path.text.len + 2); - } else { - eval_buf.reset(); - try eval_buf.growIfNeeded(contents.len + path.text.len + 2); - } - try eval_buf.append(contents); - eval_buf.list.append(eval_buf.allocator, 0); - var script_len = eval_buf.list.items.len; - if (path_text.len > 0) { - try eval_buf.append(path_text); - eval_buf.list.append(eval_buf.allocator, 0); - } + // if (!eval_buf_loaded) { + // eval_buf = try WTFString.init(this.allocator, contents.len + path_text.len); + // } else { + // eval_buf.reset(); + // try eval_buf.growIfNeeded(contents.len + path_text.len); + // } - var buf = eval_buf.toOwnedSliceLeaky(); - var script = js.JSStringCreateWithCharactersNoCopy(buf[0..script_len].ptr, script_len - 1); - var sourceURL: js.JSStringRef = null; + // try eval_buf.append(contents); + // var script_len = eval_buf.list.items.len; + // if (path_text.len > 0) { + // try eval_buf.append(path_text); + // } - if (path_text.len > 0) { - sourceURL = js.JSStringCreateWithCharactersNoCopy( - buf[script_len + 1 ..].ptr, - buf[script_len + 1 ..].len - 1, - ); - } + // var buf = eval_buf.toOwnedSliceLeaky(); + // var script = js.JSStringCreateWithCharactersNoCopy(@as([*c]js.JSChar, buf[0..script_len].ptr), script_len); + // script = js.JSStringRetain(script); + // var sourceURL: js.JSStringRef = null; - return js.JSEvaluateScript( + // if (path_text.len > 0) { + // sourceURL = js.JSStringCreateWithCharactersNoCopy( + // @as([*c]js.JSChar, buf[script_len + 1 ..].ptr), + // buf[script_len + 1 ..].len, + // ); + // sourceURL = js.JSStringRetain(sourceURL); + // } + var exception: js.JSObjectRef = null; + var val = js.JSEvaluateScript( this.ctx, - script, - js.JSValueMakeUndefined(this.ctx), - sourceURL, - 0, + js.JSStringCreateWithUTF8CString(contents.ptr), + this.global.ref, null, + 0, + &exception, ); - } -}; - -pub const BundleLoader = struct { - bundle: *const NodeModuleBundle, - allocator: *std.mem.Allocator, - vm: *VirtualMachine, - loaded: bool = false, - ref: js.JSObjectRef = null, - pub fn init(bundle: *const NodeModuleBundle, allocator: *std.mem.Allocator, vm: *VirtualMachine) BundleLoader { - return BundleLoader{ - .bundle = bundle, - .allocator = allocator, - .vm = vm, - }; + return exception; } - - pub fn loadBundle(this: *BundleLoader) !void {} }; pub const To = struct { @@ -171,12 +229,15 @@ pub const To = struct { ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, - arguments: []js.JSValueRef, - exception: [*c]js.JSValueRef, + arguments: []const js.JSValueRef, + exception: js.JSValueRef, ) js.JSValueRef, ) js.JSObjectRef { - var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback)); - js.JSObjectSetPrivate(function, @ptrCast(*c_void, zig)); + var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback).rfn); + js.JSObjectSetPrivate( + function, + @ptrCast(*c_void, @alignCast(@alignOf(*c_void), global)), + ); return function; } @@ -187,30 +248,37 @@ pub const To = struct { ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, - arguments: []js.JSValueRef, - exception: [*c]js.JSValueRef, + arguments: []const js.JSValueRef, + exception: js.JSValueRef, ) js.JSValueRef, ) type { return struct { - pub fn run( + pub fn rfn( ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, argumentCount: usize, arguments: [*c]const js.JSValueRef, - exception: [*c]js.JSValueRef, + exception: ExceptionValueRef, ) callconv(.C) js.JSValueRef { - var object_ptr = js.JSObjectGetPrivate(function) orelse { + var object_ptr_ = js.JSObjectGetPrivate(function); + if (object_ptr_ == null) { + object_ptr_ = js.JSObjectGetPrivate(thisObject); + } + + if (object_ptr_ == null) { return js.JSValueMakeUndefined(ctx); - }; + } + + var object_ptr = object_ptr_.?; return ctxfn( - @intToPtr(ZigContextType, object_ptr), + @ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr)), ctx, function, thisObject, - arguments[0..argumentCount], - exception, + if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, + null, ); } }; @@ -237,6 +305,7 @@ pub const Properties = struct { pub const exports = "exports"; pub const log = "log"; pub const debug = "debug"; + pub const name = "name"; pub const info = "info"; pub const error_ = "error"; pub const warn = "warn"; @@ -246,38 +315,45 @@ pub const Properties = struct { }; pub const UTF16 = struct { - pub const module = std.unicode.utf8ToUtf16LeStringLiteral("module"); - pub const globalThis = std.unicode.utf8ToUtf16LeStringLiteral("globalThis"); - pub const exports = std.unicode.utf8ToUtf16LeStringLiteral("exports"); - pub const log = std.unicode.utf8ToUtf16LeStringLiteral("log"); - pub const debug = std.unicode.utf8ToUtf16LeStringLiteral("debug"); - pub const info = std.unicode.utf8ToUtf16LeStringLiteral("info"); - pub const error_ = std.unicode.utf8ToUtf16LeStringLiteral("error"); - pub const warn = std.unicode.utf8ToUtf16LeStringLiteral("warn"); - pub const console = std.unicode.utf8ToUtf16LeStringLiteral("console"); - pub const require = std.unicode.utf8ToUtf16LeStringLiteral("require"); - pub const description = std.unicode.utf8ToUtf16LeStringLiteral("description"); + pub const module: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("module"); + pub const globalThis: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("globalThis"); + pub const exports: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("exports"); + pub const log: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("log"); + pub const debug: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("debug"); + pub const info: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("info"); + pub const error_: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("error"); + pub const warn: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("warn"); + pub const console: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("console"); + pub const require: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("require"); + pub const description: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("description"); + pub const name: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral("name"); }; pub const Refs = struct { - pub var module: js.JSStringRef = undefined; - pub var globalThis: js.JSStringRef = undefined; - pub var exports: js.JSStringRef = undefined; - pub var log: js.JSStringRef = undefined; - pub var debug: js.JSStringRef = undefined; - pub var info: js.JSStringRef = undefined; - pub var error_: js.JSStringRef = undefined; - pub var warn: js.JSStringRef = undefined; - pub var console: js.JSStringRef = undefined; - pub var require: js.JSStringRef = undefined; - pub var description: js.JSStringRef = undefined; + pub var module: js.JSStringRef = null; + pub var globalThis: js.JSStringRef = null; + pub var exports: js.JSStringRef = null; + pub var log: js.JSStringRef = null; + pub var debug: js.JSStringRef = null; + pub var info: js.JSStringRef = null; + pub var error_: js.JSStringRef = null; + pub var warn: js.JSStringRef = null; + pub var console: js.JSStringRef = null; + pub var require: js.JSStringRef = null; + pub var description: js.JSStringRef = null; + pub var name: js.JSStringRef = null; }; pub fn init() void { inline for (std.meta.fieldNames(UTF8)) |name| { - @field(Refs, name) = js.JSStringCreateWithCharactersNoCopy( - &@field(StringStore.UTF16, name), - @field(StringStore.UTF16, name).len, + @field(Refs, name) = js.JSStringRetain( + js.JSStringCreateWithCharactersNoCopy( + @field(StringStore.UTF16, name).ptr, + @field(StringStore.UTF16, name).len - 1, + ), + ); + std.debug.assert( + js.JSStringIsEqualToUTF8CString(@field(Refs, name), @field(UTF8, name)[0.. :0]), ); } } @@ -291,7 +367,7 @@ pub const String = struct { ref: js.JSStringRef, len: usize, - pub fn chars(this: *const String) []js.JSChar { + pub fn chars(this: *const String) []const js.JSChar { return js.JSStringGetCharactersPtr(this.ref)[0..js.JSStringGetLength(this.ref)]; } @@ -300,30 +376,571 @@ pub const String = struct { } }; +const GetterFn = fn ( + this: anytype, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: [*c]JSValueRef, +) js.JSValueRef; +const SetterFn = fn ( + this: anytype, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + value: js.JSValueRef, + exception: [*c]JSValueRef, +) js.JSValueRef; + +const JSProp = struct { + get: ?GetterFn = null, + set: ?SetterFn = null, + ro: bool = false, +}; + pub const Module = struct { - path: Fs.PathName, + path: Fs.Path, - require: RequireObject, - hash: u64, + hashid: u32, ref: js.JSObjectRef, + id: js.JSValueRef = null, + exports: js.JSValueRef = null, + + global_ref: js.JSValueRef = null, + + vm: *VirtualMachine, + + 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, + property_getters: []js.JSObjectRef, + module_property_map: ModuleIDMap, + node_module_global_class: js.JSClassRef, + node_module_global_class_def: js.JSClassDefinition, + bundle_ctx: js.JSGlobalContextRef, + vm: *VirtualMachine, + + pub const Instance = struct { + module: Module, + ref: js.JSObjectRef, + ctx: js.JSGlobalContextRef, + node_module_list: *NodeModuleList, + + const NodeModuleInstanceClassName = "NodeModule"; + const ModuleLoadStaticFunctionName = "$$m"; + + var instance_class_definition: js.JSClassDefinition = undefined; + + var instance_class_ref: js.JSClassRef = undefined; + var instance_class_loaded = false; + + threadlocal var source_code_buffer: MutableString = undefined; + threadlocal var source_code_buffer_loaded = false; + pub fn evalBundledModule( + allocator: *std.mem.Allocator, + vm: *VirtualMachine, + node_module_list: *NodeModuleList, + ctx: js.JSContextRef, + id: u32, + ) !Instance { + const bundled_module = &vm.node_modules.?.bundle.modules[id]; + if (!source_code_buffer_loaded) { + source_code_buffer = try MutableString.init(allocator, bundled_module.code.length + 1); + source_code_buffer_loaded = true; + } else { + source_code_buffer.reset(); + source_code_buffer.growIfNeeded(bundled_module.code.length + 1); + } + + var node_module_file = std.fs.File{ .handle = vm.node_modules.?.fd }; + const read = try node_module_file.pread(source_code_buffer.list.items, bundled_module.code.offset); + source_code_buffer.list.items[read] = 0; + var buf = source_code_buffer.list.items[0..read :0]; + + const bundled_package = &vm.node_modules.?.bundle.packages[bundled_module.package_id]; + // We want linear because we expect it to virtually always be at 0 + // However, out of caution we check. + var start_at: usize = std.mem.indexOfPosLinear(u8, buf, 0, "export var $") orelse return error.FailedCorruptNodeModuleMissingExport; + start_at += "export var $".len; + start_at = std.mem.indexOfPosLinear(u8, "$$m(", start_at, buf) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + var source_buf = source_code_buffer.list.items[start_at..read :0]; + var source_string = js.JSStringCreateWithUTF8CString(source_buf.ptr); + defer js.JSStringRelease(source_string); + var source_url_buf = try std.fmt.allocPrintZ( + allocator, + "node_modules.jsb/{s}/{s}", + .{ + vm.node_modules.?.str(bundled_package.path), + vm.node_modules.?.str(bundled_module.path), + }, + ); + defer allocator.free(source_url_buf); + var source_url = js.JSStringCreateWithUTF8CString(source_url_buf); + defer js.JSStringRelease(source_url); + var exception: js.JSValueRef = null; + var return_value = js.JSEvaluateScript(node_module_list.bundle_ctx, source_string, null, source_url, 1, &exception); + if (exception != null) { + var message = js.JSValueToStringCopy(node_module_list.bundle_ctx, exception.?, null); + defer js.JSStringRelease(message); + var message_str_size = js.JSStringGetMaximumUTF8CStringSize(message); + var message_str_buf = try allocator.alloc(u8, message_str_size); + defer allocator.free(message_str_buf); + var message_str_read = js.JSStringGetUTF8CString(message, message_str_buf, message_str_size); + defer Output.flush(); + Output.prettyErrorln("<r>{s}\n--<r><red>error<r> loading <cyan>\"{s}/{s}\"<r>--", .{ + message_str_buf[0..message_str_read], + vm.node_modules.?.str(bundled_package.name), + vm.node_modules.?.str(bundled_module.path), + }); + return error.FailedException; + } + + + + + + + + if (!js.JSValueIsObject(node_module_list.bundle_ctx, return_value) or js.JSObjectGetPrivate(return_value) == null) { + Output.prettyErrorln( + \\\<r><red>Failed<r> to load <cyan>"{s}/{s}"<r>.\n + \\This is an internal error. Every module in node_modules.jsb is expected to call a function + \\initializing the module on load, and that function is supposed to return an object. + \\It didn't return an object. + \\That doesn't mean there was a syntax error (syntax errors occur earlier). + \\If you weren't poking around in node_modules.jsb (or messing with object prototypes), + \\please file an issue and include your node_modules.jsb. + , .{ + vm.node_modules.?.str(bundled_package.name), + vm.node_modules.?.str(bundled_module.path), + }); + Output.flush(); + return error.FailedReturnValueInvalid; + } + + + + + + + + + } + }; + + pub const RequireBundledModule = struct { + id: u32, + list: *NodeModuleList, + }; + + // key: hash of module.path + // value: index of module + const ModuleIDMap = hash_map.AutoHashMap(u64, u32); + + pub fn initializeGlobal(ctx: JSContextRef, obj: JSObjectRef) callconv(.C) void {} + + pub fn getRequireFromBundleProperty(ctx: js.JSContextRef, thisObject: js.JSObjectRef, prop: js.JSStringRef, exception: [*c]js.JSValueRef) js.JSValueRef { + var thisPtr = js.JSObjectGetPrivate(thisObject); + if (thisPtr == null) return null; + + var this = @ptrCast( + *NodeModuleList, + @alignCast( + @alignOf( + *NodeModuleList, + ), + thisPtr.?, + ), + ); + + const size = js.JSStringGetUTF8CString(prop, this.tempbuf.ptr, this.tempbuf.len); + const key = std.hash.Wyhash.hash(0, this.tempbuf[0..size]); + const id = this.module_property_map.get(id) orelse return null; + + if (this.property_getters[id] == null) { + var require_bundled = try this.vm.allocator.create(RequireBundledModule); + require_bundled.* = RequireBundledModule{ .id = id, .list = this }; + this.property_getters[id] = To.JS.functionWithCallback( + RequireBundledModule, + require_bundled, + prop, + ctx, + requireBundledModule, + ); + } + + return this.property_getters[id]; + } + + // this is what $aosdi123() inside a node_modules.jsb calls + pub fn requireBundledModule( + obj: *const RequireBundledModule, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: [*c]js.JSValueRef, + ) js.JSValueRef { + const bundle = &obj.list.vm.node_modules.?.bundle; + const bundled_module = &bundle.modules[obj.id]; + const bundled_pkg = &bundle.packages[bundled_module.package_id]; + + const result = loadBundledModuleById(obj.list.vm, ctx, obj.id) catch |err| { + Output.prettyErrorln("<r><red>RequireError<r>: <b>{s}<r> in \"<cyan>{s}/{s}<r>\"", .{ + @errorName(err), + obj.list.vm.node_modules.?.str(bundled_pkg.name), + obj.list.vm.node_modules.?.str(bundled_module.path), + }); + var message = std.fmt.allocPrintZ(obj.list.vm.allocator, "RequireError: {s} in \"{s}/{s}\"", .{ + @errorName(err), + obj.list.vm.node_modules.?.str(bundled_pkg.name), + obj.list.vm.node_modules.?.str(bundled_module.path), + }) catch unreachable; + defer Output.flush(); + defer obj.list.vm.allocator.free(message); + var args = obj.list.vm.allocator.alloc(js.JSStringRef, 1) catch unreachable; + args[0] = js.JSStringCreateWithUTF8CString(message.ptr); + exception.* = js.JSObjectMakeError(ctx, 1, args, null); + return js.JSValueMakeUndefined(ctx); + }; + + return result.Module.internalGetExports(); + } + + pub fn init(vm: *VirtualMachine, ctx: js.JSContextRef, bundle: *const NodeModuleBundle) !NodeModuleList { + var size: usize = 0; + var longest_size: usize = 0; + for (bundle.bundle.modules) |module, i| { + var hasher = std.hash.Wyhash.init(0); + hasher.update(bundle.str(module.path)); + hasher.update( + std.mem.asBytes( + &bundle.bundle.packages[module.package_id].hash, + ), + ); + // Add one for null-terminated string offset + const this_size = std.fmt.count( + "${x}", + .{ + @truncate( + u32, + hasher.final(), + ), + }, + ) + 1; + size += this_size; + longest_size = std.math.max(this_size, longest_size); + } + var static_properties = try vm.allocator.alloc(js.JSStaticValue, bundle.bundle.modules.len); + var utf8 = try vm.allocator.alloc(u8, size + longest_size); + + var tempbuf = utf8[size..]; + + var names_buf = utf8[0..size]; + var module_property_map = ModuleIDMap.init(vm.allocator); + try module_property_map.ensureCapacity(bundle.bundle.modules.len); + + for (bundle.bundle.modules) |module, i| { + var hasher = std.hash.Wyhash.init(0); + hasher.update(bundle.str(module.path)); + hasher.update( + std.mem.asBytes( + &bundle.bundle.packages[module.package_id].hash, + ), + ); + + const hash = @truncate( + u32, + hasher.final(), + ); + + // The variable name is the hash of the module path + var name = std.fmt.bufPrint(names_buf, "${x}", .{hash}) catch unreachable; + + // But we don't store that for the hash map. Instead, we store the hash of name. + // This lets us avoid storing pointers to the name in the hash table, so if we free it later + // or something it won't cause issues. + hasher = std.hash.Wyhash.init(0); + hasher.update(name); + var property_key = hasher.final(); + + name.ptr[name.len] = 0; + const name_len = name.len; + + static_properties[i] = js.JSStaticValue{ + .name = name[0.. :0], + .getProperty = getRequireFromBundleProperty, + .attributes = .kJSPropertyAttributeReadOnly, + }; + names_buf = names_buf[name_len..]; + module_property_map.putAssumeCapacityNoClobberWithHash(property_key, property_key, @truncate(u32, i)); + } + + var node_module_global_class_def = js.kJSClassDefinitionEmpty; + node_module_global_class_def.staticValues = static_properties; + node_module_global_class_def.className = node_module_global_class_name[0.. :0]; + node_module_global_class_def.parentClass = vm.global.global_class; + + var property_getters = try vm.allocator.alloc(js.JSObjectRef, bundle.bundle.modules.len); + std.mem.set(js.JSObjectRef, property_getters, null); + + return NodeModuleList{ + .module_property_map = module_property_map, + .node_module_global_class_def = node_module_global_class_def, + .vm = vm, + .tempbuf = tempbuf, + .property_getters = property_getters, + .node_module_global_class = js.JSClassCreate(node_module_global_class_def), + }; + } + }; + pub const node_module_global_class_name = "NodeModuleGlobal"; + pub const ModuleGlobalClass = NewClass( + Module, + "ModuleGlobal", + .{ .@"require" = require }, + .{}, + false, + false, + ); + + const JSExport = NewClass( + Module, + "Module", + .{ .@"require" = require }, + .{ + .@"id" = JSProp{ + .get = getId, + .ro = true, + }, + .@"exports" = JSProp{ + .get = getExports, + .set = setExports, + .ro = false, + }, + }, + false, + false, + ); + + pub fn boot(vm: *VirtualMachine) void { + module_global_class_def = ModuleGlobalClass.define(vm.root); + module_global_class_def.parentClass = vm.global.global_class; + module_global_class = js.JSClassRetain(js.JSClassCreate(&module_global_class_def)); + module_class_def = JSExport.define(vm.root); + module_class = js.JSClassRetain(js.JSClassCreate(&module_class_def)); + } + + pub const LoadResult = union(Tag) { + Module: *Module, + Path: Fs.Path, + + pub const Tag = enum { + Module, + Path, + }; + }; + + pub fn loadBundledModuleById(vm: *VirtualMachine, ctx: js.JSContextRef, id: u32) !LoadResult {} + + pub fn loadFromResolveResult(vm: *VirtualMachine, ctx: js.JSContextRef, resolved: resolver.Result) !LoadResult { + var hash = @truncate(u32, std.hash.Wyhash.hash(0, resolved.path_pair.primary.text)); + if (vm.require_cache.getPtr(hash)) |mod| { + return .{ .Module = mod }; + } + + const path = resolved.path_pair.primary; + const loader = vm.bundler.options.loaders.get(path.name.ext) orelse .file; + switch (loader) { + .js, + .jsx, + .ts, + .tsx, + .json, + => { + if (resolved.package_json) |package_json| { + if (package_json.hash > 0) { + if (vm.node_modules) |node_modules| { + if (node_modules.getPackageIDByHash(package_json.hash)) |package_id| { + const package_relative_path = vm.bundler.fs.relative( + package_json.source.path.name.dirWithTrailingSlash(), + path.text, + ); + + if (node_modules.findModuleInPackage( + &node_modules.bundle.packages[package_id], + package_relative_path, + )) |found_module| {} + } + } + } + } + + vm.bundler.resetStore(); + var result = vm.bundler.parse( + vm.bundler.allocator, + path, + loader, + result.dirname_fd, + null, + null, + ) orelse { + return error.ParseError; + }; + }, + + // Replace imports to non-executables with paths to those files. + // In SSR or on web, these become URLs. + // Otherwise, absolute file paths. + else => { + switch (vm.bundler.options.import_path_format) { + .absolute_path => { + return LoadResult{ .Path = path }; + }, + .absolute_url => { + var fs = vm.bundler.fs; + + var base = fs.relativeTo(path.text); + if (strings.lastIndexOfChar(base, '.')) |dot| { + base = base[0..dot]; + } + + var dirname = std.fs.path.dirname(base) orelse ""; + + var basename = std.fs.path.basename(base); + + const needs_slash = dirname.len > 0 and dirname[dirname.len - 1] != '/'; + + if (needs_slash) { + const absolute_url = try std.fmt.allocPrint( + vm.allocator, + "{s}{s}/{s}{s}", + .{ + vm.bundler.options.public_url, + dirname, + basename, + path.name.ext, + }, + ); + + return LoadResult{ + .Path = Fs.Path.initWithPretty(absolute_url, absolute_url), + }; + } else { + const absolute_url = try std.fmt.allocPrint( + vm.allocator, + "{s}{s}{s}{s}", + .{ + vm.bundler.options.public_url, + dirname, + basename, + path.name.ext, + }, + ); + + return LoadResult{ + .Path = Fs.Path.initWithPretty(absolute_url, absolute_url), + }; + } + }, + } + }, + } + } + + pub fn getId( + this: *Module, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: [*c]js.JSValueRef, + ) js.JSValueRef { + if (this.id == null) { + this.id = js.JSStringCreateWithUTF8CString(this.path.text[0.. :0]); + } + + return this.id; + } + + pub fn getExports( + this: *Module, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: [*c]js.JSValueRef, + ) js.JSValueRef { + return this.internalGetExports(); + } + + pub fn internalGetExports(this: *Module) js.JSValueRef { + if (this.exports == null) { + this.exports = js.JSObjectMake(ctx, null, null); + } + + return this.exports; + } + + pub fn setExports( + this: *Module, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + value: js.JSValueRef, + exception: [*c]JSValueRef, + ) JSValueRef { + if (this.exports != null) { + if (js.JSValueIsString(this.exports.?)) { + js.JSStringRelease(this.exports.?); + } + } + + this.exports = value; + } + pub const RequireObject = struct {}; pub fn require( this: *Module, - arguments: [*c]const js.JSValueRef, - arguments_len: usize, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + arguments: []js.JSValueRef, exception: [*c]JSValueRef, - ) js.JSValueRef {} + ) js.JSValueRef { + if (arguments.len == 0 or arguments.len > 1 or !js.JSValueIsString(ctx, arguments[0]) or js.JSStringGetLength(arguments[0]) == 0) { + defer Output.flush(); + if (arguments.len == 0) { + Output.prettyErrorln("<r><red>error<r>: <s><b>require<r> needs a string, e.g. require(\"left-pad\")", .{}); + } else if (arguments.len > 1) { + Output.prettyErrorln("<r><red>error<r>: <s><b>require<r> only accepts one argument and it must be a string, e.g. require(\"left-pad\")", .{}); + } else if (!js.JSValueIsString(ctx, arguments[0])) { + Output.prettyErrorln("<r><red>error<r>: <s><b>require<r> only supports a string, e.g. require(\"left-pad\")", .{}); + } else { + Output.prettyErrorln("<r><red>error<r>: <s><b>require(\"\")<r> string cannot be empty.", .{}); + } + exception.* = js.JSObjectMakeError(ctx, 0, null, null); + return null; + } + } }; pub const GlobalObject = struct { ref: js.JSObjectRef = undefined, vm: *VirtualMachine, - console: js.JSClassRef = undefined, + ctx: js.JSGlobalContextRef = undefined, + console_class: js.JSClassRef = undefined, + console: js.JSObjectRef = undefined, console_definition: js.JSClassDefinition = undefined, + global_class_def: js.JSClassDefinition = undefined, + global_class: js.JSClassRef = undefined, + root_obj: js.JSObjectRef = undefined, - pub const ConsoleClass = NewSingletonClass( + pub const ConsoleClass = NewClass( GlobalObject, "Console", .{ @@ -335,15 +952,53 @@ pub const GlobalObject = struct { .@"error" = stderr, .@"warn" = stderr, }, + .{}, // people sometimes modify console.log, let them. false, + true, + ); + + pub const GlobalClass = NewClass( + GlobalObject, + "Global", + .{}, + .{ + .@"console" = getConsole, + }, + false, + false, ); - pub fn load(global: *GlobalObject) !void { - global.console_definition = ConsoleClass.define(global, global.vm.ctx); - global.console = js.JSClassCreate(&global.console_definition); + pub fn getConsole(global: *GlobalObject, ctx: js.JSContextRef, obj: js.JSObjectRef, exception: ExceptionValueRef) js.JSValueRef { + return global.console; } + pub fn onMissingProperty(global: *GlobalObject, ctx: js.JSContextRef, obj: js.JSObjectRef, prop: js.JSStringRef, exception: ExceptionValueRef) js.JSValueRef { + if (js.JSObjectHasProperty(ctx, global.root_obj, prop)) { + return js.JSObjectGetProperty(ctx, global.root_obj, prop, exception); + } else { + return js.JSValueMakeUndefined(ctx); + } + } + + pub fn boot(global: *GlobalObject) !void { + var private: ?*c_void = global; + global.root_obj = js.JSContextGetGlobalObject(global.vm.root); + + global.console_definition = ConsoleClass.define(global.vm.root); + global.console_class = js.JSClassRetain(js.JSClassCreate(&global.console_definition)); + global.console = js.JSObjectMake(global.vm.root, global.console_class, private); + + global.global_class_def = GlobalClass.define(global.vm.root); + global.global_class = js.JSClassRetain(js.JSClassCreate(&global.global_class_def)); + + global.ctx = js.JSGlobalContextRetain(js.JSGlobalContextCreateInGroup(global.vm.group, global.global_class)); + + std.debug.assert(js.JSObjectSetPrivate(js.JSContextGetGlobalObject(global.ctx), private)); + global.ref = js.JSContextGetGlobalObject(global.ctx); + } + + threadlocal var printer_buf: [4092]u8 = undefined; fn valuePrinter(comptime ValueType: js.JSType, ctx: js.JSContextRef, arg: js.JSValueRef, writer: anytype) !void { switch (ValueType) { .kJSTypeUndefined => { @@ -360,29 +1015,14 @@ pub const GlobalObject = struct { } }, .kJSTypeNumber => { - try writer.print("{d}", js.JSValueToNumber(ctx, arg, null)); + try writer.print( + "{d}", + .{js.JSValueToNumber(ctx, arg, null)}, + ); }, .kJSTypeString => { - var str = String{ .ref = @as(js.JSStringRef, arg) }; - var chars = str.chars(); - switch (chars.len) { - 0 => { - try writer.writeAll("\"\""); - }, - else => { - for (chars) |c, i| { - switch (c) { - 0...127 => { - // Since we're writing character by character - // it will be really slow if we check for an error every time - _ = writer.write(@intCast(u8, c)) catch 0; - }, - // TODO: - else => {}, - } - } - }, - } + const used = js.JSStringGetUTF8CString(arg, (&printer_buf), printer_buf.len); + try writer.writeAll(printer_buf[0..used]); }, .kJSTypeObject => { // TODO: @@ -391,8 +1031,8 @@ pub const GlobalObject = struct { .kJSTypeSymbol => { var description = js.JSObjectGetPropertyForKey(ctx, arg, Properties.Refs.description, null); return switch (js.JSValueGetType(ctx, description)) { - .kJSTypeString => try valuePrinter(.kJSTypeString, ctx, description, writer), - else => try valuePrinter(.kJSTypeUndefined, ctx, description, writer), + .kJSTypeString => try valuePrinter(.kJSTypeString, ctx, js.JSStringRetain(description), writer), + else => try valuePrinter(.kJSTypeUndefined, ctx, js.JSStringRetain(description), writer), }; }, else => {}, @@ -402,15 +1042,16 @@ pub const GlobalObject = struct { fn output( writer: anytype, ctx: js.JSContextRef, - arguments: []js.JSValueRef, + arguments: []const js.JSValueRef, ) !void { defer Output.flush(); // console.log(); if (arguments.len == 0) { - return js.JSValueMakeUndefined(ctx); + return; } const last = arguments.len - 1; + defer writer.writeAll("\n") catch {}; for (arguments) |arg, i| { switch (js.JSValueGetType(ctx, arg)) { @@ -459,8 +1100,6 @@ pub const GlobalObject = struct { else => {}, } } - - return js.JSValueMakeUndefined(ctx); } pub fn stdout( @@ -468,9 +1107,11 @@ pub const GlobalObject = struct { ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, - arguments: []js.JSValueRef, + arguments: []const js.JSValueRef, + exception: js.JSValueRef, ) js.JSValueRef { - return try output(Output.writer(), ctx, arguments); + output(Output.writer(), ctx, arguments) catch {}; + return js.JSValueMakeUndefined(ctx); } pub fn stderr( @@ -478,57 +1119,302 @@ pub const GlobalObject = struct { ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, - arguments: []js.JSValueRef, + arguments: []const js.JSValueRef, + exception: js.JSValueRef, ) js.JSValueRef { - return try output(Output.errorWriter(), ctx, arguments); + output(Output.errorWriter(), ctx, arguments) catch {}; + return js.JSValueMakeUndefined(ctx); // js.JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) } }; -pub fn NewSingletonClass( +pub fn NewClass( comptime ZigType: type, comptime name: string, - comptime functions: anytype, + comptime staticFunctions: anytype, + comptime properties: anytype, comptime read_only: bool, + comptime singleton: bool, ) type { return struct { const ClassDefinitionCreator = @This(); - const function_names = std.meta.fieldNames(functions); - const function_name_literals: [function_names.len][]js.JSChar = brk: { - var names = std.mem.zeroes([function_names.len][]js.JSChar); - + const function_names = std.meta.fieldNames(@TypeOf(staticFunctions)); + const names_buf = brk: { + var total_len: usize = 0; + for (function_names) |field, i| { + total_len += std.unicode.utf8ToUtf16LeStringLiteral(field).len; + } + var offset: usize = 0; + var names_buf_ = std.mem.zeroes([total_len]u16); for (function_names) |field, i| { - names[i] = std.unicode.utf8ToUtf16LeStringLiteral(field); + var name_ = std.unicode.utf8ToUtf16LeStringLiteral(field); + std.mem.copy(u16, names_buf_[offset .. name_.len + offset], name_[0..]); + offset += name_.len; + } + break :brk names_buf_; + }; + const function_name_literals: [function_names.len][]const js.JSChar = brk: { + var names = std.mem.zeroes([function_names.len][]const js.JSChar); + var len: usize = 0; + for (function_names) |field, i| { + const end = len + std.unicode.utf8ToUtf16LeStringLiteral(field).len; + names[i] = names_buf[len..end]; + len = end; } break :brk names; }; var function_name_refs: [function_names.len]js.JSStringRef = undefined; + var class_name_str = name[0.. :0].ptr; const class_name_literal = std.unicode.utf8ToUtf16LeStringLiteral(name); - var static_functions: [function_name_refs.len + 1:0]js.JSStaticFunction = undefined; - - pub fn define(zig: *ZigType, ctx: js.JSContextRef) !js.JSClassDefinition { - var def = std.mem.zeroes(js.JSClassDefinition); - - inline for (function_name_literals) |function_name, i| { - function_name_refs[i] = js.JSStringCreateWithCharactersNoCopy(&function_name, function_name.len); - static_functions[i] = js.JSStaticFunction{ - .name = (function_names[i][0.. :0]).ptr, - .callAsFunction = To.JS.functionWithCallback( - ZigType, - zig, - function_name_refs[i], - ctx, - @field(functions, function_names[i]), + var static_functions: [function_name_refs.len + 1]js.JSStaticFunction = undefined; + var instance_functions: [function_names.len]js.JSObjectRef = undefined; + const property_names = std.meta.fieldNames(@TypeOf(properties)); + var property_name_refs: [property_names.len]js.JSStringRef = undefined; + const property_name_literals: [property_names.len][]const js.JSChar = brk: { + var list = std.mem.zeroes([property_names.len][]const js.JSChar); + for (property_names) |prop_name, i| { + list[i] = std.unicode.utf8ToUtf16LeStringLiteral(prop_name); + } + break :brk list; + }; + var static_properties: [property_names.len]js.JSStaticValue = undefined; + + pub fn getPropertyCallback(ctx: js.JSContextRef, obj: js.JSObjectRef, prop: js.JSStringRef, exception: ExceptionValueRef) callconv(.C) js.JSValueRef { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return js.JSValueMakeUndefined(ctx); + var instance_pointer = instance_pointer_.?; + var ptr = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, ), - .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, - }; + instance_pointer, + ), + ); + + if (singleton) { + inline for (function_names) |propname, i| { + if (js.JSStringIsEqual(prop, function_name_refs[i])) { + return instance_functions[i]; + } + } + if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { + return ptr.onMissingProperty(ctx, obj, prop, exception); + } + } else { + inline for (property_names) |propname, i| { + if (js.JSStringIsEqual(prop, property_name_refs[i])) { + return @field( + properties, + propname, + )(ptr, ctx, obj, exception); + } + } + + if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { + return ptr.onMissingProperty(ctx, obj, prop, exception); + } } - static_functions[function_name_literals.len] = std.mem.zeroes(js.JSStaticFunction); - def.staticFunctions = static_functions[0.. :0].ptr; - def.className = name[0.. :0].ptr; + + return js.JSValueMakeUndefined(ctx); + } + + fn StaticProperty(comptime id: usize) type { + return struct { + pub fn getter(ctx: js.JSContextRef, obj: js.JSObjectRef, prop: js.JSStringRef, exception: [*c]js.JSValueRef) callconv(.C) js.JSValueRef { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return js.JSValueMakeUndefined(ctx); + var ptr = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, + ), + instance_pointer_.?, + ), + ); + + return @field( + properties, + property_names[id], + )(ptr, ctx, obj, exception); + } + }; + } + + pub fn define(ctx: js.JSContextRef) js.JSClassDefinition { + var def = js.kJSClassDefinitionEmpty; + + if (static_functions.len > 0) { + inline for (function_name_literals) |function_name, i| { + var callback = To.JS.Callback(ZigType, @field(staticFunctions, function_names[i])).rfn; + function_name_refs[i] = js.JSStringCreateWithCharactersNoCopy( + function_name.ptr, + function_name.len, + ); + + static_functions[i] = js.JSStaticFunction{ + .name = (function_names[i][0.. :0]).ptr, + .callAsFunction = callback, + .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, + }; + // if (singleton) { + // var function = js.JSObjectMakeFunctionWithCallback(ctx, function_name_refs[i], callback); + // instance_functions[i] = function; + // } + } + def.staticFunctions = &static_functions; + } + + if (property_names.len > 0) { + inline for (comptime property_name_literals) |prop_name, i| { + property_name_refs[i] = js.JSStringCreateWithCharactersNoCopy( + prop_name.ptr, + prop_name.len, + ); + static_properties[i] = std.mem.zeroes(js.JSStaticValue); + static_properties[i].getProperty = StaticProperty(i).getter; + static_properties[i].name = property_names[i][0.. :0]; + } + + def.staticValues = (&static_properties); + } + + def.className = class_name_str; + // def.getProperty = getPropertyCallback; return def; } }; } + +// This makes it so we get the defines already formatted from the user's environment with the "process.env." prefix set +// This also normalizes quoting +// Currently, it truncates any environment variables to a max of 1024 bytes +const NodeEnvBufMap = struct { + backing: std.BufMap, + pub fn init(allocator: *std.mem.Allocator) NodeEnvBufMap { + return NodeEnvBufMap{ .backing = std.BufMap.init(allocator) }; + } + pub fn get(this: *const NodeEnvBufMap, key: string) ?string { + return this.backing.get(key); + } + pub threadlocal var bufkeybuf: [1024]u8 = undefined; + pub threadlocal var bufkeybuf_first = true; + pub fn put(this: *NodeEnvBufMap, key: string, value: string) !void { + if (value.len == 0) { + return; + } + + if (bufkeybuf_first) { + bufkeybuf[0.."process.env.".len].* = "process.env."; + bufkeybuf_first = false; + } + std.mem.copy(u8, bufkeybuf_first["process.env.".len..], key); + var key_slice = bufkeybuf[0 .. key.len + "process.env.".len]; + var value_slice = value; + const max_value_slice_len = std.math.min(value.len, bufkeybuf.len - key_slice.len); + if (value[0] != '"' and value[value.len - 1] != '"') { + value_slice = bufkeybuf[key_slice.len..][0 .. max_value_slice_len + 2]; + value_slice[0] = '"'; + std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]); + value_slice[value_slice.len - 1] = '"'; + } else if (value[0] != '"') { + value_slice[0] = '"'; + std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]); + } else if (value[value.len - 1] != '"') { + std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]); + value_slice[value_slice.len - 1] = '"'; + } + + return this.backing.put(key_slice, value_slice); + } +}; + +pub fn getNodeEnvMap(allocator: *Allocator) !NodeEnvBufMap { + var result = NodeEnvBufMap.init(allocator); + errdefer result.deinit(); + + if (builtin.os.tag == .windows) { + const ptr = os.windows.peb().ProcessParameters.Environment; + + var i: usize = 0; + while (ptr[i] != 0) { + const key_start = i; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const key_w = ptr[key_start..i]; + const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); + errdefer allocator.free(key); + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const value_w = ptr[value_start..i]; + const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); + errdefer allocator.free(value); + + i += 1; // skip over null byte + + try result.putMove(key, value); + } + return result; + } else if (builtin.os.tag == .wasi) { + var environ_count: usize = undefined; + var environ_buf_size: usize = undefined; + + const environ_sizes_get_ret = os.wasi.environ_sizes_get(&environ_count, &environ_buf_size); + if (environ_sizes_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(environ_sizes_get_ret); + } + + var environ = try allocator.alloc([*:0]u8, environ_count); + defer allocator.free(environ); + var environ_buf = try allocator.alloc(u8, environ_buf_size); + defer allocator.free(environ_buf); + + const environ_get_ret = os.wasi.environ_get(environ.ptr, environ_buf.ptr); + if (environ_get_ret != os.wasi.ESUCCESS) { + return os.unexpectedErrno(environ_get_ret); + } + + for (environ) |env| { + const pair = mem.spanZ(env); + var parts = mem.split(pair, "="); + const key = parts.next().?; + const value = parts.next().?; + try result.put(key, value); + } + return result; + } else if (builtin.link_libc) { + var ptr = std.c.environ; + while (ptr.*) |line| : (ptr += 1) { + var line_i: usize = 0; + while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} + const key = line[0..line_i]; + + var end_i: usize = line_i; + while (line[end_i] != 0) : (end_i += 1) {} + const value = line[line_i + 1 .. end_i]; + + try result.put(key, value); + } + return result; + } else { + for (os.environ) |line| { + var line_i: usize = 0; + while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} + const key = line[0..line_i]; + + var end_i: usize = line_i; + while (line[end_i] != 0) : (end_i += 1) {} + const value = line[line_i + 1 .. end_i]; + + try result.put(key, value); + } + return result; + } +} diff --git a/src/main_javascript.zig b/src/main_javascript.zig new file mode 100644 index 000000000..b637d8916 --- /dev/null +++ b/src/main_javascript.zig @@ -0,0 +1,374 @@ +const std = @import("std"); +const lex = @import("js_lexer.zig"); +const logger = @import("logger.zig"); +const alloc = @import("alloc.zig"); +const options = @import("options.zig"); +const js_parser = @import("js_parser.zig"); +const json_parser = @import("json_parser.zig"); +const js_printer = @import("js_printer.zig"); +const js_ast = @import("js_ast.zig"); +const linker = @import("linker.zig"); +usingnamespace @import("ast/base.zig"); +usingnamespace @import("defines.zig"); +usingnamespace @import("global.zig"); +const panicky = @import("panic_handler.zig"); +pub const MainPanicHandler = panicky.NewPanicHandler(panicky.default_panic); +const Api = @import("api/schema.zig").Api; +const resolve_path = @import("./resolver/resolve_path.zig"); +const clap = @import("clap"); +const bundler = @import("bundler.zig"); +const fs = @import("fs.zig"); +const NodeModuleBundle = @import("./node_module_bundle.zig").NodeModuleBundle; +const js = @import("javascript/jsc/javascript.zig"); +const allocators = @import("allocators.zig"); +pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { + if (MainPanicHandler.Singleton) |singleton| { + MainPanicHandler.handle_panic(msg, error_return_trace); + } else { + panicky.default_panic(msg, error_return_trace); + } +} +const constStrToU8 = allocators.constStrToU8; +pub fn main() anyerror!void { + // The memory allocator makes a massive difference. + // std.heap.raw_c_allocator and std.heap.c_allocator perform similarly. + // std.heap.GeneralPurposeAllocator makes this about 3x _slower_ than esbuild. + // var root_alloc = std.heap.ArenaAllocator.init(std.heap.raw_c_allocator); + // var root_alloc_ = &root_alloc.allocator; + try alloc.setup(std.heap.c_allocator); + var stdout = std.io.getStdOut(); + // var stdout = std.io.bufferedWriter(stdout_file.writer()); + var stderr = std.io.getStdErr(); + // var stderr = std.io.bufferedWriter(stderr_file.writer()); + var output_source = Output.Source.init(stdout, stderr); + // defer stdout.flush() catch {}; + // defer stderr.flush() catch {}; + Output.Source.set(&output_source); + Output.enable_ansi_colors = stderr.isTty(); + defer Output.flush(); + try Cli.start( + std.heap.c_allocator, + stdout, + stderr, + ); +} + +pub const Cli = struct { + const LoaderMatcher = strings.ExactSizeMatcher(4); + pub fn ColonListType(comptime t: type, value_resolver: anytype) type { + return struct { + pub fn init(allocator: *std.mem.Allocator, count: usize) !@This() { + var keys = try allocator.alloc(string, count); + var values = try allocator.alloc(t, count); + + return @This(){ .keys = keys, .values = values }; + } + keys: []string, + values: []t, + + pub fn load(self: *@This(), input: []const string) !void { + for (input) |str, i| { + // Support either ":" or "=" as the separator, preferring whichever is first. + // ":" is less confusing IMO because that syntax is used with flags + // but "=" is what esbuild uses and I want this to be somewhat familiar for people using esbuild + const midpoint = std.math.min(strings.indexOfChar(str, ':') orelse std.math.maxInt(usize), strings.indexOfChar(str, '=') orelse std.math.maxInt(usize)); + if (midpoint == std.math.maxInt(usize)) { + return error.InvalidSeparator; + } + + self.keys[i] = str[0..midpoint]; + self.values[i] = try value_resolver(str[midpoint + 1 .. str.len]); + } + } + + pub fn resolve(allocator: *std.mem.Allocator, input: []const string) !@This() { + var list = try init(allocator, input.len); + try list.load(input); + return list; + } + }; + } + pub const LoaderColonList = ColonListType(Api.Loader, Arguments.loader_resolver); + pub const DefineColonList = ColonListType(string, Arguments.noop_resolver); + + pub const Arguments = struct { + pub fn loader_resolver(in: string) !Api.Loader { + const Matcher = strings.ExactSizeMatcher(4); + switch (Matcher.match(in)) { + Matcher.case("jsx") => return Api.Loader.jsx, + Matcher.case("js") => return Api.Loader.js, + Matcher.case("ts") => return Api.Loader.ts, + Matcher.case("tsx") => return Api.Loader.tsx, + Matcher.case("css") => return Api.Loader.css, + Matcher.case("file") => return Api.Loader.file, + Matcher.case("json") => return Api.Loader.json, + else => { + return error.InvalidLoader; + }, + } + } + + pub fn noop_resolver(in: string) !string { + return in; + } + + pub fn fileReadError(err: anyerror, stderr: anytype, filename: string, kind: string) noreturn { + stderr.writer().print("Error reading file \"{s}\" for {s}: {s}", .{ filename, kind, @errorName(err) }) catch {}; + std.process.exit(1); + } + + pub fn readFile( + allocator: *std.mem.Allocator, + cwd: string, + filename: string, + ) ![]u8 { + var paths = [_]string{ cwd, filename }; + const outpath = try std.fs.path.resolve(allocator, &paths); + defer allocator.free(outpath); + var file = try std.fs.openFileAbsolute(outpath, std.fs.File.OpenFlags{ .read = true, .write = false }); + defer file.close(); + const stats = try file.stat(); + return try file.readToEndAlloc(allocator, stats.size); + } + + pub fn parse(allocator: *std.mem.Allocator, stdout: anytype, stderr: anytype) !Api.TransformOptions { + @setEvalBranchQuota(9999); + const params = comptime [_]clap.Param(clap.Help){ + clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, + clap.parseParam("-r, --resolve <STR> Determine import/require behavior. \"disable\" ignores. \"dev\" bundles node_modules and builds everything else as independent entry points") catch unreachable, + clap.parseParam("-d, --define <STR>... Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:development") catch unreachable, + clap.parseParam("-l, --loader <STR>... Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: jsx, js, json, tsx (not implemented yet), ts (not implemented yet), css (not implemented yet)") catch unreachable, + clap.parseParam("-o, --outdir <STR> Save output to directory (default: \"out\" if none provided and multiple entry points passed)") catch unreachable, + clap.parseParam("-e, --external <STR>... Exclude module from transpilation (can use * wildcards). ex: -e react") catch unreachable, + clap.parseParam("-i, --inject <STR>... Inject module at the top of every file") catch unreachable, + clap.parseParam("--cwd <STR> Absolute path to resolve entry points from. Defaults to cwd") catch unreachable, + clap.parseParam("--public-url <STR> Rewrite import paths to start with --public-url. Useful for web browsers.") catch unreachable, + clap.parseParam("--serve Start a local dev server. This also sets resolve to \"lazy\".") catch unreachable, + clap.parseParam("--public-dir <STR> Top-level directory for .html files, fonts, images, or anything external. Only relevant with --serve. Defaults to \"<cwd>/public\", to match create-react-app and Next.js") catch unreachable, + clap.parseParam("--jsx-factory <STR> Changes the function called when compiling JSX elements using the classic JSX runtime") catch unreachable, + clap.parseParam("--jsx-fragment <STR> Changes the function called when compiling JSX fragments using the classic JSX runtime") catch unreachable, + clap.parseParam("--jsx-import-source <STR> Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: \"react\"") catch unreachable, + clap.parseParam("--jsx-runtime <STR> \"automatic\" (default) or \"classic\"") catch unreachable, + clap.parseParam("--jsx-production Use jsx instead of jsxDEV (default) for the automatic runtime") catch unreachable, + clap.parseParam("--extension-order <STR>... defaults to: .tsx,.ts,.jsx,.js,.json ") catch unreachable, + clap.parseParam("--disable-react-fast-refresh Disable React Fast Refresh. Enabled if --serve is set and --jsx-production is not set. Otherwise, it's a noop.") catch unreachable, + clap.parseParam("--tsconfig-override <STR> Load tsconfig from path instead of cwd/tsconfig.json") catch unreachable, + clap.parseParam("--platform <STR> \"browser\" or \"node\". Defaults to \"browser\"") catch unreachable, + clap.parseParam("--main-fields <STR>... Main fields to lookup in package.json. Defaults to --platform dependent") catch unreachable, + clap.parseParam("--scan Instead of bundling or transpiling, print a list of every file imported by an entry point, recursively") catch unreachable, + clap.parseParam("--new-jsb Generate a new node_modules.jsb file from node_modules and entry point(s)") catch unreachable, + clap.parseParam("--jsb <STR> Use a Speedy JavaScript Bundle (default: \"./node_modules.jsb\" if exists)") catch unreachable, + // clap.parseParam("--no-jsb Use a Speedy JavaScript Bundle (default: \"./node_modules.jsb\" if exists)") catch unreachable, + clap.parseParam("<POS>... Entry points to use") catch unreachable, + }; + + var diag = clap.Diagnostic{}; + + var args = clap.parse(clap.Help, ¶ms, .{ .diagnostic = &diag }) catch |err| { + // Report useful error and exit + diag.report(stderr.writer(), err) catch {}; + return err; + }; + + if (args.flag("--help")) { + try clap.help(stderr.writer(), ¶ms); + std.process.exit(1); + } + + var cwd_paths = [_]string{args.option("--cwd") orelse try std.process.getCwdAlloc(allocator)}; + var cwd = try std.fs.path.resolve(allocator, &cwd_paths); + var tsconfig_override = if (args.option("--tsconfig-override")) |ts| (Arguments.readFile(allocator, cwd, ts) catch |err| fileReadError(err, stderr, ts, "tsconfig.json")) else null; + var public_url = args.option("--public-url"); + var defines_tuple = try DefineColonList.resolve(allocator, args.options("--define")); + var loader_tuple = try LoaderColonList.resolve(allocator, args.options("--define")); + + var define_keys = defines_tuple.keys; + var define_values = defines_tuple.values; + var loader_keys = loader_tuple.keys; + var loader_values = loader_tuple.values; + var entry_points = args.positionals(); + var inject = args.options("--inject"); + var output_dir = args.option("--outdir"); + const serve = false; + + var write = entry_points.len > 1 or output_dir != null; + if (write and output_dir == null) { + var _paths = [_]string{ cwd, "out" }; + output_dir = try std.fs.path.resolve(allocator, &_paths); + } + var externals = std.mem.zeroes([][]u8); + if (args.options("--external").len > 0) { + externals = try allocator.alloc([]u8, args.options("--external").len); + for (args.options("--external")) |external, i| { + externals[i] = constStrToU8(external); + } + } + + var jsx_factory = args.option("--jsx-factory"); + var jsx_fragment = args.option("--jsx-fragment"); + var jsx_import_source = args.option("--jsx-import-source"); + var jsx_runtime = args.option("--jsx-runtime"); + var jsx_production = args.flag("--jsx-production"); + var react_fast_refresh = false; + + if (serve or args.flag("--new-jsb")) { + react_fast_refresh = true; + if (args.flag("--disable-react-fast-refresh") or jsx_production) { + react_fast_refresh = false; + } + } + + var main_fields = args.options("--main-fields"); + + var node_modules_bundle_path = args.option("--jsb") orelse brk: { + if (args.flag("--new-jsb")) { + break :brk null; + } + + const node_modules_bundle_path_absolute = resolve_path.joinAbs(cwd, .auto, "node_modules.jsb"); + std.fs.accessAbsolute(node_modules_bundle_path_absolute, .{}) catch |err| { + break :brk null; + }; + break :brk try allocator.dupe(u8, node_modules_bundle_path_absolute); + }; + + if (args.flag("--new-jsb")) { + node_modules_bundle_path = null; + } + + const PlatformMatcher = strings.ExactSizeMatcher(8); + const ResoveMatcher = strings.ExactSizeMatcher(8); + + var resolve = Api.ResolveMode.lazy; + if (args.option("--resolve")) |_resolve| { + switch (PlatformMatcher.match(_resolve)) { + PlatformMatcher.case("disable") => { + resolve = Api.ResolveMode.disable; + }, + PlatformMatcher.case("bundle") => { + resolve = Api.ResolveMode.bundle; + }, + PlatformMatcher.case("dev") => { + resolve = Api.ResolveMode.dev; + }, + PlatformMatcher.case("lazy") => { + resolve = Api.ResolveMode.lazy; + }, + else => { + diag.name.long = "--resolve"; + diag.arg = _resolve; + try diag.report(stderr.writer(), error.InvalidResolveOption); + std.process.exit(1); + }, + } + } + + var platform: ?Api.Platform = null; + + if (args.option("--platform")) |_platform| { + switch (PlatformMatcher.match(_platform)) { + PlatformMatcher.case("browser") => { + platform = Api.Platform.browser; + }, + PlatformMatcher.case("node") => { + platform = Api.Platform.node; + }, + else => { + diag.name.long = "--platform"; + diag.arg = _platform; + try diag.report(stderr.writer(), error.InvalidPlatform); + std.process.exit(1); + }, + } + } + + var jsx: ?Api.Jsx = null; + if (jsx_factory != null or + jsx_fragment != null or + jsx_import_source != null or + jsx_runtime != null or + jsx_production or react_fast_refresh) + { + var default_factory = "".*; + var default_fragment = "".*; + var default_import_source = "".*; + jsx = Api.Jsx{ + .factory = constStrToU8(jsx_factory orelse &default_factory), + .fragment = constStrToU8(jsx_fragment orelse &default_fragment), + .import_source = constStrToU8(jsx_import_source orelse &default_import_source), + .runtime = if (jsx_runtime != null) try resolve_jsx_runtime(jsx_runtime.?) else Api.JsxRuntime.automatic, + .development = !jsx_production, + .react_fast_refresh = react_fast_refresh, + }; + } + + if (entry_points.len == 0) { + try clap.help(stderr.writer(), ¶ms); + try diag.report(stderr.writer(), error.MissingEntryPoint); + std.process.exit(1); + } + + return Api.TransformOptions{ + .jsx = jsx, + .output_dir = output_dir, + .resolve = resolve, + .external = externals, + .absolute_working_dir = cwd, + .tsconfig_override = tsconfig_override, + .public_url = public_url, + .define = .{ + .keys = define_keys, + .values = define_values, + }, + .loaders = .{ + .extensions = loader_keys, + .loaders = loader_values, + }, + .node_modules_bundle_path = node_modules_bundle_path, + .public_dir = if (args.option("--public-dir")) |public_dir| allocator.dupe(u8, public_dir) catch unreachable else null, + .write = write, + .platform = .speedy, + .serve = serve, + .inject = inject, + .entry_points = entry_points, + .extension_order = args.options("--extension-order"), + .main_fields = args.options("--main-fields"), + .only_scan_dependencies = if (args.flag("--scan")) Api.ScanDependencyMode.all else Api.ScanDependencyMode._none, + .generate_node_module_bundle = if (args.flag("--new-jsb")) true else false, + }; + } + }; + pub fn resolve_jsx_runtime(str: string) !Api.JsxRuntime { + if (strings.eql(str, "automatic")) { + return Api.JsxRuntime.automatic; + } else if (strings.eql(str, "fallback")) { + return Api.JsxRuntime.classic; + } else { + return error.InvalidJSXRuntime; + } + } + pub fn printScanResults(scan_results: bundler.ScanResult.Summary, allocator: *std.mem.Allocator) !void { + var stdout = std.io.getStdOut(); + const print_start = std.time.nanoTimestamp(); + try std.json.stringify(scan_results.list(), .{}, stdout.writer()); + Output.printError("\nJSON printing took: {d}\n", .{std.time.nanoTimestamp() - print_start}); + } + pub fn startTransform(allocator: *std.mem.Allocator, args: Api.TransformOptions, log: *logger.Log) anyerror!void {} + pub fn start(allocator: *std.mem.Allocator, stdout: anytype, stderr: anytype) anyerror!void { + const start_time = std.time.nanoTimestamp(); + var log = logger.Log.init(allocator); + var panicker = MainPanicHandler.init(&log); + MainPanicHandler.Singleton = &panicker; + + // var args = try Arguments.parse(alloc.static, stdout, stderr); + // var serve_bundler = try bundler.ServeBundler.init(allocator, &log, args); + // var res = try serve_bundler.buildFile(&log, allocator, args.entry_points[0], std.fs.path.extension(args.entry_points[0])); + + // var results = try bundler.Bundler.bundle(allocator, &log, args); + // var file = results.output_files[0]; + var vm = try js.VirtualMachine.init(allocator); + + _ = try vm.evalUtf8("test.js", "console.log(Number(10.1));"[0.. :0]); + Output.print("Done", .{}); + } +}; diff --git a/src/options.zig b/src/options.zig index aca85ed31..9009fd88c 100644 --- a/src/options.zig +++ b/src/options.zig @@ -286,6 +286,7 @@ pub const Platform = enum { return switch (plat orelse api.Api.Platform._none) { .node => .node, .browser => .browser, + .speedy => .speedy, else => .browser, }; } @@ -674,12 +675,25 @@ pub const BundleOptions = struct { } if (transform.platform) |plat| { - opts.platform = if (plat == .browser) .browser else .node; + opts.platform = switch (plat) { + .speedy => speedy, + .neutral => .neutral, + .browser => .browser, + .node => .node, + }; opts.main_fields = Platform.DefaultMainFields.get(opts.platform); } - if (opts.platform == .node) { - opts.import_path_format = .relative_nodejs; + switch (opts.platform) { + .node => { + opts.import_path_format = .relative_nodejs; + }, + .speedy => { + // If we're doing SSR, we want all the URLs to be the same as what it would be in the browser + // If we're not doing SSR, we want all the import paths to be absolute + opts.import_path_format = if (opts.import_path_format == .absolute_url) .absolute_url else .absolute_path; + }, + else => {}, } if (transform.main_fields.len > 0) { diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 546ba8a4f..03382e7a2 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -431,11 +431,10 @@ pub const CodepointIterator = struct { inline fn nextCodepointSlice(it: *CodepointIterator) []const u8 { @setRuntimeSafety(false); - const cp_len = utf8ByteSequenceLength(it.source.contents[it.current]); - it.end = it.current; - it.current += cp_len; + const cp_len = utf8ByteSequenceLength(it.bytes[it.i]); + it.i += cp_len; - return if (!(it.current > it.source.contents.len)) it.source.contents[it.current - cp_len .. it.current] else ""; + return if (!(it.i > it.bytes.len)) it.bytes[it.i - cp_len .. it.i] else ""; } pub fn nextCodepoint(it: *CodepointIterator) CodePoint { diff --git a/src/wtf_string_mutable.zig b/src/wtf_string_mutable.zig index 2c94dae22..2d1aac522 100644 --- a/src/wtf_string_mutable.zig +++ b/src/wtf_string_mutable.zig @@ -139,7 +139,7 @@ pub const WTFStringMutable = struct { self.list.appendAssumeCapacity(char); } pub inline fn append(self: *WTFStringMutable, str: []const u8) !void { - self.growIfNeeded(str.len); + try self.growIfNeeded(str.len); var iter = strings.CodepointIterator{ .bytes = str, .i = 0 }; while (true) { @@ -148,12 +148,12 @@ pub const WTFStringMutable = struct { return; }, 0...0xFFFF => { - try self.list.append(@intCast(u16, iter.c)); + try self.list.append(self.allocator, @intCast(u16, iter.c)); }, else => { const c = iter.c - 0x10000; - try self.list.append(@intCast(u16, 0xD800 + ((c >> 10) & 0x3FF))); - try self.list.append(@intCast(u16, 0xDC00 + (c & 0x3FF))); + try self.list.append(self.allocator, @intCast(u16, 0xD800 + ((c >> 10) & 0x3FF))); + try self.list.append(self.allocator, @intCast(u16, 0xDC00 + (c & 0x3FF))); }, } } @@ -186,15 +186,15 @@ pub const WTFStringMutable = struct { return @intCast(i32, self.list.items.len); } - pub fn toOwnedSlice(self: *WTFStringMutable) string { + pub fn toOwnedSlice(self: *WTFStringMutable) []u16 { return self.list.toOwnedSlice(self.allocator); } - pub fn toOwnedSliceLeaky(self: *WTFStringMutable) string { + pub fn toOwnedSliceLeaky(self: *WTFStringMutable) []u16 { return self.list.items; } - pub fn toOwnedSliceLength(self: *WTFStringMutable, length: usize) string { + pub fn toOwnedSliceLength(self: *WTFStringMutable, length: usize) { self.list.shrinkAndFree(self.allocator, length); return self.list.toOwnedSlice(self.allocator); } |