1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 21:50:00 -05:00

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.
This commit is contained in:
Satya Rohith 2021-02-18 22:01:32 +05:30 committed by GitHub
parent c0f10e72ee
commit d9b1f96897
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 179 additions and 44 deletions

View file

@ -23,6 +23,7 @@
"cli/tests/encoding", "cli/tests/encoding",
"cli/tests/inline_js_source_map*", "cli/tests/inline_js_source_map*",
"cli/tests/badly_formatted.md", "cli/tests/badly_formatted.md",
"cli/tests/badly_formatted.json",
"cli/tsc/*typescript.js", "cli/tsc/*typescript.js",
"test_util/std", "test_util/std",
"test_util/wpt", "test_util/wpt",

12
Cargo.lock generated
View file

@ -411,6 +411,7 @@ dependencies = [
"deno_web", "deno_web",
"deno_websocket", "deno_websocket",
"dissimilar", "dissimilar",
"dprint-plugin-json",
"dprint-plugin-markdown", "dprint-plugin-markdown",
"dprint-plugin-typescript", "dprint-plugin-typescript",
"encoding_rs", "encoding_rs",
@ -659,6 +660,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "dprint-plugin-json"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b43a7746fa356617bb85828932170b5b6a35102b1d29978b0f1b388dbcd346"
dependencies = [
"dprint-core",
"jsonc-parser",
"serde",
]
[[package]] [[package]]
name = "dprint-plugin-markdown" name = "dprint-plugin-markdown"
version = "0.5.1" version = "0.5.1"

View file

@ -48,6 +48,7 @@ clap = "2.33.3"
dissimilar = "1.0.2" dissimilar = "1.0.2"
dprint-plugin-typescript = "0.40.1" dprint-plugin-typescript = "0.40.1"
dprint-plugin-markdown = "0.5.1" dprint-plugin-markdown = "0.5.1"
dprint-plugin-json = "0.8.0"
encoding_rs = "0.8.28" encoding_rs = "0.8.28"
env_logger = "0.8.2" env_logger = "0.8.2"
filetime = "0.2.14" filetime = "0.2.14"

View file

@ -797,7 +797,7 @@ fn fmt_subcommand<'a, 'b>() -> App<'a, 'b> {
SubCommand::with_name("fmt") SubCommand::with_name("fmt")
.about("Format source files") .about("Format source files")
.long_about( .long_about(
"Auto-format JavaScript, TypeScript and Markdown files. "Auto-format JavaScript, TypeScript, Markdown, and JSON files.
deno fmt deno fmt
deno fmt myfile1.ts myfile2.ts deno fmt myfile1.ts myfile2.ts
deno fmt --check 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") .help("Set standard input (stdin) content type")
.takes_value(true) .takes_value(true)
.default_value("ts") .default_value("ts")
.possible_values(&["ts", "tsx", "js", "jsx", "md"]), .possible_values(&["ts", "tsx", "js", "jsx", "md", "json", "jsonc"]),
) )
.arg( .arg(
Arg::with_name("ignore") Arg::with_name("ignore")

View file

@ -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. /// This function is similar to is_supported_ext but adds additional extensions
pub fn is_supported_ext_md(path: &Path) -> bool { /// supported by `deno fmt`.
pub fn is_supported_ext_fmt(path: &Path) -> bool {
if let Some(ext) = get_extension(path) { 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 { } else {
false false
} }
@ -221,21 +225,25 @@ mod tests {
} }
#[test] #[test]
fn test_is_supported_ext_md() { fn test_is_supported_ext_fmt() {
assert!(!is_supported_ext_md(Path::new("tests/subdir/redirects"))); assert!(!is_supported_ext_fmt(Path::new("tests/subdir/redirects")));
assert!(is_supported_ext_md(Path::new("README.md"))); assert!(is_supported_ext_fmt(Path::new("README.md")));
assert!(is_supported_ext_md(Path::new("readme.MD"))); assert!(is_supported_ext_fmt(Path::new("readme.MD")));
assert!(is_supported_ext_md(Path::new("lib/typescript.d.ts"))); assert!(is_supported_ext_fmt(Path::new("lib/typescript.d.ts")));
assert!(is_supported_ext_md(Path::new("cli/tests/001_hello.js"))); assert!(is_supported_ext_fmt(Path::new("cli/tests/001_hello.js")));
assert!(is_supported_ext_md(Path::new("cli/tests/002_hello.ts"))); assert!(is_supported_ext_fmt(Path::new("cli/tests/002_hello.ts")));
assert!(is_supported_ext_md(Path::new("foo.jsx"))); assert!(is_supported_ext_fmt(Path::new("foo.jsx")));
assert!(is_supported_ext_md(Path::new("foo.tsx"))); assert!(is_supported_ext_fmt(Path::new("foo.tsx")));
assert!(is_supported_ext_md(Path::new("foo.TS"))); assert!(is_supported_ext_fmt(Path::new("foo.TS")));
assert!(is_supported_ext_md(Path::new("foo.TSX"))); assert!(is_supported_ext_fmt(Path::new("foo.TSX")));
assert!(is_supported_ext_md(Path::new("foo.JS"))); assert!(is_supported_ext_fmt(Path::new("foo.JS")));
assert!(is_supported_ext_md(Path::new("foo.JSX"))); assert!(is_supported_ext_fmt(Path::new("foo.JSX")));
assert!(is_supported_ext_md(Path::new("foo.mjs"))); assert!(is_supported_ext_fmt(Path::new("foo.mjs")));
assert!(!is_supported_ext_md(Path::new("foo.mjsx"))); 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] #[test]

View file

@ -0,0 +1,12 @@
{
"key1": "value1",
"key2": true,
"key3": ["value2", "value3", false],
"keys": {
"more": "values"
}
}

View file

@ -26,3 +26,21 @@ function foo(): number {
return 2; return 2;
} }
``` ```
```jsonc
{
// Comment in JSON
"key": "value",
"key2":
"value2",
}
```
```json
{
"numbers":
["1", "2"]
}
```

View file

@ -0,0 +1,8 @@
{
"key1": "value1",
"key2": true,
"key3": ["value2", "value3", false],
"keys": {
"more": "values"
}
}

View file

@ -21,3 +21,17 @@ function foo(): number {
return 2; return 2;
} }
``` ```
```jsonc
{
// Comment in JSON
"key": "value",
"key2": "value2"
}
```
```json
{
"numbers": ["1", "2"]
}
```

View file

@ -1 +1 @@
Checked 3 files Checked 4 files

View file

@ -1 +1 @@
Checked 2 files Checked 3 files

View file

@ -1,2 +1,2 @@
[WILDCARD] [WILDCARD]
error: Found 6 not formatted files in [WILDCARD] files error: Found 7 not formatted files in [WILDCARD] files

View file

@ -0,0 +1,4 @@
{
// Comment
"key": "value"
}

View file

@ -496,30 +496,43 @@ mod integration {
fn fmt_test() { fn fmt_test() {
let t = TempDir::new().expect("tempdir fail"); let t = TempDir::new().expect("tempdir fail");
let fixed_js = util::root_path().join("cli/tests/badly_formatted_fixed.js"); 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 = let badly_formatted_original_js =
util::root_path().join("cli/tests/badly_formatted.mjs"); 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_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_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) std::fs::copy(&badly_formatted_original_js, &badly_formatted_js)
.expect("Failed to copy file"); .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) std::fs::copy(&badly_formatted_original_md, &badly_formatted_md)
.expect("Failed to copy file"); .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. // First, check formatting by ignoring the badly formatted file.
let status = util::deno_cmd() let status = util::deno_cmd()
.current_dir(util::root_path()) .current_dir(util::root_path())
.arg("fmt") .arg("fmt")
.arg(format!( .arg(format!(
"--ignore={},{}", "--ignore={},{},{}",
badly_formatted_js_str, badly_formatted_md_str badly_formatted_js_str,
badly_formatted_md_str,
badly_formatted_json_str
)) ))
.arg("--check") .arg("--check")
.arg(badly_formatted_js_str) .arg(badly_formatted_js_str)
.arg(badly_formatted_md_str) .arg(badly_formatted_md_str)
.arg(badly_formatted_json_str)
.spawn() .spawn()
.expect("Failed to spawn script") .expect("Failed to spawn script")
.wait() .wait()
@ -532,6 +545,7 @@ mod integration {
.arg("--check") .arg("--check")
.arg(badly_formatted_js_str) .arg(badly_formatted_js_str)
.arg(badly_formatted_md_str) .arg(badly_formatted_md_str)
.arg(badly_formatted_json_str)
.spawn() .spawn()
.expect("Failed to spawn script") .expect("Failed to spawn script")
.wait() .wait()
@ -543,6 +557,7 @@ mod integration {
.arg("fmt") .arg("fmt")
.arg(badly_formatted_js_str) .arg(badly_formatted_js_str)
.arg(badly_formatted_md_str) .arg(badly_formatted_md_str)
.arg(badly_formatted_json_str)
.spawn() .spawn()
.expect("Failed to spawn script") .expect("Failed to spawn script")
.wait() .wait()
@ -550,10 +565,13 @@ mod integration {
assert!(status.success()); assert!(status.success());
let expected_js = std::fs::read_to_string(fixed_js).unwrap(); let expected_js = std::fs::read_to_string(fixed_js).unwrap();
let expected_md = std::fs::read_to_string(fixed_md).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_js = std::fs::read_to_string(badly_formatted_js).unwrap();
let actual_md = std::fs::read_to_string(badly_formatted_md).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_js, actual_js);
assert_eq!(expected_md, actual_md); assert_eq!(expected_md, actual_md);
assert_eq!(expected_json, actual_json);
} }
mod file_watcher { mod file_watcher {
@ -2838,7 +2856,7 @@ console.log("finish");
}); });
itest!(fmt_check_tests_dir { itest!(fmt_check_tests_dir {
args: "fmt --check ./", args: "fmt --check ./ --ignore=.test_coverage",
output: "fmt/expected_fmt_check_tests_dir.out", output: "fmt/expected_fmt_check_tests_dir.out",
exit_code: 1, exit_code: 1,
}); });
@ -2850,7 +2868,7 @@ console.log("finish");
}); });
itest!(fmt_check_formatted_files { 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", output: "fmt/expected_fmt_check_formatted_files.out",
exit_code: 0, 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 { itest!(fmt_stdin_check_formatted {
args: "fmt --check -", args: "fmt --check -",
input: Some("const a = 1;\n"), input: Some("const a = 1;\n"),

View file

@ -10,7 +10,7 @@
use crate::colors; use crate::colors;
use crate::diff::diff; use crate::diff::diff;
use crate::file_watcher; 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 crate::text_encoding;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -37,7 +37,7 @@ pub async fn format(
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let target_file_resolver = || { let target_file_resolver = || {
// collect the files that are to be formatted // 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 operation = |paths: Vec<PathBuf>| {
let config = get_typescript_config(); let config = get_typescript_config();
@ -75,7 +75,14 @@ fn format_markdown(
let tag = tag.to_lowercase(); let tag = tag.to_lowercase();
if matches!( if matches!(
tag.as_str(), 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's important to tell dprint proper file extension, otherwise
// it might parse the file twice. // it might parse the file twice.
@ -84,16 +91,22 @@ fn format_markdown(
"typescript" => "ts", "typescript" => "ts",
rest => rest, rest => rest,
}; };
let fake_filename =
PathBuf::from(format!("deno_fmt_stdin.{}", extension));
let mut codeblock_config = ts_config.clone(); if matches!(extension, "json" | "jsonc") {
codeblock_config.line_width = line_width; let mut json_config = get_json_config();
dprint_plugin_typescript::format_text( json_config.line_width = line_width;
&fake_filename, dprint_plugin_json::format_text(&text, &json_config)
&text, } else {
&codeblock_config, 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 { } else {
Ok(text.to_string()) 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( async fn check_source_files(
config: dprint_plugin_typescript::configuration::Configuration, config: dprint_plugin_typescript::configuration::Configuration,
paths: Vec<PathBuf>, paths: Vec<PathBuf>,
@ -120,6 +141,8 @@ async fn check_source_files(
let ext = get_extension(&file_path).unwrap_or_else(String::new); let ext = get_extension(&file_path).unwrap_or_else(String::new);
let r = if ext == "md" { let r = if ext == "md" {
format_markdown(&file_text, config.clone()) format_markdown(&file_text, config.clone())
} else if matches!(ext.as_str(), "json" | "jsonc") {
format_json(&file_text)
} else { } else {
dprint_plugin_typescript::format_text(&file_path, &file_text, &config) 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 ext = get_extension(&file_path).unwrap_or_else(String::new);
let r = if ext == "md" { let r = if ext == "md" {
format_markdown(&file_contents.text, config.clone()) format_markdown(&file_contents.text, config.clone())
} else if matches!(ext.as_str(), "json" | "jsonc") {
format_json(&file_contents.text)
} else { } else {
dprint_plugin_typescript::format_text( dprint_plugin_typescript::format_text(
&file_path, &file_path,
@ -240,6 +265,8 @@ pub fn format_stdin(check: bool, ext: String) -> Result<(), AnyError> {
let config = get_typescript_config(); let config = get_typescript_config();
let r = if ext.as_str() == "md" { let r = if ext.as_str() == "md" {
format_markdown(&source, config) format_markdown(&source, config)
} else if matches!(ext.as_str(), "json" | "jsonc") {
format_json(&source)
} else { } else {
// dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work // dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work
dprint_plugin_typescript::format_text( dprint_plugin_typescript::format_text(
@ -291,6 +318,12 @@ fn get_markdown_config() -> dprint_plugin_markdown::configuration::Configuration
.build() .build()
} }
fn get_json_config() -> dprint_plugin_json::configuration::Configuration {
dprint_plugin_json::configuration::ConfigurationBuilder::new()
.deno()
.build()
}
struct FileContents { struct FileContents {
text: String, text: String,
had_bom: bool, had_bom: bool,