diff options
author | Satya Rohith <me@satyarohith.com> | 2021-02-18 22:01:32 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-18 17:31:32 +0100 |
commit | d9b1f96897239bf7de8cdfd11d40e3f99bb29a4a (patch) | |
tree | 4a1c60d4850111f738b485bbad5f43659bd9ed09 /cli | |
parent | c0f10e72ee64f1512e6aff20a841f4696e774217 (diff) |
feat: add json(c) support to deno fmt (#9292)
This commit adds support for formatting JSON and JSONC
in "deno fmt".
New values "json" and "jsonc" are added to "--ext" flag for
standard input processing.
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 1 | ||||
-rw-r--r-- | cli/flags.rs | 4 | ||||
-rw-r--r-- | cli/fs_util.rs | 44 | ||||
-rw-r--r-- | cli/tests/badly_formatted.json | 12 | ||||
-rw-r--r-- | cli/tests/badly_formatted.md | 18 | ||||
-rw-r--r-- | cli/tests/badly_formatted_fixed.json | 8 | ||||
-rw-r--r-- | cli/tests/badly_formatted_fixed.md | 14 | ||||
-rw-r--r-- | cli/tests/fmt/expected_fmt_check_formatted_files.out | 2 | ||||
-rw-r--r-- | cli/tests/fmt/expected_fmt_check_ignore.out | 2 | ||||
-rw-r--r-- | cli/tests/fmt/expected_fmt_check_tests_dir.out | 2 | ||||
-rw-r--r-- | cli/tests/fmt/formatted4.jsonc | 4 | ||||
-rw-r--r-- | cli/tests/integration_tests.rs | 42 | ||||
-rw-r--r-- | cli/tools/fmt.rs | 57 |
13 files changed, 166 insertions, 44 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index e8e90190a..f05865faf 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -48,6 +48,7 @@ clap = "2.33.3" dissimilar = "1.0.2" dprint-plugin-typescript = "0.40.1" dprint-plugin-markdown = "0.5.1" +dprint-plugin-json = "0.8.0" encoding_rs = "0.8.28" env_logger = "0.8.2" filetime = "0.2.14" diff --git a/cli/flags.rs b/cli/flags.rs index 80ad6e240..c58e3e3d4 100644 --- a/cli/flags.rs +++ b/cli/flags.rs @@ -797,7 +797,7 @@ fn fmt_subcommand<'a, 'b>() -> App<'a, 'b> { SubCommand::with_name("fmt") .about("Format source files") .long_about( - "Auto-format JavaScript, TypeScript and Markdown files. + "Auto-format JavaScript, TypeScript, Markdown, and JSON files. deno fmt deno fmt myfile1.ts myfile2.ts deno fmt --check @@ -823,7 +823,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file: .help("Set standard input (stdin) content type") .takes_value(true) .default_value("ts") - .possible_values(&["ts", "tsx", "js", "jsx", "md"]), + .possible_values(&["ts", "tsx", "js", "jsx", "md", "json", "jsonc"]), ) .arg( Arg::with_name("ignore") diff --git a/cli/fs_util.rs b/cli/fs_util.rs index a6cd06e78..04cdfff75 100644 --- a/cli/fs_util.rs +++ b/cli/fs_util.rs @@ -97,10 +97,14 @@ pub fn is_supported_ext(path: &Path) -> bool { } } -/// This function is similar to is_supported_ext but also allows .md extension. -pub fn is_supported_ext_md(path: &Path) -> bool { +/// This function is similar to is_supported_ext but adds additional extensions +/// supported by `deno fmt`. +pub fn is_supported_ext_fmt(path: &Path) -> bool { if let Some(ext) = get_extension(path) { - matches!(ext.as_str(), "ts" | "tsx" | "js" | "jsx" | "mjs" | "md") + matches!( + ext.as_str(), + "ts" | "tsx" | "js" | "jsx" | "mjs" | "md" | "json" | "jsonc" + ) } else { false } @@ -221,21 +225,25 @@ mod tests { } #[test] - fn test_is_supported_ext_md() { - assert!(!is_supported_ext_md(Path::new("tests/subdir/redirects"))); - assert!(is_supported_ext_md(Path::new("README.md"))); - assert!(is_supported_ext_md(Path::new("readme.MD"))); - assert!(is_supported_ext_md(Path::new("lib/typescript.d.ts"))); - assert!(is_supported_ext_md(Path::new("cli/tests/001_hello.js"))); - assert!(is_supported_ext_md(Path::new("cli/tests/002_hello.ts"))); - assert!(is_supported_ext_md(Path::new("foo.jsx"))); - assert!(is_supported_ext_md(Path::new("foo.tsx"))); - assert!(is_supported_ext_md(Path::new("foo.TS"))); - assert!(is_supported_ext_md(Path::new("foo.TSX"))); - assert!(is_supported_ext_md(Path::new("foo.JS"))); - assert!(is_supported_ext_md(Path::new("foo.JSX"))); - assert!(is_supported_ext_md(Path::new("foo.mjs"))); - assert!(!is_supported_ext_md(Path::new("foo.mjsx"))); + fn test_is_supported_ext_fmt() { + assert!(!is_supported_ext_fmt(Path::new("tests/subdir/redirects"))); + assert!(is_supported_ext_fmt(Path::new("README.md"))); + assert!(is_supported_ext_fmt(Path::new("readme.MD"))); + assert!(is_supported_ext_fmt(Path::new("lib/typescript.d.ts"))); + assert!(is_supported_ext_fmt(Path::new("cli/tests/001_hello.js"))); + assert!(is_supported_ext_fmt(Path::new("cli/tests/002_hello.ts"))); + assert!(is_supported_ext_fmt(Path::new("foo.jsx"))); + assert!(is_supported_ext_fmt(Path::new("foo.tsx"))); + assert!(is_supported_ext_fmt(Path::new("foo.TS"))); + assert!(is_supported_ext_fmt(Path::new("foo.TSX"))); + assert!(is_supported_ext_fmt(Path::new("foo.JS"))); + assert!(is_supported_ext_fmt(Path::new("foo.JSX"))); + assert!(is_supported_ext_fmt(Path::new("foo.mjs"))); + assert!(!is_supported_ext_fmt(Path::new("foo.mjsx"))); + assert!(is_supported_ext_fmt(Path::new("foo.jsonc"))); + assert!(is_supported_ext_fmt(Path::new("foo.JSONC"))); + assert!(is_supported_ext_fmt(Path::new("foo.json"))); + assert!(is_supported_ext_fmt(Path::new("foo.JsON"))); } #[test] diff --git a/cli/tests/badly_formatted.json b/cli/tests/badly_formatted.json new file mode 100644 index 000000000..f2bacf73d --- /dev/null +++ b/cli/tests/badly_formatted.json @@ -0,0 +1,12 @@ +{ + + + "key1": "value1", + "key2": true, + "key3": ["value2", "value3", false], + "keys": { + "more": "values" + } + + +}
\ No newline at end of file diff --git a/cli/tests/badly_formatted.md b/cli/tests/badly_formatted.md index adffe3e46..26afe483b 100644 --- a/cli/tests/badly_formatted.md +++ b/cli/tests/badly_formatted.md @@ -25,4 +25,22 @@ hello( "alice"); function foo(): number { return 2; } +``` + +```jsonc + +{ + // Comment in JSON + "key": "value", + "key2": + "value2", +} + +``` + +```json +{ + "numbers": + ["1", "2"] +} ```
\ No newline at end of file diff --git a/cli/tests/badly_formatted_fixed.json b/cli/tests/badly_formatted_fixed.json new file mode 100644 index 000000000..0d697a2c6 --- /dev/null +++ b/cli/tests/badly_formatted_fixed.json @@ -0,0 +1,8 @@ +{ + "key1": "value1", + "key2": true, + "key3": ["value2", "value3", false], + "keys": { + "more": "values" + } +} diff --git a/cli/tests/badly_formatted_fixed.md b/cli/tests/badly_formatted_fixed.md index 359a8aead..8ba74cac3 100644 --- a/cli/tests/badly_formatted_fixed.md +++ b/cli/tests/badly_formatted_fixed.md @@ -21,3 +21,17 @@ function foo(): number { return 2; } ``` + +```jsonc +{ + // Comment in JSON + "key": "value", + "key2": "value2" +} +``` + +```json +{ + "numbers": ["1", "2"] +} +``` diff --git a/cli/tests/fmt/expected_fmt_check_formatted_files.out b/cli/tests/fmt/expected_fmt_check_formatted_files.out index 7c1e471b9..5a4833dd4 100644 --- a/cli/tests/fmt/expected_fmt_check_formatted_files.out +++ b/cli/tests/fmt/expected_fmt_check_formatted_files.out @@ -1 +1 @@ -Checked 3 files +Checked 4 files diff --git a/cli/tests/fmt/expected_fmt_check_ignore.out b/cli/tests/fmt/expected_fmt_check_ignore.out index 158c556c2..7c1e471b9 100644 --- a/cli/tests/fmt/expected_fmt_check_ignore.out +++ b/cli/tests/fmt/expected_fmt_check_ignore.out @@ -1 +1 @@ -Checked 2 files +Checked 3 files diff --git a/cli/tests/fmt/expected_fmt_check_tests_dir.out b/cli/tests/fmt/expected_fmt_check_tests_dir.out index e2dc2b4ae..fe84cd485 100644 --- a/cli/tests/fmt/expected_fmt_check_tests_dir.out +++ b/cli/tests/fmt/expected_fmt_check_tests_dir.out @@ -1,2 +1,2 @@ [WILDCARD] -error: Found 6 not formatted files in [WILDCARD] files +error: Found 7 not formatted files in [WILDCARD] files diff --git a/cli/tests/fmt/formatted4.jsonc b/cli/tests/fmt/formatted4.jsonc new file mode 100644 index 000000000..f0f72a6ed --- /dev/null +++ b/cli/tests/fmt/formatted4.jsonc @@ -0,0 +1,4 @@ +{ + // Comment + "key": "value" +} diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs index 988372b17..b55111154 100644 --- a/cli/tests/integration_tests.rs +++ b/cli/tests/integration_tests.rs @@ -496,30 +496,43 @@ mod integration { fn fmt_test() { let t = TempDir::new().expect("tempdir fail"); let fixed_js = util::root_path().join("cli/tests/badly_formatted_fixed.js"); - let fixed_md = util::root_path().join("cli/tests/badly_formatted_fixed.md"); let badly_formatted_original_js = util::root_path().join("cli/tests/badly_formatted.mjs"); - let badly_formatted_original_md = - util::root_path().join("cli/tests/badly_formatted.md"); let badly_formatted_js = t.path().join("badly_formatted.js"); - let badly_formatted_md = t.path().join("badly_formatted.md"); let badly_formatted_js_str = badly_formatted_js.to_str().unwrap(); - let badly_formatted_md_str = badly_formatted_md.to_str().unwrap(); std::fs::copy(&badly_formatted_original_js, &badly_formatted_js) .expect("Failed to copy file"); + + let fixed_md = util::root_path().join("cli/tests/badly_formatted_fixed.md"); + let badly_formatted_original_md = + util::root_path().join("cli/tests/badly_formatted.md"); + let badly_formatted_md = t.path().join("badly_formatted.md"); + let badly_formatted_md_str = badly_formatted_md.to_str().unwrap(); std::fs::copy(&badly_formatted_original_md, &badly_formatted_md) .expect("Failed to copy file"); + + let fixed_json = + util::root_path().join("cli/tests/badly_formatted_fixed.json"); + let badly_formatted_original_json = + util::root_path().join("cli/tests/badly_formatted.json"); + let badly_formatted_json = t.path().join("badly_formatted.json"); + let badly_formatted_json_str = badly_formatted_json.to_str().unwrap(); + std::fs::copy(&badly_formatted_original_json, &badly_formatted_json) + .expect("Failed to copy file"); // First, check formatting by ignoring the badly formatted file. let status = util::deno_cmd() .current_dir(util::root_path()) .arg("fmt") .arg(format!( - "--ignore={},{}", - badly_formatted_js_str, badly_formatted_md_str + "--ignore={},{},{}", + badly_formatted_js_str, + badly_formatted_md_str, + badly_formatted_json_str )) .arg("--check") .arg(badly_formatted_js_str) .arg(badly_formatted_md_str) + .arg(badly_formatted_json_str) .spawn() .expect("Failed to spawn script") .wait() @@ -532,6 +545,7 @@ mod integration { .arg("--check") .arg(badly_formatted_js_str) .arg(badly_formatted_md_str) + .arg(badly_formatted_json_str) .spawn() .expect("Failed to spawn script") .wait() @@ -543,6 +557,7 @@ mod integration { .arg("fmt") .arg(badly_formatted_js_str) .arg(badly_formatted_md_str) + .arg(badly_formatted_json_str) .spawn() .expect("Failed to spawn script") .wait() @@ -550,10 +565,13 @@ mod integration { assert!(status.success()); let expected_js = std::fs::read_to_string(fixed_js).unwrap(); let expected_md = std::fs::read_to_string(fixed_md).unwrap(); + let expected_json = std::fs::read_to_string(fixed_json).unwrap(); let actual_js = std::fs::read_to_string(badly_formatted_js).unwrap(); let actual_md = std::fs::read_to_string(badly_formatted_md).unwrap(); + let actual_json = std::fs::read_to_string(badly_formatted_json).unwrap(); assert_eq!(expected_js, actual_js); assert_eq!(expected_md, actual_md); + assert_eq!(expected_json, actual_json); } mod file_watcher { @@ -2838,7 +2856,7 @@ console.log("finish"); }); itest!(fmt_check_tests_dir { - args: "fmt --check ./", + args: "fmt --check ./ --ignore=.test_coverage", output: "fmt/expected_fmt_check_tests_dir.out", exit_code: 1, }); @@ -2850,7 +2868,7 @@ console.log("finish"); }); itest!(fmt_check_formatted_files { - args: "fmt --check fmt/formatted1.js fmt/formatted2.ts fmt/formatted3.md", + args: "fmt --check fmt/formatted1.js fmt/formatted2.ts fmt/formatted3.md fmt/formatted4.jsonc", output: "fmt/expected_fmt_check_formatted_files.out", exit_code: 0, }); @@ -2875,6 +2893,12 @@ console.log("finish"); ), }); + itest!(fmt_stdin_json { + args: "fmt --ext=json -", + input: Some("{ \"key\": \"value\"}"), + output_str: Some("{ \"key\": \"value\" }\n"), + }); + itest!(fmt_stdin_check_formatted { args: "fmt --check -", input: Some("const a = 1;\n"), diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 61875d172..d4002984a 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -10,7 +10,7 @@ use crate::colors; use crate::diff::diff; use crate::file_watcher; -use crate::fs_util::{collect_files, get_extension, is_supported_ext_md}; +use crate::fs_util::{collect_files, get_extension, is_supported_ext_fmt}; use crate::text_encoding; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -37,7 +37,7 @@ pub async fn format( ) -> Result<(), AnyError> { let target_file_resolver = || { // collect the files that are to be formatted - collect_files(&args, &ignore, is_supported_ext_md) + collect_files(&args, &ignore, is_supported_ext_fmt) }; let operation = |paths: Vec<PathBuf>| { let config = get_typescript_config(); @@ -75,7 +75,14 @@ fn format_markdown( let tag = tag.to_lowercase(); if matches!( tag.as_str(), - "ts" | "tsx" | "js" | "jsx" | "javascript" | "typescript" + "ts" + | "tsx" + | "js" + | "jsx" + | "javascript" + | "typescript" + | "json" + | "jsonc" ) { // It's important to tell dprint proper file extension, otherwise // it might parse the file twice. @@ -84,16 +91,22 @@ fn format_markdown( "typescript" => "ts", rest => rest, }; - let fake_filename = - PathBuf::from(format!("deno_fmt_stdin.{}", extension)); - let mut codeblock_config = ts_config.clone(); - codeblock_config.line_width = line_width; - dprint_plugin_typescript::format_text( - &fake_filename, - &text, - &codeblock_config, - ) + if matches!(extension, "json" | "jsonc") { + let mut json_config = get_json_config(); + json_config.line_width = line_width; + dprint_plugin_json::format_text(&text, &json_config) + } else { + let fake_filename = + PathBuf::from(format!("deno_fmt_stdin.{}", extension)); + let mut codeblock_config = ts_config.clone(); + codeblock_config.line_width = line_width; + dprint_plugin_typescript::format_text( + &fake_filename, + &text, + &codeblock_config, + ) + } } else { Ok(text.to_string()) } @@ -101,6 +114,14 @@ fn format_markdown( ) } +/// Formats JSON and JSONC using the rules provided by .deno() +/// of configuration builder of https://github.com/dprint/dprint-plugin-json. +/// See https://git.io/Jt4ht for configuration. +fn format_json(file_text: &str) -> Result<String, String> { + let json_config = get_json_config(); + dprint_plugin_json::format_text(&file_text, &json_config) +} + async fn check_source_files( config: dprint_plugin_typescript::configuration::Configuration, paths: Vec<PathBuf>, @@ -120,6 +141,8 @@ async fn check_source_files( let ext = get_extension(&file_path).unwrap_or_else(String::new); let r = if ext == "md" { format_markdown(&file_text, config.clone()) + } else if matches!(ext.as_str(), "json" | "jsonc") { + format_json(&file_text) } else { dprint_plugin_typescript::format_text(&file_path, &file_text, &config) }; @@ -179,6 +202,8 @@ async fn format_source_files( let ext = get_extension(&file_path).unwrap_or_else(String::new); let r = if ext == "md" { format_markdown(&file_contents.text, config.clone()) + } else if matches!(ext.as_str(), "json" | "jsonc") { + format_json(&file_contents.text) } else { dprint_plugin_typescript::format_text( &file_path, @@ -240,6 +265,8 @@ pub fn format_stdin(check: bool, ext: String) -> Result<(), AnyError> { let config = get_typescript_config(); let r = if ext.as_str() == "md" { format_markdown(&source, config) + } else if matches!(ext.as_str(), "json" | "jsonc") { + format_json(&source) } else { // dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work dprint_plugin_typescript::format_text( @@ -291,6 +318,12 @@ fn get_markdown_config() -> dprint_plugin_markdown::configuration::Configuration .build() } +fn get_json_config() -> dprint_plugin_json::configuration::Configuration { + dprint_plugin_json::configuration::ConfigurationBuilder::new() + .deno() + .build() +} + struct FileContents { text: String, had_bom: bool, |