@@ -204,7 +204,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
204204 import_attrs : ? * const v8.c.FixedArray ,
205205 ) callconv (.c ) ? * v8.c.Promise {
206206 _ = host_defined_options ;
207- _ = resource_name ;
208207 _ = import_attrs ;
209208 const ctx : v8.Context = .{ .handle = v8_ctx .? };
210209 const context : * JsContext = @ptrFromInt (ctx .getEmbedderData (1 ).castTo (v8 .BigInt ).getUint64 ());
@@ -217,11 +216,54 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
217216 _ = resolver .reject (ctx , error_msg .toValue ());
218217 return @constCast (resolver .getPromise ().handle );
219218 };
219+ const resource : v8.String = .{ .handle = resource_name .? };
220+ const resource_str = jsStringToZig (context .call_arena , resource , iso ) catch {
221+ const error_msg = v8 .String .initUtf8 (iso , "Failed to parse module resource" );
222+ _ = resolver .reject (ctx , error_msg .toValue ());
223+ return @constCast (resolver .getPromise ().handle );
224+ };
225+
226+ const referrer_full_url = blk : {
227+ // Search through module_identifier values to find matching resource
228+ var it = context .module_identifier .valueIterator ();
229+ while (it .next ()) | full_url | {
230+ // Extract just the filename from the full URL
231+ const last_slash = std .mem .lastIndexOfScalar (u8 , full_url .* , '/' ) orelse 0 ;
232+ const filename = full_url .* [last_slash + 1 .. ];
233+
234+ // Compare with our resource string (removing ./ prefix if present)
235+ const resource_clean = if (std .mem .startsWith (u8 , resource_str , "./" ))
236+ resource_str [2.. ]
237+ else
238+ resource_str ;
239+
240+ if (std .mem .eql (u8 , filename , resource_clean )) {
241+ break :blk full_url .* ;
242+ }
243+ }
220244
221- log .info (.js , "dynamic import" , .{ .specifier = specifier_str });
245+ // Fallback - maybe it's already a full URL in some cases?
246+ break :blk resource_str ;
247+ };
248+
249+ const normalized_specifier = @import ("../url.zig" ).stitch (
250+ context .context_arena ,
251+ specifier_str ,
252+ referrer_full_url ,
253+ .{ .alloc = .if_needed },
254+ ) catch unreachable ;
255+
256+ // TODO: we need to resolve the full URL here and normalize it.
257+ // That way we can pass the correct one in the module_loader.
258+
259+ log .info (.js , "dynamic import" , .{
260+ .specifier = specifier_str ,
261+ .resource = resource_str ,
262+ .normalized = normalized_specifier ,
263+ });
222264
223265 const module_loader = context .module_loader ;
224- const source = module_loader .func (module_loader .ptr , specifier_str ) catch {
266+ const source = module_loader .func (module_loader .ptr , normalized_specifier ) catch {
225267 const error_msg = v8 .String .initUtf8 (iso , "Failed to load module" );
226268 _ = resolver .reject (ctx , error_msg .toValue ());
227269 return @constCast (resolver .getPromise ().handle );
@@ -250,6 +292,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
250292 return @constCast (resolver .getPromise ().handle );
251293 };
252294
295+ context .module_identifier .putNoClobber (context .context_arena , module .getIdentityHash (), normalized_specifier ) catch unreachable ;
296+ context .module_cache .putNoClobber (context .context_arena , normalized_specifier , v8 .Persistent (v8 .Module ).init (iso , module )) catch unreachable ;
297+
253298 const instantiated = module .instantiate (ctx , JsContext .resolveModuleCallback ) catch {
254299 log .err (.js , "module instantiation failed" , .{
255300 .specifier = specifier_str ,
@@ -308,8 +353,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
308353 const cb_isolate = cb_info .getIsolate ();
309354 const cb_context = cb_isolate .getCurrentContext ();
310355 const data : * EvaluationData = @ptrCast (@alignCast (cb_info .getExternalValue ()));
311- const cb_module = v8.Module { . handle = data .module .handle } ;
312- const cb_resolver = v8.PromiseResolver { . handle = data .resolver .handle } ;
356+ const cb_module = data .module .castToModule () ;
357+ const cb_resolver = data .resolver .castToPromiseResolver () ;
313358
314359 const namespace = cb_module .getModuleNamespace ();
315360 log .warn (.js , "module then promise" , .{
@@ -325,7 +370,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
325370 const cb_info = v8.FunctionCallbackInfo { .handle = info .? };
326371 const cb_context = cb_info .getIsolate ().getCurrentContext ();
327372 const data : * EvaluationData = @ptrCast (@alignCast (cb_info .getExternalValue ()));
328- const cb_resolver = v8.PromiseResolver { . handle = data .resolver .handle } ;
373+ const cb_resolver = data .resolver .castToPromiseResolver () ;
329374 _ = cb_resolver .reject (cb_context , cb_info .getData ());
330375 }
331376 }.callback , external );
@@ -878,38 +923,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
878923 _ = try m .evaluate (v8_context );
879924 }
880925
881- pub fn module2 (self : * JsContext , src : []const u8 , url : []const u8 , cacheable : bool ) ! v8.Module {
882- if (! cacheable ) {
883- return self .moduleNoCache2 (src , url );
884- }
885-
886- const arena = self .context_arena ;
887-
888- const gop = try self .module_cache .getOrPut (arena , url );
889- if (gop .found_existing ) {
890- return v8.Module { .handle = gop .value_ptr .handle };
891- }
892- errdefer _ = self .module_cache .remove (url );
893-
894- const m = try compileModule (self .isolate , src , url );
895-
896- const owned_url = try arena .dupe (u8 , url );
897- try self .module_identifier .putNoClobber (arena , m .getIdentityHash (), owned_url );
898- errdefer _ = self .module_identifier .remove (m .getIdentityHash ());
899-
900- gop .key_ptr .* = owned_url ;
901- gop .value_ptr .* = PersistentModule .init (self .isolate , m );
902-
903- // resolveModuleCallback loads module's dependencies.
904- const v8_context = self .v8_context ;
905- if (try m .instantiate (v8_context , resolveModuleCallback ) == false ) {
906- return error .ModuleInstantiationError ;
907- }
908-
909- _ = try m .evaluate (v8_context );
910- return m ;
911- }
912-
913926 fn moduleNoCache (self : * JsContext , src : []const u8 , url : []const u8 ) ! void {
914927 const m = try compileModule (self .isolate , src , url );
915928 const v8_context = self .v8_context ;
@@ -922,19 +935,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
922935 _ = try m .evaluate (v8_context );
923936 }
924937
925- fn moduleNoCache2 (self : * JsContext , src : []const u8 , url : []const u8 ) ! v8.Module {
926- const m = try compileModule (self .isolate , src , url );
927- const v8_context = self .v8_context ;
928- if (try m .instantiate (v8_context , resolveModuleCallback ) == false ) {
929- return error .ModuleInstantiationError ;
930- }
931- const arena = self .context_arena ;
932- const owned_url = try arena .dupe (u8 , url );
933- try self .module_identifier .putNoClobber (arena , m .getIdentityHash (), owned_url );
934- _ = try m .evaluate (v8_context );
935- return m ;
936- }
937-
938938 // Wrap a v8.Exception
939939 fn createException (self : * const JsContext , e : v8.Value ) Exception {
940940 return .{
@@ -1554,6 +1554,50 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15541554 try self .module_identifier .putNoClobber (arena , m .getIdentityHash (), owned_specifier );
15551555 return m .handle ;
15561556 }
1557+
1558+ fn _resolveModuleCallback2 (
1559+ self : * JsContext ,
1560+ base_url : []const u8 ,
1561+ specifier : []const u8 ,
1562+ ) ! ? * const v8.C_Module {
1563+ const normalized_specifier = try @import ("../url.zig" ).stitch (
1564+ self .call_arena ,
1565+ specifier ,
1566+ base_url ,
1567+ .{ .alloc = .if_needed },
1568+ );
1569+
1570+ if (self .module_cache .get (normalized_specifier )) | pm | {
1571+ return pm .handle ;
1572+ }
1573+
1574+ const module_loader = self .module_loader ;
1575+ const source = try module_loader .func (module_loader .ptr , normalized_specifier ) orelse return null ;
1576+
1577+ var try_catch : TryCatch = undefined ;
1578+ try_catch .init (self );
1579+ defer try_catch .deinit ();
1580+
1581+ const m = compileModule (self .isolate , source , specifier ) catch | err | {
1582+ log .warn (.js , "compile resolved module" , .{
1583+ .specifier = specifier ,
1584+ .stack = try_catch .stack (self .call_arena ) catch null ,
1585+ .src = try_catch .sourceLine (self .call_arena ) catch "err" ,
1586+ .line = try_catch .sourceLineNumber () orelse 0 ,
1587+ .exception = (try_catch .exception (self .call_arena ) catch @errorName (err )) orelse @errorName (err ),
1588+ });
1589+ return null ;
1590+ };
1591+
1592+ // We were hoping to find the module in our cache, and thus used
1593+ // the short-lived call_arena to create the normalized_specifier.
1594+ // But now this'll live for the lifetime of the context.
1595+ const arena = self .context_arena ;
1596+ const owned_specifier = try arena .dupe (u8 , normalized_specifier );
1597+ try self .module_cache .put (arena , owned_specifier , PersistentModule .init (self .isolate , m ));
1598+ try self .module_identifier .putNoClobber (arena , m .getIdentityHash (), owned_specifier );
1599+ return m .handle ;
1600+ }
15571601 };
15581602
15591603 pub const Function = struct {
0 commit comments