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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use clap::{Parser, Subcommand};
use clap_complete::Shell;
use std::process;
use wasm_slim::cmd;
/// WASM bundle size optimizer
///
/// wasm-slim automates the complex process of optimizing WASM binary sizes,
/// providing 60%+ size reductions without requiring deep expertise.
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
/// Disable emoji output (useful for CI/CD or accessibility)
#[arg(long, global = true)]
no_emoji: bool,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Build and optimize WASM binary
Build {
/// Run in dry-run mode (show what would be done without making changes)
#[arg(short, long)]
dry_run: bool,
/// Check bundle size against budget (fail if exceeded)
#[arg(long)]
check: bool,
/// Output as JSON (for CI/CD integration)
#[arg(long)]
json: bool,
/// Target directory for output
#[arg(short, long)]
target_dir: Option<String>,
},
/// Analyze WASM bundle or dependencies
Analyze {
/// WASM file to analyze (optional, omit for dependency analysis)
#[arg(value_name = "FILE")]
file: Option<String>,
/// Analysis mode: assets, deps, top, dominators, dead
#[arg(short, long, default_value = "deps")]
mode: String,
/// Automatically apply optimization suggestions to Cargo.toml
#[arg(long)]
fix: bool,
/// Show what would be changed without modifying files
#[arg(long)]
dry_run: bool,
/// Show externalization guide (for assets mode)
#[arg(long)]
guide: bool,
/// Output as JSON
#[arg(long)]
json: bool,
},
/// Initialize wasm-slim configuration
Init {
/// Template to use: minimal, balanced, aggressive
#[arg(short, long, default_value = "balanced")]
template: String,
},
/// Compare two WASM builds
Compare {
/// Before file
before: String,
/// After file
after: String,
},
/// Generate shell completions
Completions {
/// Shell to generate completions for
#[arg(value_enum)]
shell: Shell,
},
}
fn main() {
// Initialize logger (use RUST_LOG env var to control verbosity)
env_logger::init();
let cli = Cli::parse();
// Set console emoji mode based on CLI flag
if cli.no_emoji {
std::env::set_var("NO_EMOJI", "1");
}
let result = match &cli.command {
Some(Commands::Build {
dry_run,
check,
json,
target_dir,
}) => cmd::cmd_build(*dry_run, *check, *json, target_dir.as_deref()),
Some(Commands::Analyze {
file,
mode,
fix,
dry_run,
guide,
json,
}) => cmd::cmd_analyze(file, mode, *fix, *dry_run, *guide, *json),
Some(Commands::Init { template }) => cmd::cmd_init(template),
Some(Commands::Compare { before, after }) => cmd::cmd_compare(before, after),
Some(Commands::Completions { shell }) => {
cmd::cmd_completions(*shell);
Ok(())
}
None => {
// No subcommand provided, show help
println!("wasm-slim v{}", env!("CARGO_PKG_VERSION"));
println!("WASM bundle size optimizer\n");
println!("Usage: wasm-slim <COMMAND>\n");
println!("Commands:");
println!(" build Build and optimize WASM binary");
println!(" analyze Analyze WASM bundle size");
println!(" init Initialize wasm-slim configuration");
println!(" compare Compare two WASM builds");
println!("\nRun 'wasm-slim <COMMAND> --help' for more information on a command.");
Ok(())
}
};
if let Err(e) = result {
use wasm_slim::error::ErrorFormatter;
eprintln!("{}", ErrorFormatter::format(&e));
let exit_code = ErrorFormatter::exit_code(&e);
process::exit(exit_code);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verify_cli() {
use clap::CommandFactory;
Cli::command().debug_assert()
}
}