From a4be03a0f8fe78fe335fea48081b21b6ed541c20 Mon Sep 17 00:00:00 2001 From: Niels Swimberghe Date: Mon, 2 Nov 2026 17:31:54 -0500 Subject: [PATCH 1/2] feat: rename target CLI option to generator --- packages/forge/README.md | 8 +++--- packages/forge/bin/forge-plugin.mjs | 4 +-- packages/forge/src/index.ts | 2 +- src/bin/forge/main.rs | 40 ++++++++++++++--------------- tests/cli.rs | 12 ++++----- tests/wasm.rs | 12 ++++----- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/packages/forge/README.md b/packages/forge/README.md index 75d6b62a9..9b5a2df64 100644 --- a/packages/forge/README.md +++ b/packages/forge/README.md @@ -4,13 +4,13 @@ Generates SDKs from OpenAPI documents. ```sh npm install --save-dev @cloudflare/forge -npx forge openapi.json --target ts --out sdk +npx forge openapi.json --generator ts --out sdk ``` Or run it once without installing: ```sh -npx @cloudflare/forge openapi.json --target ts --out sdk +npx @cloudflare/forge openapi.json --generator ts --out sdk ``` Pass `-` instead of a path to read the document from standard input. Run @@ -93,7 +93,7 @@ WebAssembly components, so they run anywhere forge does, sandboxed: a plugin reads nothing but the document, and its only output is the files it returns. ```sh -npx forge openapi.json --target wasm://plugin.wasm --out sdk +npx forge openapi.json --generator wasm://plugin.wasm --out sdk ``` ### Writing one in TypeScript @@ -139,7 +139,7 @@ Build it, and run it: ```sh npx forge-plugin build plugin.ts -o plugin.wasm -npx forge openapi.json --target wasm://plugin.wasm --out sdk +npx forge openapi.json --generator wasm://plugin.wasm --out sdk ``` `@cloudflare/forge/plugin` is typed throughout. A type is a union you narrow diff --git a/packages/forge/bin/forge-plugin.mjs b/packages/forge/bin/forge-plugin.mjs index 8adfe0c1b..4eb375a99 100644 --- a/packages/forge/bin/forge-plugin.mjs +++ b/packages/forge/bin/forge-plugin.mjs @@ -6,7 +6,7 @@ // // `build` bundles the entry module and its dependencies, and compiles them // with QuickJS into a WebAssembly component that -// `forge --target wasm://plugin.wasm` runs. It uses jco, which projects +// `forge --generator wasm://plugin.wasm` runs. It uses jco, which projects // install themselves, since it is large and only plugin authors need it: // // npm install --save-dev @bytecodealliance/jco @@ -30,7 +30,7 @@ const usage = `usage: forge-plugin build [-o plugin.wasm] build compiles the plugin whose entry module is , a .ts or .js file that exports definePlugin's generate and generateGroup, into a WebAssembly component with QuickJS. Run it with: - forge openapi.json --target wasm://plugin.wasm --out sdk + forge openapi.json --generator wasm://plugin.wasm --out sdk -o, --out the component to write (default: plugin.wasm) wit prints the WIT plugins are built against`; diff --git a/packages/forge/src/index.ts b/packages/forge/src/index.ts index d40d60c63..ae4d9d961 100644 --- a/packages/forge/src/index.ts +++ b/packages/forge/src/index.ts @@ -4,7 +4,7 @@ * A plugin generates files from the forge of an OpenAPI document: its * groups, methods, models and types, as forge's Rust API has them. Build it * with `forge-plugin build`, and run it with - * `forge openapi.json --target wasm://plugin.wasm --out sdk`. + * `forge openapi.json --generator wasm://plugin.wasm --out sdk`. * * ```ts * import { definePlugin } from "@cloudflare/forge/plugin"; diff --git a/src/bin/forge/main.rs b/src/bin/forge/main.rs index b036b487a..84fcd66c9 100644 --- a/src/bin/forge/main.rs +++ b/src/bin/forge/main.rs @@ -1,8 +1,8 @@ //! The `forge` command, which generates an SDK from an OpenAPI document. //! //! ```text -//! forge openapi.json --target ts --out sdk -//! forge openapi.json --target wasm://plugin.wasm --out sdk +//! forge openapi.json --generator ts --out sdk +//! forge openapi.json --generator wasm://plugin.wasm --out sdk //! forge normalize specs/*.json //! ``` @@ -28,8 +28,8 @@ struct Args { spec: Option, /// The language of the SDK: `ts` for TypeScript, or `wasm://PATH` for /// the plugin compiled to a WebAssembly component at PATH. - #[arg(long, value_parser = Target::parse, required = true)] - target: Option, + #[arg(long, value_parser = Generator::parse, required = true)] + generator: Option, /// The directory to write the SDK to. #[arg(long, required = true)] out: Option, @@ -47,9 +47,9 @@ enum Command { Normalize(normalize::Args), } -/// What forge generates an SDK with. +/// What Forge generates an SDK with. #[derive(Clone)] -enum Target { +enum Generator { /// The TypeScript generator. Ts, /// A plugin compiled to a WebAssembly component, at the path. @@ -57,10 +57,10 @@ enum Target { Wasm(PathBuf), } -impl Target { - fn parse(s: &str) -> Result { +impl Generator { + fn parse(s: &str) -> Result { if s == "ts" { - return Ok(Target::Ts); + return Ok(Generator::Ts); } let Some(path) = s.strip_prefix("wasm://") else { return Err("possible values: ts, wasm://PATH".to_owned()); @@ -69,7 +69,7 @@ impl Target { return Err("wasm:// needs the path of a plugin, as in wasm://plugin.wasm".to_owned()); } #[cfg(feature = "wasm")] - return Ok(Target::Wasm(PathBuf::from(path))); + return Ok(Generator::Wasm(PathBuf::from(path))); #[cfg(not(feature = "wasm"))] Err("this forge was built without wasm plugins; build it with `--features wasm`".to_owned()) } @@ -91,21 +91,21 @@ fn run(args: Args) -> Result<(), Err> { return normalize::run(args); } // They are required, which a command is the only way around. - let (Some(spec), Some(target), Some(out)) = (args.spec, args.target, args.out) else { - unreachable!("clap asks for a spec, a target and an out when there is no command"); + let (Some(spec), Some(generator), Some(out)) = (args.spec, args.generator, args.out) else { + unreachable!("clap asks for a spec, a generator and an out when there is no command"); }; - generate(&spec, target, &out) + generate(&spec, generator, &out) } /// Generates the SDK for the document at `spec`, and writes it to `out`. -fn generate(spec: &Path, target: Target, out: &Path) -> Result<(), Err> { - match target { - Target::Ts => { +fn generate(spec: &Path, generator: Generator, out: &Path) -> Result<(), Err> { + match generator { + Generator::Ts => { let forge = read(spec)?; forge::write(out, forge::run(&forge, &ts::Generator)?) } #[cfg(feature = "wasm")] - Target::Wasm(path) => { + Generator::Wasm(path) => { // Compiling the plugin and reading the document are both slow, // so they run at the same time. let (plugin, forge) = rayon::join(|| forge::wasm::WasmPlugin::from_file(&path), || read(spec)); @@ -151,14 +151,14 @@ mod tests { let command = parse(&["normalize", "--check", "a.json"]).unwrap(); assert!(matches!(command.command, Some(Command::Normalize(_))) && command.spec.is_none()); - let document = parse(&["./normalize", "--target", "ts", "--out", "sdk"]).unwrap(); + let document = parse(&["./normalize", "--generator", "ts", "--out", "sdk"]).unwrap(); assert!(document.command.is_none() && document.spec.as_deref() == Some(Path::new("./normalize"))); // What a command or a document needs is not waived for the other. assert!(parse(&["normalize"]).is_err()); assert!(parse(&["a.json"]).is_err()); - assert!(parse(&["normalize", "--target", "ts", "a.json"]).is_err()); - assert!(parse(&["--target", "ts", "normalize", "a.json"]).is_err()); + assert!(parse(&["normalize", "--generator", "ts", "a.json"]).is_err()); + assert!(parse(&["--generator", "ts", "normalize", "a.json"]).is_err()); assert!(parse(&[]).is_err()); } } diff --git a/tests/cli.rs b/tests/cli.rs index 8926f77f0..17095cc57 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -33,26 +33,26 @@ fn failure(out: Output) -> String { #[test] fn flags_are_required() { let err = failure(forge(&["-"], SPEC)); - assert!(err.contains("--target "), "{err}"); + assert!(err.contains("--generator "), "{err}"); assert!(err.contains("--out "), "{err}"); } #[test] -fn unknown_target() { - let err = failure(forge(&["-", "--target", "go", "--out", "sdk"], SPEC)); +fn unknown_generator() { + let err = failure(forge(&["-", "--generator", "go", "--out", "sdk"], SPEC)); assert!(err.contains("invalid value 'go'"), "{err}"); assert!(err.contains("possible values: ts"), "{err}"); } #[test] fn missing_spec() { - let err = failure(forge(&["missing.json", "--target", "ts", "--out", "sdk"], "")); + let err = failure(forge(&["missing.json", "--generator", "ts", "--out", "sdk"], "")); assert!(err.starts_with("forge: missing.json: "), "{err}"); } #[test] fn invalid_spec() { - let err = failure(forge(&["-", "--target", "ts", "--out", "sdk"], "{")); + let err = failure(forge(&["-", "--generator", "ts", "--out", "sdk"], "{")); assert!(err.starts_with("forge: -: "), "{err}"); } @@ -60,7 +60,7 @@ fn invalid_spec() { fn ts() { let out = std::env::temp_dir().join(format!("forge-cli-ts-{}", std::process::id())); let _ = std::fs::remove_dir_all(&out); - let result = forge(&["-", "--target", "ts", "--out", out.to_str().unwrap()], SPEC); + let result = forge(&["-", "--generator", "ts", "--out", out.to_str().unwrap()], SPEC); assert!(result.status.success(), "{}", String::from_utf8_lossy(&result.stderr)); for name in [ "index.ts", diff --git a/tests/wasm.rs b/tests/wasm.rs index 18bedd8b9..fe3a4a915 100644 --- a/tests/wasm.rs +++ b/tests/wasm.rs @@ -261,14 +261,14 @@ fn returns_errors() { assert_eq!(wasm, native); } -/// `forge --target wasm://PATH` runs the plugin at PATH, and writes what -/// `--target ts` does. +/// `forge --generator wasm://PATH` runs the plugin at PATH, and writes what +/// `--generator ts` does. #[test] fn cli() { let out = std::env::temp_dir().join(format!("forge-cli-wasm-{}", std::process::id())); let _ = std::fs::remove_dir_all(&out); - let target = format!("wasm://{}", guest().display()); - let result = forge_cli(&["-", "--target", &target, "--out", out.to_str().unwrap()]); + let generator = format!("wasm://{}", guest().display()); + let result = forge_cli(&["-", "--generator", &generator, "--out", out.to_str().unwrap()]); assert!(result.status.success(), "{}", String::from_utf8_lossy(&result.stderr)); let mut written = Files::new(); @@ -279,7 +279,7 @@ fn cli() { #[test] fn cli_needs_a_path() { - let result = forge_cli(&["-", "--target", "wasm://", "--out", "sdk"]); + let result = forge_cli(&["-", "--generator", "wasm://", "--out", "sdk"]); assert!(!result.status.success()); let err = String::from_utf8_lossy(&result.stderr); assert!(err.contains("wasm:// needs the path of a plugin"), "{err}"); @@ -287,7 +287,7 @@ fn cli_needs_a_path() { #[test] fn cli_missing_plugin() { - let result = forge_cli(&["-", "--target", "wasm://missing.wasm", "--out", "sdk"]); + let result = forge_cli(&["-", "--generator", "wasm://missing.wasm", "--out", "sdk"]); assert!(!result.status.success()); let err = String::from_utf8_lossy(&result.stderr); assert!(err.starts_with("forge: missing.wasm: "), "{err}"); From 579617d261a60ea925cc59445fa1dd881bcac988 Mon Sep 17 00:00:00 2001 From: Niels Swimberghe Date: Mon, 2 Nov 2026 17:38:56 -0500 Subject: [PATCH 2/2] feat: keep target as a compatibility alias --- packages/forge/README.md | 2 ++ src/bin/forge/main.rs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/forge/README.md b/packages/forge/README.md index 9b5a2df64..8f85974ac 100644 --- a/packages/forge/README.md +++ b/packages/forge/README.md @@ -13,6 +13,8 @@ Or run it once without installing: npx @cloudflare/forge openapi.json --generator ts --out sdk ``` +The former `--target` option remains available as a compatibility alias. + Pass `-` instead of a path to read the document from standard input. Run `forge --help` for every option. diff --git a/src/bin/forge/main.rs b/src/bin/forge/main.rs index 84fcd66c9..3b4b29f2e 100644 --- a/src/bin/forge/main.rs +++ b/src/bin/forge/main.rs @@ -28,7 +28,7 @@ struct Args { spec: Option, /// The language of the SDK: `ts` for TypeScript, or `wasm://PATH` for /// the plugin compiled to a WebAssembly component at PATH. - #[arg(long, value_parser = Generator::parse, required = true)] + #[arg(long, alias = "target", value_parser = Generator::parse, required = true)] generator: Option, /// The directory to write the SDK to. #[arg(long, required = true)] @@ -154,6 +154,9 @@ mod tests { let document = parse(&["./normalize", "--generator", "ts", "--out", "sdk"]).unwrap(); assert!(document.command.is_none() && document.spec.as_deref() == Some(Path::new("./normalize"))); + let legacy = parse(&["a.json", "--target", "ts", "--out", "sdk"]).unwrap(); + assert!(matches!(legacy.generator, Some(super::Generator::Ts))); + // What a command or a document needs is not waived for the other. assert!(parse(&["normalize"]).is_err()); assert!(parse(&["a.json"]).is_err());