diff options
author | 2021-06-27 23:36:35 -0700 | |
---|---|---|
committer | 2021-06-27 23:36:35 -0700 | |
commit | ae113559c6dd1e1e77b69ee5edee93fe59b4be2e (patch) | |
tree | 0686604fee7ab5afe2166f3d9445f874669e419f | |
parent | 506d9b81a7c9dac5dd870f6735c39df105e72fd4 (diff) | |
download | bun-ae113559c6dd1e1e77b69ee5edee93fe59b4be2e.tar.gz bun-ae113559c6dd1e1e77b69ee5edee93fe59b4be2e.tar.zst bun-ae113559c6dd1e1e77b69ee5edee93fe59b4be2e.zip |
starting to work
-rw-r--r-- | .vscode/launch.json | 3 | ||||
-rw-r--r-- | build.zig | 33 | ||||
-rw-r--r-- | src/bundler.zig | 75 | ||||
-rw-r--r-- | src/cache.zig | 4 | ||||
-rw-r--r-- | src/cli.zig | 6 | ||||
-rw-r--r-- | src/hash_map.zig | 18 | ||||
-rw-r--r-- | src/http.zig | 6 | ||||
-rw-r--r-- | src/javascript/jsc/JavascriptCore.zig | 92 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 964 | ||||
-rw-r--r-- | src/javascript/jsc/node_env_buf_map.zig | 144 | ||||
-rw-r--r-- | src/js_lexer.zig | 5 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 12 | ||||
-rw-r--r-- | src/js_printer.zig | 73 | ||||
-rw-r--r-- | src/linker.zig | 1 | ||||
-rw-r--r-- | src/main_javascript.zig | 21 | ||||
-rw-r--r-- | src/node_module_bundle.zig | 17 | ||||
-rw-r--r-- | src/options.zig | 24 | ||||
-rw-r--r-- | src/string_mutable.zig | 13 | ||||
-rw-r--r-- | src/test/fixtures/console.log.js | 3 | ||||
-rw-r--r-- | src/test/fixtures/export-check.ts | 2 | ||||
-rw-r--r-- | src/test/fixtures/uescape.js | 3 |
21 files changed, 1011 insertions, 508 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index 3f9b8d7be..23d246ea8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,8 +17,9 @@ "name": "Eval", "program": "${workspaceFolder}/build/debug/macos-x86_64/spjs", "args": [ - "./simple.css", + "./console.log.js", "--resolve=dev", + // "--jsb=../../../demos/css-stress-test/node_modules.jsb", "--outdir=outcss", "--public-url=https://localhost:9000/" ], @@ -84,12 +84,11 @@ pub fn build(b: *std.build.Builder) void { return; } else { - exe = b.addExecutable("esdev", "src/main.zig"); - exe.linkLibC(); + exe = b.addExecutable("spjs", "src/main_javascript.zig"); } // exe.setLibCFile("libc.txt"); exe.linkLibC(); - exe.linkLibCpp(); + // exe.linkLibCpp(); exe.addPackage(.{ .name = "clap", .path = .{ .path = "src/deps/zig-clap/clap.zig" }, @@ -118,31 +117,21 @@ pub fn build(b: *std.build.Builder) void { // exe.want_lto = true; if (!target.getCpuArch().isWasm()) { - // exe.addLibPath("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib"); - // exe.addIncludeDir("/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/"); - - const env = std.process.getEnvMap(std.heap.c_allocator) catch unreachable; - - // if (env.get("SDKROOT")) |sdkroot| { - // const joined = resolve_path.joinAbs2(cwd, .auto, sdkroot, "usr/include"); - // const sys = std.heap.c_allocator.dupe(u8, joined) catch unreachable; - // 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(); + // var javascript = b.addExecutable("spjs", "src/main_javascript.zig"); + // javascript.packages = std.ArrayList(std.build.Pkg).fromOwnedSlice(std.heap.c_allocator, std.heap.c_allocator.dupe(std.build.Pkg, exe.packages.items) catch unreachable); + // javascript.setOutputDir(output_dir); + // javascript.setBuildMode(mode); + // javascript.linkLibC(); + // javascript.linkLibCpp(); if (target.getOsTag() == .macos) { - javascript.linkFramework("JavaScriptCore"); + // javascript.linkFramework("JavaScriptCore"); exe.linkFramework("JavascriptCore"); } - javascript.strip = false; - javascript.install(); + // javascript.strip = false; + // javascript.install(); } exe.install(); diff --git a/src/bundler.zig b/src/bundler.zig index 143c57c50..f052ce8a9 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -29,6 +29,7 @@ const Timer = @import("./timer.zig"); const hash_map = @import("hash_map.zig"); const PackageJSON = @import("./resolver/package_json.zig").PackageJSON; const DebugLogs = _resolver.DebugLogs; +const NodeModuleBundle = @import("./node_module_bundle.zig").NodeModuleBundle; const Css = @import("css_scanner.zig"); @@ -161,6 +162,7 @@ pub fn NewBundler(cache_files: bool) type { allocator: *std.mem.Allocator, log: *logger.Log, opts: Api.TransformOptions, + existing_bundle: ?*NodeModuleBundle, ) !ThisBundler { js_ast.Expr.Data.Store.create(allocator); js_ast.Stmt.Data.Store.create(allocator); @@ -170,6 +172,7 @@ pub fn NewBundler(cache_files: bool) type { fs, log, opts, + existing_bundle, ); // var pool = try allocator.create(ThreadPool); @@ -961,6 +964,7 @@ pub fn NewBundler(cache_files: bool) type { result, Writer, writer, + .esm, ), .input_fd = result.input_fd, }; @@ -1028,6 +1032,7 @@ pub fn NewBundler(cache_files: bool) type { result, js_printer.FileWriter, js_printer.NewFileWriter(file), + .esm, ); var file_op = options.OutputFile.FileOperation.fromFile(file.handle, file_path.pretty); @@ -1185,25 +1190,58 @@ pub fn NewBundler(cache_files: bool) type { result: ParseResult, comptime Writer: type, writer: Writer, + comptime format: js_printer.Format, ) !usize { const ast = result.ast; var symbols: [][]js_ast.Symbol = &([_][]js_ast.Symbol{ast.symbols}); - return try js_printer.printAst( - Writer, - writer, - ast, - js_ast.Symbol.Map.initList(symbols), - &result.source, - false, - js_printer.Options{ - .to_module_ref = Ref.RuntimeRef, - .externals = ast.externals, - .runtime_imports = ast.runtime_imports, - }, - Linker, - &bundler.linker, - ); + return switch (format) { + .cjs => try js_printer.printCommonJS( + Writer, + writer, + ast, + js_ast.Symbol.Map.initList(symbols), + &result.source, + false, + js_printer.Options{ + .to_module_ref = Ref.RuntimeRef, + .externals = ast.externals, + .runtime_imports = ast.runtime_imports, + }, + Linker, + &bundler.linker, + ), + .esm => try js_printer.printAst( + Writer, + writer, + ast, + js_ast.Symbol.Map.initList(symbols), + &result.source, + false, + js_printer.Options{ + .to_module_ref = Ref.RuntimeRef, + .externals = ast.externals, + .runtime_imports = ast.runtime_imports, + }, + Linker, + &bundler.linker, + ), + .speedy => try js_printer.printSpeedyCJS( + Writer, + writer, + ast, + js_ast.Symbol.Map.initList(symbols), + &result.source, + false, + js_printer.Options{ + .to_module_ref = Ref.RuntimeRef, + .externals = ast.externals, + .runtime_imports = ast.runtime_imports, + }, + Linker, + &bundler.linker, + ), + }; } pub fn parse( @@ -1246,7 +1284,8 @@ 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 = bundler.options.platform != .speedy; + opts.transform_require_to_import = false; + opts.force_commonjs = 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 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; @@ -1489,7 +1528,7 @@ pub fn NewBundler(cache_files: bool) type { ) !ScanResult.Summary { var opts = _opts; opts.resolve = .dev; - var bundler = try ThisBundler.init(allocator, log, opts); + var bundler = try ThisBundler.init(allocator, log, opts, null); bundler.configureLinker(); @@ -1571,7 +1610,7 @@ pub fn NewBundler(cache_files: bool) type { log: *logger.Log, opts: Api.TransformOptions, ) !options.TransformResult { - var bundler = try ThisBundler.init(allocator, log, opts); + var bundler = try ThisBundler.init(allocator, log, opts, null); bundler.configureLinker(); if (bundler.options.write and bundler.options.output_dir.len > 0) {} diff --git a/src/cache.zig b/src/cache.zig index 07b4b152b..efc2a90c7 100644 --- a/src/cache.zig +++ b/src/cache.zig @@ -186,10 +186,6 @@ pub fn NewCache(comptime cache_files: bool) type { ) anyerror!?js_ast.Ast { var temp_log = logger.Log.init(allocator); defer temp_log.appendTo(log) catch {}; - if (isDebug) { - Output.println("Parse!", .{}); - } - var parser = js_parser.Parser.init(opts, &temp_log, source, defines, allocator) catch |err| { return null; }; diff --git a/src/cli.zig b/src/cli.zig index 0471b4e07..b89343ec0 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"); @@ -201,7 +201,7 @@ pub const Cli = struct { std.fs.accessAbsolute(node_modules_bundle_path_absolute, .{}) catch |err| { break :brk null; }; - break :brk try allocator.dupe(u8, node_modules_bundle_path_absolute); + break :brk try std.fs.realpathAlloc(allocator, node_modules_bundle_path_absolute); }; if (args.flag("--new-jsb")) { @@ -367,7 +367,7 @@ pub const Cli = struct { } if ((args.generate_node_module_bundle orelse false)) { - var this_bundler = try bundler.ServeBundler.init(allocator, &log, args); + var this_bundler = try bundler.ServeBundler.init(allocator, &log, args, null); this_bundler.configureLinker(); var filepath = "node_modules.jsb"; var node_modules = try bundler.ServeBundler.GenerateNodeModuleBundle.generate(&this_bundler, allocator, filepath); diff --git a/src/hash_map.zig b/src/hash_map.zig index 853ad39be..431ea3611 100644 --- a/src/hash_map.zig +++ b/src/hash_map.zig @@ -410,7 +410,7 @@ pub fn HashMapUnmanaged( index: Size = 0, pub fn next(it: *Iterator) ?*Entry { - assert(it.index <= it.hm.capacity()); + if (std.builtin.mode != .ReleaseFast) assert(it.index <= it.hm.capacity()); if (it.hm.size == 0) return null; const cap = it.hm.capacity(); @@ -524,7 +524,7 @@ pub fn HashMapUnmanaged( /// Insert an entry in the map. Assumes it is not already present. pub fn putNoClobber(self: *Self, allocator: *Allocator, key: K, value: V) !void { - assert(!self.contains(key)); + if (std.builtin.mode != .ReleaseFast) assert(!self.contains(key)); try self.growIfNeeded(allocator, 1); self.putAssumeCapacityNoClobber(key, value); @@ -541,7 +541,7 @@ pub fn HashMapUnmanaged( /// Insert an entry in the map. Assumes it is not already present, /// and that no allocation is needed. pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void { - assert(!self.contains(key)); + if (std.builtin.mode != .ReleaseFast) assert(!self.contains(key)); const hash = hashFn(key); putAssumeCapacityNoClobberWithHash(self, key, hash, value); @@ -560,7 +560,7 @@ pub fn HashMapUnmanaged( } if (!metadata[0].isTombstone()) { - assert(self.available > 0); + if (std.builtin.mode != .ReleaseFast) assert(self.available > 0); self.available -= 1; } @@ -795,7 +795,7 @@ pub fn HashMapUnmanaged( /// Asserts there is an `Entry` with matching key, deletes it from the hash map, /// and discards it. pub fn removeAssertDiscard(self: *Self, key: K) void { - assert(self.contains(key)); + if (std.builtin.mode != .ReleaseFast) assert(self.contains(key)); const hash = hashFn(key); const mask = self.capacity() - 1; @@ -828,7 +828,7 @@ pub fn HashMapUnmanaged( // what has to stay under the max_load_percentage of capacity. fn load(self: *const Self) Size { const max_load = (self.capacity() * max_load_percentage) / 100; - assert(max_load >= self.available); + if (std.builtin.mode != .ReleaseFast) assert(max_load >= self.available); return @truncate(Size, max_load - self.available); } @@ -865,8 +865,8 @@ pub fn HashMapUnmanaged( fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void { const new_cap = std.math.max(new_capacity, minimal_capacity); - assert(new_cap > self.capacity()); - assert(std.math.isPowerOfTwo(new_cap)); + if (std.builtin.mode != .ReleaseFast) assert(new_cap > self.capacity()); + if (std.builtin.mode != .ReleaseFast) assert(std.math.isPowerOfTwo(new_cap)); var map = Self{}; defer map.deinit(allocator); @@ -907,7 +907,7 @@ pub fn HashMapUnmanaged( const metadata = ptr + @sizeOf(Header); var entry_ptr = ptr + meta_size; entry_ptr = (entry_ptr + alignment) & ~@as(usize, alignment); - assert(entry_ptr + @as(usize, new_capacity) * @sizeOf(Entry) <= ptr + total_size); + if (std.builtin.mode != .ReleaseFast) assert(entry_ptr + @as(usize, new_capacity) * @sizeOf(Entry) <= ptr + total_size); const hdr = @intToPtr(*Header, ptr); hdr.entries = @intToPtr([*]Entry, entry_ptr); diff --git a/src/http.zig b/src/http.zig index 665f7e978..78d9c624f 100644 --- a/src/http.zig +++ b/src/http.zig @@ -42,7 +42,7 @@ threadlocal var req_headers_buf: [100]picohttp.Header = undefined; threadlocal var res_headers_buf: [100]picohttp.Header = undefined; const sync = @import("./sync.zig"); -const Watcher = watcher.NewWatcher(*Server); +pub const Watcher = watcher.NewWatcher(*Server); const ENABLE_LOGGER = false; pub fn println(comptime fmt: string, args: anytype) void { @@ -487,7 +487,7 @@ pub const RequestContext = struct { .absolute_url, ); - var written = this.bundler.print(parse_result, @TypeOf(&this.printer), &this.printer) catch |err| { + var written = this.bundler.print(parse_result, @TypeOf(&this.printer), &this.printer, .esm) catch |err| { return WatchBuildResult{ .value = .{ .fail = std.mem.zeroes(Api.WebsocketMessageBuildFailure) }, .id = id, @@ -1505,7 +1505,7 @@ pub const Server = struct { .watcher = undefined, .timer = try std.time.Timer.start(), }; - server.bundler = try Bundler.init(allocator, &server.log, options); + server.bundler = try Bundler.init(allocator, &server.log, options, null); server.bundler.configureLinker(); try server.initWatcher(); diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig index e1bab65b8..83ccaebcf 100644 --- a/src/javascript/jsc/JavascriptCore.zig +++ b/src/javascript/jsc/JavascriptCore.zig @@ -17,8 +17,8 @@ pub const JSTypedArrayBytesDeallocator = ?fn (?*c_void, ?*c_void) callconv(.C) v 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; +pub extern fn JSEvaluateScript(ctx: JSContextRef, script: JSStringRef, thisObject: JSObjectRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: ExceptionRef) JSValueRef; +pub extern fn JSCheckScriptSyntax(ctx: JSContextRef, script: JSStringRef, sourceURL: JSStringRef, startingLineNumber: c_int, exception: ExceptionRef) bool; pub extern fn JSGarbageCollect(ctx: JSContextRef) void; pub const JSType = enum(c_uint) { kJSTypeUndefined, @@ -73,10 +73,10 @@ pub extern fn JSValueIsObject(ctx: JSContextRef, value: JSValueRef) bool; pub extern fn JSValueIsObjectOfClass(ctx: JSContextRef, value: JSValueRef, jsClass: JSClassRef) bool; pub extern fn JSValueIsArray(ctx: JSContextRef, value: JSValueRef) bool; pub extern fn JSValueIsDate(ctx: JSContextRef, value: JSValueRef) bool; -pub extern fn JSValueGetTypedArrayType(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSTypedArrayType; -pub extern fn JSValueIsEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef, exception: [*c]JSValueRef) bool; +pub extern fn JSValueGetTypedArrayType(ctx: JSContextRef, value: JSValueRef, exception: ExceptionRef) JSTypedArrayType; +pub extern fn JSValueIsEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef, exception: ExceptionRef) bool; pub extern fn JSValueIsStrictEqual(ctx: JSContextRef, a: JSValueRef, b: JSValueRef) bool; -pub extern fn JSValueIsInstanceOfConstructor(ctx: JSContextRef, value: JSValueRef, constructor: JSObjectRef, exception: [*c]JSValueRef) bool; +pub extern fn JSValueIsInstanceOfConstructor(ctx: JSContextRef, value: JSValueRef, constructor: JSObjectRef, exception: ExceptionRef) bool; pub extern fn JSValueMakeUndefined(ctx: JSContextRef) JSValueRef; pub extern fn JSValueMakeNull(ctx: JSContextRef) JSValueRef; pub extern fn JSValueMakeBoolean(ctx: JSContextRef, boolean: bool) JSValueRef; @@ -84,11 +84,11 @@ pub extern fn JSValueMakeNumber(ctx: JSContextRef, number: f64) JSValueRef; pub extern fn JSValueMakeString(ctx: JSContextRef, string: JSStringRef) JSValueRef; pub extern fn JSValueMakeSymbol(ctx: JSContextRef, description: JSStringRef) JSValueRef; pub extern fn JSValueMakeFromJSONString(ctx: JSContextRef, string: JSStringRef) JSValueRef; -pub extern fn JSValueCreateJSONString(ctx: JSContextRef, value: JSValueRef, indent: c_uint, exception: [*c]JSValueRef) JSStringRef; +pub extern fn JSValueCreateJSONString(ctx: JSContextRef, value: JSValueRef, indent: c_uint, exception: ExceptionRef) JSStringRef; pub extern fn JSValueToBoolean(ctx: JSContextRef, value: JSValueRef) bool; -pub extern fn JSValueToNumber(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) f64; -pub extern fn JSValueToStringCopy(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSStringRef; -pub extern fn JSValueToObject(ctx: JSContextRef, value: JSValueRef, exception: [*c]JSValueRef) JSObjectRef; +pub extern fn JSValueToNumber(ctx: JSContextRef, value: JSValueRef, exception: ExceptionRef) f64; +pub extern fn JSValueToStringCopy(ctx: JSContextRef, value: JSValueRef, exception: ExceptionRef) JSStringRef; +pub extern fn JSValueToObject(ctx: JSContextRef, value: JSValueRef, exception: ExceptionRef) JSObjectRef; pub extern fn JSValueProtect(ctx: JSContextRef, value: JSValueRef) void; pub extern fn JSValueUnprotect(ctx: JSContextRef, value: JSValueRef) void; pub const JSPropertyAttributes = enum(c_uint) { @@ -113,22 +113,22 @@ pub const kJSClassAttributeNoAutomaticPrototype = @enumToInt(JSClassAttributes.k pub const JSObjectInitializeCallback = ?fn (JSContextRef, JSObjectRef) callconv(.C) void; pub const JSObjectFinalizeCallback = ?fn (JSObjectRef) callconv(.C) void; pub const JSObjectHasPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef) callconv(.C) bool; -pub const JSObjectGetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, [*c]JSValueRef) callconv(.C) JSValueRef; -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 JSObjectGetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, ExceptionRef) callconv(.C) JSValueRef; +pub const JSObjectSetPropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, JSValueRef, ExceptionRef) callconv(.C) bool; +pub const JSObjectDeletePropertyCallback = ?fn (JSContextRef, JSObjectRef, JSStringRef, ExceptionRef) callconv(.C) bool; pub const JSObjectGetPropertyNamesCallback = ?fn (JSContextRef, JSObjectRef, JSPropertyNameAccumulatorRef) callconv(.C) void; - +pub const ExceptionRef = [*c]JSValueRef; pub const JSObjectCallAsFunctionCallback = ?fn ( ctx: JSContextRef, function: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, - exception: [*c]JSValueRef, + exception: ExceptionRef, ) 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; +pub const JSObjectCallAsConstructorCallback = ?fn (JSContextRef, JSObjectRef, usize, [*c]const JSValueRef, ExceptionRef) callconv(.C) JSObjectRef; +pub const JSObjectHasInstanceCallback = ?fn (JSContextRef, JSObjectRef, JSValueRef, ExceptionRef) callconv(.C) bool; +pub const JSObjectConvertToTypeCallback = ?fn (JSContextRef, JSObjectRef, JSType, ExceptionRef) callconv(.C) JSValueRef; pub const JSStaticValue = extern struct { name: [*c]const u8, getProperty: JSObjectGetPropertyCallback, @@ -166,30 +166,30 @@ 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 JSObjectMakeArray(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef; +pub extern "c" fn JSObjectMakeDate(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef; +pub extern "c" fn JSObjectMakeError(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef; +pub extern "c" fn JSObjectMakeRegExp(ctx: JSContextRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) JSObjectRef; +pub extern "c" fn JSObjectMakeDeferredPromise(ctx: JSContextRef, resolve: [*c]JSObjectRef, reject: [*c]JSObjectRef, exception: ExceptionRef) 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: ExceptionRef) 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 JSObjectGetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: ExceptionRef) JSValueRef; +pub extern "c" fn JSObjectSetProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, value: JSValueRef, attributes: c_uint, exception: ExceptionRef) void; +pub extern "c" fn JSObjectDeleteProperty(ctx: JSContextRef, object: JSObjectRef, propertyName: JSStringRef, exception: ExceptionRef) bool; +pub extern "c" fn JSObjectHasPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: ExceptionRef) bool; +pub extern "c" fn JSObjectGetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: ExceptionRef) JSValueRef; +pub extern "c" fn JSObjectSetPropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, value: JSValueRef, attributes: JSPropertyAttributes, exception: ExceptionRef) void; +pub extern "c" fn JSObjectDeletePropertyForKey(ctx: JSContextRef, object: JSObjectRef, propertyKey: JSValueRef, exception: ExceptionRef) bool; +pub extern "c" fn JSObjectGetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, exception: ExceptionRef) JSValueRef; +pub extern "c" fn JSObjectSetPropertyAtIndex(ctx: JSContextRef, object: JSObjectRef, propertyIndex: c_uint, value: JSValueRef, exception: ExceptionRef) 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 JSObjectCallAsFunction(ctx: JSContextRef, object: JSObjectRef, thisObject: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) 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 JSObjectCallAsConstructor(ctx: JSContextRef, object: JSObjectRef, argumentCount: usize, arguments: [*c]const JSValueRef, exception: ExceptionRef) 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; @@ -219,18 +219,18 @@ pub extern fn JSStringGetMaximumUTF8CStringSize(string: JSStringRef) usize; pub extern fn JSStringGetUTF8CString(string: JSStringRef, buffer: [*c]u8, bufferSize: usize) usize; pub extern fn JSStringIsEqual(a: JSStringRef, b: JSStringRef) bool; pub extern fn JSStringIsEqualToUTF8CString(a: JSStringRef, b: [*c]const u8) bool; -pub extern fn JSObjectMakeTypedArray(ctx: JSContextRef, arrayType: JSTypedArrayType, length: usize, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeTypedArrayWithBytesNoCopy(ctx: JSContextRef, arrayType: JSTypedArrayType, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeTypedArrayWithArrayBuffer(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeTypedArrayWithArrayBufferAndOffset(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, byteOffset: usize, length: usize, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectGetTypedArrayBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) ?*c_void; -pub extern fn JSObjectGetTypedArrayLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; -pub extern fn JSObjectGetTypedArrayByteLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; -pub extern fn JSObjectGetTypedArrayByteOffset(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; -pub extern fn JSObjectGetTypedArrayBuffer(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectMakeArrayBufferWithBytesNoCopy(ctx: JSContextRef, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: [*c]JSValueRef) JSObjectRef; -pub extern fn JSObjectGetArrayBufferBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) ?*c_void; -pub extern fn JSObjectGetArrayBufferByteLength(ctx: JSContextRef, object: JSObjectRef, exception: [*c]JSValueRef) usize; +pub extern fn JSObjectMakeTypedArray(ctx: JSContextRef, arrayType: JSTypedArrayType, length: usize, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithBytesNoCopy(ctx: JSContextRef, arrayType: JSTypedArrayType, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithArrayBuffer(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectMakeTypedArrayWithArrayBufferAndOffset(ctx: JSContextRef, arrayType: JSTypedArrayType, buffer: JSObjectRef, byteOffset: usize, length: usize, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectGetTypedArrayBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) ?*c_void; +pub extern fn JSObjectGetTypedArrayLength(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) usize; +pub extern fn JSObjectGetTypedArrayByteLength(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) usize; +pub extern fn JSObjectGetTypedArrayByteOffset(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) usize; +pub extern fn JSObjectGetTypedArrayBuffer(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectMakeArrayBufferWithBytesNoCopy(ctx: JSContextRef, bytes: ?*c_void, byteLength: usize, bytesDeallocator: JSTypedArrayBytesDeallocator, deallocatorContext: ?*c_void, exception: ExceptionRef) JSObjectRef; +pub extern fn JSObjectGetArrayBufferBytesPtr(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) ?*c_void; +pub extern fn JSObjectGetArrayBufferByteLength(ctx: JSContextRef, object: JSObjectRef, exception: ExceptionRef) usize; pub extern fn JSStringCreateWithCFString(string: CFStringRef) JSStringRef; pub const OpaqueJSContextGroup = struct_OpaqueJSContextGroup; pub const OpaqueJSContext = struct_OpaqueJSContext; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 1b71c0197..9f9b21044 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -10,9 +10,10 @@ const logger = @import("../../logger.zig"); const Api = @import("../../api/schema.zig").Api; const options = @import("../../options.zig"); const Bundler = @import("../../bundler.zig").ServeBundler; - +const js_printer = @import("../../js_printer.zig"); const hash_map = @import("../../hash_map.zig"); - +const http = @import("../../http.zig"); +usingnamespace @import("./node_env_buf_map.zig"); pub const ExportJavaScript = union(Tag) { Module: *Module, String: *String, @@ -27,7 +28,8 @@ 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; +pub const ExceptionValueRef = [*c]js.JSValueRef; +pub const JSValueRef = js.JSValueRef; const JSStringMapContext = struct { pub fn hash(self: @This(), s: js.JSStringRef) u64 { return hashString(s); @@ -60,7 +62,7 @@ pub fn configureTransformOptionsForSpeedy(allocator: *std.mem.Allocator, _args: env_count += @boolToInt((env_map.get(key) == null)); } } - var needs_node_env = env._map.get("NODE_ENV") == 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| { @@ -75,45 +77,48 @@ pub fn configureTransformOptionsForSpeedy(allocator: *std.mem.Allocator, _args: } if (needs_regenerate) { - var new_list = try allocator.alloc([]u8, env_count * 2 + @boolToInt(needs_node_env) * 2); + var new_list = try allocator.alloc([]const u8, env_count * 2 + @intCast(usize, @boolToInt(needs_node_env)) * 2); + var keys = new_list[0 .. new_list.len / 2]; + var values = new_list[keys.len..]; var new_map = Api.StringMap{ - .keys = new_list[0..env_count], - .values = new_list[env_count..], + .keys = keys, + .values = values, }; var iter = env_map.iterator(); var last: usize = 0; while (iter.next()) |entry| { - new_map.keys[last] = entry.key_ptr.*; + keys[last] = entry.key_ptr.*; var value = entry.value_ptr.*; - if (value.len == 0 or value.len[0] != '"' or value.len[value.len - 1] != '"') { + + if (value.len == 0 or value[0] != '"' or value[value.len - 1] != '"') { value = try std.fmt.allocPrint(allocator, "\"{s}\"", .{value}); } - new_map.values[last] = value; + values[last] = value; last += 1; } if (args.define) |def| { - var from_env = new_map.keys[0..last]; + var from_env = 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]; + for (from_env) |key, j| { + if (strings.eql(key, pre)) { + values[j] = def.values[i]; } } } else { - new_map.keys[last] = pre; - new_map.values[last] = def.values[i]; + keys[last] = pre; + 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; + keys[last] = options.DefaultUserDefines.NodeEnv.Key; + values[last] = options.DefaultUserDefines.NodeEnv.Value; } } @@ -124,36 +129,53 @@ pub fn configureTransformOptionsForSpeedy(allocator: *std.mem.Allocator, _args: // 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(u32, Module); + const RequireCacheType = std.AutoHashMap(u32, *Module); root: js.JSGlobalContextRef, ctx: js.JSGlobalContextRef = undefined, group: js.JSContextGroupRef, allocator: *std.mem.Allocator, require_cache: RequireCacheType, + node_module_list: ?*Module.NodeModuleList, node_modules: ?*NodeModuleBundle = null, node_modules_ref: js.JSObjectRef = null, global: *GlobalObject, bundler: Bundler, log: *logger.Log, + watcher: ?*http.Watcher = null, pub fn init( allocator: *std.mem.Allocator, _args: Api.TransformOptions, + existing_bundle: ?*NodeModuleBundle, + _log: ?*logger.Log, ) !*VirtualMachine { var group = js.JSContextGroupCreate(); var ctx = js.JSGlobalContextCreateInGroup(group, null); - var log = try allocator.create(logger.Log); + var log: *logger.Log = undefined; + if (_log) |__log| { + log = __log; + } else { + 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)), + .bundler = try Bundler.init( + allocator, + log, + try configureTransformOptionsForSpeedy(allocator, _args), + existing_bundle, + ), + .node_module_list = undefined, + .log = log, .group = group, .root = ctx, .require_cache = RequireCacheType.init(allocator), .global = global, }; - Properties.init(); + vm.bundler.configureLinker(); global.* = GlobalObject{ .vm = vm }; @@ -162,54 +184,13 @@ pub const VirtualMachine = struct { Module.boot(vm); - return vm; - } + Properties.init(); + if (vm.bundler.options.node_modules_bundle) |bundle| { + vm.node_modules = bundle; + vm.node_module_list = try Module.NodeModuleList.init(vm, bundle); + } - threadlocal var eval_buf: WTFString = undefined; - threadlocal var eval_buf_loaded: bool = false; - - pub fn evalUtf8( - this: *VirtualMachine, - path_text: string, - contents: [:0]const u8, - ) !js.JSValueRef { - - // 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); - // } - - // try eval_buf.append(contents); - // var script_len = eval_buf.list.items.len; - // if (path_text.len > 0) { - // try eval_buf.append(path_text); - // } - - // 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; - - // 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, - js.JSStringCreateWithUTF8CString(contents.ptr), - this.global.ref, - null, - 0, - &exception, - ); - - return exception; + return vm; } }; @@ -230,13 +211,13 @@ pub const To = struct { function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, - exception: js.JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef, ) js.JSObjectRef { var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback).rfn); - js.JSObjectSetPrivate( + _ = js.JSObjectSetPrivate( function, - @ptrCast(*c_void, @alignCast(@alignOf(*c_void), global)), + @ptrCast(*c_void, @alignCast(@alignOf(*c_void), zig)), ); return function; } @@ -249,7 +230,7 @@ pub const To = struct { function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, - exception: js.JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef, ) type { return struct { @@ -259,7 +240,7 @@ pub const To = struct { thisObject: js.JSObjectRef, argumentCount: usize, arguments: [*c]const js.JSValueRef, - exception: ExceptionValueRef, + exception: js.ExceptionRef, ) callconv(.C) js.JSValueRef { var object_ptr_ = js.JSObjectGetPrivate(function); if (object_ptr_ == null) { @@ -278,7 +259,7 @@ pub const To = struct { function, thisObject, if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, - null, + exception, ); } }; @@ -312,21 +293,25 @@ pub const Properties = struct { pub const console = "console"; pub const require = "require"; pub const description = "description"; + pub const initialize_bundled_module = "$$m"; + pub const load_module_function = "$lOaDuRcOdE$"; }; pub const UTF16 = struct { - 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 module: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.module); + pub const globalThis: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.globalThis); + pub const exports: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.exports); + pub const log: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.log); + pub const debug: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.debug); + pub const info: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.info); + pub const error_: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.error_); + pub const warn: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.warn); + pub const console: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.console); + pub const require: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.require); + pub const description: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.description); + pub const name: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.name); + pub const initialize_bundled_module = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.initialize_bundled_module); + pub const load_module_function: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.load_module_function); }; pub const Refs = struct { @@ -342,6 +327,8 @@ pub const Properties = struct { pub var require: js.JSStringRef = null; pub var description: js.JSStringRef = null; pub var name: js.JSStringRef = null; + pub var initialize_bundled_module: js.JSStringRef = null; + pub var load_module_function: js.JSStringRef = null; }; pub fn init() void { @@ -352,9 +339,12 @@ pub const Properties = struct { @field(StringStore.UTF16, name).len - 1, ), ); - std.debug.assert( - js.JSStringIsEqualToUTF8CString(@field(Refs, name), @field(UTF8, name)[0.. :0]), - ); + + if (isDebug) { + std.debug.assert( + js.JSStringIsEqualToUTF8CString(@field(Refs, name), @field(UTF8, name)[0.. :0]), + ); + } } } }; @@ -381,7 +371,7 @@ const GetterFn = fn ( ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, - exception: [*c]JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef; const SetterFn = fn ( this: anytype, @@ -389,7 +379,7 @@ const SetterFn = fn ( thisObject: js.JSValueRef, prop: js.JSStringRef, value: js.JSValueRef, - exception: [*c]JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef; const JSProp = struct { @@ -401,15 +391,13 @@ const JSProp = struct { pub const Module = struct { path: Fs.Path, - hashid: u32, ref: js.JSObjectRef, id: js.JSValueRef = null, exports: js.JSValueRef = null, - global_ref: js.JSValueRef = null, - vm: *VirtualMachine, + require_func: js.JSObjectRef = null, pub var module_class: js.JSClassRef = undefined; pub var module_global_class: js.JSClassRef = undefined; @@ -419,45 +407,58 @@ pub const Module = struct { pub const NodeModuleList = struct { tempbuf: []u8, property_names: [*]u8, + static_functions: [1]js.JSStaticFunction, 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, + // This is probably a mistake. + bundle_ctx: js.JSGlobalContextRef, - const NodeModuleInstanceClassName = "NodeModule"; - const ModuleLoadStaticFunctionName = "$$m"; + require_cache: []?*Module, - var instance_class_definition: js.JSClassDefinition = undefined; + pub fn loadBundledModuleById(node_module_list: *NodeModuleList, id: u32) !*Module { + if (node_module_list.require_cache[id]) |mod| { + return mod; + } - var instance_class_ref: js.JSClassRef = undefined; - var instance_class_loaded = false; + var module = try Module.NodeModuleList.Instance.evalBundledModule( + node_module_list.vm.allocator, + node_module_list.vm, + node_module_list, + id, + ); + node_module_list.require_cache[id] = module; + return module; + } + + pub const Instance = struct { + module: Module, + node_module_list: *NodeModuleList, 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 { + ) !*Module { const bundled_module = &vm.node_modules.?.bundle.modules[id]; + const total_length = bundled_module.code.length + 1; if (!source_code_buffer_loaded) { - source_code_buffer = try MutableString.init(allocator, bundled_module.code.length + 1); + source_code_buffer = try MutableString.init(allocator, total_length); source_code_buffer_loaded = true; } else { source_code_buffer.reset(); - source_code_buffer.growIfNeeded(bundled_module.code.length + 1); + source_code_buffer.growIfNeeded(total_length) catch {}; } + source_code_buffer.list.resize(allocator, total_length) catch unreachable; + 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; @@ -468,7 +469,18 @@ pub const Module = struct { // 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; + // export var $fooo = $$m("packageName", "id", (module, exports) => { + // ^ + start_at = std.mem.indexOfPosLinear(u8, "\",", start_at, buf) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at += 1; + + // export var $fooo = $$m("packageName", "id", (module, exports) => { + // ^ + start_at = std.mem.indexOfPosLinear(u8, "\",", start_at, buf) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at += 1; + // ((module, exports) => { + buf[start_at] = '('; + 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); @@ -476,23 +488,73 @@ pub const Module = struct { allocator, "node_modules.jsb/{s}/{s}", .{ - vm.node_modules.?.str(bundled_package.path), + vm.node_modules.?.str(bundled_package.name), vm.node_modules.?.str(bundled_module.path), }, ); - defer allocator.free(source_url_buf); + errdefer 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); + var return_value: js.JSObjectRef = null; + var module: *Module = undefined; + go: { + // Compile the wrapper function + var function = js.JSEvaluateScript( + node_module_list.bundle_ctx, + source_string, + null, + source_url, + 1, + &exception, + ); + if (exception != null) break :go; + if (!js.JSValueIsObject(node_module_list.bundle_ctx, function)) { + return error.ExpectedFunction; + } + + // Don't create the instance / module if the script has a syntax error + module = try allocator.create(Module); + module.* = Module{ + .path = Fs.Path.initWithPretty(source_url_buf, source_url_buf), + .ref = undefined, + .vm = vm, + }; + module.ref = js.JSObjectMake(node_module_list.bundle_ctx, Module.module_class, module); + var args = try allocator.alloc(js.JSValueRef, 2); + args[0] = module.ref; + args[1] = module.internalGetExports(); + + // Run the wrapper + _ = js.JSObjectCallAsFunction( + node_module_list.bundle_ctx, + function, + args[1], + 2, + args.ptr, + &exception, + ); + if (exception != null) { + allocator.destroy(module); + allocator.free(source_url_buf); + } + break :go; + } + 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); + var message_str_read = js.JSStringGetUTF8CString(message, message_str_buf.ptr, message_str_size); defer Output.flush(); + vm.log.addErrorFmt(null, logger.Loc.Empty, allocator, "Error loading \"{s}/{s}\":\n{s}", .{ + vm.node_modules.?.str(bundled_package.name), + vm.node_modules.?.str(bundled_module.path), + message_str_buf[0..message_str_read], + }) catch {}; 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), @@ -501,36 +563,7 @@ pub const Module = struct { 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; - } - - - - - - - - + return module; } }; @@ -545,7 +578,12 @@ pub const Module = struct { 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 { + pub fn getRequireFromBundleProperty( + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { var thisPtr = js.JSObjectGetPrivate(thisObject); if (thisPtr == null) return null; @@ -561,10 +599,10 @@ pub const Module = struct { 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; + const id = this.module_property_map.get(key) orelse return null; if (this.property_getters[id] == null) { - var require_bundled = try this.vm.allocator.create(RequireBundledModule); + var require_bundled = this.vm.allocator.create(RequireBundledModule) catch unreachable; require_bundled.* = RequireBundledModule{ .id = id, .list = this }; this.property_getters[id] = To.JS.functionWithCallback( RequireBundledModule, @@ -580,18 +618,18 @@ pub const Module = struct { // this is what $aosdi123() inside a node_modules.jsb calls pub fn requireBundledModule( - obj: *const RequireBundledModule, + obj: *RequireBundledModule, ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, - exception: [*c]js.JSValueRef, + exception: js.ExceptionRef, ) 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| { + const result = loadBundledModuleById(obj.list, 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), @@ -606,14 +644,14 @@ pub const Module = struct { 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); + exception.* = js.JSObjectMakeError(ctx, 1, args.ptr, null); return js.JSValueMakeUndefined(ctx); }; - return result.Module.internalGetExports(); + return result.internalGetExports(); } - pub fn init(vm: *VirtualMachine, ctx: js.JSContextRef, bundle: *const NodeModuleBundle) !NodeModuleList { + pub fn init(vm: *VirtualMachine, bundle: *const NodeModuleBundle) !*NodeModuleList { var size: usize = 0; var longest_size: usize = 0; for (bundle.bundle.modules) |module, i| { @@ -626,14 +664,14 @@ pub const Module = struct { ); // Add one for null-terminated string offset const this_size = std.fmt.count( - "${x}", + "${x}" ++ "\\x0", .{ @truncate( u32, hasher.final(), ), }, - ) + 1; + ); size += this_size; longest_size = std.math.max(this_size, longest_size); } @@ -644,7 +682,7 @@ pub const Module = struct { var names_buf = utf8[0..size]; var module_property_map = ModuleIDMap.init(vm.allocator); - try module_property_map.ensureCapacity(bundle.bundle.modules.len); + try module_property_map.ensureCapacity(@truncate(u32, bundle.bundle.modules.len)); for (bundle.bundle.modules) |module, i| { var hasher = std.hash.Wyhash.init(0); @@ -661,65 +699,139 @@ pub const Module = struct { ); // The variable name is the hash of the module path - var name = std.fmt.bufPrint(names_buf, "${x}", .{hash}) catch unreachable; + var name = std.fmt.bufPrintZ(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); + hasher.update(name[0..]); var property_key = hasher.final(); - name.ptr[name.len] = 0; - const name_len = name.len; - static_properties[i] = js.JSStaticValue{ - .name = name[0.. :0], + .name = name.ptr, .getProperty = getRequireFromBundleProperty, + .setProperty = null, .attributes = .kJSPropertyAttributeReadOnly, }; - names_buf = names_buf[name_len..]; + 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.staticValues = static_properties.ptr; node_module_global_class_def.className = node_module_global_class_name[0.. :0]; - node_module_global_class_def.parentClass = vm.global.global_class; + // 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); + var node_module_list = try vm.allocator.create(NodeModuleList); - return NodeModuleList{ + node_module_list.* = NodeModuleList{ .module_property_map = module_property_map, .node_module_global_class_def = node_module_global_class_def, .vm = vm, .tempbuf = tempbuf, + .property_names = names_buf.ptr, + .bundle_ctx = undefined, .property_getters = property_getters, - .node_module_global_class = js.JSClassCreate(node_module_global_class_def), + .node_module_global_class = undefined, + .static_functions = undefined, + .require_cache = try vm.allocator.alloc(?*Module, bundle.bundle.modules.len), }; + + std.mem.set(?*Module, node_module_list.require_cache, null); + + // node_module_list.staticFunctions[0] = js.JSStaticFunction{ + // .name = Properties.UTF8.initialize_bundled_module[0.. :0], + // .callAsFunction = To.JS.Callback(NodeModuleList, initializeNodeModule), + // }; + // node_module_global_class_def.staticFunctions = &node_module_list.static_functions; + node_module_list.node_module_global_class_def = node_module_global_class_def; + node_module_list.node_module_global_class = js.JSClassCreate(&node_module_list.node_module_global_class_def); + node_module_list.bundle_ctx = js.JSGlobalContextCreateInGroup(vm.group, node_module_list.node_module_global_class); + + return node_module_list; } }; pub const node_module_global_class_name = "NodeModuleGlobal"; - pub const ModuleGlobalClass = NewClass( - Module, - "ModuleGlobal", - .{ .@"require" = require }, - .{}, - false, - false, - ); - const JSExport = NewClass( + threadlocal var require_buf: MutableString = undefined; + threadlocal var require_buf_loaded: bool = false; + + pub fn require( + this: *Module, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + if (arguments.len != 1 or !js.JSValueIsString(ctx, arguments[0]) or js.JSStringGetMaximumUTF8CStringSize(arguments[0]) == 0) { + defer Output.flush(); + if (arguments.len == 0) { + Output.prettyErrorln("<r><red>error<r>: <b>require<r> needs a string, e.g. require(\"left-pad\")", .{}); + } else if (arguments.len > 1) { + Output.prettyErrorln("<r><red>error<r>: <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>: <b>require<r> only supports a string, e.g. require(\"left-pad\")", .{}); + } else { + Output.prettyErrorln("<r><red>error<r>: <b>require(\"\")<r> string cannot be empty.", .{}); + } + exception.* = js.JSObjectMakeError(ctx, 0, null, null); + return null; + } + + const len = js.JSStringGetLength(arguments[0]); + + if (!require_buf_loaded) { + require_buf = MutableString.init(this.vm.allocator, len + 1) catch unreachable; + require_buf_loaded = true; + } else { + require_buf.reset(); + require_buf.growIfNeeded(len + 1) catch {}; + } + + require_buf.list.resize(this.vm.allocator, len + 1) catch unreachable; + + var end = js.JSStringGetUTF8CString(arguments[0], require_buf.list.items.ptr, require_buf.list.items.len); + var import_path = require_buf.list.items[0 .. end - 1]; + var module = this; + + if (this.vm.bundler.linker.resolver.resolve(module.path.name.dirWithTrailingSlash(), import_path, .require)) |resolved| { + var load_result = Module.loadFromResolveResult(this.vm, ctx, resolved, exception) catch |err| { + return null; + }; + + switch (load_result) { + .Module => |new_module| { + return new_module.internalGetExports(); + }, + .Path => |path| { + return js.JSStringCreateWithUTF8CString(path.text.ptr); + }, + } + } else |err| { + Output.prettyErrorln( + "<r><red>RequireError<r>: Failed to load module <b>\"{s}\"<r> at \"{s}\": <red>{s}<r>", + .{ import_path, module.path.name.dirWithTrailingSlash(), @errorName(err) }, + ); + Output.flush(); + exception.* = js.JSObjectMakeError(ctx, 0, null, null); + return null; + } + } + + const ModuleClass = NewClass( Module, "Module", .{ .@"require" = require }, .{ - .@"id" = JSProp{ + .@"id" = .{ .get = getId, .ro = true, }, - .@"exports" = JSProp{ + .@"exports" = .{ .get = getExports, .set = setExports, .ro = false, @@ -729,11 +841,17 @@ pub const Module = struct { false, ); + const ExportsClassName = "module.exports"; + var ExportsClass: js.JSClassDefinition = undefined; + var exports_class_ref: js.JSClassRef = undefined; + 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); + ExportsClass = std.mem.zeroes(js.JSClassDefinition); + ExportsClass.className = ExportsClassName[0.. :0]; + + exports_class_ref = js.JSClassRetain(js.JSClassCreate(&ExportsClass)); + + module_class_def = ModuleClass.define(vm.root); module_class = js.JSClassRetain(js.JSClassCreate(&module_class_def)); } @@ -747,12 +865,101 @@ pub const Module = struct { }; }; - pub fn loadBundledModuleById(vm: *VirtualMachine, ctx: js.JSContextRef, id: u32) !LoadResult {} + threadlocal var source_code_printer: js_printer.BufferPrinter = undefined; + threadlocal var source_code_printer_loaded: bool = false; + var require_module_params: [3]js.JSStringRef = undefined; + var require_module_params_loaded: bool = false; - 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 }; + pub fn load( + vm: *VirtualMachine, + allocator: *std.mem.Allocator, + log: *logger.Log, + source: [:0]u8, + path: Fs.Path, + call_ctx: js.JSContextRef, + function_ctx: js.JSContextRef, + exception: js.ExceptionRef, + ) !*Module { + var source_code_ref = js.JSStringRetain(js.JSStringCreateWithUTF8CString(source.ptr)); + defer js.JSStringRelease(source_code_ref); + var source_url = try allocator.dupeZ(u8, path.text); + defer allocator.free(source_url); + var source_url_ref = js.JSStringRetain(js.JSStringCreateWithUTF8CString(source_url.ptr)); + defer js.JSStringRelease(source_url_ref); + + if (isDebug) { + Output.print("// {s}\n{s}", .{ path.pretty, source }); + Output.flush(); + } + + var module = try allocator.create(Module); + module.* = Module{ + .path = path, + .ref = undefined, + .vm = vm, + }; + module.ref = js.JSObjectMake(function_ctx, Module.module_class, module); + + js.JSValueProtect(function_ctx, module.ref); + + // TODO: move these allocations to only occur once + var args = try allocator.alloc(js.JSValueRef, 2); + var params = try allocator.alloc(js.JSStringRef, 2); + params[0] = js.JSStringCreateWithUTF8CString(Properties.UTF8.module[0.. :0]); + params[1] = js.JSStringCreateWithUTF8CString(Properties.UTF8.exports[0.. :0]); + args[0] = module.ref; + args[1] = module.internalGetExports(); + js.JSValueProtect(function_ctx, args[1]); + + defer allocator.free(args); + var except: js.JSValueRef = null; + go: { + var commonjs_wrapper = js.JSObjectMakeFunction( + function_ctx, + null, + @truncate(c_uint, params.len), + params.ptr, + source_code_ref, + null, + 1, + &except, + ); + if (except != null) { + break :go; + } + + _ = js.JSObjectCallAsFunction(call_ctx, commonjs_wrapper, null, 2, args.ptr, &except); + } + if (except != null) { + var message = js.JSValueToStringCopy(function_ctx, except.?, 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.ptr, message_str_size); + defer Output.flush(); + log.addErrorFmt(null, logger.Loc.Empty, allocator, "Error loading \"{s}\":\n{s}", .{ + path.pretty, + message_str_buf[0..message_str_read], + }) catch {}; + Output.prettyErrorln("<r>{s}\n--<r><red>error<r> loading <cyan>\"{s}\"<r>--", .{ + message_str_buf[0..message_str_read], + path.pretty, + }); + return error.FailedException; + } + return module; + } + + pub fn loadFromResolveResult( + vm: *VirtualMachine, + ctx: js.JSContextRef, + resolved: resolver.Result, + exception: js.ExceptionRef, + ) !LoadResult { + const hash = http.Watcher.getHash(resolved.path_pair.primary.text); + if (vm.require_cache.get(hash)) |mod| { + return LoadResult{ .Module = mod }; } const path = resolved.path_pair.primary; @@ -773,26 +980,83 @@ pub const Module = struct { path.text, ); - if (node_modules.findModuleInPackage( + if (node_modules.findModuleIDInPackage( &node_modules.bundle.packages[package_id], package_relative_path, - )) |found_module| {} + )) |id| { + var list = vm.node_module_list.?; + return LoadResult{ .Module = try list.loadBundledModuleById(id) }; + } } } } } vm.bundler.resetStore(); - var result = vm.bundler.parse( + var fd: ?StoredFileDescriptorType = null; + + if (vm.watcher) |watcher| { + if (watcher.indexOf(hash)) |index| { + fd = watcher.watchlist.items(.fd)[index]; + } + } + + var parse_result = vm.bundler.parse( vm.bundler.allocator, path, loader, - result.dirname_fd, - null, - null, + resolved.dirname_fd, + fd, + hash, ) orelse { return error.ParseError; }; + + if (!source_code_printer_loaded) { + var writer = try js_printer.BufferWriter.init(vm.allocator); + source_code_printer = js_printer.BufferPrinter.init(writer); + source_code_printer.ctx.append_null_byte = true; + + source_code_printer_loaded = true; + } + + source_code_printer.ctx.reset(); + + // We skip the linker here. + // var old_linker_allocator = vm.bundler.linker.allocator; + // defer vm.bundler.linker.allocator = old_linker_allocator; + // vm.bundler.linker.allocator = vm.allocator; + // // Always use absolute paths + // // This makes the resolver faster + // try vm.bundler.linker.link( + // Fs.Path.init(path.text), + // &parse_result, + // .absolute_path, + // ); + + var written = try vm.bundler.print( + parse_result, + @TypeOf(&source_code_printer), + &source_code_printer, + .speedy, + ); + + if (written == 0) { + return error.PrintingErrorWriteFailed; + } + + var module = try Module.load( + vm, + vm.allocator, + vm.log, + source_code_printer.ctx.sentinel, + path, + ctx, + vm.global.ctx, + exception, + ); + try vm.require_cache.put(hash, module); + return LoadResult{ .Module = module }; }, // Replace imports to non-executables with paths to those files. @@ -818,7 +1082,7 @@ pub const Module = struct { const needs_slash = dirname.len > 0 and dirname[dirname.len - 1] != '/'; if (needs_slash) { - const absolute_url = try std.fmt.allocPrint( + const absolute_url = try std.fmt.allocPrintZ( vm.allocator, "{s}{s}/{s}{s}", .{ @@ -833,7 +1097,7 @@ pub const Module = struct { .Path = Fs.Path.initWithPretty(absolute_url, absolute_url), }; } else { - const absolute_url = try std.fmt.allocPrint( + const absolute_url = try std.fmt.allocPrintZ( vm.allocator, "{s}{s}{s}{s}", .{ @@ -849,6 +1113,7 @@ pub const Module = struct { }; } }, + else => unreachable, } }, } @@ -859,10 +1124,10 @@ pub const Module = struct { ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, - exception: [*c]js.JSValueRef, - ) js.JSValueRef { + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { if (this.id == null) { - this.id = js.JSStringCreateWithUTF8CString(this.path.text[0.. :0]); + this.id = js.JSStringCreateWithUTF8CString(this.path.text.ptr); } return this.id; @@ -873,60 +1138,52 @@ pub const Module = struct { ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, - exception: [*c]js.JSValueRef, - ) js.JSValueRef { + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { return this.internalGetExports(); } pub fn internalGetExports(this: *Module) js.JSValueRef { if (this.exports == null) { - this.exports = js.JSObjectMake(ctx, null, null); + this.exports = js.JSObjectMake(this.vm.global.ctx, exports_class_ref, this); } return this.exports; } + pub fn internalGetRequire(this: *Module) js.JSValueRef { + if (this.require_func == null) { + this.require_func = To.JS.functionWithCallback( + Module, + this, + Properties.Refs.require, + this.vm.global.ctx, + require, + ); + } + + return this.require_func; + } + pub fn setExports( this: *Module, ctx: js.JSContextRef, thisObject: js.JSValueRef, prop: js.JSStringRef, value: js.JSValueRef, - exception: [*c]JSValueRef, - ) JSValueRef { + exception: js.ExceptionRef, + ) bool { if (this.exports != null) { - if (js.JSValueIsString(this.exports.?)) { - js.JSStringRelease(this.exports.?); + if (js.JSValueIsString(this.vm.global.ctx, this.exports)) { + js.JSStringRelease(this.exports); } } this.exports = value; + return true; } pub const RequireObject = struct {}; - - pub fn require( - this: *Module, - ctx: js.JSContextRef, - thisObject: js.JSValueRef, - arguments: []js.JSValueRef, - exception: [*c]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 { @@ -969,11 +1226,22 @@ pub const GlobalObject = struct { false, ); - pub fn getConsole(global: *GlobalObject, ctx: js.JSContextRef, obj: js.JSObjectRef, exception: ExceptionValueRef) js.JSValueRef { + pub fn getConsole( + global: *GlobalObject, + ctx: js.JSContextRef, + obj: js.JSObjectRef, + exception: js.ExceptionRef, + ) js.JSValueRef { return global.console; } - pub fn onMissingProperty(global: *GlobalObject, ctx: js.JSContextRef, obj: js.JSObjectRef, prop: js.JSStringRef, exception: ExceptionValueRef) js.JSValueRef { + pub fn onMissingProperty( + global: *GlobalObject, + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { if (js.JSObjectHasProperty(ctx, global.root_obj, prop)) { return js.JSObjectGetProperty(ctx, global.root_obj, prop, exception); } else { @@ -1108,7 +1376,7 @@ pub const GlobalObject = struct { function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, - exception: js.JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef { output(Output.writer(), ctx, arguments) catch {}; return js.JSValueMakeUndefined(ctx); @@ -1120,7 +1388,7 @@ pub const GlobalObject = struct { function: js.JSObjectRef, thisObject: js.JSObjectRef, arguments: []const js.JSValueRef, - exception: js.JSValueRef, + exception: js.ExceptionRef, ) js.JSValueRef { output(Output.errorWriter(), ctx, arguments) catch {}; return js.JSValueMakeUndefined(ctx); @@ -1180,7 +1448,12 @@ pub fn NewClass( }; 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 { + pub fn getPropertyCallback( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { var instance_pointer_ = js.JSObjectGetPrivate(obj); if (instance_pointer_ == null) return js.JSValueMakeUndefined(ctx); var instance_pointer = instance_pointer_.?; @@ -1223,10 +1496,15 @@ pub fn NewClass( 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 { + pub fn getter( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { var instance_pointer_ = js.JSObjectGetPrivate(obj); if (instance_pointer_ == null) return js.JSValueMakeUndefined(ctx); - var ptr = @ptrCast( + var this: *ZigType = @ptrCast( *ZigType, @alignCast( @alignOf( @@ -1236,10 +1514,85 @@ pub fn NewClass( ), ); - return @field( + var exc: js.ExceptionRef = null; + + switch (comptime @typeInfo(@TypeOf(@field( properties, property_names[id], - )(ptr, ctx, obj, exception); + )))) { + .Fn => { + return @field( + properties, + property_names[id], + )( + this, + ctx, + this.ref, + exception, + ); + }, + .Struct => { + return @field( + @field( + properties, + property_names[id], + ), + "get", + )( + this, + ctx, + this.ref, + prop, + exception, + ); + }, + else => unreachable, + } + } + + pub fn setter( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + value: js.JSValueRef, + exception: js.ExceptionRef, + ) callconv(.C) bool { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return false; + var this: *ZigType = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, + ), + instance_pointer_.?, + ), + ); + + var exc: js.ExceptionRef = null; + + switch (comptime @typeInfo(@TypeOf(@field( + properties, + property_names[id], + )))) { + .Struct => { + return @field( + @field( + properties, + property_names[id], + ), + "set", + )( + this, + ctx, + this.ref, + prop, + value, + exception, + ); + }, + else => unreachable, + } } }; } @@ -1276,6 +1629,12 @@ pub fn NewClass( ); static_properties[i] = std.mem.zeroes(js.JSStaticValue); static_properties[i].getProperty = StaticProperty(i).getter; + + const field = comptime @field(properties, property_names[i]); + const hasSetter = std.meta.trait.hasField("set"); + if (comptime hasSetter(@TypeOf(field))) { + static_properties[i].setProperty = StaticProperty(i).setter; + } static_properties[i].name = property_names[i][0.. :0]; } @@ -1289,132 +1648,3 @@ pub fn NewClass( } }; } - -// 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/javascript/jsc/node_env_buf_map.zig b/src/javascript/jsc/node_env_buf_map.zig new file mode 100644 index 000000000..4e965a8e8 --- /dev/null +++ b/src/javascript/jsc/node_env_buf_map.zig @@ -0,0 +1,144 @@ +const std = @import("std"); +usingnamespace @import("../../global.zig"); + +// 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 +pub 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 iterator(this: *NodeEnvBufMap) @typeInfo(@TypeOf(std.BufMap.iterator)).Fn.return_type.? { + return this.backing.iterator(); + } + + pub fn put(this: *NodeEnvBufMap, key: string, value: anytype) !void { + if (value.len == 0) { + return; + } + + if (bufkeybuf_first) { + std.mem.copy(u8, &bufkeybuf, "process.env."); + bufkeybuf_first = false; + } + std.mem.copy(u8, bufkeybuf["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_slice[0] != '"' and value_slice[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_slice[0] != '"') { + value_slice[0] = '"'; + std.mem.copy(u8, value_slice[1..], value[0..max_value_slice_len]); + } else if (value_slice[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 count(this: *const NodeEnvBufMap) usize { + return this.backing.count(); + } + + pub fn deinit(this: *NodeEnvBufMap) void { + this.backing.deinit(); + } +}; + +pub fn getNodeEnvMap(allocator: *std.mem.Allocator) !NodeEnvBufMap { + var result = NodeEnvBufMap.init(allocator); + errdefer result.deinit(); + const builtin = std.builtin; + 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/js_lexer.zig b/src/js_lexer.zig index 0e469122e..8b00f9a17 100644 --- a/src/js_lexer.zig +++ b/src/js_lexer.zig @@ -2630,14 +2630,15 @@ pub const CodepointIterator = struct { const cp_len = strings.utf8ByteSequenceLength(it.bytes[it.i]); it.i += cp_len; + // without branching, + it.width = @intCast(u3, @boolToInt(it.i <= it.bytes.len)) * cp_len; return if (!(it.i > it.bytes.len)) it.bytes[it.i - cp_len .. it.i] else ""; } pub fn nextCodepoint(it: *CodepointIterator) ?CodePoint { const slice = it.nextCodepointSlice(); - - it.c = switch (slice.len) { + it.c = switch (it.width) { 0 => it.c, 1 => @as(CodePoint, slice[0]), 2 => @as(CodePoint, unicode.utf8Decode2(slice) catch unreachable), diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 39c3af492..d4490a1c5 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -1611,6 +1611,9 @@ pub const Parser = struct { filepath_hash_for_hmr: u32 = 0, features: RuntimeFeatures = RuntimeFeatures{}, + // When platform is set to "speedy" + force_commonjs: bool = false, + // Used when bundling node_modules enable_bundling: bool = false, transform_require_to_import: bool = true, @@ -3132,8 +3135,13 @@ pub fn NewParser( p.hoistSymbols(p.module_scope); - p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports"); - p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module"); + if (p.options.force_commonjs) { + p.exports_ref = try p.declareCommonJSSymbol(.unbound, "exports"); + p.module_ref = try p.declareCommonJSSymbol(.unbound, "module"); + } else { + p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports"); + p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module"); + } if (p.options.enable_bundling) { p.bundle_export_ref = try p.declareSymbol(.unbound, logger.Loc.Empty, "IF_YOU_SEE_THIS_ITS_A_BUNDLER_BUG_PLEASE_FILE_AN_ISSUE_THX"); diff --git a/src/js_printer.zig b/src/js_printer.zig index 69e6ba03c..b2436bbf7 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -125,6 +125,7 @@ pub fn NewPrinter( comptime Writer: type, comptime Linker: type, comptime rewrite_esm_to_cjs: bool, + comptime speedy: bool, ) type { // comptime const comptime_buf_len = 64; // comptime var comptime_buf = [comptime_buf_len]u8{}; @@ -3129,7 +3130,10 @@ pub fn NewPrinter( p.printSymbol(s.default_name.?.ref.?); p.print(" = "); p.printLoadFromBundle(s.import_record_index); - p.print(".default"); + if (!speedy) { + p.print(".default"); + } + p.printSemicolonAfterStatement(); }, .import_star_and_import_default => { @@ -3141,7 +3145,9 @@ pub fn NewPrinter( p.printSymbol(s.default_name.?.ref.?); p.print(" = "); p.printSymbol(s.namespace_ref); - p.print(".default"); + if (!speedy) { + p.print(".default"); + } p.printSemicolonAfterStatement(); }, .import_items => { @@ -3268,8 +3274,15 @@ pub fn NewPrinter( } } pub fn printLoadFromBundle(p: *Printer, import_record_index: u32) void { - p.printLoadFromBundleWithoutCall(import_record_index); - p.print("()"); + if (speedy) { + const record = p.import_records[import_record_index]; + p.print("module.require(\""); + p.print(record.path.text); + p.print("\")"); + } else { + p.printLoadFromBundleWithoutCall(import_record_index); + p.print("()"); + } } pub fn printLoadFromBundleWithoutCall(p: *Printer, import_record_index: u32) void { const record = p.import_records[import_record_index]; @@ -3715,7 +3728,9 @@ const FileWriterInternal = struct { pub const BufferWriter = struct { buffer: MutableString = undefined, - written: []const u8 = "", + written: []u8 = "", + sentinel: [:0]u8 = "", + append_null_byte: bool = false, approximate_newline_count: usize = 0, pub fn init(allocator: *std.mem.Allocator) !BufferWriter { @@ -3753,7 +3768,11 @@ pub const BufferWriter = struct { pub fn done( ctx: *BufferWriter, ) anyerror!void { - ctx.written = ctx.buffer.toOwnedSliceLeaky(); + if (ctx.append_null_byte) { + ctx.sentinel = ctx.buffer.toOwnedSentinelLeaky(); + } else { + ctx.written = ctx.buffer.toOwnedSliceLeaky(); + } } pub fn flush( @@ -3772,6 +3791,9 @@ pub fn NewFileWriter(file: std.fs.File) FileWriter { var internal = FileWriterInternal.init(file); return FileWriter.init(internal); } + +pub const Format = enum { esm, cjs, speedy }; + pub fn printAst( comptime Writer: type, _writer: Writer, @@ -3783,7 +3805,7 @@ pub fn printAst( comptime LinkerType: type, linker: ?*LinkerType, ) !usize { - const PrinterType = NewPrinter(false, Writer, LinkerType, false); + const PrinterType = NewPrinter(false, Writer, LinkerType, false, false); var writer = _writer; var printer = try PrinterType.init( writer, @@ -3818,7 +3840,7 @@ pub fn printCommonJS( comptime LinkerType: type, linker: ?*LinkerType, ) !usize { - const PrinterType = NewPrinter(false, Writer, LinkerType, true); + const PrinterType = NewPrinter(false, Writer, LinkerType, true, false); var writer = _writer; var printer = try PrinterType.init( writer, @@ -3844,3 +3866,38 @@ pub fn printCommonJS( return @intCast(usize, std.math.max(printer.writer.written, 0)); } + +pub fn printSpeedyCJS( + comptime Writer: type, + _writer: Writer, + tree: Ast, + symbols: js_ast.Symbol.Map, + source: *const logger.Source, + ascii_only: bool, + opts: Options, + comptime LinkerType: type, + linker: ?*LinkerType, +) !usize { + const PrinterType = NewPrinter(false, Writer, LinkerType, true, true); + var writer = _writer; + var printer = try PrinterType.init( + writer, + &tree, + source, + symbols, + opts, + linker, + ); + for (tree.parts) |part| { + for (part.stmts) |stmt| { + try printer.printStmt(stmt); + if (printer.writer.getError()) {} else |err| { + return err; + } + } + } + + try printer.writer.done(); + + return @intCast(usize, std.math.max(printer.writer.written, 0)); +} diff --git a/src/linker.zig b/src/linker.zig index 0f7b1325b..46d2114ca 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -204,6 +204,7 @@ pub fn NewLinker(comptime BundlerType: type) type { continue; } + if (linker.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*_resolved_import| { var resolved_import: *Resolver.Result = _resolved_import; if (resolved_import.is_external) { diff --git a/src/main_javascript.zig b/src/main_javascript.zig index b637d8916..5c1ed6141 100644 --- a/src/main_javascript.zig +++ b/src/main_javascript.zig @@ -229,9 +229,14 @@ pub const Cli = struct { std.fs.accessAbsolute(node_modules_bundle_path_absolute, .{}) catch |err| { break :brk null; }; - break :brk try allocator.dupe(u8, node_modules_bundle_path_absolute); + + break :brk node_modules_bundle_path_absolute; }; + if (node_modules_bundle_path != null) { + node_modules_bundle_path = try std.fs.realpathAlloc(allocator, node_modules_bundle_path.?); + } + if (args.flag("--new-jsb")) { node_modules_bundle_path = null; } @@ -360,15 +365,19 @@ pub const Cli = struct { var panicker = MainPanicHandler.init(&log); MainPanicHandler.Singleton = &panicker; - // var args = try Arguments.parse(alloc.static, stdout, stderr); + 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", .{}); + var vm = try js.VirtualMachine.init(allocator, args, null, &log); + var resolved_entry_point = try vm.bundler.resolver.resolve( + vm.bundler.fs.top_level_dir, + vm.bundler.normalizeEntryPointPath(vm.bundler.options.entry_points[0]), + .entry_point, + ); + var exception: js.JSValueRef = null; + var result = try js.Module.loadFromResolveResult(vm, vm.global.ctx, resolved_entry_point, &exception); } }; diff --git a/src/node_module_bundle.zig b/src/node_module_bundle.zig index 4605df2ef..9dbc45750 100644 --- a/src/node_module_bundle.zig +++ b/src/node_module_bundle.zig @@ -162,6 +162,18 @@ pub const NodeModuleBundle = struct { package: *const Api.JavascriptBundledPackage, _query: string, ) ?*const Api.JavascriptBundledModule { + if (this.findModuleIDInPackage(package, _query)) |id| { + return &this.bundle.modules[id]; + } + + return null; + } + + pub fn findModuleIDInPackage( + this: *const NodeModuleBundle, + package: *const Api.JavascriptBundledPackage, + _query: string, + ) ?u32 { const ModuleFinder = struct { const Self = @This(); ctx: *const NodeModuleBundle, @@ -204,14 +216,13 @@ pub const NodeModuleBundle = struct { var finder = ModuleFinder{ .ctx = this, .pkg = package, .query = _query }; const modules = modulesIn(&this.bundle, package); - const module_id = std.sort.binarySearch( + return @intCast(u32, std.sort.binarySearch( Api.JavascriptBundledModule, to_find, modules, finder, ModuleFinder.cmpAsc, - ) orelse return null; - return &modules[module_id]; + ) orelse return null); } pub fn init(container: Api.JavascriptBundleContainer, allocator: *std.mem.Allocator) NodeModuleBundle { diff --git a/src/options.zig b/src/options.zig index 9009fd88c..c5329c10a 100644 --- a/src/options.zig +++ b/src/options.zig @@ -313,6 +313,7 @@ pub const Platform = enum { // that some packages may break if you do this. var list = [_]string{ MAIN_FIELD_NAMES[1], MAIN_FIELD_NAMES[2] }; array.set(Platform.node, &list); + array.set(Platform.speedy, &list); // Note that this means if a package specifies "main", "module", and // "browser" then "browser" will win out over "module". This is the @@ -641,12 +642,7 @@ pub const BundleOptions = struct { pub var ExtensionOrder = [_]string{ ".tsx", ".ts", ".jsx", ".js", ".json", ".css" }; }; - pub fn fromApi( - allocator: *std.mem.Allocator, - fs: *Fs.FileSystem, - log: *logger.Log, - transform: Api.TransformOptions, - ) !BundleOptions { + pub fn fromApi(allocator: *std.mem.Allocator, fs: *Fs.FileSystem, log: *logger.Log, transform: Api.TransformOptions, node_modules_bundle_existing: ?*NodeModuleBundle) !BundleOptions { const output_dir_parts = [_]string{ try std.process.getCwdAlloc(allocator), transform.output_dir orelse "out" }; var opts: BundleOptions = BundleOptions{ .log = log, @@ -675,12 +671,7 @@ pub const BundleOptions = struct { } if (transform.platform) |plat| { - opts.platform = switch (plat) { - .speedy => speedy, - .neutral => .neutral, - .browser => .browser, - .node => .node, - }; + opts.platform = Platform.from(plat); opts.main_fields = Platform.DefaultMainFields.get(opts.platform); } @@ -764,7 +755,14 @@ pub const BundleOptions = struct { } if (opts.resolve_mode == .lazy and !(transform.generate_node_module_bundle orelse false)) { - if (transform.node_modules_bundle_path) |bundle_path| { + if (node_modules_bundle_existing) |node_mods| { + opts.node_modules_bundle = node_mods; + const pretty_path = fs.relativeTo(transform.node_modules_bundle_path.?); + opts.node_modules_bundle_url = try std.fmt.allocPrint(allocator, "{s}{s}", .{ + opts.public_url, + pretty_path, + }); + } else if (transform.node_modules_bundle_path) |bundle_path| { if (bundle_path.len > 0) { load_bundle: { const pretty_path = fs.relativeTo(bundle_path); diff --git a/src/string_mutable.zig b/src/string_mutable.zig index 25709fd40..249ab7394 100644 --- a/src/string_mutable.zig +++ b/src/string_mutable.zig @@ -154,10 +154,21 @@ pub const MutableString = struct { return self.list.toOwnedSlice(self.allocator); } - pub fn toOwnedSliceLeaky(self: *MutableString) string { + pub fn toOwnedSliceLeaky(self: *MutableString) []u8 { return self.list.items; } + pub fn toOwnedSentinelLeaky(self: *MutableString) [:0]u8 { + if (self.list.items.len > 0 and self.list.items[self.list.items.len - 1] != 0) { + self.list.append( + self.allocator, + 0, + ) catch unreachable; + } + + return self.list.items[0 .. self.list.items.len - 1 :0]; + } + pub fn toOwnedSliceLength(self: *MutableString, length: usize) string { self.list.shrinkAndFree(self.allocator, length); return self.list.toOwnedSlice(self.allocator); diff --git a/src/test/fixtures/console.log.js b/src/test/fixtures/console.log.js new file mode 100644 index 000000000..353895b01 --- /dev/null +++ b/src/test/fixtures/console.log.js @@ -0,0 +1,3 @@ +import { isJavaScriptCore } from "./export-check"; + +console.log("Is this JavaScriptCore?", isJavaScriptCore); diff --git a/src/test/fixtures/export-check.ts b/src/test/fixtures/export-check.ts new file mode 100644 index 000000000..6e32acd50 --- /dev/null +++ b/src/test/fixtures/export-check.ts @@ -0,0 +1,2 @@ +export const isJavaScriptCore: boolean = + !("process" in globalThis) && !("location" in globalThis); diff --git a/src/test/fixtures/uescape.js b/src/test/fixtures/uescape.js new file mode 100644 index 000000000..bc8c43230 --- /dev/null +++ b/src/test/fixtures/uescape.js @@ -0,0 +1,3 @@ +var rsApos = "['\u2019]"; + +console.log(rsApos); |