1pub mod ConfigLoader;
12
13pub mod ScriptRunner;
14
15pub mod EnvironmentResolver;
16
17use rhai::Engine;
18
19pub fn create_engine() -> Engine {
26 let mut engine = Engine::new();
27
28 engine.set_max_expr_depths(0, 0);
30
31 engine.set_max_operations(0);
32
33 engine.set_allow_shadowing(true);
34
35 register_utility_functions(&mut engine);
37
38 engine
39}
40
41fn register_utility_functions(engine:&mut Engine) {
43 engine.register_fn("get_os_type", || std::env::consts::OS.to_string());
45
46 engine.register_fn("get_arch", || std::env::consts::ARCH.to_string());
47
48 engine.register_fn("get_family", || std::env::consts::FAMILY.to_string());
49
50 engine.register_fn("get_env", |name:&str| -> String { std::env::var(name).unwrap_or_default() });
52
53 engine.register_fn("path_exists", |path:&str| -> bool { std::path::Path::new(path).exists() });
55
56 engine.register_fn("timestamp", || -> i64 {
58 std::time::SystemTime::now()
59 .duration_since(std::time::UNIX_EPOCH)
60 .unwrap_or_default()
61 .as_secs() as i64
62 });
63
64 engine.register_fn("print", |s:&str| {
66 println!("[Rhai] {}", s);
67 });
68}
69
70#[cfg(test)]
75mod tests {
76
77 use std::collections::HashMap;
78
79 use super::*;
80
81 fn get_expected_env_vars(profile_name:&str) -> Vec<(&'static str, &'static str)> {
83 match profile_name {
84 "debug" => {
85 vec![
86 ("Debug", "true"),
87 ("Browser", "true"),
88 ("Bundle", "true"),
89 ("Clean", "true"),
90 ("Compile", "false"),
91 ("NODE_ENV", "development"),
92 ("NODE_VERSION", "22"),
93 ("NODE_OPTIONS", "--max-old-space-size=16384"),
94 ("RUST_LOG", "debug"),
95 ("AIR_LOG_JSON", "false"),
96 ("AIR_LOG_FILE", ""),
97 ("Dependency", "Microsoft/VSCode"),
98 ]
99 },
100
101 "production" => {
102 vec![
103 ("Debug", "false"),
104 ("Browser", "false"),
105 ("Bundle", "true"),
106 ("Clean", "true"),
107 ("Compile", "true"),
108 ("NODE_ENV", "production"),
109 ("NODE_VERSION", "22"),
110 ("NODE_OPTIONS", "--max-old-space-size=8192"),
111 ("RUST_LOG", "info"),
112 ("AIR_LOG_JSON", "false"),
113 ("Dependency", "Microsoft/VSCode"),
114 ]
115 },
116
117 "release" => {
118 vec![
119 ("Debug", "false"),
120 ("Browser", "false"),
121 ("Bundle", "true"),
122 ("Clean", "true"),
123 ("Compile", "true"),
124 ("NODE_ENV", "production"),
125 ("NODE_VERSION", "22"),
126 ("NODE_OPTIONS", "--max-old-space-size=8192"),
127 ("RUST_LOG", "warn"),
128 ("AIR_LOG_JSON", "false"),
129 ("Dependency", "Microsoft/VSCode"),
130 ]
131 },
132
133 _ => vec![],
134 }
135 }
136
137 #[test]
138 fn test_config_loader_load() {
139 let result = ConfigLoader::load(".");
140
141 assert!(
142 result.is_ok(),
143 "ConfigLoader::load() should succeed but got error: {:?}",
144 result.err()
145 );
146
147 let config = result.unwrap();
148
149 assert_eq!(config.version, "1.0.0", "Configuration version should be 1.0.0");
150
151 assert!(!config.profiles.is_empty(), "Configuration should have at least one profile");
152
153 assert!(config.profiles.contains_key("debug"), "Debug profile should exist");
154
155 assert!(config.profiles.contains_key("production"), "Production profile should exist");
156
157 assert!(config.profiles.contains_key("release"), "Release profile should exist");
158
159 assert!(config.templates.is_some(), "Configuration should have templates defined");
160 }
161
162 #[test]
163 fn test_config_loader_get_profile_debug() {
164 let config = ConfigLoader::load(".").expect("Failed to load configuration");
165
166 let profile = ConfigLoader::get_profile(&config, "debug");
167
168 assert!(profile.is_some(), "Debug profile should exist in configuration");
169
170 let debug_profile = profile.unwrap();
171
172 assert!(debug_profile.description.is_some(), "Debug profile should have a description");
173
174 assert!(
175 debug_profile.env.is_some(),
176 "Debug profile should have environment variables defined"
177 );
178
179 assert!(
180 debug_profile.rhai_script.is_some(),
181 "Debug profile should have a Rhai script defined"
182 );
183
184 let debug_env = debug_profile.env.as_ref().unwrap();
185
186 assert_eq!(debug_env.get("Debug"), Some(&"true".to_string()));
187
188 assert_eq!(debug_env.get("NODE_ENV"), Some(&"development".to_string()));
189
190 assert_eq!(debug_env.get("RUST_LOG"), Some(&"debug".to_string()));
191 }
192
193 #[test]
194 fn test_resolve_profile_env_debug() {
195 let config = ConfigLoader::load(".").expect("Failed to load configuration");
196
197 let env_vars = ConfigLoader::resolve_profile_env(&config, "debug");
198
199 assert_eq!(env_vars.get("Debug"), Some(&"true".to_string()));
200
201 assert_eq!(env_vars.get("NODE_ENV"), Some(&"development".to_string()));
202
203 assert!(env_vars.contains_key("MOUNTAIN_DIR"), "Template variable should be present");
204 }
205
206 #[test]
207 fn test_execute_profile_script_debug() {
208 let config = ConfigLoader::load(".").expect("Failed to load configuration");
209
210 let profile = ConfigLoader::get_profile(&config, "debug").expect("Profile 'debug' not found");
211
212 let script_path = profile.rhai_script.as_ref().expect("No Rhai script defined for debug profile");
213
214 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
215
216 if !full_script_path.exists() {
217 panic!("Script file not found: {}", full_script_path.display());
218 }
219
220 let engine = create_engine();
221
222 let context = ScriptRunner::ScriptContext {
223 profile_name:"debug".to_string(),
224
225 cwd:".".to_string(),
226
227 manifest_dir:".".to_string(),
228
229 target_triple:None,
230
231 workbench_type:None,
232
233 features:HashMap::new(),
234 };
235
236 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
237
238 assert!(
239 result.is_ok(),
240 "Script execution should succeed but got error: {:?}",
241 result.err()
242 );
243
244 let script_result = result.unwrap();
245
246 assert!(script_result.success, "Script execution should report success");
247
248 assert!(script_result.error.is_none(), "Script execution should not have errors");
249
250 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
251
252 let expected = get_expected_env_vars("debug");
253
254 for (key, expected_val) in expected {
255 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
256
257 assert_eq!(
258 actual_val,
259 Some(expected_val),
260 "Env var '{}' should be '{}', got {:?}",
261 key,
262 expected_val,
263 actual_val
264 );
265 }
266 }
267
268 #[test]
269 fn test_execute_profile_script_production() {
270 let config = ConfigLoader::load(".").expect("Failed to load configuration");
271
272 let profile = ConfigLoader::get_profile(&config, "production").expect("Profile 'production' not found");
273
274 let script_path = profile
275 .rhai_script
276 .as_ref()
277 .expect("No Rhai script defined for production profile");
278
279 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
280
281 if !full_script_path.exists() {
282 panic!("Script file not found: {}", full_script_path.display());
283 }
284
285 let engine = create_engine();
286
287 let context = ScriptRunner::ScriptContext {
288 profile_name:"production".to_string(),
289
290 cwd:".".to_string(),
291
292 manifest_dir:".".to_string(),
293
294 target_triple:None,
295
296 workbench_type:None,
297
298 features:HashMap::new(),
299 };
300
301 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
302
303 assert!(
304 result.is_ok(),
305 "Script execution should succeed but got error: {:?}",
306 result.err()
307 );
308
309 let script_result = result.unwrap();
310
311 assert!(script_result.success, "Script execution should report success");
312
313 assert!(script_result.error.is_none(), "Script execution should not have errors");
314
315 let expected = get_expected_env_vars("production");
316
317 for (key, expected_val) in expected {
318 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
319
320 assert_eq!(
321 actual_val,
322 Some(expected_val),
323 "Env var '{}' should be '{}', got {:?}",
324 key,
325 expected_val,
326 actual_val
327 );
328 }
329 }
330
331 #[test]
332 fn test_execute_profile_script_release() {
333 let config = ConfigLoader::load(".").expect("Failed to load configuration");
334
335 let profile = ConfigLoader::get_profile(&config, "release").expect("Profile 'release' not found");
336
337 let script_path = profile
338 .rhai_script
339 .as_ref()
340 .expect("No Rhai script defined for release profile");
341
342 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
343
344 if !full_script_path.exists() {
345 panic!("Script file not found: {}", full_script_path.display());
346 }
347
348 let engine = create_engine();
349
350 let context = ScriptRunner::ScriptContext {
351 profile_name:"release".to_string(),
352
353 cwd:".".to_string(),
354
355 manifest_dir:".".to_string(),
356
357 target_triple:None,
358
359 workbench_type:None,
360
361 features:HashMap::new(),
362 };
363
364 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
365
366 assert!(
367 result.is_ok(),
368 "Script execution should succeed but got error: {:?}",
369 result.err()
370 );
371
372 let script_result = result.unwrap();
373
374 assert!(script_result.success, "Script execution should report success");
375
376 assert!(script_result.error.is_none(), "Script execution should not have errors");
377
378 let expected = get_expected_env_vars("release");
379
380 for (key, expected_val) in expected {
381 let actual_val = script_result.env_vars.get(key).map(|s| s.as_str());
382
383 assert_eq!(
384 actual_val,
385 Some(expected_val),
386 "Env var '{}' should be '{}', got {:?}",
387 key,
388 expected_val,
389 actual_val
390 );
391 }
392 }
393
394 #[test]
395 fn test_execute_profile_script_bundler_preparation() {
396 let config = ConfigLoader::load(".").expect("Failed to load configuration");
397
398 let profile = ConfigLoader::get_profile(&config, "bundler-preparation")
399 .expect("Profile 'bundler-preparation' not found in configuration");
400
401 let script_path = profile
402 .rhai_script
403 .as_ref()
404 .expect("No Rhai script defined for bundler-preparation profile");
405
406 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
407
408 if !full_script_path.exists() {
409 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
410
411 return;
412 }
413
414 let engine = create_engine();
415
416 let context = ScriptRunner::ScriptContext {
417 profile_name:"bundler-preparation".to_string(),
418
419 cwd:".".to_string(),
420
421 manifest_dir:".".to_string(),
422
423 target_triple:None,
424
425 workbench_type:None,
426
427 features:HashMap::new(),
428 };
429
430 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
431
432 assert!(
433 result.is_ok(),
434 "Script execution should succeed but got error: {:?}",
435 result.err()
436 );
437
438 let script_result = result.unwrap();
439
440 assert!(script_result.success, "Script execution should report success");
441
442 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
443
444 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"swc".to_string()));
446
447 assert_eq!(script_result.env_vars.get("SWC_TARGET"), Some(&"esnext".to_string()));
448 }
449
450 #[test]
451 fn test_execute_profile_script_swc_bundle() {
452 let config = ConfigLoader::load(".").expect("Failed to load configuration");
453
454 let profile =
455 ConfigLoader::get_profile(&config, "swc-bundle").expect("Profile 'swc-bundle' not found in configuration");
456
457 let script_path = profile
458 .rhai_script
459 .as_ref()
460 .expect("No Rhai script defined for swc-bundle profile");
461
462 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
463
464 if !full_script_path.exists() {
465 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
466
467 return;
468 }
469
470 let engine = create_engine();
471
472 let context = ScriptRunner::ScriptContext {
473 profile_name:"swc-bundle".to_string(),
474
475 cwd:".".to_string(),
476
477 manifest_dir:".".to_string(),
478
479 target_triple:None,
480
481 workbench_type:None,
482
483 features:HashMap::new(),
484 };
485
486 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
487
488 assert!(
489 result.is_ok(),
490 "Script execution should succeed but got error: {:?}",
491 result.err()
492 );
493
494 let script_result = result.unwrap();
495
496 assert!(script_result.success, "Script execution should report success");
497
498 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
499
500 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"swc".to_string()));
501
502 assert_eq!(script_result.env_vars.get("NODE_ENV"), Some(&"production".to_string()));
503 }
504
505 #[test]
506 fn test_execute_profile_script_oxc_bundle() {
507 let config = ConfigLoader::load(".").expect("Failed to load configuration");
508
509 let profile =
510 ConfigLoader::get_profile(&config, "oxc-bundle").expect("Profile 'oxc-bundle' not found in configuration");
511
512 let script_path = profile
513 .rhai_script
514 .as_ref()
515 .expect("No Rhai script defined for oxc-bundle profile");
516
517 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
518
519 if !full_script_path.exists() {
520 eprintln!("Skipping test - script file not found: {}", full_script_path.display());
521
522 return;
523 }
524
525 let engine = create_engine();
526
527 let context = ScriptRunner::ScriptContext {
528 profile_name:"oxc-bundle".to_string(),
529
530 cwd:".".to_string(),
531
532 manifest_dir:".".to_string(),
533
534 target_triple:None,
535
536 workbench_type:None,
537
538 features:HashMap::new(),
539 };
540
541 let result = ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context);
542
543 assert!(
544 result.is_ok(),
545 "Script execution should succeed but got error: {:?}",
546 result.err()
547 );
548
549 let script_result = result.unwrap();
550
551 assert!(script_result.success, "Script execution should report success");
552
553 assert!(!script_result.env_vars.is_empty(), "Script should return environment variables");
554
555 assert_eq!(script_result.env_vars.get("BUNDLER_TYPE"), Some(&"oxc".to_string()));
556
557 assert_eq!(script_result.env_vars.get("NODE_ENV"), Some(&"production".to_string()));
558 }
559
560 #[test]
561 fn test_env_vars_match_static_config() {
562 let config = ConfigLoader::load(".").expect("Failed to load configuration");
563
564 for profile_name in &["debug", "production", "release"] {
565 let profile = ConfigLoader::get_profile(&config, profile_name)
566 .expect(&format!("Profile '{}' not found", profile_name));
567
568 let script_path = profile
569 .rhai_script
570 .as_ref()
571 .expect(&format!("No Rhai script defined for {}", profile_name));
572
573 let full_script_path = std::path::Path::new(".").join(".vscode").join(script_path);
574
575 if !full_script_path.exists() {
576 continue;
577 }
578
579 let engine = create_engine();
580
581 let context = ScriptRunner::ScriptContext {
582 profile_name:profile_name.to_string(),
583
584 cwd:".".to_string(),
585
586 manifest_dir:".".to_string(),
587
588 target_triple:None,
589
590 workbench_type:None,
591
592 features:HashMap::new(),
593 };
594
595 let script_result =
596 ScriptRunner::ExecuteProfileScript(&engine, full_script_path.to_str().unwrap(), &context)
597 .expect(&format!("Failed to execute script for profile '{}'", profile_name));
598
599 let static_env = ConfigLoader::resolve_profile_env(&config, profile_name);
600
601 if let Some(static_debug) = static_env.get("Debug") {
604 let dynamic_debug = script_result.env_vars.get("Debug");
605
606 assert_eq!(
607 dynamic_debug,
608 Some(static_debug),
609 "Debug value should match between static config and Rhai script for profile '{}'",
610 profile_name
611 );
612 }
613 }
614 }
615}