Skip to content

Commit 740e7b0

Browse files
authored
chore: Improve Rust-based help command printing. (#678)
1 parent 264a499 commit 740e7b0

18 files changed

Lines changed: 674 additions & 165 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_global_cli/src/cli.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use std::process::ExitStatus;
77

88
use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
9+
use owo_colors::OwoColorize;
910
use vite_install::commands::{
1011
add::SaveDependencyType, install::InstallCommandOptions, outdated::Format,
1112
};
@@ -1843,9 +1844,19 @@ pub async fn run_command(cwd: AbsolutePathBuf, args: Args) -> Result<ExitStatus,
18431844
commands::delegate::execute(cwd, "pack", &args).await
18441845
}
18451846

1846-
Commands::Run { args } => commands::run_or_delegate::execute(cwd, &args).await,
1847+
Commands::Run { args } => {
1848+
if help::maybe_print_unified_delegate_help("run", &args) {
1849+
return Ok(ExitStatus::default());
1850+
}
1851+
commands::run_or_delegate::execute(cwd, &args).await
1852+
}
18471853

1848-
Commands::Exec { args } => commands::delegate::execute(cwd, "exec", &args).await,
1854+
Commands::Exec { args } => {
1855+
if help::maybe_print_unified_delegate_help("exec", &args) {
1856+
return Ok(ExitStatus::default());
1857+
}
1858+
commands::delegate::execute(cwd, "exec", &args).await
1859+
}
18491860

18501861
Commands::Preview { args } => {
18511862
if help::maybe_print_unified_delegate_help("preview", &args) {
@@ -1854,7 +1865,12 @@ pub async fn run_command(cwd: AbsolutePathBuf, args: Args) -> Result<ExitStatus,
18541865
commands::delegate::execute(cwd, "preview", &args).await
18551866
}
18561867

1857-
Commands::Cache { args } => commands::delegate::execute(cwd, "cache", &args).await,
1868+
Commands::Cache { args } => {
1869+
if help::maybe_print_unified_delegate_help("cache", &args) {
1870+
return Ok(ExitStatus::default());
1871+
}
1872+
commands::delegate::execute(cwd, "cache", &args).await
1873+
}
18581874

18591875
Commands::Env(args) => commands::env::execute(cwd, args).await,
18601876

@@ -1895,15 +1911,14 @@ pub fn command_with_help() -> clap::Command {
18951911

18961912
/// Apply custom help formatting to a clap Command to match the JS CLI output.
18971913
fn apply_custom_help(cmd: clap::Command) -> clap::Command {
1898-
let version = env!("CARGO_PKG_VERSION");
18991914
let after_help = help::render_help_doc(&help::top_level_help_doc());
1900-
let help_template = format!(
1901-
"Vite+/{version}\
1902-
{{after-help}}
1903-
Options:
1904-
{{options}}
1905-
"
1906-
);
1915+
let options_heading = help::render_heading("Options");
1916+
let header = if help::should_style_help() {
1917+
"VITE+ - The Unified Toolchain for the Web".bold().to_string()
1918+
} else {
1919+
"VITE+ - The Unified Toolchain for the Web".to_string()
1920+
};
1921+
let help_template = format!("{header}{{after-help}}\n{options_heading}\n{{options}}\n");
19071922

19081923
cmd.after_help(after_help).help_template(help_template)
19091924
}

crates/vite_global_cli/src/commands/env/mod.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,18 @@ pub async fn execute(cwd: AbsolutePathBuf, args: EnvArgs) -> Result<ExitStatus,
120120
return print_env(cwd).await;
121121
}
122122

123-
// No flags provided - show help (use clap's built-in help printer)
124-
use clap::CommandFactory;
125-
let bin_name = crate::cli::Args::command().get_bin_name().unwrap_or("vp").to_string();
126-
let display_name: &'static str = Box::leak(format!("{bin_name} env").into_boxed_str());
127-
crate::cli::Args::command()
128-
.find_subcommand("env")
129-
.unwrap()
130-
.clone()
131-
.name(display_name)
132-
.disable_help_subcommand(true)
133-
.print_help()
134-
.ok();
123+
// No flags provided - show unified help to match `vp env --help`.
124+
if !crate::help::print_unified_clap_help_for_path(&["env"]) {
125+
// Fallback to clap's built-in help printer if unified rendering fails.
126+
use clap::CommandFactory;
127+
crate::cli::Args::command()
128+
.find_subcommand("env")
129+
.unwrap()
130+
.clone()
131+
.disable_help_subcommand(true)
132+
.print_help()
133+
.ok();
134+
}
135135
Ok(ExitStatus::default())
136136
}
137137

