mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
fmt: deno fmt -
formats stdin and print to stdout (#3920)
This commit is contained in:
parent
1c0ffa1383
commit
5066018412
4 changed files with 115 additions and 7 deletions
17
cli/flags.rs
17
cli/flags.rs
|
@ -550,7 +550,10 @@ fn fmt_subcommand<'a, 'b>() -> App<'a, 'b> {
|
||||||
|
|
||||||
deno fmt myfile1.ts myfile2.ts
|
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(
|
||||||
Arg::with_name("check")
|
Arg::with_name("check")
|
||||||
|
@ -1390,6 +1393,18 @@ mod tests {
|
||||||
..DenoFlags::default()
|
..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]
|
#[test]
|
||||||
|
|
63
cli/fmt.rs
63
cli/fmt.rs
|
@ -7,10 +7,17 @@
|
||||||
//! the future it can be easily extended to provide
|
//! the future it can be easily extended to provide
|
||||||
//! the same functions as ops available in JS runtime.
|
//! 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 dprint_plugin_typescript as dprint;
|
||||||
use glob;
|
use glob;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::fs;
|
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::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Instant;
|
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`
|
/// First argument supports globs, and if it is `None`
|
||||||
/// then the current directory is recursively walked.
|
/// 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
|
// TODO: improve glob to look for tsx?/jsx? files only
|
||||||
let glob_paths = maybe_files.unwrap_or_else(|| vec!["**/*".to_string()]);
|
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_matching_files(glob_paths);
|
||||||
let matching_files = get_supported_files(matching_files);
|
let matching_files = get_supported_files(matching_files);
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
@ -169,4 +196,38 @@ pub fn format_files(maybe_files: Option<Vec<String>>, check: bool) {
|
||||||
} else {
|
} else {
|
||||||
format_source_files(config, matching_files);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -405,7 +405,9 @@ async fn run_script(flags: DenoFlags, script: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fmt_command(files: Option<Vec<String>>, check: bool) {
|
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() {
|
pub fn main() {
|
||||||
|
|
|
@ -603,6 +603,32 @@ itest!(bundle {
|
||||||
output: "bundle.test.out",
|
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 {
|
itest!(circular1 {
|
||||||
args: "run --reload circular1.js",
|
args: "run --reload circular1.js",
|
||||||
output: "circular1.js.out",
|
output: "circular1.js.out",
|
||||||
|
@ -917,6 +943,7 @@ mod util {
|
||||||
pub args: &'static str,
|
pub args: &'static str,
|
||||||
pub output: &'static str,
|
pub output: &'static str,
|
||||||
pub input: Option<&'static str>,
|
pub input: Option<&'static str>,
|
||||||
|
pub output_str: Option<&'static str>,
|
||||||
pub exit_code: i32,
|
pub exit_code: i32,
|
||||||
pub check_stderr: bool,
|
pub check_stderr: bool,
|
||||||
pub http_server: bool,
|
pub http_server: bool,
|
||||||
|
@ -982,10 +1009,13 @@ mod util {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let expected = if let Some(s) = self.output_str {
|
||||||
|
s.to_owned()
|
||||||
|
} else {
|
||||||
let output_path = tests_dir.join(self.output);
|
let output_path = tests_dir.join(self.output);
|
||||||
println!("output path {}", output_path.display());
|
println!("output path {}", output_path.display());
|
||||||
let expected =
|
std::fs::read_to_string(output_path).expect("cannot read output")
|
||||||
std::fs::read_to_string(output_path).expect("cannot read output");
|
};
|
||||||
|
|
||||||
if !wildcard_match(&expected, &actual) {
|
if !wildcard_match(&expected, &actual) {
|
||||||
println!("OUTPUT\n{}\nOUTPUT", actual);
|
println!("OUTPUT\n{}\nOUTPUT", actual);
|
||||||
|
|
Loading…
Reference in a new issue