1use anyhow::{bail, Error};
2use clap::{Parser, ValueEnum};
3use std::ffi::OsString;
4use std::path::PathBuf;
5use wasm_bindgen_cli_support::{Bindgen, EncodeInto};
6
7#[derive(Debug, Clone, ValueEnum)]
8#[clap(rename_all = "kebab-case")]
9enum Target {
10 Bundler,
11 Web,
12 Nodejs,
13 NoModules,
14 Deno,
15 ExperimentalNodejsModule,
16 Module,
17}
18
19#[derive(Debug, Parser)]
20#[command(
21 name = "wasm-bindgen",
22 version,
23 about,
24 after_help = "Additional documentation: https://wasm-bindgen.github.io/wasm-bindgen/reference/cli.html"
25)]
26struct Args {
27 input: PathBuf,
28 #[arg(
29 long,
30 value_name = "TARGET",
31 value_enum,
32 default_value_t = Target::Bundler,
33 help = "What type of output to generate",
34 group = "target-group"
35 )]
36 target: Target,
37 #[arg(long, value_name = "DIR", help = "Output directory")]
38 out_dir: PathBuf,
39 #[arg(
40 long,
41 help = "Hint that JS should only be compatible with a browser",
42 group = "target-group"
43 )]
44 browser: bool,
45 #[arg(long, help = "Don't emit a *.d.ts file", conflicts_with = "typescript")]
46 no_typescript: bool,
47 #[arg(long, help = "Don't emit imports in generated JavaScript")]
48 omit_imports: bool,
49 #[arg(
50 long,
51 value_name = "VAR",
52 help = "Set a custom output filename (Without extension. Defaults to crate name)"
53 )]
54 out_name: Option<String>,
55 #[arg(long, help = "Include otherwise-extraneous debug checks in output")]
56 debug: bool,
57 #[arg(long, help = "Don't demangle Rust symbol names")]
58 no_demangle: bool,
59 #[arg(
60 long,
61 value_name = "VAR",
62 help = "Name of the global variable to initialize"
63 )]
64 no_modules_global: Option<String>,
65 #[arg(long, help = "Remove the debugging `name` section of the file")]
66 remove_name_section: bool,
67 #[arg(long, help = "Remove the telemetry `producers` section")]
68 remove_producers_section: bool,
69 #[arg(long, help = "Keep exports synthesized by LLD")]
70 keep_lld_exports: bool,
71 #[arg(long, help = "Keep debug sections in Wasm files")]
72 keep_debug: bool,
73 #[arg(
74 long,
75 help = "Write DWARF debug info to a separate `*_bg.debug.wasm` file (implies `--keep-debug`)"
76 )]
77 split_debug_info: bool,
78 #[arg(
79 long,
80 value_name = "URL",
81 requires = "split_debug_info",
82 help = "URL of the split debug info file, recorded in the main Wasm file\n\
83 (defaults to the file name of the debug info file)"
84 )]
85 debug_info_url: Option<String>,
86 #[arg(
87 long,
88 value_name = "MODE",
89 help = "Whether or not to use TextEncoder#encodeInto",
90 value_parser = ["test", "always", "never"]
91 )]
92 encode_into: Option<String>,
93 #[arg(
94 long,
95 help = "Don't add WebAssembly fallback imports in generated JavaScript"
96 )]
97 omit_default_module_path: bool,
98 #[arg(
99 long,
100 help = "Split linked modules out into their own files. Recommended if possible.\n\
101 If a bundler is used, it needs to be set up accordingly."
102 )]
103 split_linked_modules: bool,
104 #[arg(
105 long,
106 help = "Import `static_string` constants as JS String Builtins string constants\n\
107 from `wasm:js/string-constants`. Requires a host with the\n\
108 `importedStringConstants` compile option or the ESM integration."
109 )]
110 js_string_builtins: bool,
111 #[arg(
112 long,
113 help = "Emit `Uint8Array<ArrayBuffer>`-style generic typed array return types\n\
114 in generated TypeScript. Requires TypeScript 5.7 or later."
115 )]
116 ts_typed_array_buffers: bool,
117 #[arg(
118 long = "experimental-reset-state-function",
119 help = "Generate __wbg_reset_state function for Wasm reinitialization (experimental)"
120 )]
121 generate_reset_state: bool,
122 #[arg(
123 long = "force-enable-abort-handler",
124 help = "Enable abort handler even if building with panic=abort. Does nothing when panic=unwind."
125 )]
126 force_enable_abort_handler: bool,
127 #[arg(long, hide = true)]
130 #[deprecated(note = "implied default, only `--no-typescript` is needed")]
131 typescript: bool,
132 #[arg(long, hide = true, group = "target-group")]
133 #[deprecated(note = "use `Args::target` instead")]
134 nodejs: bool,
135 #[arg(long, hide = true, group = "target-group")]
136 #[deprecated(note = "use `Args::target` instead")]
137 web: bool,
138 #[arg(long, hide = true, group = "target-group")]
139 #[deprecated(note = "use `Args::target` instead")]
140 no_modules: bool,
141 #[arg(long, hide = true)]
142 #[deprecated(note = "runtime-detected")]
143 #[allow(dead_code)]
144 weak_refs: bool,
145 #[arg(long, hide = true)]
146 #[deprecated(note = "automatically inferred from the Wasm features")]
147 reference_types: bool,
148}
149
150pub fn run_cli_with_args<I, T>(args: I) -> anyhow::Result<()>
151where
152 I: IntoIterator<Item = T>,
153 T: Into<OsString> + Clone,
154{
155 let args = match Args::try_parse_from(args) {
156 Ok(a) => a,
157 Err(e) => match e.kind() {
158 clap::error::ErrorKind::DisplayHelp | clap::error::ErrorKind::DisplayVersion => {
160 print!("{e}");
161 return Ok(());
162 }
163 _ => bail!(e),
164 },
165 };
166 rmain(&args)
167}
168
169fn rmain(args: &Args) -> Result<(), Error> {
170 let mut b = Bindgen::new();
171 match &args.target {
172 Target::Bundler => b.bundler(true)?,
173 Target::Web => b.web(true)?,
174 Target::NoModules => b.no_modules(true)?,
175 Target::Nodejs => b.nodejs(true)?,
176 Target::Deno => b.deno(true)?,
177 Target::ExperimentalNodejsModule => b.nodejs_module(true)?,
178 Target::Module => b.module(true)?,
179 };
180 #[allow(deprecated)]
181 b.input_path(&args.input)
182 .nodejs(args.nodejs)?
183 .web(args.web)?
184 .browser(args.browser)?
185 .no_modules(args.no_modules)?
186 .debug(args.debug)
187 .demangle(!args.no_demangle)
188 .keep_lld_exports(args.keep_lld_exports)
189 .keep_debug(args.keep_debug)
190 .split_debug_info(args.split_debug_info)
191 .remove_name_section(args.remove_name_section)
192 .remove_producers_section(args.remove_producers_section)
193 .typescript(!args.no_typescript)
194 .omit_imports(args.omit_imports)
195 .omit_default_module_path(args.omit_default_module_path)
196 .split_linked_modules(args.split_linked_modules)
197 .js_string_builtins(args.js_string_builtins)
198 .ts_typed_array_buffers(args.ts_typed_array_buffers)
199 .reference_types(args.reference_types)
200 .reset_state_function(args.generate_reset_state)
201 .force_enable_abort_handler(args.force_enable_abort_handler);
202
203 if let Some(ref url) = args.debug_info_url {
204 b.debug_info_url(url);
205 }
206 if let Some(ref name) = args.no_modules_global {
207 b.no_modules_global(name)?;
208 }
209 if let Some(ref name) = args.out_name {
210 b.out_name(name);
211 }
212
213 if let Some(mode) = &args.encode_into {
214 let mode = match mode.as_str() {
215 "test" => EncodeInto::Test,
216 "always" => EncodeInto::Always,
217 "never" => EncodeInto::Never,
218 _ => unreachable!(),
220 };
221 b.encode_into(mode);
222 }
223
224 b.generate(&args.out_dir)
225}