diff options
-rw-r--r-- | cli/flags.rs | 17 | ||||
-rw-r--r-- | cli/fmt.rs | 63 | ||||
-rw-r--r-- | cli/lib.rs | 4 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 38 |
4 files changed, 115 insertions, 7 deletions
diff --git a/cli/flags.rs b/cli/flags.rs index 748a7f95e..a8c51093e 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -550,7 +550,10 @@ fn fmt_subcommand<'a, 'b>() -> App<'a, 'b> { deno fmt myfile1.ts myfile2.ts - deno fmt --check", + deno fmt --check + + # Format stdin and write to stdout + cat file.ts | deno fmt -", ) .arg( Arg::with_name("check") @@ -1390,6 +1393,18 @@ mod tests { ..DenoFlags::default() } ); + + let r = flags_from_vec_safe(svec!["deno", "fmt"]); + assert_eq!( + r.unwrap(), + DenoFlags { + subcommand: DenoSubcommand::Format { + check: false, + files: None + }, + ..DenoFlags::default() + } + ); } #[test] diff --git a/cli/fmt.rs b/cli/fmt.rs index f007d8da8..390203f14 100644 --- a/cli/fmt.rs +++ b/cli/fmt.rs @@ -7,10 +7,17 @@ //! the future it can be easily extended to provide //! the same functions as ops available in JS runtime. +use crate::deno_error::DenoError; +use crate::deno_error::ErrorKind; +use deno_core::ErrBox; use dprint_plugin_typescript as dprint; use glob; use regex::Regex; use std::fs; +use std::io::stdin; +use std::io::stdout; +use std::io::Read; +use std::io::Write; use std::path::Path; use std::path::PathBuf; use std::time::Instant; @@ -157,9 +164,29 @@ fn get_matching_files(glob_paths: Vec<String>) -> Vec<PathBuf> { /// /// First argument supports globs, and if it is `None` /// then the current directory is recursively walked. -pub fn format_files(maybe_files: Option<Vec<String>>, check: bool) { +pub fn format_files( + maybe_files: Option<Vec<String>>, + check: bool, +) -> Result<(), ErrBox> { // TODO: improve glob to look for tsx?/jsx? files only let glob_paths = maybe_files.unwrap_or_else(|| vec!["**/*".to_string()]); + + for glob_path in glob_paths.iter() { + if glob_path == "-" { + // If the only given path is '-', format stdin. + if glob_paths.len() == 1 { + format_stdin(check); + } else { + // Otherwise it is an error + return Err(ErrBox::from(DenoError::new( + ErrorKind::Other, + "Ambiguous filename input. To format stdin, provide a single '-' instead.".to_owned() + ))); + } + return Ok(()); + } + } + let matching_files = get_matching_files(glob_paths); let matching_files = get_supported_files(matching_files); let config = get_config(); @@ -169,4 +196,38 @@ pub fn format_files(maybe_files: Option<Vec<String>>, check: bool) { } else { format_source_files(config, matching_files); } + + Ok(()) +} + +/// Format stdin and write result to stdout. +/// Treats input as TypeScript. +/// Compatible with `--check` flag. +fn format_stdin(check: bool) { + let mut source = String::new(); + if stdin().read_to_string(&mut source).is_err() { + eprintln!("Failed to read from stdin"); + } + let config = get_config(); + + match dprint::format_text("_stdin.ts", &source, &config) { + Ok(None) => { + // Should not happen. + unreachable!(); + } + Ok(Some(formatted_text)) => { + if check { + if formatted_text != source { + println!("Not formatted stdin"); + } + } else { + let _r = stdout().write_all(formatted_text.as_bytes()); + // TODO(ry) Only ignore SIGPIPE. Currently ignoring all errors. + } + } + Err(e) => { + eprintln!("Error formatting from stdin"); + eprintln!(" {}", e); + } + } } diff --git a/cli/lib.rs b/cli/lib.rs index 945554434..4e100127b 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -405,7 +405,9 @@ async fn run_script(flags: DenoFlags, script: String) { } async fn fmt_command(files: Option<Vec<String>>, check: bool) { - fmt::format_files(files, check); + if let Err(err) = fmt::format_files(files, check) { + print_err_and_exit(err); + } } pub fn main() { diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index ac00a04c4..dd242a32a 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -603,6 +603,32 @@ itest!(bundle { output: "bundle.test.out", }); +itest!(fmt_stdin { + args: "fmt -", + input: Some("const a = 1\n"), + output_str: Some("const a = 1;\n"), +}); + +itest!(fmt_stdin_ambiguous { + args: "fmt - file.ts", + input: Some("const a = 1\n"), + check_stderr: true, + exit_code: 1, + output_str: Some("Ambiguous filename input. To format stdin, provide a single '-' instead.\n"), +}); + +itest!(fmt_stdin_check_formatted { + args: "fmt --check -", + input: Some("const a = 1;\n"), + output_str: Some(""), +}); + +itest!(fmt_stdin_check_not_formatted { + args: "fmt --check -", + input: Some("const a = 1\n"), + output_str: Some("Not formatted stdin\n"), +}); + itest!(circular1 { args: "run --reload circular1.js", output: "circular1.js.out", @@ -917,6 +943,7 @@ mod util { pub args: &'static str, pub output: &'static str, pub input: Option<&'static str>, + pub output_str: Option<&'static str>, pub exit_code: i32, pub check_stderr: bool, pub http_server: bool, @@ -982,10 +1009,13 @@ mod util { ); } - let output_path = tests_dir.join(self.output); - println!("output path {}", output_path.display()); - let expected = - std::fs::read_to_string(output_path).expect("cannot read output"); + let expected = if let Some(s) = self.output_str { + s.to_owned() + } else { + let output_path = tests_dir.join(self.output); + println!("output path {}", output_path.display()); + std::fs::read_to_string(output_path).expect("cannot read output") + }; if !wildcard_match(&expected, &actual) { println!("OUTPUT\n{}\nOUTPUT", actual); |