Skip to content

Commit e56d5c8

Browse files
fengmk2claude
andcommitted
feat(cli): default to dev command when no subcommand provided
Running `vite` without arguments or with options (e.g., `vite --port 3000`) now defaults to the dev command, matching the behavior of the upstream Vite CLI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0aca328 commit e56d5c8

5 files changed

Lines changed: 103 additions & 19 deletions

File tree

packages/cli/binding/src/lib.rs

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ impl From<JsCommandResolvedResult> for ResolveCommandResult {
8080
}
8181
}
8282

83-
static BUILTIN_COMMANDS: &[&str] = &["lint", "fmt", "build", "test", "doc", "lib", "preview"];
83+
static BUILTIN_COMMANDS: &[&str] =
84+
&["dev", "lint", "fmt", "build", "test", "doc", "lib", "preview"];
8485

8586
/// Main entry point for the CLI, called from JavaScript.
8687
///
@@ -267,30 +268,70 @@ fn js_error_to_resolve_universal_vite_config_error(err: napi::Error) -> Error {
267268
fn parse_args() -> Args {
268269
// ArgsOs [node, vite-plus, ...]
269270
let mut raw_args = std::env::args_os().skip(2);
270-
if let Some(first) = raw_args.next()
271-
&& let Some(first) = first.to_str()
272-
&& BUILTIN_COMMANDS.contains(&first)
273-
{
274-
let forwarded_args = raw_args
271+
272+
// No arguments provided, default to dev command
273+
let Some(first) = raw_args.next() else {
274+
return Args {
275+
task: None,
276+
task_args: vec![],
277+
commands: Commands::Dev { args: vec![] },
278+
debug: false,
279+
no_debug: true,
280+
};
281+
};
282+
283+
// If first arg is not valid UTF-8, fall through to clap parsing
284+
let Some(first_str) = first.to_str() else {
285+
return Args::parse_from(std::env::args_os().skip(1));
286+
};
287+
288+
// Collect remaining args for potential forwarding
289+
let remaining_args: Vec<_> = raw_args.collect();
290+
291+
// Handle builtin commands with fast-path parsing (bypasses clap for better arg forwarding)
292+
if let Some(cmd) = parse_builtin_command(first_str, &remaining_args) {
293+
return cmd;
294+
}
295+
296+
// If first arg starts with '-' but is NOT a help/version flag, treat as options for dev command
297+
// e.g. `vite --port 3000` should be treated as `vite dev --port 3000`
298+
if first_str.starts_with('-') && !matches!(first_str, "-h" | "--help" | "-V" | "--version") {
299+
let forwarded_args: Vec<String> = std::iter::once(first)
300+
.chain(remaining_args)
275301
.map(|a| a.into_string().unwrap_or_else(|os_str| os_str.to_string_lossy().into_owned()))
276302
.collect();
277303
return Args {
278304
task: None,
279305
task_args: vec![],
280-
commands: match first {
281-
"lint" => Commands::Lint { args: forwarded_args },
282-
"fmt" => Commands::Fmt { args: forwarded_args },
283-
"build" => Commands::Build { args: forwarded_args },
284-
"test" => Commands::Test { args: forwarded_args },
285-
"doc" => Commands::Doc { args: forwarded_args },
286-
"lib" => Commands::Lib { args: forwarded_args },
287-
"preview" => Commands::Preview { args: forwarded_args },
288-
_ => unreachable!(),
289-
},
306+
commands: Commands::Dev { args: forwarded_args },
290307
debug: false,
291308
no_debug: true,
292309
};
293310
}
294-
// Parse CLI arguments (skip first arg which is the node binary)
311+
312+
// Fall through to clap parsing for other commands (run, cache, install, help, etc.)
295313
Args::parse_from(std::env::args_os().skip(1))
296314
}
315+
316+
fn parse_builtin_command(cmd: &str, raw_args: &[std::ffi::OsString]) -> Option<Args> {
317+
if !BUILTIN_COMMANDS.contains(&cmd) {
318+
return None;
319+
}
320+
321+
let forwarded_args: Vec<String> =
322+
raw_args.iter().map(|a| a.to_string_lossy().into_owned()).collect();
323+
324+
let commands = match cmd {
325+
"dev" => Commands::Dev { args: forwarded_args },
326+
"lint" => Commands::Lint { args: forwarded_args },
327+
"fmt" => Commands::Fmt { args: forwarded_args },
328+
"build" => Commands::Build { args: forwarded_args },
329+
"test" => Commands::Test { args: forwarded_args },
330+
"doc" => Commands::Doc { args: forwarded_args },
331+
"lib" => Commands::Lib { args: forwarded_args },
332+
"preview" => Commands::Preview { args: forwarded_args },
333+
_ => return None,
334+
};
335+
336+
Some(Args { task: None, task_args: vec![], commands, debug: false, no_debug: true })
337+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
> vite dev --port 12312312312 2>&1 | grep RangeError # intentionally use an invalid port (exceeds 0-65535) to trigger RangeError
22
RangeError [ERR_SOCKET_BAD_PORT]: options.port should be >= 0 and < 65536. Received type number (12312312312).
3+
4+
> vite --port 12312312313 2>&1 | grep RangeError # vite without args should be alias to dev command
5+
RangeError [ERR_SOCKET_BAD_PORT]: options.port should be >= 0 and < 65536. Received type number (12312312313).

packages/cli/snap-tests/command-dev-with-port/steps.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"VITE_DISABLE_AUTO_INSTALL": "1"
55
},
66
"commands": [
7-
"vite dev --port 12312312312 2>&1 | grep RangeError # intentionally use an invalid port (exceeds 0-65535) to trigger RangeError"
7+
"vite dev --port 12312312312 2>&1 | grep RangeError # intentionally use an invalid port (exceeds 0-65535) to trigger RangeError",
8+
"vite --port 12312312313 2>&1 | grep RangeError # vite without args should be alias to dev command"
89
]
910
}

packages/cli/snap-tests/command-helper/snap.txt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,41 @@ Options:
357357
-m, --mode <mode> [string] set env mode
358358
-h, --help Display this message
359359

360+
361+
> vite dev -h # dev help message
362+
vite/<semver>
363+
364+
Usage:
365+
$ vite [root]
366+
367+
Commands:
368+
[root] start dev server
369+
build [root] build for production
370+
optimize [root] pre-bundle dependencies (deprecated, the pre-bundle process runs automatically and does not need to be called)
371+
preview [root] locally preview production build
372+
373+
For more info, run any command with the `--help` flag:
374+
$ vite --help
375+
$ vite build --help
376+
$ vite optimize --help
377+
$ vite preview --help
378+
379+
Options:
380+
--host [host] [string] specify hostname
381+
--port <port> [number] specify port
382+
--open [path] [boolean | string] open browser on startup
383+
--cors [boolean] enable CORS
384+
--strictPort [boolean] exit if specified port is already in use
385+
--force [boolean] force the optimizer to ignore the cache and re-bundle
386+
--experimentalBundle [boolean] use experimental full bundle mode (this is highly experimental)
387+
-c, --config <file> [string] use specified config file
388+
--base <path> [string] public base path (default: /)
389+
-l, --logLevel <level> [string] info | warn | error | silent
390+
--clearScreen [boolean] allow/disable clear screen when logging
391+
--configLoader <loader> [string] use 'bundle' to bundle the config with Rolldown, or 'runner' (experimental) to process it on the fly, or 'native' (experimental) to load using the native runtime (default: bundle)
392+
-d, --debug [feat] [string | boolean] show debug logs
393+
-f, --filter <filter> [string] filter debug logs
394+
-m, --mode <mode> [string] set env mode
395+
-h, --help Display this message
396+
-v, --version Display version number
397+

packages/cli/snap-tests/command-helper/steps.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"vite lint -h # lint help message",
1111
"vite build -h # build help message",
1212
"vite test -h # test help message",
13-
"vite preview -h # preview help message"
13+
"vite preview -h # preview help message",
14+
"vite dev -h # dev help message"
1415
]
1516
}

0 commit comments

Comments
 (0)