diff options
author | 2021-07-28 20:56:29 -0700 | |
---|---|---|
committer | 2021-07-28 20:56:29 -0700 | |
commit | 4a8b2546526e97583a2743d17405f664cbf6a16e (patch) | |
tree | 7cfdef87ee13374afc908dd5b0860502036d1c70 /src | |
parent | 86296897e55e0c80a3e93e27031e244525fb757c (diff) | |
download | bun-4a8b2546526e97583a2743d17405f664cbf6a16e.tar.gz bun-4a8b2546526e97583a2743d17405f664cbf6a16e.tar.zst bun-4a8b2546526e97583a2743d17405f664cbf6a16e.zip |
esmodules work?
Former-commit-id: 5cb5af4416c12518eb195d1b310990fc5c94d6c8
Diffstat (limited to 'src')
-rw-r--r-- | src/bundler.zig | 22 | ||||
-rw-r--r-- | src/fs.zig | 2 | ||||
-rw-r--r-- | src/http.zig | 5 | ||||
-rw-r--r-- | src/javascript/jsc/api/router.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 12 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 8 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 70 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 27 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/exports.zig | 12 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.h | 4 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 182 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 98 | ||||
-rw-r--r-- | src/js_printer.zig | 40 | ||||
-rw-r--r-- | src/linker.zig | 35 | ||||
-rw-r--r-- | src/main_javascript.zig | 19 | ||||
-rw-r--r-- | src/options.zig | 4 | ||||
-rw-r--r-- | src/runtime.zig | 2 | ||||
-rw-r--r-- | src/string_immutable.zig | 24 | ||||
-rw-r--r-- | src/test/fixtures/console.log.js | 3 | ||||
-rw-r--r-- | src/test/fixtures/hello.js | 1 |
22 files changed, 375 insertions, 201 deletions
diff --git a/src/bundler.zig b/src/bundler.zig index 9cbb97626..b31fb8308 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -1032,7 +1032,7 @@ pub fn NewBundler(cache_files: bool) type { }; }; - try bundler.linker.link(file_path, &result, import_path_format); + try bundler.linker.link(file_path, &result, import_path_format, false); return BuildResolveResultPair{ .written = try bundler.print( @@ -1099,6 +1099,7 @@ pub fn NewBundler(cache_files: bool) type { file_path, &result, import_path_format, + false, ); output_file.size = try bundler.print( @@ -1301,22 +1302,6 @@ pub fn NewBundler(cache_files: bool) type { 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, - .require_ref = ast.require_ref, - }, - Linker, - &bundler.linker, - ), }; } @@ -1360,8 +1345,7 @@ 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.implementsRequire(); - opts.force_commonjs = bundler.options.platform == .speedy; + opts.transform_require_to_import = true; 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; diff --git a/src/fs.zig b/src/fs.zig index a0e6e8f42..ba7eaf32f 100644 --- a/src/fs.zig +++ b/src/fs.zig @@ -885,7 +885,7 @@ pub const PathName = struct { // so if dir does not have a trailing slash, but is spaced one apart from the basename // we can assume there is a trailing slash there // so we extend the original slice's length by one - return this.dir.ptr[0 .. this.dir.len + @intCast( + return if (this.dir.len == 0) "./" else this.dir.ptr[0 .. this.dir.len + @intCast( usize, @boolToInt( this.dir[this.dir.len - 1] != std.fs.path.sep_posix and (@ptrToInt(this.dir.ptr) + this.dir.len + 1) == @ptrToInt(this.base.ptr), diff --git a/src/http.zig b/src/http.zig index 780cce9fe..16daae48e 100644 --- a/src/http.zig +++ b/src/http.zig @@ -529,6 +529,7 @@ pub const RequestContext = struct { Fs.Path.init(file_path_str), &parse_result, .absolute_url, + false, ); var written = this.bundler.print(parse_result, @TypeOf(&this.printer), &this.printer, .esm) catch |err| { @@ -691,8 +692,8 @@ pub const RequestContext = struct { boot, .entry_point, ); - JavaScript.VirtualMachine.instance = vm; - javascript_vm = JavaScript.VirtualMachine.instance; + JavaScript.VirtualMachine.vm = vm; + javascript_vm = JavaScript.VirtualMachine.vm; var exception: js.JSValueRef = null; var load_result = try JavaScript.Module.loadFromResolveResult(vm, vm.ctx, resolved_entry_point, &exception); diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig index 84dad71ef..5c3559636 100644 --- a/src/javascript/jsc/api/router.zig +++ b/src/javascript/jsc/api/router.zig @@ -20,7 +20,7 @@ pub fn importRoute( arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSObjectRef { - return JavaScript.VirtualMachine.instance.require( + return JavaScript.VirtualMachine.vm.require( ctx, std.fs.path.dirname(this.route.file_path).?, this.route.file_path, diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 36567000a..7bd9b96a4 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -1296,6 +1296,18 @@ pub fn NewClass( def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn; } else if (comptime strings.eqlComptime(function_names[i], "finalize")) { def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn; + } else if (comptime strings.eqlComptime(function_names[i], "callAsFunction")) { + const ctxfn = @field(staticFunctions, function_names[i]).rfn; + const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; + + const PointerType = std.meta.Child(Func.args[0].arg_type.?); + + var callback = if (Func.calling_convention == .C) ctxfn else To.JS.Callback( + PointerType, + ctxfn, + ).rfn; + + def.callAsFunction = callback; } else { const ctxfn = @field(staticFunctions, function_names[i]).rfn; const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index ce66998ec..26ad666ba 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -82,7 +82,7 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &shouldInterruptScript, &javaScriptRuntimeFlags, nullptr, // queueTaskToEventLoop -nullptr, // &shouldInterruptScriptBeforeTimeout, + nullptr, // &shouldInterruptScriptBeforeTimeout, &moduleLoaderImportModule, // moduleLoaderImportModule &moduleLoaderResolve, // moduleLoaderResolve &moduleLoaderFetch, // moduleLoaderFetch @@ -127,7 +127,6 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count) { } this->addStaticGlobals(extraStaticGlobals.data(), count); extraStaticGlobals.releaseBuffer(); - } JSC::Identifier GlobalObject::moduleLoaderResolve( @@ -140,11 +139,11 @@ JSC::Identifier GlobalObject::moduleLoaderResolve( auto res = Zig__GlobalObject__resolve( globalObject, toZigString(key, globalObject), - toZigString(referrer, globalObject) + referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty ); if (res.success) { - return toIdentifier(res.result.value, globalObject); + return toIdentifier(res.result.value, globalObject); } else { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); throwException(scope, res.result.err.message, globalObject); @@ -215,6 +214,7 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb scope.release(); promise->resolve(globalObject, sourceCode); + globalObject->vm().drainMicrotasks(); return promise; } diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index c18638ba3..866c00540 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -116,8 +116,74 @@ JSC__JSInternalPromise* JSC__JSModuleLoader__importModule(JSC__JSGlobalObject* a JSC__JSValue JSC__JSModuleLoader__linkAndEvaluateModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1) { return JSC::JSValue::encode(JSC::linkAndEvaluateModule(arg0, *arg1, JSC::JSValue{})); } -JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const WTF__String* arg1) { - return JSC::loadAndEvaluateModule(arg0, *arg1, JSC::JSValue{}, JSC::JSValue{}); + +static JSC::Identifier jsValueToModuleKey(JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue value) +{ + if (value.isSymbol()) + return JSC::Identifier::fromUid(JSC::jsCast<JSC::Symbol*>(value)->privateName()); + return JSC::asString(value)->toIdentifier(lexicalGlobalObject); +} + +static JSC::JSValue doLink(JSC__JSGlobalObject* globalObject, JSC::JSValue moduleKeyValue) { + JSC::VM& vm = globalObject->vm(); + JSC::JSLockHolder lock { vm }; + if (!(moduleKeyValue.isString() || moduleKeyValue.isSymbol())) { + return JSC::jsUndefined(); + } + auto scope = DECLARE_THROW_SCOPE(vm); + JSC::Identifier moduleKey = jsValueToModuleKey(globalObject, moduleKeyValue); + RETURN_IF_EXCEPTION(scope, { }); + + return JSC::linkAndEvaluateModule(globalObject, moduleKey, JSC::JSValue()); +} + +static JSC::JSNativeStdFunction* resolverFunction; +static JSC::JSNativeStdFunction* rejecterFunction; +static bool resolverFunctionInitialized = false; + +static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { + return JSC::JSValue::encode(doLink(globalObject, callFrame->argument(0))); +} + +static JSC::EncodedJSValue rejecterFunctionCallback(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) { + return JSC::JSValue::encode(callFrame->argument(0)); +} + + +JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, ZigString arg1) { + globalObject->vm().drainMicrotasks(); + auto name = Zig::toString(arg1); + name.impl()->ref(); + + auto* promise = JSC::loadAndEvaluateModule( + globalObject, + name, + JSC::jsUndefined(), + JSC::jsUndefined() + ); + + if (!resolverFunctionInitialized) { + resolverFunction = JSC::JSNativeStdFunction::create(globalObject->vm(), globalObject, 1, String(), resolverFunctionCallback); + rejecterFunction = JSC::JSNativeStdFunction::create(globalObject->vm(), globalObject, 1, String(), rejecterFunctionCallback); + resolverFunctionInitialized = true; + JSC::gcProtect(resolverFunction); + JSC::gcProtect(rejecterFunction); + } + + auto result = promise->then(globalObject, resolverFunction, rejecterFunction); + globalObject->vm().drainMicrotasks(); + + + // if (promise->status(globalObject->vm()) == JSC::JSPromise::Status::Fulfilled) { + // return reinterpret_cast<JSC::JSInternalPromise*>( + // JSC::JSInternalPromise::resolvedPromise( + // globalObject, + // doLink(globalObject, promise->result(globalObject->vm())) + // ) + // ); + // } + + return result; } JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(JSC__JSGlobalObject* arg0, const JSC__SourceCode* arg1) { return JSC::loadAndEvaluateModule(arg0, *arg1, JSC::JSValue{}); diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 0e585d49f..09f40d475 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -174,46 +174,46 @@ pub fn NewGlobalObject(comptime Type: type) type { return struct { pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { if (comptime @hasDecl(Type, "import")) { - return @call(.{ .modifier = .always_inline }, Interface.import, .{ global, specifier, source }); + return @call(.{ .modifier = .always_inline }, Type.import, .{ global, specifier, source }); } return ErrorableZigString.err(error.ImportFailed, "Import not implemented"); } pub fn resolve(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { if (comptime @hasDecl(Type, "resolve")) { - return @call(.{ .modifier = .always_inline }, Interface.resolve, .{ global, specifier, source }); + return @call(.{ .modifier = .always_inline }, Type.resolve, .{ global, specifier, source }); } return ErrorableZigString.err(error.ResolveFailed, "resolve not implemented"); } pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { if (comptime @hasDecl(Type, "fetch")) { - return @call(.{ .modifier = .always_inline }, Interface.fetch, .{ global, specifier, source }); + return @call(.{ .modifier = .always_inline }, Type.fetch, .{ global, specifier, source }); } return ErrorableZigString.err(error.FetchFailed, "Module fetch not implemented"); } pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, rejection: JSPromiseRejectionOperation) callconv(.C) JSValue { if (comptime @hasDecl(Type, "promiseRejectionTracker")) { - return @call(.{ .modifier = .always_inline }, Interface.promiseRejectionTracker, .{ global, promise, rejection }); + return @call(.{ .modifier = .always_inline }, Type.promiseRejectionTracker, .{ global, promise, rejection }); } return JSValue.jsUndefined(); } pub fn reportUncaughtException(global: *JSGlobalObject, exception: *Exception) callconv(.C) JSValue { if (comptime @hasDecl(Type, "reportUncaughtException")) { - return @call(.{ .modifier = .always_inline }, Interface.reportUncaughtException, .{ global, exception }); + return @call(.{ .modifier = .always_inline }, Type.reportUncaughtException, .{ global, exception }); } return JSValue.jsUndefined(); } pub fn createImportMetaProperties(global: *JSGlobalObject, loader: *JSModuleLoader, obj: JSValue, record: *JSModuleRecord, specifier: JSValue) callconv(.C) JSValue { if (comptime @hasDecl(Type, "createImportMetaProperties")) { - return @call(.{ .modifier = .always_inline }, Interface.createImportMetaProperties, .{ global, loader, obj, record, specifier }); + return @call(.{ .modifier = .always_inline }, Type.createImportMetaProperties, .{ global, loader, obj, record, specifier }); } return JSValue.jsUndefined(); } pub fn onCrash() callconv(.C) void { if (comptime @hasDecl(Type, "onCrash")) { - return @call(.{ .modifier = .always_inline }, Interface.onCrash, .{}); + return @call(.{ .modifier = .always_inline }, Type.onCrash, .{}); } Global.panic("C++ crashed :(", .{}); @@ -256,7 +256,7 @@ pub const JSModuleLoader = extern struct { }); } - pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const String) *JSInternalPromise { + pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: ZigString) *JSInternalPromise { return shim.cppFn("loadAndEvaluateModule", .{ globalObject, module_name, @@ -393,13 +393,7 @@ pub const JSInternalPromise = extern struct { pub const name = "JSC::JSInternalPromise"; pub const namespace = "JSC"; - pub const Status = enum(u32) { - Pending = 0, // Making this as 0, so that, we can change the status from Pending to others without masking. - Fulfilled = 1, - Rejected = 2, - }; - - pub fn status(this: *const JSInternalPromise, vm: *VM) Status { + pub fn status(this: *const JSInternalPromise, vm: *VM) JSPromise.Status { return shim.cppFn("status", .{ this, vm }); } pub fn result(this: *const JSInternalPromise, vm: *VM) JSValue { @@ -1356,8 +1350,9 @@ pub const VM = extern struct { pub fn throwError(vm: *VM, global_object: *JSGlobalObject, scope: *ThrowScope, message: [*]const u8, len: usize) bool { return cppFn("throwError", .{ vm, - scope, + global_object, + scope, message, len, diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index 8c94f1577..82c9e8dd0 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -2,7 +2,7 @@ usingnamespace @import("./bindings.zig"); usingnamespace @import("./shared.zig"); const Fs = @import("../../../fs.zig"); const CAPI = @import("../JavaScriptCore.zig"); - +const JS = @import("../javascript.zig"); const Handler = struct { pub export fn global_signal_handler_fn(sig: i32, info: *const std.os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void { Global.panic("C++ Crash!!", .{}); @@ -16,7 +16,7 @@ pub const ZigGlobalObject = extern struct { pub const name = "Zig::GlobalObject"; pub const include = "\"ZigGlobalObject.h\""; pub const namespace = shim.namespace; - pub const Interface: type = NewGlobalObject(std.meta.globalOption("JavaScript", type) orelse struct {}); + pub const Interface: type = NewGlobalObject(std.meta.globalOption("JavaScriptVirtualMachine", type) orelse struct {}); pub var sigaction: std.os.Sigaction = undefined; pub var sigaction_installed = false; @@ -149,7 +149,7 @@ pub fn Errorable(comptime Type: type) type { threadlocal var err_buf: [4096]u8 = undefined; pub fn errFmt(code: anyerror, comptime fmt: []const u8, args: anytype) @This() { - const message = std.fmt.bufPrint(&err_buf, fmt, args) catch @errorName(code); + const message = std.fmt.bufPrint(&err_buf, fmt, args) catch @as([]const u8, @errorName(code)[0..]); return @call(.{ .modifier = .always_inline }, err, .{ code, message }); } @@ -207,15 +207,15 @@ pub const ZigConsoleClient = struct { vals: [*]JSValue, len: usize, ) callconv(.C) void { - var console = zigCast(ZigConsoleClient, console_); + var console = JS.VirtualMachine.vm.console; var i: usize = 0; var writer = console.writer; if (len == 1) { var str = vals[0].toWTFString(global); var slice = str.slice(); - _ = writer.unbuffered_writer.write(slice) catch 0; - if (slice.len > 0 and slice[slice.len - 1] != '\n') { + var written = writer.unbuffered_writer.write(slice) catch 0; + if (written > 0 and slice[slice.len - 1] != '\n') { _ = writer.unbuffered_writer.write("\n") catch 0; } return; diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index 85b09d7ff..46486557a 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1627506357 +//-- AUTOGENERATED FILE -- 1627515944 #pragma once #include <stddef.h> diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index 08851f3e1..944ceffb5 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1627506357 +//-- AUTOGENERATED FILE -- 1627515944 #pragma once #include <stddef.h> @@ -257,7 +257,7 @@ CPP_DECL bool JSC__JSModuleLoader__checkSyntax(JSC__JSGlobalObject* arg0, const CPP_DECL JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* arg0, const unsigned char* arg1, size_t arg2, const unsigned char* arg3, size_t arg4, JSC__JSValue JSValue5, JSC__JSValue* arg6); CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__importModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1); CPP_DECL JSC__JSValue JSC__JSModuleLoader__linkAndEvaluateModule(JSC__JSGlobalObject* arg0, const JSC__Identifier* arg1); -CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const WTF__String* arg1); +CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, ZigString arg1); CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(JSC__JSGlobalObject* arg0, const JSC__SourceCode* arg1); #pragma mark - JSC::JSModuleRecord diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index 90e142bd5..783d9c82d 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -119,7 +119,7 @@ pub extern fn JSC__JSModuleLoader__checkSyntax(arg0: [*c]JSC__JSGlobalObject, ar pub extern fn JSC__JSModuleLoader__evaluate(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const u8, arg2: usize, arg3: [*c]const u8, arg4: usize, JSValue5: JSC__JSValue, arg6: [*c]JSC__JSValue) JSC__JSValue; pub extern fn JSC__JSModuleLoader__importModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__Identifier) [*c]JSC__JSInternalPromise; pub extern fn JSC__JSModuleLoader__linkAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__Identifier) JSC__JSValue; -pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const WTF__String) [*c]JSC__JSInternalPromise; +pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: [*c]JSC__JSGlobalObject, arg1: ZigString) [*c]JSC__JSInternalPromise; pub extern fn JSC__JSModuleLoader__loadAndEvaluateModuleEntryPoint(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]const JSC__SourceCode) [*c]JSC__JSInternalPromise; pub extern fn JSC__JSModuleRecord__sourceCode(arg0: [*c]JSC__JSModuleRecord) bJSC__SourceCode; pub extern fn JSC__JSPromise__isHandled(arg0: [*c]const JSC__JSPromise, arg1: [*c]JSC__VM) bool; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 51a287b27..9850f0c6a 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -19,10 +19,13 @@ usingnamespace @import("./config.zig"); usingnamespace @import("./bindings/exports.zig"); usingnamespace @import("./bindings/bindings.zig"); +const Runtime = @import("../../runtime.zig"); + pub const GlobalClasses = [_]type{ Request.Class, Response.Class, Headers.Class, + EventListenerMixin.addEventListener(VirtualMachine), }; pub const Module = struct { @@ -39,9 +42,11 @@ pub const VirtualMachine = struct { node_modules: ?*NodeModuleBundle = null, bundler: Bundler, watcher: ?*http.Watcher = null, - console: ZigConsoleClient, + console: *ZigConsoleClient, require_cache: RequireCacheType, log: *logger.Log, + event_listeners: EventListenerMixin.Map, + pub threadlocal var vm_loaded = false; pub threadlocal var vm: *VirtualMachine = undefined; pub fn init( @@ -58,21 +63,27 @@ pub const VirtualMachine = struct { } vm = try allocator.create(VirtualMachine); + var console = try allocator.create(ZigConsoleClient); + console.* = ZigConsoleClient.init(Output.errorWriter(), Output.writer()); + vm.* = VirtualMachine{ .global = undefined, .allocator = allocator, .require_cache = RequireCacheType.init(allocator), + .event_listeners = EventListenerMixin.Map.init(allocator), .bundler = try Bundler.init( allocator, log, try configureTransformOptionsForSpeedy(allocator, _args), existing_bundle, ), - .console = ZigConsoleClient.init(Output.errorWriter(), Output.writer()), + .console = console, .node_modules = existing_bundle, .log = log, }; + vm.bundler.configureLinker(); + var global_classes: [GlobalClasses.len]js.JSClassRef = undefined; inline for (GlobalClasses) |Class, i| { global_classes[i] = Class.get().*; @@ -80,11 +91,150 @@ pub const VirtualMachine = struct { vm.global = ZigGlobalObject.create( &global_classes, @intCast(i32, global_classes.len), - &vm.console, + vm.console, ); + vm_loaded = true; return vm; } + + // dynamic import + // pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { + + // } + + threadlocal var source_code_printer: js_printer.BufferPrinter = undefined; + threadlocal var source_code_printer_loaded: bool = false; + + inline fn _fetch(global: *JSGlobalObject, specifier: string, source: string) !string { + std.debug.assert(VirtualMachine.vm_loaded); + std.debug.assert(VirtualMachine.vm.global == global); + + if (strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { + return Runtime.Runtime.sourceContent(); + } + + const result = vm.bundler.resolve_results.get(specifier) orelse return error.MissingResolveResult; + const path = result.path_pair.primary; + const loader = vm.bundler.options.loaders.get(path.name.ext) orelse .file; + + switch (loader) { + .js, .jsx, .ts, .tsx, .json => { + vm.bundler.resetStore(); + const hash = http.Watcher.getHash(path.text); + + 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, + fd, + hash, + ) orelse { + return error.ParseError; + }; + + // We _must_ link because: + // - node_modules bundle won't be properly + try vm.bundler.linker.link( + path, + &parse_result, + .absolute_path, + true, + ); + + 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 = false; + + source_code_printer_loaded = true; + } + + source_code_printer.ctx.reset(); + + var written = try vm.bundler.print( + parse_result, + @TypeOf(&source_code_printer), + &source_code_printer, + .esm, + ); + + if (written == 0) { + return error.PrintingErrorWriteFailed; + } + + return vm.allocator.dupe(u8, source_code_printer.ctx.written) catch unreachable; + }, + else => { + return try strings.quotedAlloc(VirtualMachine.vm.allocator, path.pretty); + }, + } + } + inline fn _resolve(global: *JSGlobalObject, specifier: string, source: string) !string { + std.debug.assert(VirtualMachine.vm_loaded); + std.debug.assert(VirtualMachine.vm.global == global); + if (strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { + return Runtime.Runtime.Imports.Name; + } + + const result: resolver.Result = vm.bundler.resolve_results.get(specifier) orelse brk: { + // We don't want to write to the hash table if there's an error + // That's why we don't use getOrPut here + const res = try vm.bundler.resolver.resolve( + Fs.PathName.init(source).dirWithTrailingSlash(), + specifier, + .stmt, + ); + try vm.bundler.resolve_results.put(res.path_pair.primary.text, res); + break :brk res; + }; + + return result.path_pair.primary.text; + } + + pub fn resolve(global: *JSGlobalObject, specifier: ZigString, source: ZigString) ErrorableZigString { + const result = _resolve(global, specifier.slice(), source.slice()) catch |err| { + return ErrorableZigString.errFmt(err, "ResolveError {s} for \"{s}\"\nfrom\"{s}\"", .{ + @errorName(err), + specifier.slice(), + source.slice(), + }); + }; + + return ErrorableZigString.ok(ZigString.init(result)); + } + + pub fn fetch(global: *JSGlobalObject, specifier: ZigString, source: ZigString) ErrorableZigString { + const result = _fetch(global, specifier.slice(), source.slice()) catch |err| { + return ErrorableZigString.errFmt(err, "{s}: \"{s}\"", .{ + @errorName(err), + specifier.slice(), + }); + }; + + return ErrorableZigString.ok(ZigString.init(result)); + } + + pub fn loadEntryPoint(this: *VirtualMachine, entry_point: string) !void { + var path = this.bundler.normalizeEntryPointPath(entry_point); + + var promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(path)); + + this.global.vm().drainMicrotasks(); + if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) { + var str = promise.result(this.global.vm()).toWTFString(this.global); + Output.prettyErrorln("<r><red>Error<r>: <b>{s}<r>", .{str.slice()}); + } + } }; pub const Object = struct { @@ -216,13 +366,14 @@ pub const EventListenerMixin = struct { ) type { const Handler = struct { pub fn addListener( - ptr: *Struct, ctx: js.JSContextRef, function: js.JSObjectRef, thisObject: js.JSObjectRef, - arguments: []const js.JSValueRef, + argumentCount: usize, + _arguments: [*c]const js.JSValueRef, exception: js.ExceptionRef, - ) js.JSValueRef { + ) callconv(.C) js.JSValueRef { + const arguments = _arguments[0 .. argumentCount - 1]; if (arguments.len == 0 or arguments.len == 1 or !js.JSValueIsString(ctx, arguments[0]) or !js.JSValueIsObject(ctx, arguments[arguments.len - 1]) or !js.JSObjectIsFunction(ctx, arguments[arguments.len - 1])) { return js.JSValueMakeUndefined(ctx); } @@ -235,10 +386,10 @@ pub const EventListenerMixin = struct { const name_used_len = js.JSStringGetUTF8CString(arguments[0], &event_listener_names_buf, event_listener_names_buf.len); const name = event_listener_names_buf[0 .. name_used_len - 1]; const event = EventType.match(name) orelse return js.JSValueMakeUndefined(ctx); - var entry = VirtualMachine.instance.event_listeners.getOrPut(event) catch unreachable; + var entry = VirtualMachine.vm.event_listeners.getOrPut(event) catch unreachable; if (!entry.found_existing) { - entry.value_ptr.* = List.initCapacity(VirtualMachine.instance.allocator, 1) catch unreachable; + entry.value_ptr.* = List.initCapacity(VirtualMachine.vm.allocator, 1) catch unreachable; } var callback = arguments[arguments.len - 1]; @@ -249,6 +400,19 @@ pub const EventListenerMixin = struct { } }; - return Handler; + return NewClass( + Struct, + .{ + .name = "addEventListener", + .read_only = true, + }, + .{ + .@"callAsFunction" = .{ + .rfn = Handler.addListener, + .ts = d.ts{}, + }, + }, + .{}, + ); } }; diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index be442728b..5ee6eab94 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -1611,9 +1611,6 @@ 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, @@ -1832,28 +1829,26 @@ pub const Parser = struct { declared_symbols_i += 1; declared_symbols[declared_symbols_i] = .{ .ref = automatic_namespace_ref, .is_top_level = true }; declared_symbols_i += 1; - if (!p.options.force_commonjs) { - var require_call = p.callRequireOrBundledRequire(require_call_args); + var require_call = p.callRequireOrBundledRequire(require_call_args); - decls[decl_i] = G.Decl{ - .binding = p.b( - B.Identifier{ - .ref = p.jsx_runtime_ref, - }, - loc, - ), - .value = p.e( - E.Dot{ - .target = require_call, - .name = p.options.jsx.jsx, - .name_loc = loc, - .can_be_removed_if_unused = true, - }, - loc, - ), - }; - decl_i += 1; - } + decls[decl_i] = G.Decl{ + .binding = p.b( + B.Identifier{ + .ref = p.jsx_runtime_ref, + }, + loc, + ), + .value = p.e( + E.Dot{ + .target = require_call, + .name = p.options.jsx.jsx, + .name_loc = loc, + .can_be_removed_if_unused = true, + }, + loc, + ), + }; + decl_i += 1; if (jsx_filename_symbol.use_count_estimate > 0) { declared_symbols[declared_symbols_i] = .{ .ref = p.jsx_filename_ref, .is_top_level = true }; @@ -1874,34 +1869,12 @@ pub const Parser = struct { // When everything is CommonJS // We import JSX like this: // var {jsxDev} = require("react/jsx-dev") - if (p.options.force_commonjs) { - var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable; - clause_items[0] = js_ast.ClauseItem{ - .alias = p.options.jsx.jsx, - .alias_loc = loc, - .name = LocRef{ .loc = loc, .ref = p.jsx_runtime_ref }, - .original_name = "", - }; - jsx_part_stmts[stmt_i] = p.s( - S.Import{ - .namespace_ref = automatic_namespace_ref, - .items = clause_items, - .star_name_loc = loc, - .is_single_line = true, - .import_record_index = import_record_id, - }, - loc, - ); - // Otherwise, it looks like this - // var - } else { - jsx_part_stmts[stmt_i] = p.s(S.Import{ - .namespace_ref = automatic_namespace_ref, - .star_name_loc = loc, - .is_single_line = true, - .import_record_index = import_record_id, - }, loc); - } + jsx_part_stmts[stmt_i] = p.s(S.Import{ + .namespace_ref = automatic_namespace_ref, + .star_name_loc = loc, + .is_single_line = true, + .import_record_index = import_record_id, + }, loc); stmt_i += 1; p.named_imports.put( @@ -2026,7 +1999,7 @@ pub const Parser = struct { var runtime_imports_iter = p.runtime_imports.iter(); // don't import runtime if we're bundling, it's already included - if (!p.options.enable_bundling) { + if (!p.options.enable_bundling and p.has_called_runtime) { while (runtime_imports_iter.next()) |entry| { const imports = [_]u16{entry.key}; p.generateImportStmt( @@ -2354,6 +2327,8 @@ pub fn NewParser( hmr_module_class_ref: js_ast.Ref = js_ast.Ref.None, hmr_exports_list: std.ArrayList(js_ast.ClauseItem), + has_called_runtime: bool = false, + cjs_import_stmts: std.ArrayList(Stmt), bundle_export_ref: ?Ref = null, @@ -2849,9 +2824,7 @@ pub fn NewParser( // This means we need to transform from require(react) to react() // unless we're building inside of speedy, then it's just normal commonjs pub fn callRequireOrBundledRequire(p: *P, require_args: []Expr) Expr { - if (p.options.force_commonjs) { - return require_args[0]; - } else if (p.options.can_import_from_bundle) { + if (p.options.can_import_from_bundle) { return p.e(E.Call{ .target = require_args[0] }, require_args[0].loc); } else { return p.callRuntime(require_args[0].loc, "__require", require_args); @@ -3169,15 +3142,9 @@ pub fn NewParser( p.hoistSymbols(p.module_scope); - if (p.options.force_commonjs) { - p.exports_ref = try p.declareCommonJSSymbol(.unbound, "exports"); - p.module_ref = try p.declareCommonJSSymbol(.unbound, "module"); - p.require_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "require"); - } else { - p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports"); - p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module"); - p.require_ref = try p.declareCommonJSSymbol(.unbound, "require"); - } + p.exports_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "exports"); + p.module_ref = try p.declareSymbol(.hoisted, logger.Loc.Empty, "module"); + p.require_ref = try p.declareCommonJSSymbol(.unbound, "require"); 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"); @@ -13066,6 +13033,7 @@ pub fn NewParser( pub fn callRuntime(p: *P, loc: logger.Loc, comptime name: string, args: []Expr) Expr { var ref: Ref = undefined; + p.has_called_runtime = true; if (!p.runtime_imports.contains(name)) { ref = p.newSymbol(.other, name) catch unreachable; diff --git a/src/js_printer.zig b/src/js_printer.zig index 1ea436589..2f02e4208 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -3813,7 +3813,10 @@ pub fn NewFileWriter(file: std.fs.File) FileWriter { return FileWriter.init(internal); } -pub const Format = enum { esm, cjs, speedy }; +pub const Format = enum { + esm, + cjs, +}; pub fn printAst( comptime Writer: type, @@ -3888,38 +3891,3 @@ 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 44fd140c2..d0f48e8a1 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -175,6 +175,7 @@ pub fn NewLinker(comptime BundlerType: type) type { file_path: Fs.Path, result: *_bundler.ParseResult, comptime import_path_format: Options.BundleOptions.ImportPathFormat, + comptime ignore_runtime: bool, ) !void { var needs_runtime = result.ast.uses_exports_ref or result.ast.uses_module_ref or result.ast.runtime_imports.hasAny(); const source_dir = file_path.name.dir; @@ -186,22 +187,24 @@ pub fn NewLinker(comptime BundlerType: type) type { .jsx, .js, .ts, .tsx => { for (result.ast.import_records) |*import_record, _record_index| { const record_index = @truncate(u32, _record_index); - if (strings.eqlComptime(import_record.path.text, Runtime.Imports.Name)) { - // runtime is included in the bundle, so we don't need to dynamically import it - if (linker.options.node_modules_bundle) |node_modules_bundle| { - import_record.path.text = if (linker.options.node_modules_bundle_url.len > 0) linker.options.node_modules_bundle_url else node_modules_bundle.bundle.import_from_name; - } else { - import_record.path = try linker.generateImportPath( - source_dir, - linker.runtime_source_path, - Runtime.version(), - false, - import_path_format, - ); - result.ast.runtime_import_record_id = record_index; - result.ast.needs_runtime = true; + if (comptime !ignore_runtime) { + if (strings.eqlComptime(import_record.path.text, Runtime.Imports.Name)) { + // runtime is included in the bundle, so we don't need to dynamically import it + if (linker.options.node_modules_bundle) |node_modules_bundle| { + import_record.path.text = if (linker.options.node_modules_bundle_url.len > 0) linker.options.node_modules_bundle_url else node_modules_bundle.bundle.import_from_name; + } else { + import_record.path = try linker.generateImportPath( + source_dir, + linker.runtime_source_path, + Runtime.version(), + false, + import_path_format, + ); + result.ast.runtime_import_record_id = record_index; + result.ast.needs_runtime = true; + } + continue; } - continue; } if (linker.resolver.resolve(source_dir, import_record.path.text, import_record.kind)) |*_resolved_import| { @@ -307,7 +310,7 @@ pub fn NewLinker(comptime BundlerType: type) type { switch (err) { error.ModuleNotFound => { if (Resolver.isPackagePath(import_record.path.text)) { - if (linker.options.platform != .node and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) { + if (linker.options.platform.isWebLike() and Options.ExternalModules.isNodeBuiltin(import_record.path.text)) { try linker.log.addRangeErrorFmt( &result.source, import_record.range, diff --git a/src/main_javascript.zig b/src/main_javascript.zig index 721098e86..be822c793 100644 --- a/src/main_javascript.zig +++ b/src/main_javascript.zig @@ -386,23 +386,10 @@ pub const Cli = struct { // .entry_point, // ); - var exception = js.JSValue.jsUndefined(); - var result = js.JSModuleLoader.evaluate( - vm.global, - StringS.src, - StringS.src.len, - "/hi.js", - "/hi.js".len, - js.JSValue.jsUndefined(), - @ptrCast([*]js.JSValue, &exception), - ); - - if (!exception.isUndefined()) { - var str = exception.toWTFString(vm.global); - var slice = str.slice(); - _ = Output.errorWriter().write(slice) catch 0; - } + try vm.loadEntryPoint(vm.bundler.options.entry_points[0]); } }; pub const JavaScript = struct {}; + +pub const JavaScriptVirtualMachine = VirtualMachine; diff --git a/src/options.zig b/src/options.zig index 39b3ec0d9..c90f671a9 100644 --- a/src/options.zig +++ b/src/options.zig @@ -250,9 +250,9 @@ pub const Platform = enum { speedy, node, - pub fn implementsRequire(platform: Platform) bool { + pub fn isWebLike(platform: Platform) bool { return switch (platform) { - .speedy, .node => true, + .neutral, .browser => true, else => false, }; } diff --git a/src/runtime.zig b/src/runtime.zig index 39518e58c..ac2a2f0ff 100644 --- a/src/runtime.zig +++ b/src/runtime.zig @@ -6,7 +6,7 @@ pub const ProdSourceContent = @embedFile("./runtime.out.js"); pub const Runtime = struct { pub fn sourceContent() string { - if (isDebug) { + if (comptime isDebug) { var runtime_path = std.fs.path.join(std.heap.c_allocator, &[_]string{ std.fs.path.dirname(@src().file).?, "runtime.out.js" }) catch unreachable; const file = std.fs.openFileAbsolute(runtime_path, .{}) catch unreachable; defer file.close(); diff --git a/src/string_immutable.zig b/src/string_immutable.zig index b65b142bf..d5f41c82e 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -80,6 +80,30 @@ pub fn endsWithAny(self: string, str: string) bool { pub fn lastNonwhitespace(self: string, str: string) bool {} +pub fn quotedAlloc(allocator: *std.mem.Allocator, self: string) !string { + var count: usize = 0; + for (self) |char| { + count += @boolToInt(char == '"'); + } + + if (count == 0) { + return allocator.dupe(u8, self); + } + + var i: usize = 0; + var out = try allocator.alloc(u8, self.len + count); + for (self) |char| { + if (char == '"') { + out[i] = '\\'; + i += 1; + } + out[i] = char; + i += 1; + } + + return out; +} + pub fn endsWithAnyComptime(self: string, comptime str: string) bool { if (str.len < 10) { const last = self[self.len - 1]; diff --git a/src/test/fixtures/console.log.js b/src/test/fixtures/console.log.js index 3088f90d5..5a6a50b33 100644 --- a/src/test/fixtures/console.log.js +++ b/src/test/fixtures/console.log.js @@ -1 +1,2 @@ -console.log("Waiting for debugger."); +import hello from "./hello"; +console.log(hello); diff --git a/src/test/fixtures/hello.js b/src/test/fixtures/hello.js new file mode 100644 index 000000000..34b58e6e1 --- /dev/null +++ b/src/test/fixtures/hello.js @@ -0,0 +1 @@ +export default "hello"; |