Lines changed: 156 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,169 @@
1-
//! Version command (Category B: JavaScript Command).
1+
//! Version command.
22
3-
use std::process::ExitStatus;
3+
use std::{
4+
collections::BTreeMap,
5+
fs,
6+
path::{Path, PathBuf},
7+
process::ExitStatus,
8+
};
49

10+
use owo_colors::OwoColorize;
11+
use serde::Deserialize;
512
use vite_path::AbsolutePathBuf;
613

7-
use crate::{error::Error, js_executor::JsExecutor};
14+
use crate::{error::Error, help};
815

9-
/// Execute the `--version` command by delegating to local or global vite-plus.
10-
///
11-
/// Uses the CLI's own runtime instead of the project's runtime to avoid
12-
/// side effects (e.g., writing `.node-version` in the caller's directory).
13-
pub async fn execute(cwd: AbsolutePathBuf) -> Result<ExitStatus, Error> {
14-
// Pass the Rust binary's version to JS so it can display the correct global version,
15-
// even when delegation resolves to a local node_modules copy.
16-
unsafe {
17-
std::env::set_var(
18-
vite_shared::env_vars::VITE_PLUS_GLOBAL_VERSION,
19-
env!("CARGO_PKG_VERSION"),
20-
);
16+
#[derive(Debug, Deserialize)]
17+
#[serde(rename_all = "camelCase")]
18+
struct PackageJson {
19+
version: String,
20+
#[serde(default)]
21+
bundled_versions: BTreeMap<String, String>,
22+
}
23+
24+
#[derive(Debug)]
25+
struct LocalVitePlus {
26+
version: String,
27+
package_dir: PathBuf,
28+
}
29+
30+
#[derive(Debug, Clone, Copy)]
31+
struct ToolSpec {
32+
display_name: &'static str,
33+
package_name: &'static str,
34+
bundled_version_key: Option<&'static str>,
35+
}
36+
37+
const TOOL_SPECS: [ToolSpec; 7] = [
38+
ToolSpec {
39+
display_name: "vite",
40+
package_name: "@voidzero-dev/vite-plus-core",
41+
bundled_version_key: Some("vite"),
42+
},
43+
ToolSpec {
44+
display_name: "rolldown",
45+
package_name: "@voidzero-dev/vite-plus-core",
46+
bundled_version_key: Some("rolldown"),
47+
},
48+
ToolSpec {
49+
display_name: "vitest",
50+
package_name: "@voidzero-dev/vite-plus-test",
51+
bundled_version_key: Some("vitest"),
52+
},
53+
ToolSpec { display_name: "oxfmt", package_name: "oxfmt", bundled_version_key: None },
54+
ToolSpec { display_name: "oxlint", package_name: "oxlint", bundled_version_key: None },
55+
ToolSpec {
56+
display_name: "oxlint-tsgolint",
57+
package_name: "oxlint-tsgolint",
58+
bundled_version_key: None,
59+
},
60+
ToolSpec {
61+
display_name: "tsdown",
62+
package_name: "@voidzero-dev/vite-plus-core",
63+
bundled_version_key: Some("tsdown"),
64+
},
65+
];
66+
67+
fn read_package_json(package_json_path: &Path) -> Option<PackageJson> {
68+
let content = fs::read_to_string(package_json_path).ok()?;
69+
serde_json::from_str(&content).ok()
70+
}
71+
72+
fn find_local_vite_plus(start: &Path) -> Option<LocalVitePlus> {
73+
let mut current = Some(start);
74+
while let Some(dir) = current {
75+
let package_json_path = dir.join("node_modules").join("vite-plus").join("package.json");
76+
if let Some(pkg) = read_package_json(&package_json_path) {
77+
let package_dir = package_json_path.parent()?.to_path_buf();
78+
return Some(LocalVitePlus { version: pkg.version, package_dir });
79+
}
80+
current = dir.parent();
81+
}
82+
None
83+
}
84+
85+
fn resolve_package_json(base_dir: &Path, package_name: &str) -> Option<PackageJson> {
86+
let mut current = Some(base_dir);
87+
while let Some(dir) = current {
88+
let package_json_path = dir.join("node_modules").join(package_name).join("package.json");
89+
if let Some(pkg) = read_package_json(&package_json_path) {
90+
return Some(pkg);
91+
}
92+
current = dir.parent();
93+
}
94+
None
95+
}
96+
97+
fn resolve_tool_version(local: &LocalVitePlus, tool: ToolSpec) -> Option<String> {
98+
let pkg = resolve_package_json(&local.package_dir, tool.package_name)?;
99+
if let Some(key) = tool.bundled_version_key
100+
&& let Some(version) = pkg.bundled_versions.get(key)
101+
{
102+
return Some(version.clone());
21103
}
22-
let mut executor = JsExecutor::new(None);
23-
executor.delegate_with_cli_runtime(&cwd, &["--version".to_string()]).await
104+
Some(pkg.version)
105+
}
106+
107+
fn accent(text: &str) -> String {
108+
if help::should_style_help() { text.bright_blue().to_string() } else { text.to_string() }
109+
}
110+
111+
fn print_rows(title: &str, rows: &[(&str, String)]) {
112+
println!("{}", help::render_heading(title));
113+
let label_width = rows.iter().map(|(label, _)| label.chars().count()).max().unwrap_or(0);
114+
for (label, value) in rows {
115+
let padding = " ".repeat(label_width.saturating_sub(label.chars().count()));
116+
println!(" {}{} {value}", accent(label), padding);
117+
}
118+
}
119+
120+
fn format_version(version: Option<String>) -> String {
121+
match version {
122+
Some(v) => format!("v{v}"),
123+
None => "Not found".to_string(),
124+
}
125+
}
126+
127+
/// Execute the `--version` command.
128+
pub async fn execute(cwd: AbsolutePathBuf) -> Result<ExitStatus, Error> {
129+
let header = if help::should_style_help() {
130+
"VITE+ - The Unified Toolchain for the Web".bold().to_string()
131+
} else {
132+
"VITE+ - The Unified Toolchain for the Web".to_string()
133+
};
134+
println!("{header}");
135+
println!();
136+
137+
println!("vp v{}", env!("CARGO_PKG_VERSION"));
138+
println!();
139+
140+
let local = find_local_vite_plus(cwd.as_path());
141+
print_rows(
142+
"Local vite-plus",
143+
&[("vite-plus", format_version(local.as_ref().map(|pkg| pkg.version.clone())))],
144+
);
145+
println!();
146+
147+
let tool_rows = TOOL_SPECS
148+
.iter()
149+
.map(|tool| {
150+
let version =
151+
local.as_ref().and_then(|local_pkg| resolve_tool_version(local_pkg, *tool));
152+
(tool.display_name, format_version(version))
153+
})
154+
.collect::<Vec<_>>();
155+
print_rows("Tools", &tool_rows);
156+
157+
Ok(ExitStatus::default())
24158
}
25159

26160
#[cfg(test)]
27161
mod tests {
162+
use super::format_version;
163+
28164
#[test]
29-
fn test_version_command_module_exists() {
30-
// Basic test to ensure the module compiles
31-
assert!(true);
165+
fn format_version_values() {
166+
assert_eq!(format_version(Some("1.2.3".to_string())), "v1.2.3");
167+
assert_eq!(format_version(None), "Not found");
32168
}
33169
}

0 commit comments

Comments
 (0)