1use std::env::current_dir;
2use std::path::PathBuf;
3use std::process::Command;
4
5use bon::Builder;
6
7#[cfg_attr(docsrs, doc(cfg(feature = "build")))]
8#[derive(Builder)]
9pub struct BunBuild {
10 src_files: Vec<PathBuf>,
12 output_dir: PathBuf,
14 output_file: Option<String>,
16 #[builder(default = true)]
17 minify: bool,
18 extra_flags: Option<Vec<String>>,
20 #[builder(default = false)]
24 skip_if_no_bun: bool,
25}
26
27impl BunBuild {
28 pub fn run(&self) {
29 fn bun_exists() -> bool {
30 Command::new("bun")
31 .arg("--version")
32 .output()
33 .map(|o| o.status.success())
34 .unwrap_or(false)
35 }
36 if self.skip_if_no_bun && !bun_exists() {
37 println!("cargo:warning=Bun does not exist and configuration allows this, skipping bun build.");
38 return;
39 }
40
41 let src_files = self
42 .src_files
43 .iter()
44 .map(|e| {
45 e.to_str()
46 .expect("a src file is not a valid utf-8 string")
47 .to_owned()
48 })
49 .collect::<Vec<_>>();
50 let output_dir = self
51 .output_dir
52 .to_str()
53 .expect("The output file is not a valid utf-8 string")
54 .to_owned();
55
56 let mut args = Vec::new();
57 args.push("build".to_owned());
58 args.extend(src_files);
59
60 if let Some(output_file) = &self.output_file {
61 args.extend([
62 "--outfile".to_owned(),
63 self.output_dir
64 .join(output_file)
65 .to_str()
66 .unwrap()
67 .to_owned(),
68 ])
69 } else {
70 args.extend(["--outdir".to_owned(), output_dir]);
71 }
72 if self.minify {
73 args.push("--minify".to_owned());
74 }
75 args.push("--target=browser".to_owned());
76 args.extend(self.extra_flags.iter().flatten().cloned());
77
78 let output = Command::new("bun")
80 .args(&args)
81 .output()
82 .expect("Failed to execute bun build");
83 if !output.status.success() {
84 panic!(
85 "bun build failed.\nArgs: `{}`\nCurrent Directory: `{}`\nStderr:\n{}",
86 args.join(" "),
87 current_dir().unwrap().display(),
88 String::from_utf8_lossy(&output.stderr)
89 );
90 }
91 }
92}