1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
mod pack;
#[cfg(test)]
mod test;

use pack::pack;
use std::{
    env::var,
    ffi::OsStr,
    fs::create_dir_all,
    io::ErrorKind,
    path::{Path, PathBuf},
    process::{exit, Command, ExitStatus},
};

use clap::{Args, Parser};

#[derive(Args, Debug, Default)]
pub struct Opt {
    /// Build with the dev profile
    #[clap(long)]
    pub release: bool,
    #[clap(long)]
    pub example: Option<String>,
}

#[derive(Parser, Debug)]
#[clap(bin_name = "cargo")]
pub enum Cli {
    /// A cargo subcommand for generating flamegraphs, using inferno
    #[clap(version)]
    WasmBundle(Opt),
}

pub fn exec(base_dir: &Path, opt: &Opt) {
    let target_profile = match opt.release {
        true => "release",
        false => "debug",
    };
    let cargo_meta = cargo_metadata::MetadataCommand::new()
        .current_dir(base_dir)
        .no_deps()
        .exec()
        .unwrap();
    let mut cargo_build_target_dir = cargo_meta
        .target_directory
        .as_std_path()
        .join("wasm32-unknown-unknown")
        .join(target_profile);
    let mut wasm_bindgen_target_dir = cargo_meta
        .target_directory
        .as_std_path()
        .join("wasm-bindgen")
        .join(target_profile);
    let mut html_target_dir = cargo_meta
        .target_directory
        .as_std_path()
        .join("wasm-bundle")
        .join(target_profile);
    let mut name = &cargo_meta.root_package().unwrap().name;

    if let Some(example) = &opt.example {
        name = example;
        cargo_build_target_dir = cargo_build_target_dir.join("examples");
        wasm_bindgen_target_dir = wasm_bindgen_target_dir.join("examples");
        html_target_dir = html_target_dir.join("examples");
    }

    create_dir_all(&html_target_dir).unwrap();

    cargo_build(base_dir, &opt);
    check_wasm_bindgen(base_dir);
    wasm_bindgen(
        base_dir,
        &cargo_build_target_dir,
        &name,
        &wasm_bindgen_target_dir,
    );
    pack(name, &wasm_bindgen_target_dir, &html_target_dir);
}

fn cargo_build(base_dir: &Path, opt: &Opt) {
    let mut cmd = Command::new(var("CARGO").unwrap_or("cargo".into()));
    cmd.arg("build")
        .args(["--target", "wasm32-unknown-unknown"]);
    if opt.release {
        cmd.arg("--release");
    }
    if let Some(example) = &opt.example {
        cmd.args(["--example", example]);
    }
    run(base_dir, cmd)
}

fn check_wasm_bindgen(base_dir: &Path) {
    let mut cmd = Command::new("wasm-bindgen");
    cmd.arg("--version");
    eprintln!("running {:?}", cmd);
    match cmd.status() {
        Ok(status) => check_status(status),
        Err(err) => match err.kind() {
            ErrorKind::NotFound => {
                let mut cmd = Command::new(var("CARGO").unwrap());
                cmd.args(["install", "wasm-bindgen-cli"]);
                run(base_dir, cmd);
            }
            _ => Err(err).unwrap(),
        },
    }
}

fn wasm_bindgen(
    base_dir: &Path,
    cargo_build_target_dir: &PathBuf,
    name: &str,
    wasm_bindgen_target_dir: &PathBuf,
) {
    let mut cmd = Command::new("wasm-bindgen");
    let infile = cargo_build_target_dir.join(format!("{name}.wasm"));
    cmd.args(["--target", "web"])
        .arg(infile.into_os_string())
        .args([
            &OsStr::new("--out-dir"),
            wasm_bindgen_target_dir.as_os_str(),
        ]);
    run(base_dir, cmd)
}

pub(crate) fn run(base_dir: &Path, mut cmd: Command) {
    cmd.current_dir(base_dir);
    eprintln!("running {:?}", cmd);
    check_status(cmd.status().unwrap());
}

fn check_status(status: ExitStatus) {
    let code = status.code().unwrap();
    if code != 0 {
        exit(code)
    }
}