1pub mod extra_args_warning;
4pub mod formatting;
5pub mod get_default_tsconfig_path;
6pub mod gracefully_exit_on_prompt_error;
7pub mod is_module_available;
8pub mod load_configuration;
9pub mod local_binaries;
10pub mod os_info_utils;
11pub mod project_utils;
12pub mod remaining_flags;
13pub mod tree_kill;
14pub mod type_assertions;
15
16pub use formatting::normalize_to_kebab_or_snake_case;
17pub use get_default_tsconfig_path::get_default_tsconfig_path;
18pub use is_module_available::is_module_available;
19pub use remaining_flags::{
20 CommanderOption, camel_case_flag, get_remaining_flags, get_remaining_flags_simple,
21};
22
23pub fn normalize_to_path_case(value: &str) -> String {
24 let mut output = String::with_capacity(value.len());
25 let mut previous: Option<char> = None;
26
27 for character in value.chars() {
28 if character.is_ascii_uppercase()
29 && previous
30 .is_some_and(|previous| previous.is_ascii_lowercase() || previous.is_ascii_digit())
31 {
32 output.push('-');
33 }
34
35 match character {
36 c if c.is_whitespace() => output.push('-'),
37 c if c.is_ascii_alphanumeric() || c == '_' || c == '-' => {
38 output.push(c.to_ascii_lowercase())
39 }
40 _ if !output.ends_with('-') => output.push('-'),
41 _ => {}
42 }
43
44 previous = Some(character);
45 }
46
47 output.trim_matches('-').to_string()
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn normalizes_camel_case() {
56 assert_eq!(normalize_to_kebab_or_snake_case("dryRun"), "dry-run");
57 }
58
59 #[test]
60 fn filters_consumed_remaining_flags() {
61 let args = vec![
62 "nest".to_string(),
63 "build".to_string(),
64 "--watch".to_string(),
65 "--webpack".to_string(),
66 ];
67
68 assert_eq!(
69 get_remaining_flags_simple(&args, &["--watch"]),
70 vec!["--webpack".to_string()]
71 );
72 }
73}