Library/Fn/Worker/
Bootstrap.rs1use super::{WorkerConfig, WorkerType};
6
7pub struct WorkerBootstrap {
9 config:WorkerConfig,
10}
11
12impl WorkerBootstrap {
13 pub fn new(config:WorkerConfig) -> Self { Self { config } }
14
15 pub fn generate_module_worker(&self, entry_point:&str) -> String {
17 let mut code = String::new();
18
19 code.push_str("// Module worker bootstrap\n");
21
22 code.push_str(&self.generate_polyfills());
24
25 for script in &self.config.bootstrap_scripts {
27 code.push_str(&format!("import '{}';\n", script));
28 }
29
30 code.push_str(&format!("import '{}';\n", entry_point));
32
33 code
34 }
35
36 pub fn generate_classic_worker(&self, entry_point:&str) -> String {
38 let mut code = String::new();
39
40 code.push_str("// Classic worker bootstrap\n");
42
43 code.push_str(&self.generate_classic_polyfills());
45
46 for script in &self.config.bootstrap_scripts {
48 code.push_str(&format!("importScripts('{}');\n", script));
49 }
50
51 code.push_str(&format!("importScripts('{}');\n", entry_point));
53
54 code
55 }
56
57 pub fn generate_shared_worker(&self, entry_point:&str) -> String {
59 let mut code = String::new();
60
61 code.push_str("// Shared worker bootstrap\n");
62
63 code.push_str(
65 r#"
66self.onconnect = function(event) {
67 const port = event.ports[0];
68
69 port.onmessage = function(event) {
70 // Handle messages from the main thread
71 self.dispatchEvent(new MessageEvent('message', event));
72 };
73
74 port.start();
75};
76
77"#,
78 );
79
80 code.push_str(&self.generate_classic_polyfills());
82
83 code.push_str(&format!("importScripts('{}');\n", entry_point));
85
86 code
87 }
88
89 fn generate_polyfills(&self) -> String {
91 r#"
92// Polyfills for worker environment
93(function() {
94 // Ensure globalThis is available
95 if (typeof globalThis === 'undefined') {
96 self.globalThis = self;
97 }
98
99 // Ensure MessageChannel is available
100 if (typeof MessageChannel === 'undefined') {
101 self.MessageChannel = class MessageChannel {
102 constructor() {
103 this.port1 = new MessagePort();
104
105 this.port2 = new MessagePort();
106 }
107 };
108 }
109
110 // Ensure MessagePort is available
111 if (typeof MessagePort === 'undefined') {
112 self.MessagePort = class MessagePort {
113 constructor() {
114 this.onmessage = null;
115
116 this.onmessageerror = null;
117 }
118
119 postMessage(data) {}
120
121 start() {}
122
123 close() {}
124 };
125 }
126})();
127
128"#
129 .to_string()
130 }
131
132 fn generate_classic_polyfills(&self) -> String {
134 r#"
135// Classic worker polyfills
136(function() {
137 // Minimal polyfills for classic workers
138 if (typeof globalThis === 'undefined') {
139 self.globalThis = self;
140 }
141})();
142
143"#
144 .to_string()
145 }
146
147 pub fn generate_worker_loader(&self, worker_name:&str, module_url:&str) -> String {
149 format!(
150 r#"
151(function() {{
152 const workerCode = `
153 {loader_code}
154
155 `;
156
157 const blob = new Blob([workerCode], {{ type: 'application/javascript' }});
158
159 const url = URL.createObjectURL(blob);
160
161 self["{worker_name}"] = new Worker(url, {{ type: 'module' }});
162
163 // Clean up blob URL after worker is created
164 URL.revokeObjectURL(url);
165}})();
166"#,
167 loader_code = self
168 .generate_module_worker(module_url)
169 .replace("`", "\\`")
170 .replace("${", "\\${")
171 )
172 }
173}
174
175pub fn generate_inline_worker(code:&str, worker_type:WorkerType) -> String {
177 match worker_type {
178 WorkerType::Module => {
179 format!(
180 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})), {{ type: \
181 'module' }})",
182 code.replace("`", "\\`").replace("${", "\\${")
183 )
184 },
185
186 WorkerType::Classic => {
187 format!(
188 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})))",
189 code.replace("`", "\\`").replace("${", "\\${")
190 )
191 },
192 }
193}
194
195pub fn generate_worker_declaration(worker_name:&str) -> String {
197 format!(
198 r#"declare const {worker_name}: Worker;
199export {{ {worker_name} }};
200"#
201 )
202}
203
204#[cfg(test)]
205mod tests {
206
207 use super::*;
208
209 #[test]
210 fn test_module_worker_bootstrap() {
211 let config = WorkerConfig::new();
212
213 let bootstrap = WorkerBootstrap::new(config);
214
215 let code = bootstrap.generate_module_worker("./entry.js");
216
217 assert!(code.contains("Module worker bootstrap"));
218
219 assert!(code.contains("./entry.js"));
220 }
221
222 #[test]
223 fn test_classic_worker_bootstrap() {
224 let config = WorkerConfig::new();
225
226 let bootstrap = WorkerBootstrap::new(config);
227
228 let code = bootstrap.generate_classic_worker("./entry.js");
229
230 assert!(code.contains("Classic worker bootstrap"));
231
232 assert!(code.contains("./entry.js"));
233 }
234
235 #[test]
236 fn test_shared_worker_bootstrap() {
237 let config = WorkerConfig::new();
238
239 let bootstrap = WorkerBootstrap::new(config);
240
241 let code = bootstrap.generate_shared_worker("./entry.js");
242
243 assert!(code.contains("Shared worker bootstrap"));
244
245 assert!(code.contains("onconnect"));
246 }
247}