Skip to content

Commit 018e4ef

Browse files
authored
feat: add 11 missing pm subcommands (login, logout, whoami, token, audit, dist-tag, deprecate, search, rebuild, fund, ping) (#613)
Implements the commands from issue #611. Each command follows existing patterns: "always npm" commands (token, deprecate, search, fund, ping) hardcode npm as the binary, while PM-specific commands (login, logout, whoami, audit, dist-tag, rebuild) branch on the detected package manager with appropriate mappings for pnpm, npm, yarn@1, and yarn@2+. Includes CLI definitions, dispatch wiring, unit tests for all command resolution logic, and updated RFC documentation. closes VP-191
1 parent 5fff361 commit 018e4ef

16 files changed

Lines changed: 3046 additions & 182 deletions

File tree

crates/vite_global_cli/src/cli.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,153 @@ pub enum PmCommands {
10401040
/// Manage package manager configuration
10411041
#[command(subcommand, visible_alias = "c")]
10421042
Config(ConfigCommands),
1043+
1044+
/// Log in to a registry
1045+
#[command(visible_alias = "adduser")]
1046+
Login {
1047+
/// Registry URL
1048+
#[arg(long, value_name = "URL")]
1049+
registry: Option<String>,
1050+
1051+
/// Scope for the login
1052+
#[arg(long, value_name = "SCOPE")]
1053+
scope: Option<String>,
1054+
1055+
/// Additional arguments
1056+
#[arg(last = true, allow_hyphen_values = true)]
1057+
pass_through_args: Option<Vec<String>>,
1058+
},
1059+
1060+
/// Log out from a registry
1061+
Logout {
1062+
/// Registry URL
1063+
#[arg(long, value_name = "URL")]
1064+
registry: Option<String>,
1065+
1066+
/// Scope for the logout
1067+
#[arg(long, value_name = "SCOPE")]
1068+
scope: Option<String>,
1069+
1070+
/// Additional arguments
1071+
#[arg(last = true, allow_hyphen_values = true)]
1072+
pass_through_args: Option<Vec<String>>,
1073+
},
1074+
1075+
/// Show the current logged-in user
1076+
Whoami {
1077+
/// Registry URL
1078+
#[arg(long, value_name = "URL")]
1079+
registry: Option<String>,
1080+
1081+
/// Additional arguments
1082+
#[arg(last = true, allow_hyphen_values = true)]
1083+
pass_through_args: Option<Vec<String>>,
1084+
},
1085+
1086+
/// Manage authentication tokens
1087+
#[command(subcommand)]
1088+
Token(TokenCommands),
1089+
1090+
/// Run a security audit
1091+
Audit {
1092+
/// Automatically fix vulnerabilities
1093+
#[arg(long)]
1094+
fix: bool,
1095+
1096+
/// Output in JSON format
1097+
#[arg(long)]
1098+
json: bool,
1099+
1100+
/// Minimum vulnerability level to report
1101+
#[arg(long, value_name = "LEVEL")]
1102+
level: Option<String>,
1103+
1104+
/// Only audit production dependencies
1105+
#[arg(long)]
1106+
production: bool,
1107+
1108+
/// Additional arguments
1109+
#[arg(last = true, allow_hyphen_values = true)]
1110+
pass_through_args: Option<Vec<String>>,
1111+
},
1112+
1113+
/// Manage distribution tags
1114+
#[command(name = "dist-tag", subcommand)]
1115+
DistTag(DistTagCommands),
1116+
1117+
/// Deprecate a package version
1118+
Deprecate {
1119+
/// Package name with version (e.g., "my-pkg@1.0.0")
1120+
package: String,
1121+
1122+
/// Deprecation message
1123+
message: String,
1124+
1125+
/// One-time password for authentication
1126+
#[arg(long, value_name = "OTP")]
1127+
otp: Option<String>,
1128+
1129+
/// Registry URL
1130+
#[arg(long, value_name = "URL")]
1131+
registry: Option<String>,
1132+
1133+
/// Additional arguments
1134+
#[arg(last = true, allow_hyphen_values = true)]
1135+
pass_through_args: Option<Vec<String>>,
1136+
},
1137+
1138+
/// Search for packages in the registry
1139+
Search {
1140+
/// Search terms
1141+
#[arg(required = true, num_args = 1..)]
1142+
terms: Vec<String>,
1143+
1144+
/// Output in JSON format
1145+
#[arg(long)]
1146+
json: bool,
1147+
1148+
/// Show extended information
1149+
#[arg(long)]
1150+
long: bool,
1151+
1152+
/// Registry URL
1153+
#[arg(long, value_name = "URL")]
1154+
registry: Option<String>,
1155+
1156+
/// Additional arguments
1157+
#[arg(last = true, allow_hyphen_values = true)]
1158+
pass_through_args: Option<Vec<String>>,
1159+
},
1160+
1161+
/// Rebuild native modules
1162+
#[command(visible_alias = "rb")]
1163+
Rebuild {
1164+
/// Additional arguments
1165+
#[arg(last = true, allow_hyphen_values = true)]
1166+
pass_through_args: Option<Vec<String>>,
1167+
},
1168+
1169+
/// Show funding information for installed packages
1170+
Fund {
1171+
/// Output in JSON format
1172+
#[arg(long)]
1173+
json: bool,
1174+
1175+
/// Additional arguments
1176+
#[arg(last = true, allow_hyphen_values = true)]
1177+
pass_through_args: Option<Vec<String>>,
1178+
},
1179+
1180+
/// Ping the registry
1181+
Ping {
1182+
/// Registry URL
1183+
#[arg(long, value_name = "URL")]
1184+
registry: Option<String>,
1185+
1186+
/// Additional arguments
1187+
#[arg(last = true, allow_hyphen_values = true)]
1188+
pass_through_args: Option<Vec<String>>,
1189+
},
10431190
}
10441191

10451192
/// Configuration subcommands
@@ -1153,6 +1300,92 @@ pub enum OwnerCommands {
11531300
},
11541301
}
11551302

