diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index b9c88bd1b6f..56e564ea591 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -22,7 +22,7 @@ pkg-url = "https://github.com/rustwasm/wasm-bindgen/releases/download/{ version [dependencies] anyhow = "1.0" -docopt = "1.0" +clap = { version = "4", features = ["derive"] } env_logger = "0.11.5" log = "0.4" native-tls = { version = "0.2", default-features = false, optional = true } diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/deno.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/deno.rs index 390b2761512..58c824e0b31 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner/deno.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/deno.rs @@ -1,35 +1,28 @@ -use std::ffi::OsString; use std::fs; use std::path::Path; use std::process::Command; use anyhow::{Context, Error}; -use crate::node::{exec, SHARED_SETUP}; +use crate::{ + node::{exec, SHARED_SETUP}, + Cli, +}; -pub fn execute( - module: &str, - tmpdir: &Path, - args: &[OsString], - tests: &[String], -) -> Result<(), Error> { +pub fn execute(module: &str, tmpdir: &Path, cli: Cli, tests: &[String]) -> Result<(), Error> { let mut js_to_execute = format!( - r#"import * as wasm from "./{0}.js"; + r#"import * as wasm from "./{module}.js"; {console_override} window.__wbg_test_invoke = f => f(); - // Forward runtime arguments. These arguments are also arguments to the - // `wasm-bindgen-test-runner` which forwards them to deno which we - // forward to the test harness. this is basically only used for test - // filters for now. - cx.args(Deno.args); + {args} const tests = []; "#, - module, console_override = SHARED_SETUP, + args = cli.into_args(), ); for test in tests { @@ -69,7 +62,6 @@ if (!ok) Deno.exit(1);"#, Command::new("deno") .arg("run") .arg("--allow-read") - .arg(&js_path) - .args(args), + .arg(&js_path), ) } diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs index b1ebea28c1e..7d33d7b4208 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/main.rs @@ -12,6 +12,7 @@ //! and source code. use anyhow::{anyhow, bail, Context}; +use clap::Parser; use log::error; use std::env; use std::fs; @@ -26,70 +27,61 @@ mod node; mod server; mod shell; -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum TestMode { - Node { no_modules: bool }, - Deno, - Browser { no_modules: bool }, - DedicatedWorker { no_modules: bool }, - SharedWorker { no_modules: bool }, - ServiceWorker { no_modules: bool }, +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[arg( + index = 1, + help = "The file to test. `cargo test` passes this argument for you." + )] + file: PathBuf, + #[arg(long = "include-ignored", help = "Run ignored tests")] + include_ignored: bool, + #[arg( + long = "skip", + value_name = "FILTER", + help = "Skip tests whose names contain FILTER (this flag can be used multiple times)" + )] + skip: Vec, + #[arg( + index = 2, + value_name = "FILTER", + help = "The FILTER string is tested against the name of all tests, and only those tests \ + whose names contain the filter are run." + )] + filter: Option, } -impl TestMode { - fn is_worker(self) -> bool { - matches!( - self, - Self::DedicatedWorker { .. } | Self::SharedWorker { .. } | Self::ServiceWorker { .. } +impl Cli { + fn into_args(self) -> String { + let include_ignored = self.include_ignored; + let skip = self.skip; + let filter = if let Some(filter) = self.filter { + &format!("\"{filter}\"") + } else { + "undefined" + }; + + format!( + r#" + // Forward runtime arguments. + cx.include_ignored({include_ignored:?}); + cx.skip({skip:?}); + cx.filter({filter}); + "# ) } - - fn no_modules(self) -> bool { - match self { - Self::Deno => true, - Self::Browser { no_modules } - | Self::Node { no_modules } - | Self::DedicatedWorker { no_modules } - | Self::SharedWorker { no_modules } - | Self::ServiceWorker { no_modules } => no_modules, - } - } - - fn env(self) -> &'static str { - match self { - TestMode::Node { .. } => "WASM_BINDGEN_USE_NODE_EXPERIMENTAL", - TestMode::Deno => "WASM_BINDGEN_USE_DENO", - TestMode::Browser { .. } => "WASM_BINDGEN_USE_BROWSER", - TestMode::DedicatedWorker { .. } => "WASM_BINDGEN_USE_DEDICATED_WORKER", - TestMode::SharedWorker { .. } => "WASM_BINDGEN_USE_SHARED_WORKER", - TestMode::ServiceWorker { .. } => "WASM_BINDGEN_USE_SERVICE_WORKER", - } - } -} - -struct TmpDirDeleteGuard(PathBuf); - -impl Drop for TmpDirDeleteGuard { - fn drop(&mut self) { - if let Err(e) = fs::remove_dir_all(&self.0) { - error!("failed to remove temporary directory: {}", e); - } - } } fn main() -> anyhow::Result<()> { env_logger::init(); - let mut args = env::args_os().skip(1); - let shell = shell::Shell::new(); - // Currently no flags are supported, and assume there's only one argument - // which is the Wasm file to test. This'll want to improve over time! - let wasm_file_to_test = match args.next() { - Some(file) => PathBuf::from(file), - None => bail!("must have a file to test as first argument"), - }; + let cli = Cli::parse(); - let file_name = wasm_file_to_test + let shell = shell::Shell::new(); + + let file_name = cli + .file .file_name() .and_then(|s| s.to_str()) .context("file to test is not a valid file, can't extract file name")?; @@ -100,12 +92,12 @@ fn main() -> anyhow::Result<()> { // - a tmp directory, generated by rustdoc // we would like a directory we have write access to. if we assume cargo-like directories, // we end up with the path `/wbg-out` - let wasm_file_str = wasm_file_to_test.to_string_lossy(); + let wasm_file_str = cli.file.to_string_lossy(); let tmpdir = if wasm_file_str.starts_with("/tmp/rustdoc") || wasm_file_str.starts_with("/var/folders") { - wasm_file_to_test.parent() // chop off the file name and give us the /tmp/rustdoc directory + cli.file.parent() // chop off the file name and give us the /tmp/rustdoc directory } else { - wasm_file_to_test + cli.file .parent() // chop off file name .and_then(|p| p.parent()) // chop off `deps` .and_then(|p| p.parent()) // chop off `debug` @@ -123,7 +115,7 @@ fn main() -> anyhow::Result<()> { // Collect all tests that the test harness is supposed to run. We assume // that any exported function with the prefix `__wbg_test` is a test we need // to execute. - let wasm = fs::read(&wasm_file_to_test).context("failed to read Wasm file")?; + let wasm = fs::read(&cli.file).context("failed to read Wasm file")?; let mut wasm = walrus::Module::from_buffer(&wasm).context("failed to deserialize Wasm module")?; let mut tests = Vec::new(); @@ -267,13 +259,11 @@ fn main() -> anyhow::Result<()> { .context("executing `wasm-bindgen` over the Wasm file")?; shell.clear(); - let args: Vec<_> = args.collect(); - match test_mode { TestMode::Node { no_modules } => { - node::execute(module, &tmpdir, &args, &tests, !no_modules, coverage)? + node::execute(module, &tmpdir, cli, &tests, !no_modules, coverage)? } - TestMode::Deno => deno::execute(module, &tmpdir, &args, &tests)?, + TestMode::Deno => deno::execute(module, &tmpdir, cli, &tests)?, TestMode::Browser { .. } | TestMode::DedicatedWorker { .. } | TestMode::SharedWorker { .. } @@ -289,7 +279,7 @@ fn main() -> anyhow::Result<()> { headless, module, &tmpdir, - &args, + cli, &tests, test_mode, std::env::var("WASM_BINDGEN_TEST_NO_ORIGIN_ISOLATION").is_err(), @@ -321,6 +311,57 @@ fn main() -> anyhow::Result<()> { Ok(()) } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum TestMode { + Node { no_modules: bool }, + Deno, + Browser { no_modules: bool }, + DedicatedWorker { no_modules: bool }, + SharedWorker { no_modules: bool }, + ServiceWorker { no_modules: bool }, +} + +impl TestMode { + fn is_worker(self) -> bool { + matches!( + self, + Self::DedicatedWorker { .. } | Self::SharedWorker { .. } | Self::ServiceWorker { .. } + ) + } + + fn no_modules(self) -> bool { + match self { + Self::Deno => true, + Self::Browser { no_modules } + | Self::Node { no_modules } + | Self::DedicatedWorker { no_modules } + | Self::SharedWorker { no_modules } + | Self::ServiceWorker { no_modules } => no_modules, + } + } + + fn env(self) -> &'static str { + match self { + TestMode::Node { .. } => "WASM_BINDGEN_USE_NODE_EXPERIMENTAL", + TestMode::Deno => "WASM_BINDGEN_USE_DENO", + TestMode::Browser { .. } => "WASM_BINDGEN_USE_BROWSER", + TestMode::DedicatedWorker { .. } => "WASM_BINDGEN_USE_DEDICATED_WORKER", + TestMode::SharedWorker { .. } => "WASM_BINDGEN_USE_SHARED_WORKER", + TestMode::ServiceWorker { .. } => "WASM_BINDGEN_USE_SERVICE_WORKER", + } + } +} + +struct TmpDirDeleteGuard(PathBuf); + +impl Drop for TmpDirDeleteGuard { + fn drop(&mut self) { + if let Err(e) = fs::remove_dir_all(&self.0) { + error!("failed to remove temporary directory: {}", e); + } + } +} + fn coverage_args(tmpdir: &Path) -> PathBuf { fn generated(tmpdir: &Path, prefix: &str) -> String { let res = format!( diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/node.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/node.rs index 3ac475fcb3e..c9bb0a7d4d4 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner/node.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/node.rs @@ -1,11 +1,12 @@ use std::env; -use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use anyhow::{Context, Error}; +use crate::Cli; + // depends on the variable 'wasm' and initializes te WasmBindgenTestContext cx pub const SHARED_SETUP: &str = r#" const handlers = {}; @@ -41,7 +42,7 @@ handlers.on_console_error = wasm.__wbgtest_console_error; pub fn execute( module: &str, tmpdir: &Path, - args: &[OsString], + cli: Cli, tests: &[String], module_format: bool, coverage: PathBuf, @@ -57,11 +58,7 @@ pub fn execute( global.__wbg_test_invoke = f => f(); async function main(tests) {{ - // Forward runtime arguments. These arguments are also arguments to the - // `wasm-bindgen-test-runner` which forwards them to node which we - // forward to the test harness. this is basically only used for test - // filters for now. - cx.args(process.argv.slice(2)); + {args} const ok = await cx.run(tests.map(n => wasm.__wasm[n])); @@ -92,6 +89,7 @@ pub fn execute( }, coverage = coverage.display(), console_override = SHARED_SETUP, + args = cli.into_args(), ); // Note that we're collecting *JS objects* that represent the functions to @@ -139,8 +137,7 @@ pub fn execute( .env("NODE_PATH", env::join_paths(&path).unwrap()) .arg("--expose-gc") .args(&extra_node_args) - .arg(&js_path) - .args(args), + .arg(&js_path), ) } diff --git a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs index f77768c3c61..cae510afc43 100644 --- a/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs +++ b/crates/cli/src/bin/wasm-bindgen-test-runner/server.rs @@ -1,5 +1,4 @@ use std::borrow::Cow; -use std::ffi::OsString; use std::fs; use std::io::{Read, Write}; use std::net::SocketAddr; @@ -8,14 +7,14 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context, Error}; use rouille::{Request, Response, Server}; -use crate::TestMode; +use crate::{Cli, TestMode}; pub(crate) fn spawn( addr: &SocketAddr, headless: bool, module: &'static str, tmpdir: &Path, - args: &[OsString], + cli: Cli, tests: &[String], test_mode: TestMode, isolate_origin: bool, @@ -71,6 +70,8 @@ pub(crate) fn spawn( ) }; + let args = cli.into_args(); + if test_mode.is_worker() { let mut worker_script = if test_mode.no_modules() { format!(r#"importScripts("{0}.js");"#, module) @@ -125,7 +126,7 @@ pub(crate) fn spawn( wrap("error"); async function run_in_worker(tests) {{ - const wasm = await init("./{0}_bg.wasm"); + const wasm = await init("./{module}_bg.wasm"); const t = self; const cx = new Context(); @@ -134,8 +135,9 @@ pub(crate) fn spawn( self.on_console_info = __wbgtest_console_info; self.on_console_warn = __wbgtest_console_warn; self.on_console_error = __wbgtest_console_error; + + {args} - cx.args({1:?}); await cx.run(tests.map(s => wasm[s])); {cov_dump} }} @@ -145,7 +147,6 @@ pub(crate) fn spawn( run_in_worker(tests); }} "#, - module, args, )); if matches!( @@ -256,7 +257,7 @@ pub(crate) fn spawn( document.getElementById('output').textContent = "Loading Wasm module..."; async function main(test) {{ - const wasm = await init('./{0}_bg.wasm'); + const wasm = await init('./{module}_bg.wasm'); const cx = new Context(); window.on_console_debug = __wbgtest_console_debug; @@ -265,11 +266,7 @@ pub(crate) fn spawn( window.on_console_warn = __wbgtest_console_warn; window.on_console_error = __wbgtest_console_error; - // Forward runtime arguments. These arguments are also arguments to the - // `wasm-bindgen-test-runner` which forwards them to node which we - // forward to the test harness. this is basically only used for test - // filters for now. - cx.args({1:?}); + {args} await cx.run(test.map(s => wasm[s])); {cov_dump} @@ -277,7 +274,6 @@ pub(crate) fn spawn( const tests = []; "#, - module, args, )); } for test in tests { diff --git a/crates/cli/src/bin/wasm-bindgen.rs b/crates/cli/src/bin/wasm-bindgen.rs index e3d89b32272..fb7bf5462c0 100644 --- a/crates/cli/src/bin/wasm-bindgen.rs +++ b/crates/cli/src/bin/wasm-bindgen.rs @@ -1,90 +1,94 @@ use anyhow::{bail, Error}; -use docopt::Docopt; -use serde::Deserialize; +use clap::Parser; use std::path::PathBuf; use std::process; use wasm_bindgen_cli_support::{Bindgen, EncodeInto}; -const USAGE: &str = " -Generating JS bindings for a Wasm file - -Usage: - wasm-bindgen [options] - wasm-bindgen -h | --help - wasm-bindgen -V | --version - -Options: - -h --help Show this screen. - --out-dir DIR Output directory - --out-name VAR Set a custom output filename (Without extension. Defaults to crate name) - --target TARGET What type of output to generate, valid - values are [web, bundler, nodejs, no-modules, deno, experimental-nodejs-module], - and the default is [bundler] - --no-modules-global VAR Name of the global variable to initialize - --browser Hint that JS should only be compatible with a browser - --typescript Output a TypeScript definition file (on by default) - --no-typescript Don't emit a *.d.ts file - --omit-imports Don't emit imports in generated JavaScript - --debug Include otherwise-extraneous debug checks in output - --no-demangle Don't demangle Rust symbol names - --keep-lld-exports Keep exports synthesized by LLD - --keep-debug Keep debug sections in Wasm files - --remove-name-section Remove the debugging `name` section of the file - --remove-producers-section Remove the telemetry `producers` section - --omit-default-module-path Don't add WebAssembly fallback imports in generated JavaScript - --split-linked-modules Split linked modules out into their own files. Recommended if possible. - If a bundler is used, it needs to be set up accordingly. - --encode-into MODE Whether or not to use TextEncoder#encodeInto, - valid values are [test, always, never] - --nodejs Deprecated, use `--target nodejs` - --web Deprecated, use `--target web` - --no-modules Deprecated, use `--target no-modules` - --weak-refs Deprecated, is runtime-detected - --reference-types Deprecated, use `-Ctarget-feature=+reference-types` - -V --version Print the version number of wasm-bindgen - -Additional documentation: https://rustwasm.github.io/wasm-bindgen/reference/cli.html -"; - -#[derive(Debug, Deserialize)] +#[derive(Debug, Parser)] +#[command( + version, + about, + long_about = None, + after_help = "Additional documentation: https://rustwasm.github.io/wasm-bindgen/reference/cli.html", +)] struct Args { - flag_nodejs: bool, - flag_browser: bool, - flag_web: bool, - flag_no_modules: bool, - flag_typescript: bool, - flag_no_typescript: bool, - flag_omit_imports: bool, - flag_out_dir: Option, - flag_out_name: Option, - flag_debug: bool, - flag_version: bool, - flag_no_demangle: bool, - flag_no_modules_global: Option, - flag_remove_name_section: bool, - flag_remove_producers_section: bool, + #[arg(long, help = "Deprecated, use `--target nodejs`")] + nodejs: bool, + #[arg(long, help = "Hint that JS should only be compatible with a browser")] + browser: bool, + #[arg(long, help = "Deprecated, use `--target web`")] + web: bool, + #[arg(long, help = "Deprecated, use `--target no-modules`")] + no_modules: bool, + #[arg(long, help = "Output a TypeScript definition file (on by default)")] + typescript: bool, + #[arg(long, help = "Don't emit a *.d.ts file")] + no_typescript: bool, + #[arg(long, help = "Don't emit imports in generated JavaScript")] + omit_imports: bool, + #[arg(long, value_name = "DIR", help = "Output directory")] + out_dir: Option, + #[arg( + long, + value_name = "VAR", + help = "Set a custom output filename (Without extension. Defaults to crate name)" + )] + out_name: Option, + #[arg(long, help = "Include otherwise-extraneous debug checks in output")] + debug: bool, + #[arg(long, help = "Don't demangle Rust symbol names")] + no_demangle: bool, + #[arg( + long, + value_name = "VAR", + help = "Name of the global variable to initialize" + )] + no_modules_global: Option, + #[arg(long, help = "Remove the debugging `name` section of the file")] + remove_name_section: bool, + #[arg(long, help = "Remove the telemetry `producers` section")] + remove_producers_section: bool, + #[arg(long, help = "Deprecated, is runtime-detected")] #[allow(dead_code)] - flag_weak_refs: Option, - flag_reference_types: Option, - flag_keep_lld_exports: bool, - flag_keep_debug: bool, - flag_encode_into: Option, - flag_target: Option, - flag_omit_default_module_path: bool, - flag_split_linked_modules: bool, - arg_input: Option, + weak_refs: bool, + #[arg(long, help = "Deprecated, use `-Ctarget-feature=+reference-types`")] + reference_types: bool, + #[arg(long, help = "Keep exports synthesized by LLD")] + keep_lld_exports: bool, + #[arg(long, help = "Keep debug sections in Wasm files")] + keep_debug: bool, + #[arg( + long, + value_name = "MODE", + help = "Whether or not to use TextEncoder#encodeInto, valid values are [test, always, never]" + )] + encode_into: Option, + #[arg( + long, + value_name = "TARGET", + help = "What type of output to generate, valid\n\ + values are [web, bundler, nodejs, no-modules, deno, experimental-nodejs-module],\n\ + and the default is [bundler]" + )] + target: Option, + #[arg( + long, + help = "Don't add WebAssembly fallback imports in generated JavaScript" + )] + omit_default_module_path: bool, + #[arg( + long, + help = "Split linked modules out into their own files. Recommended if possible.\n\ + If a bundler is used, it needs to be set up accordingly." + )] + split_linked_modules: bool, + input: PathBuf, } fn main() { env_logger::init(); - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.deserialize()) - .unwrap_or_else(|e| e.exit()); + let args = Args::parse(); - if args.flag_version { - println!("wasm-bindgen {}", wasm_bindgen_shared::version()); - return; - } let err = match rmain(&args) { Ok(()) => return, Err(e) => e, @@ -94,15 +98,10 @@ fn main() { } fn rmain(args: &Args) -> Result<(), Error> { - let input = match args.arg_input { - Some(ref s) => s, - None => bail!("input file expected"), - }; - - let typescript = args.flag_typescript || !args.flag_no_typescript; + let typescript = args.typescript || !args.no_typescript; let mut b = Bindgen::new(); - if let Some(name) = &args.flag_target { + if let Some(name) = &args.target { match name.as_str() { "bundler" => b.bundler(true)?, "web" => b.web(true)?, @@ -113,32 +112,32 @@ fn rmain(args: &Args) -> Result<(), Error> { s => bail!("invalid encode-into mode: `{}`", s), }; } - b.input_path(input) - .nodejs(args.flag_nodejs)? - .web(args.flag_web)? - .browser(args.flag_browser)? - .no_modules(args.flag_no_modules)? - .debug(args.flag_debug) - .demangle(!args.flag_no_demangle) - .keep_lld_exports(args.flag_keep_lld_exports) - .keep_debug(args.flag_keep_debug) - .remove_name_section(args.flag_remove_name_section) - .remove_producers_section(args.flag_remove_producers_section) + b.input_path(&args.input) + .nodejs(args.nodejs)? + .web(args.web)? + .browser(args.browser)? + .no_modules(args.no_modules)? + .debug(args.debug) + .demangle(!args.no_demangle) + .keep_lld_exports(args.keep_lld_exports) + .keep_debug(args.keep_debug) + .remove_name_section(args.remove_name_section) + .remove_producers_section(args.remove_producers_section) .typescript(typescript) - .omit_imports(args.flag_omit_imports) - .omit_default_module_path(args.flag_omit_default_module_path) - .split_linked_modules(args.flag_split_linked_modules); - if let Some(true) = args.flag_reference_types { + .omit_imports(args.omit_imports) + .omit_default_module_path(args.omit_default_module_path) + .split_linked_modules(args.split_linked_modules); + if args.reference_types { #[allow(deprecated)] b.reference_types(true); } - if let Some(ref name) = args.flag_no_modules_global { + if let Some(ref name) = args.no_modules_global { b.no_modules_global(name)?; } - if let Some(ref name) = args.flag_out_name { + if let Some(ref name) = args.out_name { b.out_name(name); } - if let Some(mode) = &args.flag_encode_into { + if let Some(mode) = &args.encode_into { match mode.as_str() { "test" => b.encode_into(EncodeInto::Test), "always" => b.encode_into(EncodeInto::Always), @@ -147,7 +146,7 @@ fn rmain(args: &Args) -> Result<(), Error> { }; } - let out_dir = match args.flag_out_dir { + let out_dir = match args.out_dir { Some(ref p) => p, None => bail!("the `--out-dir` argument is now required"), }; diff --git a/crates/cli/src/bin/wasm2es6js.rs b/crates/cli/src/bin/wasm2es6js.rs index 593dcfb2d11..20204277629 100644 --- a/crates/cli/src/bin/wasm2es6js.rs +++ b/crates/cli/src/bin/wasm2es6js.rs @@ -1,52 +1,46 @@ use anyhow::{Context, Error}; -use docopt::Docopt; -use serde::Deserialize; +use clap::Parser; use std::fs; use std::path::PathBuf; -const USAGE: &str = " -Converts a Wasm file to an ES6 JS module - -Usage: - wasm2es6js [options] - wasm2es6js -h | --help - -Options: - -h --help Show this screen. - -o --output FILE File to place output in - --out-dir DIR Directory to place output in - --typescript Output a `*.d.ts` file next to the JS output - --base64 Inline the Wasm module using base64 encoding - --fetch PATH Load module by passing the PATH argument to `fetch()` - -Note that this is not intended to produce a production-ready output module -but rather is intended purely as a temporary \"hack\" until it's standard in -bundlers for working with wasm. Use this program with care! -"; - -#[derive(Debug, Deserialize)] +#[derive(Parser, Debug)] +#[command( + version, + about, + long_about = None, + after_help = "Note that this is not intended to produce a production-ready output module but rather\n\ + is intended purely as a temporary \"hack\" until it's standard in\n\ + bundlers for working with wasm. Use this program with care!", +)] struct Args { - flag_output: Option, - flag_out_dir: Option, - flag_typescript: bool, - flag_base64: bool, - flag_fetch: Option, - arg_input: PathBuf, + #[arg(long, short, value_name = "FILE", help = "File to place output in")] + output: Option, + #[arg(long, value_name = "DIR", help = "Directory to place output in")] + out_dir: Option, + #[arg(long, help = "Output a `*.d.ts` file next to the JS output")] + typescript: bool, + #[arg(long, help = "Inline the Wasm module using base64 encoding")] + base64: bool, + #[arg( + long, + value_name = "PATH", + help = "Load module by passing the PATH argument to `fetch()`" + )] + fetch: Option, + input: PathBuf, } fn main() -> anyhow::Result<()> { - let args: Args = Docopt::new(USAGE) - .and_then(|d| d.deserialize()) - .unwrap_or_else(|e| e.exit()); - let wasm = fs::read(&args.arg_input) - .with_context(|| format!("failed to read `{}`", args.arg_input.display()))?; + let args = Args::parse(); + let wasm = fs::read(&args.input) + .with_context(|| format!("failed to read `{}`", args.input.display()))?; let object = wasm_bindgen_cli_support::wasm2es6js::Config::new() - .base64(args.flag_base64) - .fetch(args.flag_fetch.clone()) + .base64(args.base64) + .fetch(args.fetch.clone()) .generate(&wasm)?; - if args.flag_typescript { + if args.typescript { let ts = object.typescript()?; write(&args, "d.ts", ts.as_bytes(), false)?; } @@ -61,12 +55,12 @@ fn main() -> anyhow::Result<()> { } fn write(args: &Args, extension: &str, contents: &[u8], print_fallback: bool) -> Result<(), Error> { - if let Some(p) = &args.flag_output { + if let Some(p) = &args.output { let dst = p.with_extension(extension); fs::write(&dst, contents) .with_context(|| format!("failed to write `{}`", dst.display()))?; - } else if let Some(p) = &args.flag_out_dir { - let filename = args.arg_input.file_name().unwrap(); + } else if let Some(p) = &args.out_dir { + let filename = args.input.file_name().unwrap(); let dst = p.join(filename).with_extension(extension); fs::write(&dst, contents) .with_context(|| format!("failed to write `{}`", dst.display()))?; diff --git a/crates/test/src/rt/mod.rs b/crates/test/src/rt/mod.rs index 6b35c145b56..b36f59100b9 100644 --- a/crates/test/src/rt/mod.rs +++ b/crates/test/src/rt/mod.rs @@ -344,35 +344,19 @@ impl Context { } } - /// Inform this context about runtime arguments passed to the test - /// harness. - pub fn args(&mut self, args: Vec) { - let mut filter = self.state.filter.borrow_mut(); - let mut skip = self.state.skip.borrow_mut(); - - let mut args = args.into_iter(); - - while let Some(arg) = args.next() { - let arg = arg.as_string().unwrap(); - if arg == "--include-ignored" { - self.state.include_ignored.set(true); - } else if arg == "--skip" { - skip.push( - args.next() - .expect("Argument to option 'skip' missing") - .as_string() - .unwrap(), - ); - } else if let Some(arg) = arg.strip_prefix("--skip=") { - skip.push(arg.to_owned()) - } else if arg.starts_with('-') { - panic!("flag {} not supported", arg); - } else if filter.is_some() { - panic!("more than one filter argument cannot be passed"); - } else { - *filter = Some(arg); - } - } + /// Handle `--include-ignored` flag. + pub fn include_ignored(&mut self, include_ignored: bool) { + self.state.include_ignored.set(include_ignored); + } + + /// Handle `--skip` arguments. + pub fn skip(&mut self, skip: Vec) { + *self.state.skip.borrow_mut() = skip; + } + + /// Handle filter argument. + pub fn filter(&mut self, filter: Option) { + *self.state.filter.borrow_mut() = filter; } /// Executes a list of tests, returning a promise representing their