diff options
| -rw-r--r-- | src/http.zig | 2 | ||||
| -rw-r--r-- | src/javascript/jsc/api/router.zig | 39 | ||||
| -rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 27 | ||||
| -rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 19 | ||||
| -rw-r--r-- | src/javascript/jsc/bindings/headers-cpp.h | 2 | ||||
| -rw-r--r-- | src/javascript/jsc/bindings/headers.h | 3 | ||||
| -rw-r--r-- | src/javascript/jsc/bindings/headers.zig | 1 | ||||
| -rw-r--r-- | src/javascript/jsc/javascript.zig | 111 | ||||
| -rw-r--r-- | src/linker.zig | 10 | ||||
| -rw-r--r-- | src/resolver/resolver.zig | 2 | ||||
| -rw-r--r-- | src/router.zig | 13 |
11 files changed, 210 insertions, 19 deletions
diff --git a/src/http.zig b/src/http.zig index 46ef9bdc9..3bd4a98f1 100644 --- a/src/http.zig +++ b/src/http.zig @@ -771,6 +771,7 @@ pub const RequestContext = struct { js_ast.Stmt.Data.Store.reset(); js_ast.Expr.Data.Store.reset(); + JavaScript.Wundle.flushCSSImports(); try runLoop(vm); } @@ -783,6 +784,7 @@ pub const RequestContext = struct { std.debug.assert(ZigGlobalObject.resetModuleRegistryMap(vm.global, module_map)); js_ast.Stmt.Data.Store.reset(); js_ast.Expr.Data.Store.reset(); + JavaScript.Wundle.flushCSSImports(); } var handler: *JavaScriptHandler = try channel.readItem(); diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig index 14deaea66..89aa9a715 100644 --- a/src/javascript/jsc/api/router.zig +++ b/src/javascript/jsc/api/router.zig @@ -11,9 +11,14 @@ usingnamespace @import("../webcore/response.zig"); const Router = @This(); const VirtualMachine = JavaScript.VirtualMachine; +const ScriptSrcStream = std.io.FixedBufferStream([]u8); route: *const FilesystemRouter.Match, query_string_map: ?QueryStringMap = null, +script_src: ?string = null, +script_src_buf: [1024]u8 = undefined, + +script_src_buf_writer: ScriptSrcStream = undefined, pub fn importRoute( this: *Router, @@ -95,6 +100,7 @@ fn createRouteObject(ctx: js.JSContextRef, req: *const http.RequestContext, exce router.* = Router{ .route = route, }; + router.script_src_buf_writer = ScriptSrcStream{ .pos = 0, .buffer = std.mem.span(&router.script_src_buf) }; return Instance.make(ctx, router); } @@ -168,7 +174,7 @@ pub const Instance = NewClass( .@"tsdoc" = "URL path as appears in a web browser's address bar", }, }, - .filepath = .{ + .filePath = .{ .get = getFilePath, .ro = true, .ts = d.ts{ @@ -178,6 +184,16 @@ pub const Instance = NewClass( , }, }, + .scriptSrc = .{ + .get = getScriptSrc, + .ro = true, + .ts = d.ts{ + .@"return" = "string", + .tsdoc = + \\src attribute of the script tag that loads the route. + , + }, + }, .kind = .{ .get = getKind, .ro = true, @@ -320,6 +336,25 @@ pub fn createQueryObject(ctx: js.JSContextRef, map: *QueryStringMap, exception: return value.asRef(); } +pub fn getScriptSrc( + this: *Router, + ctx: js.JSContextRef, + thisObject: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, +) js.JSValueRef { + const src = this.script_src orelse brk: { + var writer = this.script_src_buf_writer.writer(); + + JavaScript.Wundle.getPublicPath(this.route.file_path, ScriptSrcStream.Writer, writer); + break :brk this.script_src_buf[0..this.script_src_buf_writer.pos]; + }; + + this.script_src = src; + + return js.JSValueMakeString(ctx, ZigString.init(src).toJSStringRef()); +} + pub fn getQuery( this: *Router, ctx: js.JSContextRef, @@ -338,7 +373,7 @@ pub fn getQuery( ))) |map| { this.query_string_map = map; } else |err| {} - } else { + } else if (this.route.query_string.len > 0) { if (QueryStringMap.init(getAllocator(ctx), this.route.query_string)) |map| { this.query_string_map = map; } else |err| {} diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index c66b5c94b..33aded945 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -333,6 +333,33 @@ static JSC::JSValue doLink(JSC__JSGlobalObject *globalObject, JSC::JSValue modul return JSC::linkAndEvaluateModule(globalObject, moduleKey, JSC::JSValue()); } +JSC__JSValue JSC__JSValue__createStringArray(JSC__JSGlobalObject *globalObject, ZigString *arg1, + size_t arg2) { + JSC::VM &vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSC::JSArray *array = nullptr; + { + JSC::ObjectInitializationScope initializationScope(vm); + if ((array = JSC::JSArray::tryCreateUninitializedRestricted( + initializationScope, nullptr, + globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), + arg2))) { + + for (size_t i = 0; i < arg2; ++i) { + array->initializeIndexWithoutBarrier(initializationScope, i, + JSC::jsString(vm, Zig::toString(arg1[i]))); + } + } + } + if (!array) { + JSC::throwOutOfMemoryError(globalObject, scope); + return JSC::JSValue::encode(JSC::JSValue()); + } + + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::JSValue(array))); +} + JSC__JSValue JSC__JSGlobalObject__createAggregateError(JSC__JSGlobalObject *globalObject, void **errors, uint16_t errors_count, ZigString arg3) { diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index e2b2679ae..07f177cb1 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -3,7 +3,7 @@ usingnamespace @import("./headers.zig"); pub const Shimmer = @import("./shimmer.zig").Shimmer; const hasRef = std.meta.trait.hasField("ref"); const C_API = @import("../JavaScriptCore.zig"); - +const StringPointer = @import("../../../api/schema.zig").Api.StringPointer; pub const JSObject = extern struct { pub const shim = Shimmer("JSC", "JSObject", @This()); bytes: shim.Bytes, @@ -89,6 +89,13 @@ pub const ZigString = extern struct { pub const name = "ZigString"; pub const namespace = ""; + pub fn fromStringPointer(ptr: StringPointer, buf: string, to: *ZigString) void { + to.* = ZigString{ + .len = ptr.length, + .ptr = buf[ptr.offset..ptr.length].ptr, + }; + } + pub fn init(slice_: []const u8) ZigString { return ZigString{ .ptr = slice_.ptr, .len = slice_.len }; } @@ -1194,6 +1201,14 @@ pub const JSValue = enum(i64) { return cppFn("jsDoubleNumber", .{i}); } + pub fn createStringArray(globalThis: *JSGlobalObject, str: [*c]ZigString, strings_count: usize) JSValue { + return cppFn("createStringArray", .{ + globalThis, + str, + strings_count, + }); + } + pub fn jsNumberFromDouble(i: f64) JSValue { return cppFn("jsNumberFromDouble", .{i}); } @@ -1400,7 +1415,7 @@ pub const JSValue = enum(i64) { return @intToPtr(*c_void, @intCast(usize, @enumToInt(this))); } - pub const Extern = [_][]const u8{ "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" }; + pub const Extern = [_][]const u8{ "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" }; }; pub const PropertyName = extern struct { diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index e23e9dd84..5f3781646 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1628316335 +//-- AUTOGENERATED FILE -- 1628394202 // clang-format off #pragma once diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index abafa1279..95e6c6d47 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1628316335 +//-- AUTOGENERATED FILE -- 1628394202 // clang-format: off #pragma once @@ -412,6 +412,7 @@ CPP_DECL double JSC__JSValue__asNumber(JSC__JSValue JSValue0); CPP_DECL bJSC__JSObject JSC__JSValue__asObject(JSC__JSValue JSValue0); CPP_DECL JSC__JSString* JSC__JSValue__asString(JSC__JSValue JSValue0); CPP_DECL JSC__JSValue JSC__JSValue__createEmptyObject(JSC__JSGlobalObject* arg0, size_t arg1); +CPP_DECL JSC__JSValue JSC__JSValue__createStringArray(JSC__JSGlobalObject* arg0, ZigString* arg1, size_t arg2); CPP_DECL bool JSC__JSValue__eqlCell(JSC__JSValue JSValue0, JSC__JSCell* arg1); CPP_DECL bool JSC__JSValue__eqlValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1); CPP_DECL void JSC__JSValue__forEach(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, void (* ArgFn2)(JSC__VM* arg0, JSC__JSGlobalObject* arg1, JSC__JSValue JSValue2)); diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index de854fe53..98b2b505f 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -236,6 +236,7 @@ pub extern fn JSC__JSValue__asNumber(JSValue0: JSC__JSValue) f64; pub extern fn JSC__JSValue__asObject(JSValue0: JSC__JSValue) bJSC__JSObject; pub extern fn JSC__JSValue__asString(JSValue0: JSC__JSValue) [*c]JSC__JSString; pub extern fn JSC__JSValue__createEmptyObject(arg0: [*c]JSC__JSGlobalObject, arg1: usize) JSC__JSValue; +pub extern fn JSC__JSValue__createStringArray(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]ZigString, arg2: usize) JSC__JSValue; pub extern fn JSC__JSValue__eqlCell(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSCell) bool; pub extern fn JSC__JSValue__eqlValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue) bool; pub extern fn JSC__JSValue__forEach(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, ArgFn2: ?fn ([*c]JSC__VM, [*c]JSC__JSGlobalObject, JSC__JSValue) callconv(.C) void) void; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index dccfdcf00..9bfcfa21c 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -1,7 +1,7 @@ const std = @import("std"); const Fs = @import("../../fs.zig"); -const resolver = @import("../../resolver/resolver.zig"); +const Resolver = @import("../../resolver/resolver.zig"); const ast = @import("../../import_record.zig"); const NodeModuleBundle = @import("../../node_module_bundle.zig").NodeModuleBundle; const logger = @import("../../logger.zig"); @@ -20,6 +20,7 @@ usingnamespace @import("./bindings/exports.zig"); usingnamespace @import("./bindings/bindings.zig"); const Runtime = @import("../../runtime.zig"); const Router = @import("./api/router.zig"); +const ImportRecord = ast.ImportRecord; pub const GlobalClasses = [_]type{ Request.Class, @@ -34,6 +35,85 @@ pub const GlobalClasses = [_]type{ pub const Wundle = struct { top_level_dir: string, + threadlocal var css_imports_list_strings: [512]ZigString = undefined; + threadlocal var css_imports_list: [512]Api.StringPointer = undefined; + threadlocal var css_imports_list_tail: u16 = 0; + threadlocal var css_imports_buf: std.ArrayList(u8) = undefined; + threadlocal var css_imports_buf_loaded: bool = false; + + pub fn flushCSSImports() void { + if (css_imports_buf_loaded) { + css_imports_buf.clearRetainingCapacity(); + css_imports_list_tail = 0; + } + } + + pub fn getCSSImports() []ZigString { + var i: u16 = 0; + const tail = css_imports_list_tail; + while (i < tail) : (i += 1) { + ZigString.fromStringPointer(css_imports_list[i], css_imports_buf.items, &css_imports_list_strings[i]); + } + return css_imports_list_strings[0..tail]; + } + + pub fn getImportedStyles( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + defer flushCSSImports(); + const styles = getCSSImports(); + if (styles.len == 0) { + return js.JSObjectMakeArray(ctx, 0, null, null); + } + + return JSValue.createStringArray(VirtualMachine.vm.global, styles.ptr, styles.len).asRef(); + } + + pub fn onImportCSS( + resolve_result: *const Resolver.Result, + import_record: *ImportRecord, + source_dir: string, + ) void { + if (!css_imports_buf_loaded) { + css_imports_buf = std.ArrayList(u8).initCapacity( + VirtualMachine.vm.allocator, + import_record.path.text.len, + ) catch unreachable; + css_imports_buf_loaded = true; + } + + var writer = css_imports_buf.writer(); + const offset = css_imports_buf.items.len; + css_imports_list[css_imports_list_tail] = .{ + .offset = @truncate(u32, offset), + .length = 0, + }; + getPublicPath(resolve_result.path_pair.primary.text, @TypeOf(writer), writer); + const length = css_imports_buf.items.len - offset; + css_imports_list[css_imports_list_tail].length = @truncate(u32, length); + css_imports_list_tail += 1; + } + + pub fn getPublicPath(to: string, comptime Writer: type, writer: Writer) void { + const relative_path = VirtualMachine.vm.bundler.fs.relativeTo(to); + if (VirtualMachine.vm.bundler.options.public_url.len > 0) { + writer.print( + "{s}/{s}", + .{ + std.mem.trimRight(u8, VirtualMachine.vm.bundler.options.public_url, "/"), + std.mem.trimLeft(u8, relative_path, "/"), + }, + ) catch unreachable; + } else { + writer.writeAll(std.mem.trimLeft(u8, relative_path, "/")) catch unreachable; + } + } + pub const Class = NewClass( void, .{ @@ -51,6 +131,13 @@ pub const Wundle = struct { .rfn = Router.match, .ts = Router.match_type_definition, }, + .getImportedStyles = .{ + .rfn = Wundle.getImportedStyles, + .ts = d.ts{ + .name = "getImportedStyles", + .@"return" = "string[]", + }, + }, }, .{ .Route = Router.Instance.GetClass(void){}, @@ -116,6 +203,10 @@ pub const VirtualMachine = struct { VirtualMachine.vm.bundler.configureLinker(); + if (_args.serve orelse false) { + VirtualMachine.vm.bundler.linker.onImportCSS = Wundle.onImportCSS; + } + var global_classes: [GlobalClasses.len]js.JSClassRef = undefined; inline for (GlobalClasses) |Class, i| { global_classes[i] = Class.get().*; @@ -267,7 +358,7 @@ pub const VirtualMachine = struct { } } pub const ResolveFunctionResult = struct { - result: ?resolver.Result, + result: ?Resolver.Result, path: string, }; @@ -282,7 +373,7 @@ pub const VirtualMachine = struct { return; } - const result: resolver.Result = vm.bundler.resolve_results.get(specifier) orelse brk: { + 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( @@ -862,6 +953,12 @@ pub const EventListenerMixin = struct { var res = promise.result(vm.global.vm()); if (res.isException(vm.global.vm())) { exception = @ptrCast(*Exception, res.asVoid()); + } else { + vm.defaultErrorHandler(res); + if (!request_context.has_called_done) { + request_context.sendInternalError(error.JavaScriptErrorNeedARealErrorPageSorryAboutThisSeeTheTerminal) catch {}; + } + return; } } } else { @@ -957,14 +1054,14 @@ pub const ResolveError = struct { pub fn fmt(allocator: *std.mem.Allocator, specifier: string, referrer: string, err: anyerror) !string { switch (err) { error.ModuleNotFound => { - if (resolver.isPackagePath(specifier)) { + if (Resolver.isPackagePath(specifier)) { return try std.fmt.allocPrint(allocator, "Cannot find package \"{s}\" from \"{s}\"", .{ specifier, referrer }); } else { return try std.fmt.allocPrint(allocator, "Cannot find module \"{s}\" from \"{s}\"", .{ specifier, referrer }); } }, else => { - if (resolver.isPackagePath(specifier)) { + if (Resolver.isPackagePath(specifier)) { return try std.fmt.allocPrint(allocator, "{s} while resolving package \"{s}\" from \"{s}\"", .{ @errorName(err), specifier, referrer }); } else { return try std.fmt.allocPrint(allocator, "{s} while resolving \"{s}\" from \"{s}\"", .{ @errorName(err), specifier, referrer }); @@ -1098,7 +1195,7 @@ pub const ResolveError = struct { pub const BuildError = struct { msg: logger.Msg, - // resolve_result: resolver.Result, + // resolve_result: Resolver.Result, allocator: *std.mem.Allocator, pub const Class = NewClass( @@ -1128,7 +1225,7 @@ pub const BuildError = struct { pub fn create( allocator: *std.mem.Allocator, msg: logger.Msg, - // resolve_result: *const resolver.Result, + // resolve_result: *const Resolver.Result, ) js.JSObjectRef { var build_error = allocator.create(BuildError) catch unreachable; build_error.* = BuildError{ diff --git a/src/linker.zig b/src/linker.zig index f92d0c1a3..0c68d53f1 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -30,6 +30,8 @@ const Runtime = @import("./runtime.zig").Runtime; pub const CSSResolveError = error{ResolveError}; +pub const OnImportCallback = fn (resolve_result: *const Resolver.Result, import_record: *ImportRecord, source_dir: string) void; + pub fn NewLinker(comptime BundlerType: type) type { return struct { const HashedFileNameMap = std.AutoHashMap(u64, string); @@ -46,6 +48,8 @@ pub fn NewLinker(comptime BundlerType: type) type { runtime_source_path: string, hashed_filenames: HashedFileNameMap, + onImportCSS: ?OnImportCallback = null, + pub fn init( allocator: *std.mem.Allocator, log: *logger.Log, @@ -583,6 +587,12 @@ pub fn NewLinker(comptime BundlerType: type) type { BundlerType.isCacheEnabled and loader == .file, import_path_format, ); + + if (loader == .css) { + if (linker.onImportCSS) |callback| { + callback(resolve_result, import_record, source_dir); + } + } } pub fn resolveResultHashKey(linker: *ThisLinker, resolve_result: *const Resolver.Result) string { diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 63acc11bf..a69b7685a 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -374,6 +374,8 @@ pub fn NewResolver(cache_files: bool) type { r.debug_logs = try DebugLogs.init(r.allocator); } + if (import_path.len == 0) return error.ModuleNotFound; + // Certain types of URLs default to being external for convenience if (r.isExternalPattern(import_path) or // "fill: url(#filter);" diff --git a/src/router.zig b/src/router.zig index ffba58e5e..5ba7c3457 100644 --- a/src/router.zig +++ b/src/router.zig @@ -465,19 +465,21 @@ pub const RouteMap = struct { redirect = false; } + const routes_slice = this.routes.slice(); + if (path.len == 0) { if (this.index) |index| { - const entry = Fs.FileSystem.DirEntry.EntryStore.instance.at(this.routes.items(.entry_index)[index]).?; + const entry = Fs.FileSystem.DirEntry.EntryStore.instance.at(routes_slice.items(.entry_index)[index]).?; const parts = [_]string{ entry.dir, entry.base }; return Match{ .params = params, - .name = "index", - .path = this.routes.items(.path)[index], - .file_path = Fs.FileSystem.instance.absBuf(&parts, file_path_buf), - .basename = entry.base, + .name = routes_slice.items(.name)[index], + .path = routes_slice.items(.path)[index], .pathname = url_path.pathname, + .basename = entry.base, .hash = index_route_hash, + .file_path = Fs.FileSystem.instance.absBuf(&parts, file_path_buf), .query_string = url_path.query_string, }; } @@ -486,7 +488,6 @@ pub const RouteMap = struct { } const full_hash = @truncate(u32, std.hash.Wyhash.hash(0, path)); - const routes_slice = this.routes.slice(); // Check for an exact match // These means there are no params. |
