Skip to main content

wasm_bindgen_cli/
wasm_bindgen.rs

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    // The options below are deprecated. They're still parsed for backwards compatibility,
128    // but we don't want to show them in `--help` to avoid distracting users.
129    #[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            // Passing --version and --help should not result in a failure.
159            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            // clap guarantees
219            _ => unreachable!(),
220        };
221        b.encode_into(mode);
222    }
223
224    b.generate(&args.out_dir)
225}