@@ -22,6 +22,7 @@ pub const WebWorker = struct {
2222
2323 /// Already resolved.
2424 specifier : []const u8 = "" ,
25+ preloads : [][]const u8 = &.{},
2526 store_fd : bool = false ,
2627 arena : ? bun.MimallocArena = null ,
2728 name : [:0 ]const u8 = "Worker" ,
@@ -76,6 +77,94 @@ pub const WebWorker = struct {
7677 return true ;
7778 }
7879
80+ fn resolveEntryPointSpecifier (
81+ parent : * JSC.VirtualMachine ,
82+ str : []const u8 ,
83+ error_message : * bun.String ,
84+ logger : * bun.logger.Log ,
85+ ) ? []const u8 {
86+ if (parent .standalone_module_graph ) | graph | {
87+ if (graph .find (str ) != null ) {
88+ return str ;
89+ }
90+
91+ // Since `bun build --compile` renames files to `.js` by
92+ // default, we need to do the reverse of our file extension
93+ // mapping.
94+ //
95+ // new Worker("./foo") -> new Worker("./foo.js")
96+ // new Worker("./foo.ts") -> new Worker("./foo.js")
97+ // new Worker("./foo.jsx") -> new Worker("./foo.js")
98+ // new Worker("./foo.mjs") -> new Worker("./foo.js")
99+ // new Worker("./foo.mts") -> new Worker("./foo.js")
100+ // new Worker("./foo.cjs") -> new Worker("./foo.js")
101+ // new Worker("./foo.cts") -> new Worker("./foo.js")
102+ // new Worker("./foo.tsx") -> new Worker("./foo.js")
103+ //
104+ if (bun .strings .hasPrefixComptime (str , "./" ) or bun .strings .hasPrefixComptime (str , "../" )) try_from_extension : {
105+ var pathbuf : bun.PathBuffer = undefined ;
106+ var base = str ;
107+
108+ base = bun .path .joinAbsStringBuf (bun .StandaloneModuleGraph .base_public_path_with_default_suffix , & pathbuf , &.{str }, .loose );
109+ const extname = std .fs .path .extension (base );
110+
111+ // ./foo -> ./foo.js
112+ if (extname .len == 0 ) {
113+ pathbuf [base .len .. ][0.. 3].* = ".js" .* ;
114+ if (graph .find (pathbuf [0 .. base .len + 3 ])) | js_file | {
115+ return js_file .name ;
116+ }
117+
118+ break :try_from_extension ;
119+ }
120+
121+ // ./foo.ts -> ./foo.js
122+ if (bun .strings .eqlComptime (extname , ".ts" )) {
123+ pathbuf [base .len - 3 .. base .len ][0.. 3].* = ".js" .* ;
124+ if (graph .find (pathbuf [0.. base .len ])) | js_file | {
125+ return js_file .name ;
126+ }
127+
128+ break :try_from_extension ;
129+ }
130+
131+ if (extname .len == 4 ) {
132+ inline for (.{ ".tsx" , ".jsx" , ".mjs" , ".mts" , ".cts" , ".cjs" }) | ext | {
133+ if (bun .strings .eqlComptime (extname , ext )) {
134+ pathbuf [base .len - ext .len .. ][0.. ".js" .len ].* = ".js" .* ;
135+ const as_js = pathbuf [0 .. base .len - ext .len + ".js" .len ];
136+ if (graph .find (as_js )) | js_file | {
137+ return js_file .name ;
138+ }
139+ break :try_from_extension ;
140+ }
141+ }
142+ }
143+ }
144+ }
145+
146+ if (JSC .WebCore .ObjectURLRegistry .isBlobURL (str )) {
147+ if (JSC .WebCore .ObjectURLRegistry .singleton ().has (str ["blob:" .len .. ])) {
148+ return str ;
149+ } else {
150+ error_message .* = bun .String .static ("Blob URL is missing" );
151+ return null ;
152+ }
153+ }
154+
155+ var resolved_entry_point : bun.resolver.Result = parent .bundler .resolveEntryPoint (str ) catch {
156+ const out = logger .toJS (parent .global , bun .default_allocator , "Error resolving Worker entry point" ).toBunString (parent .global );
157+ error_message .* = out ;
158+ return null ;
159+ };
160+
161+ const entry_path : * bun.fs.Path = resolved_entry_point .path () orelse {
162+ error_message .* = bun .String .static ("Worker entry point is missing" );
163+ return null ;
164+ };
165+ return entry_path .text ;
166+ }
167+
79168 pub fn create (
80169 cpp_worker : * void ,
81170 parent : * JSC.VirtualMachine ,
@@ -90,6 +179,8 @@ pub const WebWorker = struct {
90179 argv_len : u32 ,
91180 execArgv_ptr : ? [* ]WTFStringImpl ,
92181 execArgv_len : u32 ,
182+ preload_modules_ptr : ? [* ]bun.String ,
183+ preload_modules_len : u32 ,
93184 ) callconv (.C ) ? * WebWorker {
94185 JSC .markBinding (@src ());
95186 log ("[{d}] WebWorker.create" , .{this_context_id });
@@ -101,89 +192,31 @@ pub const WebWorker = struct {
101192 defer parent .bundler .setLog (prev_log );
102193 defer temp_log .deinit ();
103194
104- const path = brk : {
105- const str = spec_slice .slice ();
106- if (parent .standalone_module_graph ) | graph | {
107- if (graph .find (str ) != null ) {
108- break :brk str ;
109- }
195+ const preload_modules = if (preload_modules_ptr ) | ptr |
196+ ptr [0.. preload_modules_len ]
197+ else
198+ &.{};
110199
111- // Since `bun build --compile` renames files to `.js` by
112- // default, we need to do the reverse of our file extension
113- // mapping.
114- //
115- // new Worker("./foo") -> new Worker("./foo.js")
116- // new Worker("./foo.ts") -> new Worker("./foo.js")
117- // new Worker("./foo.jsx") -> new Worker("./foo.js")
118- // new Worker("./foo.mjs") -> new Worker("./foo.js")
119- // new Worker("./foo.mts") -> new Worker("./foo.js")
120- // new Worker("./foo.cjs") -> new Worker("./foo.js")
121- // new Worker("./foo.cts") -> new Worker("./foo.js")
122- // new Worker("./foo.tsx") -> new Worker("./foo.js")
123- //
124- if (bun .strings .hasPrefixComptime (str , "./" ) or bun .strings .hasPrefixComptime (str , "../" )) try_from_extension : {
125- var pathbuf : bun.PathBuffer = undefined ;
126- var base = str ;
127-
128- base = bun .path .joinAbsStringBuf (bun .StandaloneModuleGraph .base_public_path_with_default_suffix , & pathbuf , &.{str }, .loose );
129- const extname = std .fs .path .extension (base );
130-
131- // ./foo -> ./foo.js
132- if (extname .len == 0 ) {
133- pathbuf [base .len .. ][0.. 3].* = ".js" .* ;
134- if (graph .find (pathbuf [0 .. base .len + 3 ])) | js_file | {
135- break :brk js_file .name ;
136- }
137-
138- break :try_from_extension ;
139- }
140-
141- // ./foo.ts -> ./foo.js
142- if (bun .strings .eqlComptime (extname , ".ts" )) {
143- pathbuf [base .len - 3 .. base .len ][0.. 3].* = ".js" .* ;
144- if (graph .find (pathbuf [0.. base .len ])) | js_file | {
145- break :brk js_file .name ;
146- }
147-
148- break :try_from_extension ;
149- }
200+ const path = resolveEntryPointSpecifier (parent , spec_slice .slice (), error_message , & temp_log ) orelse {
201+ return null ;
202+ };
150203
151- if (extname .len == 4 ) {
152- inline for (.{ ".tsx" , ".jsx" , ".mjs" , ".mts" , ".cts" , ".cjs" }) | ext | {
153- if (bun .strings .eqlComptime (extname , ext )) {
154- pathbuf [base .len - ext .len .. ][0.. ".js" .len ].* = ".js" .* ;
155- const as_js = pathbuf [0 .. base .len - ext .len + ".js" .len ];
156- if (graph .find (as_js )) | js_file | {
157- break :brk js_file .name ;
158- }
159- break :try_from_extension ;
160- }
161- }
162- }
163- }
204+ var preloads = std .ArrayList ([]const u8 ).initCapacity (bun .default_allocator , preload_modules_len ) catch bun .outOfMemory ();
205+ for (preload_modules ) | module | {
206+ const utf8_slice = module .toUTF8 (bun .default_allocator );
207+ defer utf8_slice .deinit ();
208+ if (resolveEntryPointSpecifier (parent , utf8_slice .slice (), error_message , & temp_log )) | preload | {
209+ preloads .append (bun .default_allocator .dupe (u8 , preload ) catch bun .outOfMemory ()) catch bun .outOfMemory ();
164210 }
165211
166- if (JSC .WebCore .ObjectURLRegistry .isBlobURL (str )) {
167- if (JSC .WebCore .ObjectURLRegistry .singleton ().has (str ["blob:" .len .. ])) {
168- break :brk str ;
169- } else {
170- error_message .* = bun .String .static ("Blob URL is missing" );
171- return null ;
212+ if (! error_message .isEmpty ()) {
213+ for (preloads .items ) | preload | {
214+ bun .default_allocator .free (preload );
172215 }
173- }
174-
175- var resolved_entry_point : bun.resolver.Result = parent .bundler .resolveEntryPoint (str ) catch {
176- const out = temp_log .toJS (parent .global , bun .default_allocator , "Error resolving Worker entry point" ).toBunString (parent .global );
177- error_message .* = out ;
178- return null ;
179- };
180-
181- const entry_path : * bun.fs.Path = resolved_entry_point .path () orelse {
182- error_message .* = bun .String .static ("Worker entry point is missing" );
216+ preloads .deinit ();
183217 return null ;
184- };
185- break :brk entry_path .text ;
186- };
218+ }
219+ }
187220
188221 var worker = bun .default_allocator .create (WebWorker ) catch bun .outOfMemory ();
189222 worker .* = WebWorker {
@@ -204,6 +237,7 @@ pub const WebWorker = struct {
204237 .worker_event_loop_running = true ,
205238 .argv = if (argv_ptr ) | ptr | ptr [0.. argv_len ] else null ,
206239 .execArgv = if (execArgv_ptr ) | ptr | ptr [0.. execArgv_len ] else null ,
240+ .preloads = preloads .items ,
207241 };
208242
209243 worker .parent_poll_ref .ref (parent );
@@ -282,6 +316,10 @@ pub const WebWorker = struct {
282316 log ("[{d}] deinit" , .{this .execution_context_id });
283317 this .parent_poll_ref .unrefConcurrently (this .parent );
284318 bun .default_allocator .free (this .specifier );
319+ for (this .preloads ) | preload | {
320+ bun .default_allocator .free (preload );
321+ }
322+ bun .default_allocator .free (this .preloads );
285323 bun .default_allocator .destroy (this );
286324 }
287325
@@ -355,7 +393,7 @@ pub const WebWorker = struct {
355393 var vm = this .vm .? ;
356394 assert (this .status .load (.acquire ) == .start );
357395 this .setStatus (.starting );
358-
396+ vm . preload = this . preloads ;
359397 var promise = vm .loadEntryPointForWebWorker (this .specifier ) catch {
360398 this .flushLogs ();
361399 this .exitAndDeinit ();
0 commit comments