Skip to content

Commit c7215df

Browse files
committed
fix(cli): address Codex review round 5
- split the required-value flag table per forwarded tool: Vite commands and pack have different flags (--env-file/--on-success/--copy/... for pack, and Vite's -d/--debug is optional-value so it no longer consumes), plus pack's --env.NAME <value> pattern flags - bin/vpr: a bare 'vpr -C' with no directory no longer inserts run into the -C value position; bin.ts reports the missing-argument error
1 parent 4b6e3e5 commit c7215df

3 files changed

Lines changed: 84 additions & 44 deletions

File tree

packages/cli/bin/vpr

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,16 @@ if (module.enableCompileCache) {
77

88
// Insert `run` after a leading global `-C <dir>` so `vpr -C <dir> <task>`
99
// behaves like `vp -C <dir> run <task>` (bin.ts consumes -C before dispatch).
10+
// A bare `vpr -C` (missing value) inserts nothing: bin.ts then reports the
11+
// missing-argument error instead of misreading `run` as the directory.
1012
const first = process.argv[2];
11-
const chdirTokens = first === '-C' ? 2 : first?.startsWith('-C') && first.length > 2 ? 1 : 0;
12-
process.argv.splice(2 + chdirTokens, 0, 'run');
13+
if (first === '-C') {
14+
if (process.argv[3] !== undefined) {
15+
process.argv.splice(4, 0, 'run');
16+
}
17+
} else if (first?.startsWith('-C') && first.length > 2) {
18+
process.argv.splice(3, 0, 'run');
19+
} else {
20+
process.argv.splice(2, 0, 'run');
21+
}
1322
await import('../dist/bin.js');

packages/cli/binding/src/cli/app_target.rs

Lines changed: 72 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,49 +44,74 @@ fn app_command_parts(subcommand: &SynthesizableSubcommand) -> Option<(&'static s
4444
}
4545
}
4646

47-
/// Flags of the app tools (Vite dev/build/preview and tsdown) that always
48-
/// consume the next argv token as their value. Space-separated values of
49-
/// these flags are not positional targets. Flags with optional values
50-
/// (`--host`, `--sourcemap`, `--minify`, ...) are deliberately absent: a
51-
/// token after them stays ambiguous and keeps the conservative fallback.
52-
const VALUE_TAKING_FLAGS: &[&str] = &[
47+
/// Required-value flags of the Vite CLI (dev/build/preview). Space-separated
48+
/// values of these flags are not positional targets. Optional-value flags
49+
/// (`--host`, `--open`, `--debug`, `--ssr`, `--sourcemap`, `--minify`, ...)
50+
/// are deliberately absent: a token after them stays ambiguous and keeps the
51+
/// conservative fallback.
52+
const VITE_VALUE_FLAGS: &[&str] = &[
5353
"-c",
5454
"--config",
55-
"-m",
56-
"--mode",
55+
"--base",
5756
"-l",
5857
"--logLevel",
58+
"-m",
59+
"--mode",
60+
"--configLoader",
61+
"-f",
62+
"--filter",
5963
"--port",
60-
"--base",
6164
"--outDir",
6265
"--assetsDir",
6366
"--assetsInlineLimit",
64-
"--filter",
65-
"--configLoader",
67+
"--target",
68+
];
69+
70+
/// Required-value flags of the bundled pack CLI (see pack-bin.ts; cac accepts
71+
/// both camelCase and kebab-case spellings). Optional-value flags (`--debug`,
72+
/// `--watch`, `--from-vite`) are deliberately absent, as above.
73+
const PACK_VALUE_FLAGS: &[&str] = &[
6674
"--config-loader",
67-
"--tsconfig",
68-
"--log-level",
75+
"--configLoader",
76+
"-f",
77+
"--format",
6978
"--deps.never-bundle",
79+
"--target",
80+
"-l",
81+
"--logLevel",
82+
"--log-level",
7083
"-d",
7184
"--out-dir",
72-
"--target",
73-
"-f",
74-
"--format",
85+
"--outDir",
7586
"--platform",
87+
"--tsconfig",
88+
"--ignore-watch",
89+
"--ignoreWatch",
90+
"--env-file",
91+
"--envFile",
92+
"--env-prefix",
93+
"--envPrefix",
94+
"--on-success",
95+
"--onSuccess",
96+
"--copy",
97+
"--public-dir",
98+
"--publicDir",
7699
];
77100

78-
/// Bare = no positional target and no help-like flag. Values of known
79-
/// value-taking flags (`--port 3000`) are skipped; any other non-flag token
80-
/// may be a positional target and conservatively disables elicitation.
81-
/// Help/version requests are answered by the underlying tool and must never
82-
/// be redirected.
83-
fn is_bare(args: &[String]) -> bool {
101+
/// Bare = no positional target and no help-like flag. Values of the
102+
/// forwarded tool's known required-value flags (`--port 3000`) are skipped;
103+
/// any other non-flag token may be a positional target and conservatively
104+
/// disables elicitation. Help/version requests are answered by the
105+
/// underlying tool and must never be redirected.
106+
fn is_bare(command: &str, args: &[String]) -> bool {
107+
let value_flags = if command == "pack" { PACK_VALUE_FLAGS } else { VITE_VALUE_FLAGS };
84108
let mut iter = args.iter();
85109
while let Some(arg) = iter.next() {
86110
if !arg.starts_with('-') || super::help::is_app_tool_help_or_version_flag(arg) {
87111
return false;
88112
}
89-
if VALUE_TAKING_FLAGS.contains(&arg.as_str()) {
113+
// `--env.NAME <value>` defines a compile-time env variable in pack.
114+
if value_flags.contains(&arg.as_str()) || (command == "pack" && arg.starts_with("--env.")) {
90115
// Consume the flag's value; a missing value is the tool's error.
91116
iter.next();
92117
}
@@ -204,10 +229,10 @@ pub(super) fn needs_elicitation(
204229
subcommand: &SynthesizableSubcommand,
205230
cwd: &AbsolutePathBuf,
206231
) -> bool {
207-
let Some((_, args)) = app_command_parts(subcommand) else {
232+
let Some((command, args)) = app_command_parts(subcommand) else {
208233
return false;
209234
};
210-
if !is_bare(args) {
235+
if !is_bare(command, args) {
211236
return false;
212237
}
213238
if vite_static_config::resolve_static_config(cwd).get_declared("defaultPackage").is_some() {
@@ -227,7 +252,7 @@ pub(super) fn resolve_app_target(
227252
let Some((command, args)) = app_command_parts(subcommand) else {
228253
return Ok(AppTarget::CurrentDir);
229254
};
230-
if !is_bare(args) {
255+
if !is_bare(command, args) {
231256
return Ok(AppTarget::CurrentDir);
232257
}
233258

@@ -314,26 +339,32 @@ mod tests {
314339
#[test]
315340
fn bare_means_no_positional_target_and_no_help() {
316341
let to_args = |args: &[&str]| args.iter().map(|s| (*s).to_string()).collect::<Vec<_>>();
317-
assert!(is_bare(&to_args(&[])));
318-
assert!(is_bare(&to_args(&["--watch"])));
319-
assert!(is_bare(&to_args(&["-w", "--minify"])));
342+
assert!(is_bare("dev", &to_args(&[])));
343+
assert!(is_bare("dev", &to_args(&["--watch"])));
344+
assert!(is_bare("build", &to_args(&["-w", "--minify"])));
320345
// A positional target disables elicitation.
321-
assert!(!is_bare(&to_args(&["apps/web"])));
322-
// Known value-taking flags consume their value.
323-
assert!(is_bare(&to_args(&["--port", "3000"])));
324-
assert!(is_bare(&to_args(&["--mode", "production", "--minify"])));
325-
assert!(is_bare(&to_args(&["--assetsDir", "assets"])));
326-
assert!(is_bare(&to_args(&["--port=3000"])));
346+
assert!(!is_bare("dev", &to_args(&["apps/web"])));
347+
// Known required-value flags consume their value, per command.
348+
assert!(is_bare("dev", &to_args(&["--port", "3000"])));
349+
assert!(is_bare("build", &to_args(&["--mode", "production", "--minify"])));
350+
assert!(is_bare("build", &to_args(&["--assetsDir", "assets"])));
351+
assert!(is_bare("build", &to_args(&["--port=3000"])));
352+
assert!(is_bare("pack", &to_args(&["--env-file", ".env"])));
353+
assert!(is_bare("pack", &to_args(&["-d", "out", "--env.FOO", "1"])));
354+
// The tables are command-specific: pack's flags mean nothing to Vite,
355+
// and Vite's optional-value `-d, --debug [feat]` must not consume.
356+
assert!(!is_bare("dev", &to_args(&["--env-file", ".env"])));
357+
assert!(!is_bare("dev", &to_args(&["-d", "apps/web"])));
327358
// A token after an unknown or optional-value flag is ambiguous with a
328359
// positional target, so it conservatively counts as non-bare.
329-
assert!(!is_bare(&to_args(&["--watch", "apps/web"])));
330-
assert!(!is_bare(&to_args(&["--host", "0.0.0.0"])));
360+
assert!(!is_bare("build", &to_args(&["--watch", "apps/web"])));
361+
assert!(!is_bare("dev", &to_args(&["--host", "0.0.0.0"])));
331362
// Help/version requests go to the underlying tool, never elicitation.
332-
assert!(!is_bare(&to_args(&["--help"])));
333-
assert!(!is_bare(&to_args(&["-h"])));
334-
assert!(!is_bare(&to_args(&["--watch", "--version"])));
363+
assert!(!is_bare("dev", &to_args(&["--help"])));
364+
assert!(!is_bare("dev", &to_args(&["-h"])));
365+
assert!(!is_bare("build", &to_args(&["--watch", "--version"])));
335366
// Vite and tsdown are cac-based and use `-v` for version.
336-
assert!(!is_bare(&to_args(&["-v"])));
367+
assert!(!is_bare("build", &to_args(&["-v"])));
337368
}
338369

339370
#[test]

rfcs/cwd-flag.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ Rejected alternatives: repurposing the app-command positional to mean "run there
246246

247247
### Target directory resolution
248248

249-
An app command invocation is **bare** when it has no `-C` and no positional target (no Vite `[root]`, no pack entries). Flags keep it bare, including known value-taking flags together with their values (`--port 3000`, `--mode production`); a token following an unknown or optional-value flag is ambiguous with a positional target and conservatively counts as one. For `vp dev` / `build` / `preview` / `pack`, the target directory is resolved in this order:
249+
An app command invocation is **bare** when it has no `-C` and no positional target (no Vite `[root]`, no pack entries). Flags keep it bare, including the forwarded tool's known required-value flags together with their values (`--port 3000` for Vite commands, `--env-file .env` for pack); a token following an unknown or optional-value flag is ambiguous with a positional target and conservatively counts as one. For `vp dev` / `build` / `preview` / `pack`, the target directory is resolved in this order:
250250

251251
1. **`-C <dir>`**: run there. Never triggers the picker.
252252
2. **Positional target present**: forward as today, upstream semantics, vp does not interfere.

0 commit comments

Comments
 (0)