diff --git a/Cargo.lock b/Cargo.lock index bcdfe2da7e8..598eae1a21e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2188,7 +2188,6 @@ version = "0.0.0" dependencies = [ "bpaf", "dashmap", - "globset", "indexmap", "insta", "rome_analyze", @@ -2204,6 +2203,7 @@ dependencies = [ "rome_js_parser", "rome_js_semantic", "rome_js_syntax", + "rome_json_analyze", "rome_json_formatter", "rome_json_parser", "rome_json_syntax", diff --git a/crates/rome_cli/tests/commands/format.rs b/crates/rome_cli/tests/commands/format.rs index be9247a0055..4091095bac5 100644 --- a/crates/rome_cli/tests/commands/format.rs +++ b/crates/rome_cli/tests/commands/format.rs @@ -19,6 +19,15 @@ const CUSTOM_FORMAT_AFTER: &str = r#"function f() { } "#; +const APPLY_JSX_QUOTE_STYLE_BEFORE: &str = r#" +
"#; + +const APPLY_JSX_QUOTE_STYLE_AFTER: &str = r#"
; +"#; + const APPLY_QUOTE_STYLE_BEFORE: &str = r#" let a = "something"; let b = { @@ -43,6 +52,23 @@ const APPLY_TRAILING_COMMA_AFTER: &str = r#"const a = [ ]; "#; +const APPLY_ARROW_PARENTHESES_BEFORE: &str = r#" +action => {} +(action) => {} +({ action }) => {} +([ action ]) => {} +(...action) => {} +(action = 1) => {} +"#; + +const APPLY_ARROW_PARENTHESES_AFTER: &str = r#"action => {}; +action => {}; +({ action }) => {}; +([action]) => {}; +(...action) => {}; +(action = 1) => {}; +"#; + const DEFAULT_CONFIGURATION_BEFORE: &str = r#"function f() { return { a, b } }"#; @@ -72,7 +98,7 @@ fn format_help() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), "--help"]), + Args::from([("format"), "--help"].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -97,7 +123,7 @@ fn print() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), file_path.as_os_str().to_str().unwrap()]), + Args::from([("format"), file_path.as_os_str().to_str().unwrap()].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -133,11 +159,14 @@ fn write() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -181,7 +210,7 @@ fn write_only_files_in_correct_base() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--write"), ("./src")]), + Args::from([("format"), ("--write"), ("./src")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -227,7 +256,7 @@ fn lint_warning() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), file_path.as_os_str().to_str().unwrap()]), + Args::from([("format"), file_path.as_os_str().to_str().unwrap()].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -281,16 +310,19 @@ fn custom_config_file_path() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - format!( - "--config-path={}", - config_path.display().to_string().as_str() - ) - .as_str(), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + format!( + "--config-path={}", + config_path.display().to_string().as_str() + ) + .as_str(), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -330,13 +362,16 @@ fn invalid_config_file_path() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--config-path"), - (config_path.display().to_string().as_str()), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--config-path"), + (config_path.display().to_string().as_str()), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -361,17 +396,20 @@ fn applies_custom_configuration() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--line-width"), - ("10"), - ("--indent-style"), - ("space"), - ("--indent-size"), - ("8"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--line-width"), + ("10"), + ("--indent-style"), + ("space"), + ("--indent-size"), + ("8"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -410,17 +448,20 @@ fn applies_custom_configuration_over_config_file() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--line-width"), - ("10"), - ("--indent-style"), - ("space"), - ("--indent-size"), - ("8"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--line-width"), + ("10"), + ("--indent-style"), + ("space"), + ("--indent-size"), + ("8"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -459,12 +500,15 @@ fn applies_custom_configuration_over_config_file_issue_3175_v1() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--quote-style"), - ("single"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--quote-style"), + ("single"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -508,12 +552,15 @@ fn applies_custom_configuration_over_config_file_issue_3175_v2() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--indent-style"), - ("space"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--indent-style"), + ("space"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -538,6 +585,53 @@ fn applies_custom_configuration_over_config_file_issue_3175_v2() { )); } +#[test] +fn applies_custom_jsx_quote_style() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let file_path = Path::new("file.js"); + fs.insert(file_path.into(), APPLY_JSX_QUOTE_STYLE_BEFORE.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("format"), + ("--jsx-quote-style"), + ("single"), + ("--quote-properties"), + ("preserve"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + let mut file = fs + .open(file_path) + .expect("formatting target file was removed by the CLI"); + + let mut content = String::new(); + file.read_to_string(&mut content) + .expect("failed to read file from memory FS"); + + assert_eq!(content, APPLY_JSX_QUOTE_STYLE_AFTER); + + drop(file); + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "applies_custom_jsx_quote_style", + fs, + console, + result, + )); +} + #[test] fn applies_custom_quote_style() { let mut fs = MemoryFileSystem::default(); @@ -549,15 +643,18 @@ fn applies_custom_quote_style() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--quote-style"), - ("single"), - ("--quote-properties"), - ("preserve"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--quote-style"), + ("single"), + ("--quote-properties"), + ("preserve"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -593,13 +690,16 @@ fn applies_custom_trailing_comma() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--trailing-comma"), - ("none"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--trailing-comma"), + ("none"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -624,6 +724,51 @@ fn applies_custom_trailing_comma() { )); } +#[test] +fn applies_custom_arrow_parentheses() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let file_path = Path::new("file.js"); + fs.insert(file_path.into(), APPLY_ARROW_PARENTHESES_BEFORE.as_bytes()); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("format"), + ("--arrow-parentheses"), + ("as-needed"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + let mut file = fs + .open(file_path) + .expect("formatting target file was removed by the CLI"); + + let mut content = String::new(); + file.read_to_string(&mut content) + .expect("failed to read file from memory FS"); + + assert_eq!(content, APPLY_ARROW_PARENTHESES_AFTER); + + drop(file); + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "applies_custom_arrow_parentheses", + fs, + console, + result, + )); +} + #[test] fn trailing_comma_parse_errors() { let mut console = BufferConsole::default(); @@ -632,7 +777,7 @@ fn trailing_comma_parse_errors() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--trailing-comma"), ("NONE"), ("file.js")]), + Args::from([("format"), ("--trailing-comma"), ("NONE"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -657,12 +802,15 @@ fn with_semicolons_options() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--semicolons=as-needed"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--semicolons=as-needed"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -695,7 +843,7 @@ fn with_invalid_semicolons_option() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--semicolons"), ("asneed"), ("file.js")]), + Args::from([("format"), ("--semicolons"), ("asneed"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -717,7 +865,7 @@ fn indent_style_parse_errors() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--indent-style"), ("invalid"), ("file.js")]), + Args::from([("format"), ("--indent-style"), ("invalid"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -739,7 +887,7 @@ fn indent_size_parse_errors_negative() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--indent-size=-1"), ("file.js")]), + Args::from([("format"), ("--indent-size=-1"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -761,7 +909,7 @@ fn indent_size_parse_errors_overflow() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--indent-size=257"), ("file.js")]), + Args::from([("format"), ("--indent-size=257"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -783,7 +931,7 @@ fn line_width_parse_errors_negative() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&["format", "--line-width=-1", "file.js"]), + Args::from(["format", "--line-width=-1", "file.js"].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -804,7 +952,7 @@ fn line_width_parse_errors_overflow() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--line-width"), ("321"), ("file.js")]), + Args::from([("format"), ("--line-width"), ("321"), ("file.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -826,12 +974,15 @@ fn quote_properties_parse_errors_letter_case() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--quote-properties"), - ("As-needed"), - ("file.js"), - ]), + Args::from( + [ + ("format"), + ("--quote-properties"), + ("As-needed"), + ("file.js"), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -858,7 +1009,7 @@ fn format_with_configuration() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("file.js"), ("--write")]), + Args::from([("format"), ("file.js"), ("--write")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -896,7 +1047,7 @@ fn format_is_disabled() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("file.js"), ("--write")]), + Args::from([("format"), ("file.js"), ("--write")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -933,7 +1084,7 @@ fn format_stdin_successfully() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--stdin-file-path"), ("mock.js")]), + Args::from([("format"), ("--stdin-file-path"), ("mock.js")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -966,7 +1117,7 @@ fn format_stdin_with_errors() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--stdin-file-path"), ("mock.js")]), + Args::from([("format"), ("--stdin-file-path"), ("mock.js")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -995,7 +1146,7 @@ fn does_not_format_if_disabled() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--stdin-file-path"), ("mock.js")]), + Args::from([("format"), ("--stdin-file-path"), ("mock.js")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1033,7 +1184,7 @@ fn does_not_format_ignored_files() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("test.js"), ("--write")]), + Args::from([("format"), ("test.js"), ("--write")].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1078,12 +1229,15 @@ fn does_not_format_if_files_are_listed_in_ignore_option() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - file_path_test1.as_os_str().to_str().unwrap(), - file_path_test2.as_os_str().to_str().unwrap(), - ("--write"), - ]), + Args::from( + [ + ("format"), + file_path_test1.as_os_str().to_str().unwrap(), + file_path_test2.as_os_str().to_str().unwrap(), + ("--write"), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1144,7 +1298,7 @@ fn does_not_format_ignored_directories() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("./"), ("--write")]), + Args::from([("format"), ("./"), ("--write")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1190,11 +1344,14 @@ fn fs_error_read_only() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--write"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--write"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1222,11 +1379,14 @@ fn file_too_large() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - file_path.as_os_str().to_str().unwrap(), - ("--write"), - ]), + Args::from( + [ + ("format"), + file_path.as_os_str().to_str().unwrap(), + ("--write"), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1256,7 +1416,7 @@ fn file_too_large_config_limit() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), file_path.as_os_str().to_str().unwrap()]), + Args::from([("format"), file_path.as_os_str().to_str().unwrap()].as_slice()), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1281,11 +1441,14 @@ fn file_too_large_cli_limit() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--files-max-size=16"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--files-max-size=16"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1310,11 +1473,14 @@ fn files_max_size_parse_error() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--files-max-size=-1"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--files-max-size=-1"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_err(), "run_cli returned {result:?}"); @@ -1341,7 +1507,7 @@ fn max_diagnostics_default() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("src")]), + Args::from([("format"), ("src")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1378,7 +1544,7 @@ fn max_diagnostics_default() { result, )); - assert_eq!(diagnostic_count, 50); + assert_eq!(diagnostic_count, 20); } #[test] @@ -1394,7 +1560,7 @@ fn max_diagnostics() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), ("--max-diagnostics"), ("10"), ("src")]), + Args::from([("format"), ("--max-diagnostics"), ("10"), ("src")].as_slice()), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1442,7 +1608,7 @@ fn no_supported_file_found() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("check"), "."]), + Args::from([("format"), "."].as_slice()), ); eprintln!("{:?}", console.out_buffer); @@ -1467,11 +1633,14 @@ fn print_verbose() { let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--verbose"), - file_path.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--verbose"), + file_path.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1532,12 +1701,15 @@ file2.js let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--write"), - file_path1.as_os_str().to_str().unwrap(), - file_path2.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--write"), + file_path1.as_os_str().to_str().unwrap(), + file_path2.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1586,16 +1758,19 @@ file2.js let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[ - ("format"), - ("--vcs-enabled=true"), - ("--vcs-client-kind=git"), - ("--vcs-use-ignore-file=true"), - ("--vcs-root=."), - ("--write"), - file_path1.as_os_str().to_str().unwrap(), - file_path2.as_os_str().to_str().unwrap(), - ]), + Args::from( + [ + ("format"), + ("--vcs-enabled=true"), + ("--vcs-client-kind=git"), + ("--vcs-use-ignore-file=true"), + ("--vcs-root=."), + ("--write"), + file_path1.as_os_str().to_str().unwrap(), + file_path2.as_os_str().to_str().unwrap(), + ] + .as_slice(), + ), ); assert!(result.is_ok(), "run_cli returned {result:?}"); @@ -1610,15 +1785,69 @@ file2.js } #[test] -fn ignore_comments_error_when_allow_comments() { +fn ignores_unknown_file() { let mut fs = MemoryFileSystem::default(); let mut console = BufferConsole::default(); - let config_json = r#" + let file_path1 = Path::new("test.txt"); + fs.insert(file_path1.into(), *b"content"); + + let file_path2 = Path::new("test.js"); + fs.insert(file_path2.into(), *b"console.log('bar');\n"); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from( + [ + ("format"), + file_path1.as_os_str().to_str().unwrap(), + file_path2.as_os_str().to_str().unwrap(), + "--files-ignore-unknown=true", + ] + .as_slice(), + ), + ); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "ignores_unknown_file", + fs, + console, + result, + )); +} + +#[test] +fn doesnt_error_if_no_files_were_processed() { + let mut console = BufferConsole::default(); + let mut fs = MemoryFileSystem::default(); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from([("format"), "--no-errors-on-unmatched", ("file.js")].as_slice()), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "doesnt_error_if_no_files_were_processed", + fs, + console, + result, + )); +} + +#[test] +fn ignore_comments_error_when_allow_comments() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); -{ + let config_json = r#"{ "json": { - "allowComments": ["*.json"] + "parser": { "allowComments": true } } } @@ -1631,11 +1860,10 @@ fn ignore_comments_error_when_allow_comments() { fs.insert(file_path.into(), code.as_bytes()); fs.insert(rome_config.into(), config_json); - println!("something--------------------xxxxxxxxxxxxxxxxxx"); let result = run_cli( DynRef::Borrowed(&mut fs), &mut console, - Args::from(&[("format"), file_path.as_os_str().to_str().unwrap()]), + Args::from([("format"), file_path.as_os_str().to_str().unwrap()].as_slice()), ); // assert!(result.is_ok(), "run_cli returned {result:?}"); diff --git a/crates/rome_cli/tests/snapshots/main_commands_format/ignore_comments_error_when_allow_comments.snap b/crates/rome_cli/tests/snapshots/main_commands_format/ignore_comments_error_when_allow_comments.snap index fcd637df533..0ac9eabf624 100644 --- a/crates/rome_cli/tests/snapshots/main_commands_format/ignore_comments_error_when_allow_comments.snap +++ b/crates/rome_cli/tests/snapshots/main_commands_format/ignore_comments_error_when_allow_comments.snap @@ -7,7 +7,7 @@ expression: content ```json { "json": { - "allowComments": ["*.json"] + "parser": { "allowComments": true } } } ``` diff --git a/crates/rome_json_analyze/src/lib.rs b/crates/rome_json_analyze/src/lib.rs index 97b71e61005..2750196b964 100644 --- a/crates/rome_json_analyze/src/lib.rs +++ b/crates/rome_json_analyze/src/lib.rs @@ -103,7 +103,7 @@ mod tests { use rome_console::{markup, Markup}; use rome_diagnostics::termcolor::NoColor; use rome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic, Severity}; - use rome_json_parser::parse_json; + use rome_json_parser::{parse_json, JsonParserOptions}; use rome_json_syntax::TextRange; use std::slice; @@ -130,7 +130,7 @@ mod tests { } "#; - let parsed = parse_json(SOURCE); + let parsed = parse_json(SOURCE, JsonParserOptions::default()); let mut error_ranges: Vec = Vec::new(); let rule_filter = RuleFilter::Rule("nursery", "noDuplicateKeys"); diff --git a/crates/rome_json_analyze/tests/spec_tests.rs b/crates/rome_json_analyze/tests/spec_tests.rs index a56ded400c0..af746f24f30 100644 --- a/crates/rome_json_analyze/tests/spec_tests.rs +++ b/crates/rome_json_analyze/tests/spec_tests.rs @@ -9,7 +9,7 @@ use rome_deserialize::json::deserialize_from_json_str; use rome_diagnostics::advice::CodeSuggestionAdvice; use rome_diagnostics::termcolor::{Buffer, NoColor}; use rome_diagnostics::{DiagnosticExt, Error, PrintDiagnostic, Severity}; -use rome_json_parser::{parse_json, JsonParse}; +use rome_json_parser::{parse_json, JsonParse, JsonParserOptions}; use rome_json_syntax::{JsonLanguage, JsonSyntaxNode}; use rome_rowan::SyntaxKind; use rome_rowan::SyntaxSlot; @@ -77,7 +77,7 @@ pub(crate) fn write_analysis_to_snapshot( file_name: &str, input_file: &Path, ) -> usize { - let parsed = parse_json(input_code); + let parsed = parse_json(input_code, JsonParserOptions::default()); let root = parsed.tree(); let mut diagnostics = Vec::new(); @@ -236,7 +236,7 @@ fn check_code_action(path: &Path, source: &str, action: &AnalyzerAction AnyParse { - parse_json(text, JsonParserOptions::default()).into() + let parse = parse_json(text, JsonParserOptions::default()); + + AnyParse::new( + parse.syntax().as_send().unwrap(), + parse.into_diagnostics(), + self.source_type.as_any_file_source(), + ) } fn deserialize_format_options( diff --git a/crates/rome_json_parser/src/lib.rs b/crates/rome_json_parser/src/lib.rs index e29601bac37..980240fb9f2 100644 --- a/crates/rome_json_parser/src/lib.rs +++ b/crates/rome_json_parser/src/lib.rs @@ -18,9 +18,9 @@ mod token_source; pub(crate) type JsonLosslessTreeSink<'source> = LosslessTreeSink<'source, JsonLanguage, JsonSyntaxFactory>; -pub fn parse_json(source: &str, config: JsonParserOptions) -> JsonParse { +pub fn parse_json(source: &str, options: JsonParserOptions) -> JsonParse { let mut cache = NodeCache::default(); - parse_json_with_cache(source, &mut cache, config) + parse_json_with_cache(source, &mut cache, options) } /// Parses the provided string as JSON program using the provided node cache. diff --git a/crates/rome_service/Cargo.toml b/crates/rome_service/Cargo.toml index 9468292d3c6..4ac3a4cd7a8 100644 --- a/crates/rome_service/Cargo.toml +++ b/crates/rome_service/Cargo.toml @@ -1,51 +1,51 @@ [package] -authors = { workspace = true } -edition = { workspace = true } -license = { workspace = true } -name = "rome_service" -repository = { workspace = true } -version = "0.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +name = "rome_service" +repository.workspace = true +version = "0.0.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] bpaf = { workspace = true } dashmap = { workspace = true } -globset = "0.4.10" indexmap = { workspace = true, features = ["serde"] } -rome_analyze = { path = "../rome_analyze", features = ["serde"] } -rome_console = { path = "../rome_console" } -rome_deserialize = { path = "../rome_deserialize" } -rome_diagnostics = { path = "../rome_diagnostics" } -rome_flags = { path = "../rome_flags" } -rome_formatter = { path = "../rome_formatter", features = ["serde"] } -rome_fs = { path = "../rome_fs", features = ["serde"] } -rome_js_analyze = { path = "../rome_js_analyze" } -rome_js_factory = { path = "../rome_js_factory", optional = true } -rome_js_formatter = { path = "../rome_js_formatter", features = ["serde"] } -rome_js_parser = { path = "../rome_js_parser" } -rome_js_semantic = { path = "../rome_js_semantic" } -rome_js_syntax = { path = "../rome_js_syntax", features = ["serde"] } -rome_json_formatter = { path = "../rome_json_formatter" } -rome_json_parser = { path = "../rome_json_parser" } -rome_json_syntax = { path = "../rome_json_syntax" } -rome_parser = { path = "../rome_parser" } -rome_rowan = { path = "../rome_rowan", features = ["serde"] } -rome_text_edit = { path = "../rome_text_edit" } -schemars = { version = "0.8.10", features = ["indexmap1"], optional = true } -serde = { version = "1.0.133", features = ["derive"] } -serde_json = { version = "1.0.74", features = ["raw_value"] } +rome_analyze = { workspace = true, features = ["serde"] } +rome_console = { workspace = true } +rome_deserialize = { workspace = true } +rome_diagnostics = { workspace = true } +rome_flags = { workspace = true } +rome_formatter = { workspace = true, features = ["serde"] } +rome_fs = { workspace = true, features = ["serde"] } +rome_js_analyze = { workspace = true } +rome_js_factory = { workspace = true, optional = true } +rome_js_formatter = { workspace = true, features = ["serde"] } +rome_js_parser = { workspace = true } +rome_js_semantic = { workspace = true } +rome_js_syntax = { workspace = true, features = ["serde"] } +rome_json_analyze = { workspace = true } +rome_json_formatter = { workspace = true } +rome_json_parser = { workspace = true } +rome_json_syntax = { workspace = true } +rome_parser = { workspace = true } +rome_rowan = { workspace = true, features = ["serde"] } +rome_text_edit = { workspace = true } +schemars = { workspace = true, features = ["indexmap1"], optional = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true, features = ["raw_value"] } tracing = { workspace = true, features = ["attributes"] } [features] schemars = [ - "dep:schemars", - "rome_formatter/serde", - "rome_js_factory", - "rome_text_edit/schemars", - "rome_js_analyze/schemars", + "dep:schemars", + "rome_js_analyze/schemars", + "rome_formatter/serde", + "rome_js_factory", + "rome_text_edit/schemars", ] [dev-dependencies] insta = { workspace = true } -tests_macros = { path = "../tests_macros" } +tests_macros = { workspace = true } diff --git a/crates/rome_service/src/configuration/json.rs b/crates/rome_service/src/configuration/json.rs index 329205ea964..bd52ee9b71d 100644 --- a/crates/rome_service/src/configuration/json.rs +++ b/crates/rome_service/src/configuration/json.rs @@ -40,7 +40,7 @@ pub struct JsonParser { } impl JsonParser { - pub(crate) const KNOWN_KEYS: &'static [&'static str] = &["allow_comments"]; + pub(crate) const KNOWN_KEYS: &'static [&'static str] = &["allowComments"]; } impl MergeWith for JsonParser { diff --git a/crates/rome_service/src/configuration/mod.rs b/crates/rome_service/src/configuration/mod.rs index 5897d39584a..93848330826 100644 --- a/crates/rome_service/src/configuration/mod.rs +++ b/crates/rome_service/src/configuration/mod.rs @@ -118,6 +118,7 @@ impl Configuration { "linter", "formatter", "javascript", + "json", "$schema", "organizeImports", "extends", diff --git a/crates/rome_service/src/configuration/parse/json/json.rs b/crates/rome_service/src/configuration/parse/json/json_configuration.rs similarity index 100% rename from crates/rome_service/src/configuration/parse/json/json.rs rename to crates/rome_service/src/configuration/parse/json/json_configuration.rs diff --git a/crates/rome_service/src/configuration/parse/json.rs b/crates/rome_service/src/configuration/parse/json/mod.rs similarity index 98% rename from crates/rome_service/src/configuration/parse/json.rs rename to crates/rome_service/src/configuration/parse/json/mod.rs index 92910f05183..74e37e25eff 100644 --- a/crates/rome_service/src/configuration/parse/json.rs +++ b/crates/rome_service/src/configuration/parse/json/mod.rs @@ -5,7 +5,7 @@ mod configuration; mod files; mod formatter; mod javascript; -mod json; +mod json_configuration; mod linter; mod organize_imports; mod rules; diff --git a/crates/rome_service/src/file_handlers/json.rs b/crates/rome_service/src/file_handlers/json.rs index eff597167ef..6103c344a49 100644 --- a/crates/rome_service/src/file_handlers/json.rs +++ b/crates/rome_service/src/file_handlers/json.rs @@ -1,4 +1,5 @@ use super::{ExtensionHandler, Mime}; +use crate::configuration::to_analyzer_configuration; use crate::file_handlers::javascript::JsonParserSettings; use crate::file_handlers::{ AnalyzerCapabilities, Capabilities, FixAllParams, FormatterCapabilities, LintParams, @@ -12,10 +13,12 @@ use crate::workspace::{ FixFileResult, GetSyntaxTreeResult, OrganizeImportsResult, PullActionsResult, }; use crate::{Configuration, Rules, WorkspaceError}; +use rome_analyze::{AnalyzerOptions, ControlFlow, Never, RuleCategories}; use rome_deserialize::json::deserialize_from_json_ast; -use rome_diagnostics::{Diagnostic, Severity}; +use rome_diagnostics::{category, Diagnostic, DiagnosticExt, Severity}; use rome_formatter::{FormatError, Printed}; use rome_fs::{RomePath, CONFIG_NAME}; +use rome_json_analyze::analyze; use rome_json_formatter::context::JsonFormatOptions; use rome_json_formatter::format_node; use rome_json_parser::JsonParserOptions; @@ -23,6 +26,7 @@ use rome_json_syntax::{JsonFileSource, JsonLanguage, JsonRoot, JsonSyntaxNode}; use rome_parser::AnyParse; use rome_rowan::{AstNode, FileSource, NodeCache}; use rome_rowan::{TextRange, TextSize, TokenAtOffset}; +use std::path::PathBuf; impl Language for JsonLanguage { type FormatterSettings = (); @@ -98,13 +102,7 @@ fn parse( }; let source_type = JsonFileSource::try_from(rome_path.as_path()).unwrap_or_else(|_| match language_hint { - LanguageId::Json => { - if parser.allow_comments { - JsonFileSource::jsonc() - } else { - JsonFileSource::json() - } - } + LanguageId::Json => JsonFileSource::json(), LanguageId::Jsonc => JsonFileSource::jsonc(), _ => JsonFileSource::json(), }); @@ -209,39 +207,98 @@ fn format_on_type( let printed = rome_json_formatter::format_sub_tree(options, &root_node)?; Ok(printed) } - fn lint(params: LintParams) -> LintResults { - let root: JsonRoot = params.parse.tree(); - let mut diagnostics = params.parse.into_diagnostics(); + tracing::debug_span!("lint").in_scope(move || { + let root: JsonRoot = params.parse.tree(); + let mut diagnostics = params.parse.into_diagnostics(); + + // if we're parsing the `rome.json` file, we deserialize it, so we can emit diagnostics for + // malformed configuration + if params.path.ends_with(CONFIG_NAME) { + let deserialized = deserialize_from_json_ast::(&root); + diagnostics.extend( + deserialized + .into_diagnostics() + .into_iter() + .map(rome_diagnostics::serde::Diagnostic::new) + .collect::>(), + ); + } + + let mut diagnostic_count = diagnostics.len() as u64; + let mut errors = diagnostics + .iter() + .filter(|diag| diag.severity() <= Severity::Error) + .count(); + + let skipped_diagnostics = diagnostic_count - diagnostics.len() as u64; + + let has_lint = params.filter.categories.contains(RuleCategories::LINT); + let analyzer_options = + compute_analyzer_options(¶ms.settings, PathBuf::from(params.path.as_path())); + + let (_, analyze_diagnostics) = analyze( + &root.value().unwrap(), + params.filter, + &analyzer_options, + |signal| { + if let Some(mut diagnostic) = signal.diagnostic() { + // Do not report unused suppression comment diagnostics if this is a syntax-only analyzer pass + if !has_lint && diagnostic.category() == Some(category!("suppressions/unused")) + { + return ControlFlow::::Continue(()); + } + + diagnostic_count += 1; + + // We do now check if the severity of the diagnostics should be changed. + // The configuration allows to change the severity of the diagnostics emitted by rules. + let severity = diagnostic + .category() + .filter(|category| category.name().starts_with("lint/")) + .map(|category| { + params + .rules + .and_then(|rules| rules.get_severity_from_code(category)) + .unwrap_or(Severity::Warning) + }) + .unwrap_or_else(|| diagnostic.severity()); + + if severity <= Severity::Error { + errors += 1; + } + + if diagnostic_count <= params.max_diagnostics { + for action in signal.actions() { + if !action.is_suppression() { + diagnostic = diagnostic.add_code_suggestion(action.into()); + } + } + + let error = diagnostic.with_severity(severity); + + diagnostics.push(rome_diagnostics::serde::Diagnostic::new(error)); + } + } + + ControlFlow::::Continue(()) + }, + ); - // if we're parsing the `rome.json` file, we deserialize it, so we can emit diagnostics for - // malformed configuration - if params.path.ends_with(CONFIG_NAME) { - let deserialized = deserialize_from_json_ast::(&root); diagnostics.extend( - deserialized - .into_diagnostics() + analyze_diagnostics .into_iter() .map(rome_diagnostics::serde::Diagnostic::new) .collect::>(), ); - } - - let diagnostic_count = diagnostics.len() as u64; - let errors = diagnostics - .iter() - .filter(|diag| diag.severity() <= Severity::Error) - .count(); - let skipped_diagnostics = diagnostic_count - diagnostics.len() as u64; - - LintResults { - diagnostics, - errors, - skipped_diagnostics, - } + LintResults { + diagnostics, + errors, + skipped_diagnostics, + } + }) } - fn code_actions( _parse: AnyParse, _range: TextRange, @@ -269,3 +326,15 @@ fn organize_imports(parse: AnyParse) -> Result().to_string(), }) } + +fn compute_analyzer_options(settings: &SettingsHandle, file_path: PathBuf) -> AnalyzerOptions { + let configuration = to_analyzer_configuration( + settings.as_ref().linter(), + &settings.as_ref().languages, + |_| vec![], + ); + AnalyzerOptions { + configuration, + file_path, + } +} diff --git a/crates/rome_service/src/settings.rs b/crates/rome_service/src/settings.rs index 500fcfed82b..5ab6e74f6f5 100644 --- a/crates/rome_service/src/settings.rs +++ b/crates/rome_service/src/settings.rs @@ -96,6 +96,15 @@ impl WorkspaceSettings { if let Some(_organize_imports) = organize_imports {} } + // json settings + let json = configuration.json; + if let Some(json) = json { + if let Some(parser) = json.parser { + self.languages.json.parser.allow_comments = + parser.allow_comments.unwrap_or_default(); + } + } + Ok(()) } diff --git a/crates/rome_service/src/workspace/client.rs b/crates/rome_service/src/workspace/client.rs index ecefe206682..48347a9b10d 100644 --- a/crates/rome_service/src/workspace/client.rs +++ b/crates/rome_service/src/workspace/client.rs @@ -161,7 +161,6 @@ where } fn format_file(&self, params: FormatFileParams) -> Result { - dbg!(&"format_file"); self.request("rome/format_file", params) }