Skip to content

Commit

Permalink
feat(cli/lint): add --rule option
Browse files Browse the repository at this point in the history
  • Loading branch information
Conaclos committed May 14, 2024
1 parent 9c920a1 commit 3fec75f
Show file tree
Hide file tree
Showing 24 changed files with 1,995 additions and 209 deletions.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b

### CLI

#### New features

- Introduce a new valued oprion `--rule` for `biome lint` ([#58](https://github.com/biomejs/biome/issues/58)).

This new option allows you to execute a single rule.
This is convenient to test a rule or apply the code fixes of a single rule.

For example, you can execute the `style/useNamingConvention` rule on a set of files:

```shell
biome lint --rule=style/useNamingConvention index,js
```

And the napply its code fixes:

```shell
biome lint --rule=style/useNamingConvention --apply index,js
```

The option takes the rule configuration into account.

The severity level of the executed rule is always set to its default,
i.e. `error` for a recommended rule or `warn` otherwise.

The option is compatible with other options such as `--apply` and `--reporter`.

Contributed by @Conaclos

#### Enhancements

- Biome now executes commands (lint, format, check and ci) on the working directory by default. [#2266](https://github.com/biomejs/biome/issues/2266) Contributed by @unvalley
Expand Down
4 changes: 4 additions & 0 deletions crates/biome_cli/src/commands/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::commands::{
use crate::{
execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution, TraversalMode,
};
use biome_configuration::linter::RuleCode;
use biome_configuration::vcs::PartialVcsConfiguration;
use biome_configuration::{
PartialConfiguration, PartialFilesConfiguration, PartialLinterConfiguration,
Expand All @@ -24,6 +25,7 @@ pub(crate) struct LintCommandPayload {
pub(crate) vcs_configuration: Option<PartialVcsConfiguration>,
pub(crate) files_configuration: Option<PartialFilesConfiguration>,
pub(crate) paths: Vec<OsString>,
pub(crate) rule: Option<RuleCode>,
pub(crate) stdin_file_path: Option<String>,
pub(crate) staged: bool,
pub(crate) changed: bool,
Expand All @@ -38,6 +40,7 @@ pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<(
cli_options,
mut linter_configuration,
mut paths,
rule,
stdin_file_path,
vcs_configuration,
files_configuration,
Expand Down Expand Up @@ -128,6 +131,7 @@ pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<(
Execution::new(TraversalMode::Lint {
fix_file_mode,
stdin,
rule,
})
.set_report(&cli_options),
session,
Expand Down
6 changes: 6 additions & 0 deletions crates/biome_cli/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::diagnostics::DeprecatedConfigurationFile;
use crate::execute::Stdin;
use crate::logging::LoggingKind;
use crate::{CliDiagnostic, CliSession, LoggingLevel, VERSION};
use biome_configuration::linter::RuleCode;
use biome_configuration::{
css::partial_css_formatter, javascript::partial_javascript_formatter,
json::partial_json_formatter, partial_configuration, partial_files_configuration,
Expand Down Expand Up @@ -151,6 +152,11 @@ pub enum BiomeCommand {

#[bpaf(external, hide_usage)]
cli_options: CliOptions,

/// Execute only this rule taking into account its configurations.
#[bpaf(long("rule"), argument("NAME"))]
rule: Option<RuleCode>,

/// Use this option when you want to format code piped from `stdin`, and print the output to `stdout`.
///
/// The file doesn't need to exist on disk, what matters is the extension of the file. Based on the extension, Biome knows how to lint the code.
Expand Down
3 changes: 3 additions & 0 deletions crates/biome_cli/src/execute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::execute::traverse::traverse;
use crate::reporter::json::{JsonReporter, JsonReporterVisitor};
use crate::reporter::terminal::{ConsoleReporter, ConsoleReporterVisitor};
use crate::{CliDiagnostic, CliSession, DiagnosticsPayload, Reporter};
use biome_configuration::linter::RuleCode;
use biome_console::{markup, ConsoleExt};
use biome_diagnostics::adapters::SerdeJsonError;
use biome_diagnostics::{category, Category};
Expand Down Expand Up @@ -125,6 +126,8 @@ pub enum TraversalMode {
/// 1. The virtual path to the file
/// 2. The content of the file
stdin: Option<Stdin>,

rule: Option<RuleCode>,
},
/// This mode is enabled when running the command `biome ci`
CI {
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_cli/src/execute/process_file/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub(crate) fn format_with_guard<'ctx>(
debug!("Pulling diagnostics from parsed file");
let diagnostics_result = workspace_file
.guard()
.pull_diagnostics(RuleCategories::SYNTAX, max_diagnostics.into())
.pull_diagnostics(RuleCategories::SYNTAX, max_diagnostics.into(), None)
.with_file_path_and_code(
workspace_file.path.display().to_string(),
category!("format"),
Expand Down
7 changes: 7 additions & 0 deletions crates/biome_cli/src/execute/process_file/lint.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::execute::diagnostics::ResultExt;
use crate::execute::process_file::workspace_file::WorkspaceFile;
use crate::execute::process_file::{FileResult, FileStatus, Message, SharedTraversalOptions};
use crate::TraversalMode;
use biome_diagnostics::{category, Error};
use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler};
use biome_service::workspace::RuleCategories;
Expand Down Expand Up @@ -56,11 +57,17 @@ pub(crate) fn lint_with_guard<'ctx>(
}

let max_diagnostics = ctx.remaining_diagnostics.load(Ordering::Relaxed);
let rule = if let TraversalMode::Lint { rule, .. } = ctx.execution.traversal_mode() {
*rule
} else {
None
};
let pull_diagnostics_result = workspace_file
.guard()
.pull_diagnostics(
RuleCategories::LINT | RuleCategories::SYNTAX,
max_diagnostics.into(),
rule,
)
.with_file_path_and_code(
workspace_file.path.display().to_string(),
Expand Down
6 changes: 6 additions & 0 deletions crates/biome_cli/src/execute/std_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,17 @@ pub(crate) fn run<'a>(
}
}

let rule = if let TraversalMode::Lint { rule, .. } = mode.traversal_mode() {
*rule
} else {
None
};
if !mode.is_check_apply_unsafe() {
let result = workspace.pull_diagnostics(PullDiagnosticsParams {
categories: RuleCategories::LINT | RuleCategories::SYNTAX,
path: biome_path.clone(),
max_diagnostics: mode.max_diagnostics.into(),
rule,
})?;
diagnostics.extend(result.diagnostics);
}
Expand Down
2 changes: 2 additions & 0 deletions crates/biome_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ impl<'app> CliSession<'app> {
cli_options,
linter_configuration,
paths,
rule,
stdin_file_path,
vcs_configuration,
files_configuration,
Expand All @@ -129,6 +130,7 @@ impl<'app> CliSession<'app> {
cli_options,
linter_configuration,
paths,
rule,
stdin_file_path,
vcs_configuration,
files_configuration,
Expand Down
151 changes: 148 additions & 3 deletions crates/biome_cli/tests/commands/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ fn parse_error() {
}

#[test]
fn lint_error() {
fn lint_rule_rule_doesnt_exist() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

Expand All @@ -142,14 +142,21 @@ fn lint_error() {
let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from([("lint"), file_path.as_os_str().to_str().unwrap()].as_slice()),
Args::from(
[
("lint"),
"--rule=suspicious/inexistant",
file_path.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"lint_error",
"lint_rule_rule_doesnt_exist",
fs,
console,
result,
Expand Down Expand Up @@ -3271,3 +3278,141 @@ fn should_lint_error_without_file_paths() {
result,
));
}

#[test]
fn lint_error() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path = Path::new("check.js");
fs.insert(file_path.into(), LINT_ERROR.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from([("lint"), file_path.as_os_str().to_str().unwrap()].as_slice()),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"lint_error",
fs,
console,
result,
));
}

#[test]
fn lint_rule_missing_group() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let file_path = Path::new("check.js");
fs.insert(file_path.into(), LINT_ERROR.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from(
[
("lint"),
"--rule=noDebugger",
file_path.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"lint_rule_missing_group",
fs,
console,
result,
));
}

#[test]
fn lint_rule_filter() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();
let content = "debugger; delete obj.prop;";

let file_path = Path::new("check.js");
fs.insert(file_path.into(), content.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from(
[
("lint"),
"--rule=suspicious/noDebugger",
file_path.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
);

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"lint_rule_filter",
fs,
console,
result,
));
}

#[test]
fn lint_rule_filter_with_config() {
let mut fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();
let config = r#"{
"linter": {
"rules": {
"style": {
"useNamingConvention": {
"level": "off",
"options": {
"strictCase": false
}
}
}
}
}
}"#;
let content = r#"
export function NotSTrictPAscal(){}
export function CONSTANT_CASE(){}
"#;

let file_path = Path::new("check.js");
fs.insert(file_path.into(), content.as_bytes());
let config_path = Path::new("biome.json");
fs.insert(config_path.into(), config.as_bytes());

let result = run_cli(
DynRef::Borrowed(&mut fs),
&mut console,
Args::from(
[
("lint"),
"--rule=style/useNamingConvention",
file_path.as_os_str().to_str().unwrap(),
]
.as_slice(),
),
);

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"lint_rule_filter_with_config",
fs,
console,
result,
));
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ expression: content
```block
Run various checks on a set of files.
Usage: lint [--apply] [--apply-unsafe] [--staged] [--changed] [--since=REF] [PATH]...
Usage: lint [--apply] [--apply-unsafe] [--rule=NAME] [--staged] [--changed] [--since=REF] [PATH]...
Set of properties to integrate Biome with a VCS software.
--vcs-client-kind=<git> The kind of client.
Expand Down Expand Up @@ -60,6 +60,7 @@ Available positional items:
Available options:
--apply Apply safe fixes, formatting and import sorting
--apply-unsafe Apply safe fixes and unsafe fixes, formatting and import sorting
--rule=NAME Execute only this rule taking into account its configurations.
--stdin-file-path=PATH Use this option when you want to format code piped from `stdin`, and
print the output to `stdout`.
The file doesn't need to exist on disk, what matters is the extension of
Expand Down
Loading

0 comments on commit 3fec75f

Please sign in to comment.