From 5de7c5d46a8dea23a7c460139b7340645d0d045e Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 15 Jun 2023 09:54:55 +0300 Subject: [PATCH 1/5] feat(rome_service): add `extends` functionality --- Cargo.lock | 4 +- Cargo.toml | 124 +++++----- crates/rome_cli/src/commands/check.rs | 19 +- crates/rome_cli/src/commands/ci.rs | 11 +- crates/rome_cli/src/commands/format.rs | 10 +- crates/rome_cli/src/commands/migrate.rs | 9 +- crates/rome_cli/src/commands/mod.rs | 22 +- crates/rome_cli/src/commands/rage.rs | 5 +- crates/rome_cli/src/configuration.rs | 102 +++++++-- crates/rome_cli/src/diagnostics.rs | 10 +- crates/rome_cli/src/execute/process_file.rs | 13 +- crates/rome_cli/src/execute/traverse.rs | 5 +- crates/rome_cli/src/main.rs | 7 +- crates/rome_cli/src/vcs.rs | 7 +- crates/rome_cli/tests/cases/config_extends.rs | 215 ++++++++++++++++++ crates/rome_cli/tests/cases/mod.rs | 4 + crates/rome_cli/tests/commands/check.rs | 6 +- crates/rome_cli/tests/main.rs | 1 + ...extends_config_ok_formatter_no_linter.snap | 48 ++++ ...xtends_config_ok_linter_not_formatter.snap | 71 ++++++ ...tends_resolves_when_using_config_path.snap | 61 +++++ ...an_error_for_unresolved_configuration.snap | 44 ++++ ...solved_configuration_and_show_verbose.snap | 44 ++++ .../deprecated_suppression_comment.snap | 13 ++ .../downgrade_severity.snap | 12 + .../ignore_configured_globals.snap | 13 ++ .../ignores_unknown_file.snap | 12 + ..._if_files_are_listed_in_ignore_option.snap | 14 +- .../no_lint_if_linter_is_disabled.snap | 16 +- ..._if_linter_is_disabled_when_run_apply.snap | 16 +- .../no_lint_when_file_is_ignored.snap | 14 +- .../should_apply_correct_file_source.snap | 13 ++ ...ould_not_enable_all_recommended_rules.snap | 24 ++ .../should_not_enable_nursery_rules.snap | 10 + .../top_level_all_down_level_not_all.snap | 20 ++ .../main_commands_format/format_help.snap | 6 +- .../invalid_config_file_path.snap | 2 + .../main_configuration/incorrect_globals.snap | 2 +- .../incorrect_rule_name.snap | 3 +- crates/rome_fs/src/fs.rs | 30 ++- crates/rome_fs/src/lib.rs | 4 +- crates/rome_lsp/src/session.rs | 4 +- .../src/configuration/diagnostics.rs | 69 ++++-- crates/rome_service/src/configuration/mod.rs | 40 +++- .../configuration/parse/json/configuration.rs | 6 + crates/rome_service/src/diagnostics.rs | 43 +++- .../top_level_extraneous_field.json.snap | 1 + .../wrong_extends_incorrect_items.json | 3 + .../wrong_extends_incorrect_items.json.snap | 42 ++++ .../tests/invalid/wrong_extends_type.json | 3 + .../invalid/wrong_extends_type.json.snap | 16 ++ 51 files changed, 1123 insertions(+), 170 deletions(-) create mode 100644 crates/rome_cli/tests/cases/config_extends.rs create mode 100644 crates/rome_cli/tests/cases/mod.rs create mode 100644 crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_config_ok_formatter_no_linter.snap create mode 100644 crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_config_ok_linter_not_formatter.snap create mode 100644 crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_resolves_when_using_config_path.snap create mode 100644 crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_should_raise_an_error_for_unresolved_configuration.snap create mode 100644 crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_should_raise_an_error_for_unresolved_configuration_and_show_verbose.snap create mode 100644 crates/rome_service/tests/invalid/wrong_extends_incorrect_items.json create mode 100644 crates/rome_service/tests/invalid/wrong_extends_incorrect_items.json.snap create mode 100644 crates/rome_service/tests/invalid/wrong_extends_type.json create mode 100644 crates/rome_service/tests/invalid/wrong_extends_type.json.snap diff --git a/Cargo.lock b/Cargo.lock index 344ee9b7c87..f57123d69c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -901,9 +901,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", diff --git a/Cargo.toml b/Cargo.toml index 929d617ba6a..bcc390261b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,88 +1,88 @@ [workspace] # Use the newer version of the cargo resolver # https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions +resolver = "2" members = [ - "crates/*", - "xtask/bench", - "xtask/codegen", - "xtask/coverage", - "xtask/lintdoc", - "xtask/libs_bench", - "xtask/contributors", + "crates/*", + "xtask/bench", + "xtask/codegen", + "xtask/coverage", + "xtask/lintdoc", + "xtask/libs_bench", + "xtask/contributors", ] -resolver = "2" [workspace.package] -authors = ["Rome Tools Developers and Contributors"] -edition = "2021" -homepage = "https://rome.tools" -license = "MIT" +edition = "2021" +authors = ["Rome Tools Developers and Contributors"] +license = "MIT" repository = "https://github.com/rome/tools" +homepage = "https://rome.tools" [profile.release-with-debug] -debug = true inherits = "release" +debug = true [workspace.dependencies] -countme = "3.0.1" -dashmap = "5.4.0" -indexmap = "1.9.1" +indexmap = "1.9.3" +tracing = { version = "0.1.31", default-features = false, features = ["std"] } +dashmap = "5.4.0" rustc-hash = "1.1.0" -tracing = { version = "0.1.31", default-features = false, features = ["std"] } +countme = "3.0.1" # pinning to version 1.18 to avoid multiple versions of windows-sys as dependency -bitflags = "2.2.1" -bpaf = { version = "0.8.0", features = ["derive"] } -insta = "1.21.2" -lazy_static = "1.4.0" -quote = { version = "1.0.21" } -rome_analyze = { path = "./crates/rome_analyze" } -rome_aria = { path = "./crates/rome_aria" } -rome_aria_metadata = { path = "./crates/rome_aria_metadata" } -rome_console = { version = "0.0.1", path = "./crates/rome_console" } -rome_control_flow = { path = "./crates/rome_control_flow" } -rome_css_syntax = { path = "./crates/rome_css_syntax" } -rome_deserialize = { version = "0.0.0", path = "./crates/rome_deserialize" } -rome_diagnostics = { version = "0.0.1", path = "./crates/rome_diagnostics" } +tokio = { version = "~1.18.5" } +insta = "1.21.2" +quote = { version = "1.0.21" } +lazy_static = "1.4.0" +bpaf = { version = "0.8.0", features = ["derive"] } +bitflags = "2.2.1" +rome_rowan = { version = "0.0.1", path = "./crates/rome_rowan" } +rome_console = { version = "0.0.1", path = "./crates/rome_console" } +rome_diagnostics = { version = "0.0.1", path = "./crates/rome_diagnostics" } +rome_json_parser = { path = "./crates/rome_json_parser" } +rome_deserialize = { version = "0.0.0", path = "./crates/rome_deserialize" } +rome_aria_metadata = { path = "./crates/rome_aria_metadata" } +rome_aria = { path = "./crates/rome_aria" } +rome_formatter = { version = "0.0.1", path = "./crates/rome_formatter" } +rome_service = { path = "./crates/rome_service" } +rome_flags = { path = "./crates/rome_flags" } +rome_fs = { path = "./crates/rome_fs" } +rome_text_edit = { version = "0.0.1", path = "./crates/rome_text_edit" } +rome_lsp = { path = "./crates/rome_lsp" } +rome_text_size = { version = "0.0.1", path = "./crates/rome_text_size" } +rome_json_formatter = { path = "./crates/rome_json_formatter" } +rome_json_syntax = { version = "0.0.1", path = "./crates/rome_json_syntax" } +rome_migrate = { path = "./crates/rome_migrate" } +rome_js_formatter = { path = "./crates/rome_js_formatter" } +rome_markup = { version = "0.0.1", path = "./crates/rome_markup" } +rome_css_syntax = { path = "./crates/rome_css_syntax" } +rome_diagnostics_macros = { version = "0.0.1", path = "./crates/rome_diagnostics_macros" } rome_diagnostics_categories = { version = "0.0.1", path = "./crates/rome_diagnostics_categories" } -rome_diagnostics_macros = { version = "0.0.1", path = "./crates/rome_diagnostics_macros" } -rome_flags = { path = "./crates/rome_flags" } -rome_formatter = { version = "0.0.1", path = "./crates/rome_formatter" } -rome_formatter_test = { path = "./crates/rome_formatter_test" } -rome_fs = { path = "./crates/rome_fs" } -rome_js_analyze = { path = "./crates/rome_js_analyze" } -rome_js_factory = { version = "0.0.2", path = "./crates/rome_js_factory" } -rome_js_formatter = { path = "./crates/rome_js_formatter" } -rome_js_parser = { path = "./crates/rome_js_parser" } -rome_js_semantic = { path = "./crates/rome_js_semantic" } -rome_js_syntax = { version = "0.0.2", path = "./crates/rome_js_syntax" } -rome_js_unicode_table = { version = "0.0.1", path = "./crates/rome_js_unicode_table" } -rome_json_analyze = { path = "./crates/rome_json_analyze" } -rome_json_factory = { version = "0.0.1", path = "./crates/rome_json_factory" } -rome_json_formatter = { path = "./crates/rome_json_formatter" } -rome_json_parser = { path = "./crates/rome_json_parser" } -rome_json_syntax = { version = "0.0.1", path = "./crates/rome_json_syntax" } -rome_lsp = { path = "./crates/rome_lsp" } -rome_markup = { version = "0.0.1", path = "./crates/rome_markup" } -rome_migrate = { path = "./crates/rome_migrate" } -rome_parser = { version = "0.0.1", path = "./crates/rome_parser" } -rome_rowan = { version = "0.0.1", path = "./crates/rome_rowan" } -rome_service = { path = "./crates/rome_service" } -rome_text_edit = { version = "0.0.1", path = "./crates/rome_text_edit" } -rome_text_size = { version = "0.0.1", path = "./crates/rome_text_size" } -schemars = { version = "0.8.10" } -smallvec = { version = "1.8.0", features = ["union", "const_new"] } -tests_macros = { path = "./crates/tests_macros" } -tokio = { version = "~1.18.5" } +rome_js_parser = { path = "./crates/rome_js_parser" } +rome_js_syntax = { version = "0.0.2", path = "./crates/rome_js_syntax" } +rome_js_factory = { version = "0.0.2", path = "./crates/rome_js_factory" } +rome_parser = { version = "0.0.1", path = "./crates/rome_parser" } +rome_analyze = { path = "./crates/rome_analyze" } +rome_control_flow = { path = "./crates/rome_control_flow" } +rome_js_semantic = { path = "./crates/rome_js_semantic" } +rome_js_unicode_table = { version = "0.0.1", path = "./crates/rome_js_unicode_table" } +rome_json_factory = { version = "0.0.1", path = "./crates/rome_json_factory" } +tests_macros = { path = "./crates/tests_macros" } +rome_formatter_test = { path = "./crates/rome_formatter_test" } +rome_js_analyze = { path = "./crates/rome_js_analyze" } +rome_json_analyze = { path = "./crates/rome_json_analyze" } +schemars = { version = "0.8.10" } +smallvec = { version = "1.8.0", features = ["union", "const_new"] } [profile.dev.package.rome_wasm] -debug = true opt-level = 1 +debug = true [profile.test.package.rome_wasm] -debug = true opt-level = 1 +debug = true [profile.release.package.rome_wasm] -debug = false opt-level = 3 +debug = false diff --git a/crates/rome_cli/src/commands/check.rs b/crates/rome_cli/src/commands/check.rs index 8df9074ff8e..0abcb816604 100644 --- a/crates/rome_cli/src/commands/check.rs +++ b/crates/rome_cli/src/commands/check.rs @@ -1,9 +1,9 @@ use crate::cli_options::CliOptions; -use crate::configuration::load_configuration; +use crate::configuration::{load_configuration, LoadedConfiguration}; use crate::vcs::store_path_to_ignore_from_vcs; use crate::{execute_mode, CliDiagnostic, CliSession, Execution, TraversalMode}; use rome_console::{markup, ConsoleExt}; -use rome_diagnostics::{DiagnosticExt, PrintDiagnostic, Severity}; +use rome_diagnostics::{PrintDiagnostic, Severity}; use rome_service::configuration::organize_imports::OrganizeImports; use rome_service::configuration::{FormatterConfiguration, LinterConfiguration}; use rome_service::workspace::{FixFileMode, UpdateSettingsParams}; @@ -53,17 +53,20 @@ pub(crate) fn check( Some(FixFileMode::SafeAndUnsafeFixes) }; - let (mut fs_configuration, diagnostics, configuration_path) = - load_configuration(&mut session, &cli_options)?.consume(); + let LoadedConfiguration { + configuration: mut fs_configuration, + diagnostics, + directory_path: configuration_path, + .. + } = load_configuration(&mut session, &cli_options)?; if !diagnostics.is_empty() { let console = &mut session.app.console; console.log(markup!{ - "Found errors in the configuration file, Rome will use its defaults for the sections that are incorrect." + "Found errors in the configuration file, Rome will use its defaults for the sections that are incorrect." }); for diagnostic in diagnostics { - let diagnostic = diagnostic.with_severity(Severity::Warning); - console.log(markup! { - {PrintDiagnostic::verbose(&diagnostic)} + console.error(markup! { + {if cli_options.verbose { PrintDiagnostic::verbose(&diagnostic) } else { PrintDiagnostic::simple(&diagnostic) }} }) } } diff --git a/crates/rome_cli/src/commands/ci.rs b/crates/rome_cli/src/commands/ci.rs index cc62d3fabae..4ab1a5a1c04 100644 --- a/crates/rome_cli/src/commands/ci.rs +++ b/crates/rome_cli/src/commands/ci.rs @@ -1,4 +1,5 @@ use crate::cli_options::CliOptions; +use crate::configuration::LoadedConfiguration; use crate::vcs::store_path_to_ignore_from_vcs; use crate::{ configuration::load_configuration, execute_mode, CliDiagnostic, CliSession, Execution, @@ -23,14 +24,18 @@ pub(crate) struct CiCommandPayload { /// Handler for the "ci" command of the Rome CLI pub(crate) fn ci(mut session: CliSession, payload: CiCommandPayload) -> Result<(), CliDiagnostic> { - let (mut configuration, diagnostics, configuration_path) = - load_configuration(&mut session, &payload.cli_options)?.consume(); + let LoadedConfiguration { + mut configuration, + diagnostics, + directory_path: configuration_path, + .. + } = load_configuration(&mut session, &payload.cli_options)?; if !diagnostics.is_empty() { let console = &mut session.app.console; for diagnostic in diagnostics { console.error(markup! { - {PrintDiagnostic::verbose(&diagnostic)} + {if payload.cli_options.verbose { PrintDiagnostic::verbose(&diagnostic) } else { PrintDiagnostic::simple(&diagnostic) }} }) } return Err(CliDiagnostic::incompatible_end_configuration( diff --git a/crates/rome_cli/src/commands/format.rs b/crates/rome_cli/src/commands/format.rs index bca508570f4..3c8973a762d 100644 --- a/crates/rome_cli/src/commands/format.rs +++ b/crates/rome_cli/src/commands/format.rs @@ -1,5 +1,5 @@ use crate::cli_options::CliOptions; -use crate::configuration::load_configuration; +use crate::configuration::{load_configuration, LoadedConfiguration}; use crate::execute::ReportMode; use crate::vcs::store_path_to_ignore_from_vcs; use crate::{execute_mode, CliDiagnostic, CliSession, Execution, TraversalMode}; @@ -38,8 +38,12 @@ pub(crate) fn format( files_configuration, write, } = payload; - let (mut configuration, diagnostics, configuration_path) = - load_configuration(&mut session, &cli_options)?.consume(); + let LoadedConfiguration { + mut configuration, + diagnostics, + directory_path: configuration_path, + .. + } = load_configuration(&mut session, &cli_options)?; if !diagnostics.is_empty() { let console = &mut session.app.console; console.log(markup!{ diff --git a/crates/rome_cli/src/commands/migrate.rs b/crates/rome_cli/src/commands/migrate.rs index 69468a63daa..c3bc61d6a68 100644 --- a/crates/rome_cli/src/commands/migrate.rs +++ b/crates/rome_cli/src/commands/migrate.rs @@ -1,5 +1,5 @@ use crate::cli_options::CliOptions; -use crate::configuration::load_configuration; +use crate::configuration::{load_configuration, LoadedConfiguration}; use crate::diagnostics::MigrationDiagnostic; use crate::execute::{execute_mode, Execution, TraversalMode}; use crate::{CliDiagnostic, CliSession}; @@ -10,7 +10,12 @@ pub(crate) fn migrate( cli_options: CliOptions, write: bool, ) -> Result<(), CliDiagnostic> { - let (_, _, path) = load_configuration(&mut session, &cli_options)?.consume(); + let LoadedConfiguration { + configuration: _, + diagnostics: _, + directory_path: path, + .. + } = load_configuration(&mut session, &cli_options)?; let config_name = session.app.fs.config_name(); if let Some(path) = path { execute_mode( diff --git a/crates/rome_cli/src/commands/mod.rs b/crates/rome_cli/src/commands/mod.rs index e8652dc4643..2330679447e 100644 --- a/crates/rome_cli/src/commands/mod.rs +++ b/crates/rome_cli/src/commands/mod.rs @@ -90,17 +90,18 @@ pub enum RomeCommand { #[bpaf(external, optional, hide_usage)] files_configuration: Option, - /// A file name with its extension to pass when reading from standard in, e.g. echo 'let a;' | rome format --stdin-file-path=file.js" + /// A file name with its extension to pass when reading from standard in, e.g. echo 'let a;' | rome format --stdin-file-path=file.js". #[bpaf(long("stdin-file-path"), argument("PATH"), hide_usage)] stdin_file_path: Option, #[bpaf(external, hide_usage)] cli_options: CliOptions, + /// Writes formatted files to file system. #[bpaf(switch)] write: bool, - /// Single file, single path or list of paths + /// Single file, single path or list of paths. #[bpaf(positional("PATH"), many)] paths: Vec, }, @@ -189,6 +190,23 @@ impl RomeCommand { pub const fn has_metrics(&self) -> bool { false } + + pub fn is_verbose(&self) -> bool { + match self { + RomeCommand::Version(_) => false, + RomeCommand::Rage(_) => false, + RomeCommand::Start => false, + RomeCommand::Stop => false, + RomeCommand::Check { cli_options, .. } => cli_options.verbose, + RomeCommand::Format { cli_options, .. } => cli_options.verbose, + RomeCommand::Ci { cli_options, .. } => cli_options.verbose, + RomeCommand::Init => false, + RomeCommand::LspProxy(cli_options) => cli_options.verbose, + RomeCommand::Migrate(cli_options, _) => cli_options.verbose, + RomeCommand::RunServer { .. } => false, + RomeCommand::PrintSocket => false, + } + } } pub fn parse_command() -> OptionParser { diff --git a/crates/rome_cli/src/commands/rage.rs b/crates/rome_cli/src/commands/rage.rs index ed980e33e09..884272ff6c3 100644 --- a/crates/rome_cli/src/commands/rage.rs +++ b/crates/rome_cli/src/commands/rage.rs @@ -165,9 +165,8 @@ impl Display for RageConfiguration<'_, '_> { match load_config(self.0, ConfigurationBasePath::default()) { Ok(None) => KeyValuePair("Status", markup!("unset")).fmt(fmt)?, - Ok(Some(deserialized)) => { - let (deserialized, _) = deserialized; - let (configuration, diagnostics) = deserialized.consume(); + Ok(Some(result)) => { + let (configuration, diagnostics) = result.deserialized.consume(); let status = if !diagnostics.is_empty() { for diagnostic in diagnostics { (markup! { diff --git a/crates/rome_cli/src/configuration.rs b/crates/rome_cli/src/configuration.rs index 33496c6aa95..0a1008747a3 100644 --- a/crates/rome_cli/src/configuration.rs +++ b/crates/rome_cli/src/configuration.rs @@ -1,30 +1,97 @@ -use std::path::PathBuf; - use crate::cli_options::CliOptions; use crate::{CliDiagnostic, CliSession}; +use rome_console::markup; +use rome_deserialize::json::deserialize_from_json_str; use rome_deserialize::Deserialized; -use rome_service::{load_config, Configuration, ConfigurationBasePath}; +use rome_diagnostics::Error; +use rome_fs::{FileSystem, OpenOptions}; +use rome_service::configuration::diagnostics::CantLoadExtendFile; +use rome_service::configuration::ConfigurationPayload; +use rome_service::{ + load_config, Configuration, ConfigurationBasePath, DynRef, MergeWith, WorkspaceError, +}; +use std::path::PathBuf; #[derive(Default)] pub struct LoadedConfiguration { - deserialized: Deserialized, - path: Option, + pub(crate) directory_path: Option, + pub(crate) file_path: Option, + pub(crate) configuration: Configuration, + pub(crate) diagnostics: Vec, } impl LoadedConfiguration { - pub fn consume(self) -> (Configuration, Vec, Option) { - let path = self.path; - let (configuration, diagnostics) = self.deserialized.consume(); - (configuration, diagnostics, path) + /// It updates the loaded configuration by resolving the `extends` field. + /// + /// If a configuration can't be resolved from the file system, the operation will fail. + pub fn apply_extends(&mut self, fs: &DynRef) -> Result<(), WorkspaceError> { + let deserialized = self.deserialize_extends(fs)?; + let (configurations, errors): (Vec<_>, Vec<_>) = + deserialized.into_iter().map(|d| d.consume()).unzip(); + for c in configurations { + self.configuration.merge_with(c); + } + self.diagnostics + .extend(errors.into_iter().flatten().collect::>()); + + Ok(()) + } + + fn deserialize_extends( + &mut self, + fs: &DynRef, + ) -> Result>, WorkspaceError> { + let Some(extends) = &self.configuration.extends else { + return Ok(vec![]); + }; + + let directory_path = self + .directory_path + .as_ref() + .cloned() + .unwrap_or(fs.working_directory().unwrap_or(PathBuf::from("./"))); + let mut deserialized_configurations = vec![]; + for path in extends.index_set() { + let config_path = directory_path.join(path); + let mut file = fs + .open_with_options(config_path.as_path(), OpenOptions::default().read(true)) + .map_err(|err| { + CantLoadExtendFile::new(config_path.display().to_string(), err.to_string()).with_verbose_advice( + markup!{ + "Rome tried to load the configuration file "{directory_path.display().to_string()}" using "{config_path.display().to_string()}" as base path." + } + ) + })?; + let mut content = String::new(); + file.read_to_string(&mut content).map_err(|err| { + CantLoadExtendFile::new(config_path.display().to_string(), err.to_string()).with_verbose_advice( + markup!{ + "It's possible that the file was created with a different user/group. Make sure you have the rights to read the file." + } + ) + + })?; + let deserialized = deserialize_from_json_str::(content.as_str()); + deserialized_configurations.push(deserialized) + } + Ok(deserialized_configurations) } } -impl From, PathBuf)>> for LoadedConfiguration { - fn from(value: Option<(Deserialized, PathBuf)>) -> Self { - if let Some((deserialized, path)) = value { - LoadedConfiguration { +impl From> for LoadedConfiguration { + fn from(value: Option) -> Self { + if let Some(value) = value { + let ConfigurationPayload { + configuration_directory_path, + configuration_file_path, deserialized, - path: Some(path), + } = value; + let (configuration, diagnostics) = deserialized.consume(); + LoadedConfiguration { + configuration, + diagnostics, + directory_path: Some(configuration_directory_path), + file_path: Some(configuration_file_path), } } else { LoadedConfiguration::default() @@ -43,7 +110,10 @@ pub(crate) fn load_configuration( Some(path) => ConfigurationBasePath::FromUser(PathBuf::from(path)), }; - let config = load_config(&session.app.fs, base_path)?; + let fs = &session.app.fs; + let config = load_config(fs, base_path)?; + let mut loaded_configuration = LoadedConfiguration::from(config); - Ok(config.into()) + loaded_configuration.apply_extends(fs)?; + Ok(loaded_configuration) } diff --git a/crates/rome_cli/src/diagnostics.rs b/crates/rome_cli/src/diagnostics.rs index 1334d2c97e3..6da7013d1b9 100644 --- a/crates/rome_cli/src/diagnostics.rs +++ b/crates/rome_cli/src/diagnostics.rs @@ -163,12 +163,18 @@ pub struct CheckError { action_kind: CheckActionKind, } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Copy)] pub enum CheckActionKind { Check, Apply, } +impl Debug for CheckActionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + impl Display for CheckActionKind { fn fmt(&self, fmt: &mut Formatter) -> std::io::Result<()> { match self { @@ -688,7 +694,7 @@ mod test { fn termination_diagnostic_size() { assert_eq!( std::mem::size_of::(), - 88, + 104, "you successfully decreased the size of the diagnostic!" ) } diff --git a/crates/rome_cli/src/execute/process_file.rs b/crates/rome_cli/src/execute/process_file.rs index 6c36031e776..09204789832 100644 --- a/crates/rome_cli/src/execute/process_file.rs +++ b/crates/rome_cli/src/execute/process_file.rs @@ -102,6 +102,17 @@ pub(crate) fn process_file(ctx: &TraversalOptions, path: &Path) -> FileResult { None } }) + .and( + file_features + .support_kind_for(&FeatureName::Format) + .and_then(|support_kind| { + if support_kind.is_not_enabled() { + Some(support_kind) + } else { + None + } + }), + ) .and( file_features .support_kind_for(&FeatureName::OrganizeImports) @@ -323,7 +334,7 @@ pub(crate) fn process_file(ctx: &TraversalOptions, path: &Path) -> FileResult { if ctx.execution.as_fix_file_mode().is_some() { true } else { - return Ok(result); + false } } TraversalMode::CI { .. } => false, diff --git a/crates/rome_cli/src/execute/traverse.rs b/crates/rome_cli/src/execute/traverse.rs index c2ecb160499..73502179814 100644 --- a/crates/rome_cli/src/execute/traverse.rs +++ b/crates/rome_cli/src/execute/traverse.rs @@ -666,7 +666,10 @@ impl<'ctx, 'app> TraversalContext for TraversalOptions<'ctx, 'app> { }; match self.execution.traversal_mode() { - TraversalMode::Check { .. } => file_features.supports_for(&FeatureName::Lint), + TraversalMode::Check { .. } => { + file_features.supports_for(&FeatureName::Lint) + || file_features.supports_for(&FeatureName::Format) + } TraversalMode::CI { .. } => { file_features.supports_for(&FeatureName::Lint) || file_features.supports_for(&FeatureName::Format) diff --git a/crates/rome_cli/src/main.rs b/crates/rome_cli/src/main.rs index 7975f4c4e0f..d6a3ee9d3f2 100644 --- a/crates/rome_cli/src/main.rs +++ b/crates/rome_cli/src/main.rs @@ -33,12 +33,15 @@ fn main() -> ExitCode { Ok(command) => { let color_mode = to_color_mode(command.get_color()); console.set_color(color_mode); + let is_verbose = command.is_verbose(); let result = run_workspace(&mut console, command); match result { Err(termination) => { + dbg!(&is_verbose); console.error(markup! { - {PrintDiagnostic::verbose(&termination)} - }); + {if is_verbose { PrintDiagnostic::verbose(&termination) } else { PrintDiagnostic::simple(&termination) }} + }); + termination.report() } Ok(_) => ExitCode::SUCCESS, diff --git a/crates/rome_cli/src/vcs.rs b/crates/rome_cli/src/vcs.rs index 7d76a2072bc..2104cabd745 100644 --- a/crates/rome_cli/src/vcs.rs +++ b/crates/rome_cli/src/vcs.rs @@ -71,12 +71,13 @@ pub(crate) fn read_vcs_ignore_file( } } if !configuration.ignore_file_disabled() { - let buffer = file_system + let result = file_system .auto_search(current_directory, client_kind.ignore_file(), false) .map_err(WorkspaceError::from)?; - if let Some((buffer, _)) = buffer { - return Ok(buffer + if let Some(result) = result { + return Ok(result + .content .lines() // remove empty lines .filter(|line| !line.is_empty()) diff --git a/crates/rome_cli/tests/cases/config_extends.rs b/crates/rome_cli/tests/cases/config_extends.rs new file mode 100644 index 00000000000..0796e5d6187 --- /dev/null +++ b/crates/rome_cli/tests/cases/config_extends.rs @@ -0,0 +1,215 @@ +use crate::run_cli; +use crate::snap_test::{assert_cli_snapshot, SnapshotPayload}; +use bpaf::Args; +use rome_console::BufferConsole; +use rome_fs::MemoryFileSystem; +use rome_service::DynRef; +use std::path::Path; + +#[test] +fn extends_config_ok_formatter_no_linter() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let rome_json = Path::new("rome.json"); + fs.insert( + rome_json.into(), + r#"{ "extends": ["format.json", "linter.json"] }"#, + ); + let format = Path::new("format.json"); + fs.insert( + format.into(), + r#"{ "javascript": { "formatter": { "quoteStyle": "single" } } }"#, + ); + let lint = Path::new("linter.json"); + fs.insert(lint.into(), r#"{ "linter": { "enabled": false } }"#); + + let test_file = Path::new("test.js"); + fs.insert(test_file.into(), r#"debugger; console.log("string"); "#); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from(&[("check"), test_file.as_os_str().to_str().unwrap()]), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_config_ok_formatter_no_linter", + fs, + console, + result, + )); +} + +#[test] +fn extends_config_ok_linter_not_formatter() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let rome_json = Path::new("rome.json"); + fs.insert( + rome_json.into(), + r#"{ "extends": ["format.json", "linter.json"] }"#, + ); + let format = Path::new("format.json"); + fs.insert(format.into(), r#"{ "formatter": { "enabled": true } }"#); + let lint = Path::new("linter.json"); + fs.insert( + lint.into(), + r#"{ + "linter": { + "rules": { + "all": false, + "suspicious": { + "noDebugger": "warn" + } + } + } +} + "#, + ); + + let test_file = Path::new("test.js"); + fs.insert(test_file.into(), r#"debugger; console.log("string"); "#); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from(&[("check"), test_file.as_os_str().to_str().unwrap()]), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_config_ok_linter_not_formatter", + fs, + console, + result, + )); +} + +#[test] +fn extends_should_raise_an_error_for_unresolved_configuration() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let rome_json = Path::new("rome.json"); + fs.insert( + rome_json.into(), + r#"{ "extends": ["formatTYPO.json", "linter.json"] }"#, + ); + let format = Path::new("format.json"); + fs.insert( + format.into(), + r#"{ "javascript": { "formatter": { "quoteStyle": "single" } } }"#, + ); + let lint = Path::new("linter.json"); + fs.insert(lint.into(), r#"{ "linter": { "enabled": false } }"#); + + let test_file = Path::new("test.js"); + fs.insert(test_file.into(), r#"debugger; console.log("string"); "#); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from(&[("check"), test_file.as_os_str().to_str().unwrap()]), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_should_raise_an_error_for_unresolved_configuration", + fs, + console, + result, + )); +} + +#[test] +fn extends_should_raise_an_error_for_unresolved_configuration_and_show_verbose() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let rome_json = Path::new("rome.json"); + fs.insert( + rome_json.into(), + r#"{ "extends": ["formatTYPO.json", "linter.json"] }"#, + ); + let format = Path::new("format.json"); + fs.insert( + format.into(), + r#"{ "javascript": { "formatter": { "quoteStyle": "single" } } }"#, + ); + let lint = Path::new("linter.json"); + fs.insert(lint.into(), r#"{ "linter": { "enabled": false } }"#); + + let test_file = Path::new("test.js"); + fs.insert(test_file.into(), r#"debugger; console.log("string"); "#); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from(&[ + ("check"), + "--verbose", + test_file.as_os_str().to_str().unwrap(), + ]), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_should_raise_an_error_for_unresolved_configuration_and_show_verbose", + fs, + console, + result, + )); +} + +#[test] +fn extends_resolves_when_using_config_path() { + let mut fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let rome_json = Path::new("config/rome.json"); + fs.insert( + rome_json.into(), + r#"{ "extends": ["format.json", "linter.json"] }"#, + ); + let format = Path::new("config/format.json"); + fs.insert( + format.into(), + r#"{ "javascript": { "formatter": { "quoteStyle": "single" } } }"#, + ); + let lint = Path::new("config/linter.json"); + fs.insert(lint.into(), r#"{ "linter": { "enabled": true } }"#); + + let test_file = Path::new("test.js"); + fs.insert(test_file.into(), r#"debugger; console.log("string"); "#); + + let result = run_cli( + DynRef::Borrowed(&mut fs), + &mut console, + Args::from(&[ + ("check"), + "--config-path=config/", + test_file.as_os_str().to_str().unwrap(), + ]), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "extends_resolves_when_using_config_path", + fs, + console, + result, + )); +} diff --git a/crates/rome_cli/tests/cases/mod.rs b/crates/rome_cli/tests/cases/mod.rs new file mode 100644 index 00000000000..176f3d8eb39 --- /dev/null +++ b/crates/rome_cli/tests/cases/mod.rs @@ -0,0 +1,4 @@ +//! Add here test cases that are not related directly to a command, but to specific +//! case that affects many commands + +mod config_extends; diff --git a/crates/rome_cli/tests/commands/check.rs b/crates/rome_cli/tests/commands/check.rs index fe053588f96..e27e4cdbadb 100644 --- a/crates/rome_cli/tests/commands/check.rs +++ b/crates/rome_cli/tests/commands/check.rs @@ -424,7 +424,7 @@ fn no_lint_if_linter_is_disabled_when_run_apply() { ]), ); - assert!(result.is_err(), "run_cli returned {result:?}"); + assert!(result.is_ok(), "run_cli returned {result:?}"); let mut buffer = String::new(); fs.open(file_path) @@ -432,7 +432,7 @@ fn no_lint_if_linter_is_disabled_when_run_apply() { .read_to_string(&mut buffer) .unwrap(); - assert_eq!(buffer, FIX_BEFORE); + assert_eq!(buffer, FIX_AFTER); assert_cli_snapshot(SnapshotPayload::new( module_path!(), @@ -460,7 +460,7 @@ fn no_lint_if_linter_is_disabled() { Args::from(&[("check"), file_path.as_os_str().to_str().unwrap()]), ); - assert!(result.is_err(), "run_cli returned {result:?}"); + assert!(result.is_ok(), "run_cli returned {result:?}"); let mut buffer = String::new(); fs.open(file_path) diff --git a/crates/rome_cli/tests/main.rs b/crates/rome_cli/tests/main.rs index 4267041692a..15875b8286e 100644 --- a/crates/rome_cli/tests/main.rs +++ b/crates/rome_cli/tests/main.rs @@ -2,6 +2,7 @@ mod commands; mod configs; #[cfg(test)] mod snap_test; +mod cases; #[cfg(test)] use snap_test::assert_cli_snapshot; diff --git a/crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_config_ok_formatter_no_linter.snap b/crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_config_ok_formatter_no_linter.snap new file mode 100644 index 00000000000..c0d0e6440ce --- /dev/null +++ b/crates/rome_cli/tests/snapshots/main_cases_config_extends/extends_config_ok_formatter_no_linter.snap @@ -0,0 +1,48 @@ +--- +source: crates/rome_cli/tests/snap_test.rs +expression: content +--- +## `rome.json` + +```json +{ "extends": ["format.json", "linter.json"] } +``` + +## `format.json` + +```json +{ "javascript": { "formatter": { "quoteStyle": "single" } } } +``` + +## `linter.json` + +```json +{ "linter": { "enabled": false } } +``` + +## `test.js` + +```js +debugger; console.log("string"); +``` + +# Emitted Messages + +```block +test.js format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + i Formatter would have printed the following content: + + 1 │ - debugger;·console.log("string");· + 1 │ + debugger; + 2 │ + console.log('string'); + 3 │ + + + +``` + +```block +Checked 1 file(s) in