1303+
/// Token subcommands
1304+
#[derive(Subcommand, Debug, Clone)]
1305+
pub enum TokenCommands {
1306+
/// List all known tokens
1307+
#[command(visible_alias = "ls")]
1308+
List {
1309+
/// Output in JSON format
1310+
#[arg(long)]
1311+
json: bool,
1312+
1313+
/// Registry URL
1314+
#[arg(long, value_name = "URL")]
1315+
registry: Option<String>,
1316+
1317+
/// Additional arguments
1318+
#[arg(last = true, allow_hyphen_values = true)]
1319+
pass_through_args: Option<Vec<String>>,
1320+
},
1321+
1322+
/// Create a new authentication token
1323+
Create {
1324+
/// Output in JSON format
1325+
#[arg(long)]
1326+
json: bool,
1327+
1328+
/// Registry URL
1329+
#[arg(long, value_name = "URL")]
1330+
registry: Option<String>,
1331+
1332+
/// CIDR ranges to restrict the token to
1333+
#[arg(long, value_name = "CIDR")]
1334+
cidr: Option<Vec<String>>,
1335+
1336+
/// Create a read-only token
1337+
#[arg(long)]
1338+
readonly: bool,
1339+
1340+
/// Additional arguments
1341+
#[arg(last = true, allow_hyphen_values = true)]
1342+
pass_through_args: Option<Vec<String>>,
1343+
},
1344+
1345+
/// Revoke an authentication token
1346+
Revoke {
1347+
/// Token or token ID to revoke
1348+
token: String,
1349+
1350+
/// Registry URL
1351+
#[arg(long, value_name = "URL")]
1352+
registry: Option<String>,
1353+
1354+
/// Additional arguments
1355+
#[arg(last = true, allow_hyphen_values = true)]
1356+
pass_through_args: Option<Vec<String>>,
1357+
},
1358+
}
1359+
1360+
/// Distribution tag subcommands
1361+
#[derive(Subcommand, Debug, Clone)]
1362+
pub enum DistTagCommands {
1363+
/// List distribution tags for a package
1364+
#[command(visible_alias = "ls")]
1365+
List {
1366+
/// Package name
1367+
package: Option<String>,
1368+
},
1369+
1370+
/// Add a distribution tag
1371+
Add {
1372+
/// Package name with version (e.g., "my-pkg@1.0.0")
1373+
package_at_version: String,
1374+
1375+
/// Tag name
1376+
tag: String,
1377+
},
1378+
1379+
/// Remove a distribution tag
1380+
Rm {
1381+
/// Package name
1382+
package: String,
1383+
1384+
/// Tag name
1385+
tag: String,
1386+
},
1387+
}
1388+
11561389
/// Determine the save dependency type from CLI flags.
11571390
fn determine_save_dependency_type(
11581391
save_dev: bool,

crates/vite_global_cli/src/commands/pm.rs

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,31 @@
77
use std::process::ExitStatus;
88

99
use vite_install::commands::{
10-
cache::CacheCommandOptions, config::ConfigCommandOptions, list::ListCommandOptions,
11-
owner::OwnerSubcommand, pack::PackCommandOptions, prune::PruneCommandOptions,
12-
publish::PublishCommandOptions, view::ViewCommandOptions,
10+
audit::AuditCommandOptions,
11+
cache::CacheCommandOptions,
12+
config::ConfigCommandOptions,
13+
deprecate::DeprecateCommandOptions,
14+
dist_tag::{DistTagCommandOptions, DistTagSubcommand},
15+
fund::FundCommandOptions,
16+
list::ListCommandOptions,
17+
login::LoginCommandOptions,
18+
logout::LogoutCommandOptions,
19+
owner::OwnerSubcommand,
20+
pack::PackCommandOptions,
21+
ping::PingCommandOptions,
22+
prune::PruneCommandOptions,
23+
publish::PublishCommandOptions,
24+
rebuild::RebuildCommandOptions,
25+
search::SearchCommandOptions,
26+
token::TokenSubcommand,
27+
view::ViewCommandOptions,
28+
whoami::WhoamiCommandOptions,
1329
};
1430
use vite_path::AbsolutePathBuf;
1531

1632
use super::{build_package_manager, prepend_js_runtime_to_path_env};
1733
use crate::{
18-
cli::{ConfigCommands, OwnerCommands, PmCommands},
34+
cli::{ConfigCommands, DistTagCommands, OwnerCommands, PmCommands, TokenCommands},
1935
error::Error,
2036
};
2137

@@ -228,6 +244,111 @@ pub async fn execute_pm_subcommand(
228244
Ok(package_manager.run_config_command(&options, &cwd).await?)
229245
}
230246
},
247+
248+
PmCommands::Login { registry, scope, pass_through_args } => {
249+
let options = LoginCommandOptions {
250+
registry: registry.as_deref(),
251+
scope: scope.as_deref(),
252+
pass_through_args: pass_through_args.as_deref(),
253+
};
254+
Ok(package_manager.run_login_command(&options, &cwd).await?)
255+
}
256+
257+
PmCommands::Logout { registry, scope, pass_through_args } => {
258+
let options = LogoutCommandOptions {
259+
registry: registry.as_deref(),
260+
scope: scope.as_deref(),
261+
pass_through_args: pass_through_args.as_deref(),
262+
};
263+
Ok(package_manager.run_logout_command(&options, &cwd).await?)
264+
}
265+
266+
PmCommands::Whoami { registry, pass_through_args } => {
267+
let options = WhoamiCommandOptions {
268+
registry: registry.as_deref(),
269+
pass_through_args: pass_through_args.as_deref(),
270+
};
271+
Ok(package_manager.run_whoami_command(&options, &cwd).await?)
272+
}
273+
274+
PmCommands::Token(token_command) => {
275+
let subcommand = match token_command {
276+
TokenCommands::List { json, registry, pass_through_args } => {
277+
TokenSubcommand::List { json, registry, pass_through_args }
278+
}
279+
TokenCommands::Create { json, registry, cidr, readonly, pass_through_args } => {
280+
TokenSubcommand::Create { json, registry, cidr, readonly, pass_through_args }
281+
}
282+
TokenCommands::Revoke { token, registry, pass_through_args } => {
283+
TokenSubcommand::Revoke { token, registry, pass_through_args }
284+
}
285+
};
286+
Ok(package_manager.run_token_command(&subcommand, &cwd).await?)
287+
}
288+
289+
PmCommands::Audit { fix, json, level, production, pass_through_args } => {
290+
let options = AuditCommandOptions {
291+
fix,
292+
json,
293+
level: level.as_deref(),
294+
production,
295+
pass_through_args: pass_through_args.as_deref(),
296+
};
297+
Ok(package_manager.run_audit_command(&options, &cwd).await?)
298+
}
299+
300+
PmCommands::DistTag(dist_tag_command) => {
301+
let subcommand = match dist_tag_command {
302+
DistTagCommands::List { package } => DistTagSubcommand::List { package },
303+
DistTagCommands::Add { package_at_version, tag } => {
304+
DistTagSubcommand::Add { package_at_version, tag }
305+
}
306+
DistTagCommands::Rm { package, tag } => DistTagSubcommand::Rm { package, tag },
307+
};
308+
let options = DistTagCommandOptions { subcommand, pass_through_args: None };
309+
Ok(package_manager.run_dist_tag_command(&options, &cwd).await?)
310+
}
311+
312+
PmCommands::Deprecate { package, message, otp, registry, pass_through_args } => {
313+
let options = DeprecateCommandOptions {
314+
package: &package,
315+
message: &message,
316+
otp: otp.as_deref(),
317+
registry: registry.as_deref(),
318+
pass_through_args: pass_through_args.as_deref(),
319+
};
320+
Ok(package_manager.run_deprecate_command(&options, &cwd).await?)
321+
}
322+
323+
PmCommands::Search { terms, json, long, registry, pass_through_args } => {
324+
let options = SearchCommandOptions {
325+
terms: &terms,
326+
json,
327+
long,
328+
registry: registry.as_deref(),
329+
pass_through_args: pass_through_args.as_deref(),
330+
};
331+
Ok(package_manager.run_search_command(&options, &cwd).await?)
332+
}
333+
334+
PmCommands::Rebuild { pass_through_args } => {
335+
let options = RebuildCommandOptions { pass_through_args: pass_through_args.as_deref() };
336+
Ok(package_manager.run_rebuild_command(&options, &cwd).await?)
337+
}
338+
339+
PmCommands::Fund { json, pass_through_args } => {
340+
let options =
341+
FundCommandOptions { json, pass_through_args: pass_through_args.as_deref() };
342+
Ok(package_manager.run_fund_command(&options, &cwd).await?)
343+
}
344+
345+
PmCommands::Ping { registry, pass_through_args } => {
346+
let options = PingCommandOptions {
347+
registry: registry.as_deref(),
348+
pass_through_args: pass_through_args.as_deref(),
349+
};
350+
Ok(package_manager.run_ping_command(&options, &cwd).await?)
351+
}
231352
}
232353
}
233354

0 commit comments

Comments
 (0)