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 port.onmessage = function(event) {
69 // Handle messages from the main thread
70 self.dispatchEvent(new MessageEvent('message', event));
71 };
72 port.start();
73};
74
75"#,
76 );
77
78 code.push_str(&self.generate_classic_polyfills());
80
81 code.push_str(&format!("importScripts('{}');\n", entry_point));
83
84 code
85 }
86
87 fn generate_polyfills(&self) -> String {
89 r#"
90// Polyfills for worker environment
91(function() {
92 // Ensure globalThis is available
93 if (typeof globalThis === 'undefined') {
94 self.globalThis = self;
95 }
96
97 // Ensure MessageChannel is available
98 if (typeof MessageChannel === 'undefined') {
99 self.MessageChannel = class MessageChannel {
100 constructor() {
101 this.port1 = new MessagePort();
102 this.port2 = new MessagePort();
103 }
104 };
105 }
106
107 // Ensure MessagePort is available
108 if (typeof MessagePort === 'undefined') {
109 self.MessagePort = class MessagePort {
110 constructor() {
111 this.onmessage = null;
112 this.onmessageerror = null;
113 }
114 postMessage(data) {}
115 start() {}
116 close() {}
117 };
118 }
119})();
120
121"#
122 .to_string()
123 }
124
125 fn generate_classic_polyfills(&self) -> String {
127 r#"
128// Classic worker polyfills
129(function() {
130 // Minimal polyfills for classic workers
131 if (typeof globalThis === 'undefined') {
132 self.globalThis = self;
133 }
134})();
135
136"#
137 .to_string()
138 }
139
140 pub fn generate_worker_loader(&self, worker_name:&str, module_url:&str) -> String {
142 format!(
143 r#"
144(function() {{
145 const workerCode = `
146 {loader_code}
147 `;
148
149 const blob = new Blob([workerCode], {{ type: 'application/javascript' }});
150 const url = URL.createObjectURL(blob);
151
152 self["{worker_name}"] = new Worker(url, {{ type: 'module' }});
153
154 // Clean up blob URL after worker is created
155 URL.revokeObjectURL(url);
156}})();
157"#,
158 loader_code = self
159 .generate_module_worker(module_url)
160 .replace("`", "\\`")
161 .replace("${", "\\${")
162 )
163 }
164}
165
166pub fn generate_inline_worker(code:&str, worker_type:WorkerType) -> String {
168 match worker_type {
169 WorkerType::Module => {
170 format!(
171 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})), {{ type: \
172 'module' }})",
173 code.replace("`", "\\`").replace("${", "\\${")
174 )
175 },
176
177 WorkerType::Classic => {
178 format!(
179 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})))",
180 code.replace("`", "\\`").replace("${", "\\${")
181 )
182 },
183 }
184}
185
186pub fn generate_worker_declaration(worker_name:&str) -> String {
188 format!(
189 r#"declare const {worker_name}: Worker;
190export {{ {worker_name} }};
191"#
192 )
193}
194
195#[cfg(test)]
196mod tests {
197
198 use super::*;
199
200 #[test]
201 fn test_module_worker_bootstrap() {
202 let config = WorkerConfig::new();
203
204 let bootstrap = WorkerBootstrap::new(config);
205
206 let code = bootstrap.generate_module_worker("./entry.js");
207
208 assert!(code.contains("Module worker bootstrap"));
209
210 assert!(code.contains("./entry.js"));
211 }
212
213 #[test]
214 fn test_classic_worker_bootstrap() {
215 let config = WorkerConfig::new();
216
217 let bootstrap = WorkerBootstrap::new(config);
218
219 let code = bootstrap.generate_classic_worker("./entry.js");
220
221 assert!(code.contains("Classic worker bootstrap"));
222
223 assert!(code.contains("./entry.js"));
224 }
225
226 #[test]
227 fn test_shared_worker_bootstrap() {
228 let config = WorkerConfig::new();
229
230 let bootstrap = WorkerBootstrap::new(config);
231
232 let code = bootstrap.generate_shared_worker("./entry.js");
233
234 assert!(code.contains("Shared worker bootstrap"));
235
236 assert!(code.contains("onconnect"));
237 }
238}