1mod pack;
2#[cfg(test)]
3mod test;
4
5use pack::pack;
6use std::{
7 env::var,
8 ffi::OsStr,
9 fs::create_dir_all,
10 io::ErrorKind,
11 path::{Path, PathBuf},
12 process::{exit, Command, ExitStatus},
13};
14
15use clap::{Args, Parser};
16
17#[derive(Args, Debug, Default)]
18pub struct Opt {
19 #[clap(long)]
21 pub release: bool,
22 #[clap(long)]
23 pub example: Option<String>,
24}
25
26#[derive(Parser, Debug)]
27#[clap(bin_name = "cargo")]
28pub enum Cli {
29 #[clap(version)]
31 WasmBundle(Opt),
32}
33
34pub fn exec(base_dir: &Path, opt: &Opt) {
35 let base_dir = std::fs::canonicalize(base_dir).unwrap();
36 let target_profile = match opt.release {
37 true => "release",
38 false => "debug",
39 };
40 let cargo_meta = cargo_metadata::MetadataCommand::new()
41 .current_dir(base_dir.clone())
42 .no_deps()
43 .exec()
44 .unwrap();
45 let mut cargo_build_target_dir = cargo_meta
46 .target_directory
47 .as_std_path()
48 .join("wasm32-unknown-unknown")
49 .join(target_profile);
50 let mut wasm_bindgen_target_dir = cargo_meta
51 .target_directory
52 .as_std_path()
53 .join("wasm-bindgen")
54 .join(target_profile);
55 let mut html_target_dir = cargo_meta
56 .target_directory
57 .as_std_path()
58 .join("wasm-bundle")
59 .join(target_profile);
60
61 let mut name = None;
62 for package in cargo_meta.workspace_packages() {
63 let manifest_path = package.manifest_path.clone().into_std_path_buf();
64 let manifest_path = std::fs::canonicalize(manifest_path).unwrap();
65 let manifest_dir = manifest_path.parent().unwrap();
66 if manifest_dir == base_dir {
67 name = Some(&package.name);
68 }
69 }
70
71 let mut name = name.expect("cargo wasm-bundle must be executed in a package root");
72
73 if let Some(example) = &opt.example {
74 name = example;
75 cargo_build_target_dir = cargo_build_target_dir.join("examples");
76 wasm_bindgen_target_dir = wasm_bindgen_target_dir.join("examples");
77 html_target_dir = html_target_dir.join("examples");
78 }
79
80 create_dir_all(&html_target_dir).unwrap();
81
82 cargo_build(&base_dir, &opt);
83 check_wasm_bindgen(&base_dir);
84 wasm_bindgen(
85 &base_dir,
86 &cargo_build_target_dir,
87 &name,
88 &wasm_bindgen_target_dir,
89 );
90 pack(name, &wasm_bindgen_target_dir, &html_target_dir);
91}
92
93fn cargo_build(base_dir: &Path, opt: &Opt) {
94 let mut cmd = Command::new(var("CARGO").unwrap_or("cargo".into()));
95 cmd.arg("build")
96 .args(["--target", "wasm32-unknown-unknown"]);
97 if opt.release {
98 cmd.arg("--release");
99 }
100 if let Some(example) = &opt.example {
101 cmd.args(["--example", example]);
102 }
103 run(base_dir, cmd)
104}
105
106fn check_wasm_bindgen(base_dir: &Path) {
107 let mut cmd = Command::new("wasm-bindgen");
108 cmd.arg("--version");
109 eprintln!("running {:?}", cmd);
110 match cmd.status() {
111 Ok(status) => check_status(status),
112 Err(err) => match err.kind() {
113 ErrorKind::NotFound => {
114 let mut cmd = Command::new(var("CARGO").unwrap());
115 cmd.args(["install", "wasm-bindgen-cli"]);
116 run(base_dir, cmd);
117 }
118 _ => Err(err).unwrap(),
119 },
120 }
121}
122
123fn wasm_bindgen(
124 base_dir: &Path,
125 cargo_build_target_dir: &PathBuf,
126 name: &str,
127 wasm_bindgen_target_dir: &PathBuf,
128) {
129 let mut cmd = Command::new("wasm-bindgen");
130 let infile = cargo_build_target_dir.join(format!("{name}.wasm"));
131 cmd.args(["--target", "web"])
132 .arg(infile.into_os_string())
133 .args([
134 &OsStr::new("--out-dir"),
135 wasm_bindgen_target_dir.as_os_str(),
136 ]);
137 run(base_dir, cmd)
138}
139
140pub(crate) fn run(base_dir: &Path, mut cmd: Command) {
141 cmd.current_dir(base_dir);
142 eprintln!("running {:?}", cmd);
143 check_status(cmd.status().unwrap());
144}
145
146fn check_status(status: ExitStatus) {
147 let code = status.code().unwrap();
148 if code != 0 {
149 exit(code)
150 }
151}