diff --git a/.github/ISSUE_TEMPLATE/01_formatter_bug.yml b/.github/ISSUE_TEMPLATE/01_formatter_bug.yml
index e24b2e5ea7d7..38d176a6dc41 100644
--- a/.github/ISSUE_TEMPLATE/01_formatter_bug.yml
+++ b/.github/ISSUE_TEMPLATE/01_formatter_bug.yml
@@ -1,7 +1,7 @@
name: π Formatter bug report
description: Report a bug or regression of the formatter
title: "π
"
-labels: [ "S-To triage" ]
+labels: [ "S-Needs triage" ]
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/02_lint_bug.yml b/.github/ISSUE_TEMPLATE/02_lint_bug.yml
index ded21f6e0ab0..0944224f7fb2 100644
--- a/.github/ISSUE_TEMPLATE/02_lint_bug.yml
+++ b/.github/ISSUE_TEMPLATE/02_lint_bug.yml
@@ -1,7 +1,7 @@
name: π
Linter bug report
description: Report a bug or regression of the linter
title: "π
"
-labels: [ "S-To triage" ]
+labels: [ "S-Needs triage" ]
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/03_bug.yml b/.github/ISSUE_TEMPLATE/03_bug.yml
index 2c6fc38bbbc9..66e3aa9f14fe 100644
--- a/.github/ISSUE_TEMPLATE/03_bug.yml
+++ b/.github/ISSUE_TEMPLATE/03_bug.yml
@@ -1,7 +1,7 @@
name: π Bug Report
description: Report a possible bug or regression
title: "π "
-labels: [ "S-To triage" ]
+labels: [ "S-Needs triage" ]
body:
- type: markdown
attributes:
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 0fff462a6d5a..3b87649f942f 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -11,6 +11,7 @@ on:
- 'crates/**_parser/**/*.rs'
- 'crates/**_formatter/**/*.rs'
- 'crates/**_analyze/**/*.rs'
+ - 'crates/biome_grit_patterns/**/*.rs'
push:
branches:
- main
@@ -19,6 +20,7 @@ on:
- 'crates/**_parser/**/*.rs'
- 'crates/**_formatter/**/*.rs'
- 'crates/**_analyze/**/*.rs'
+ - 'crates/biome_grit_patterns/**/*.rs'
env:
RUST_LOG: info
diff --git a/.github/workflows/close-issue.yml b/.github/workflows/close-issue.yml
new file mode 100644
index 000000000000..3a976b492301
--- /dev/null
+++ b/.github/workflows/close-issue.yml
@@ -0,0 +1,22 @@
+name: Close issues
+
+on:
+ schedule:
+ - cron: "0 0 * * *"
+
+
+permissions:
+ issues: write
+
+jobs:
+ close-issues:
+ if: github.repository == 'biomejs/biome'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Close issue without reproduction
+ uses: actions-cool/issues-helper@v3
+ with:
+ actions: "close-issues"
+ token: ${{ secrets.GITHUB_TOKEN }}
+ labels: "S-Needs repro"
+ inactive-day: 3
diff --git a/.github/workflows/needs-repro.yml b/.github/workflows/needs-repro.yml
new file mode 100644
index 000000000000..f8285a29ee35
--- /dev/null
+++ b/.github/workflows/needs-repro.yml
@@ -0,0 +1,38 @@
+name: Needs reproduction
+
+on:
+ issues:
+ types: [ labeled ]
+
+permissions:
+ issues: write
+
+jobs:
+ reply-labeled:
+ if: github.repository == 'biomejs/biome'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Remove triaging label
+ if: contains(github.event.issue.labels.*.name, 'S-Bug-confirmed') && contains(github.event.issue.labels.*.name, 'S-Needs triage')
+ uses: actions-cool/issues-helper@v3
+ with:
+ actions: "remove-labels"
+ token: ${{ secrets.GITHUB_TOKEN }}
+ issue-number: ${{ github.event.issue.number }}
+ labels: "S-Needs triage"
+
+ - name: Needs reproduction
+ if: github.event.label.name == 'S-Needs repro'
+ uses: actions-cool/issues-helper@v3
+ with:
+ actions: "create-comment, remove-labels"
+ token: ${{ secrets.GITHUB_TOKEN }}
+ issue-number: ${{ github.event.issue.number }}
+ body: |
+ Hello @${{ github.event.issue.user.login }}, please provide a minimal reproduction. You can use one of the following options:
+
+ - Provide a link to [our playground](https://biomejs.dev/playground), if it's applicable.
+ - Provide a link to GitHub repository. To easily create a reproduction, you can use our interactive CLI via `npm create @biomejs/biome-reproduction`
+
+ Issues marked with `S-Needs repro` will be **closed** if they have **no activity within 3 days**.
+ labels: "S-Needs triage"
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index d36dc709ef94..acb8c3a6ad42 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -124,7 +124,7 @@ jobs:
with:
node-version: 20
- name: Cache pnpm modules
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
diff --git a/.github/workflows/pull_request_js.yml b/.github/workflows/pull_request_js.yml
index 995a6205637e..6cd4cef01a29 100644
--- a/.github/workflows/pull_request_js.yml
+++ b/.github/workflows/pull_request_js.yml
@@ -28,7 +28,7 @@ jobs:
- name: Free Disk Space
uses: ./.github/actions/free-disk-space
- name: Cache pnpm modules
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
diff --git a/.github/workflows/release_cli.yml b/.github/workflows/release_cli.yml
index c357fe78d720..214dfad3e305 100644
--- a/.github/workflows/release_cli.yml
+++ b/.github/workflows/release_cli.yml
@@ -27,7 +27,7 @@ jobs:
run: echo "nightly=true" >> $GITHUB_ENV
- name: Check version changes
- uses: EndBug/version-check@d4be4219408b50d1bbbfd350a47cbcb126878692 # v2.1.4
+ uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5
if: env.nightly != 'true'
id: version
with:
diff --git a/.github/workflows/release_js_api.yml b/.github/workflows/release_js_api.yml
index 8ee1b413980e..60bb02596d1d 100644
--- a/.github/workflows/release_js_api.yml
+++ b/.github/workflows/release_js_api.yml
@@ -27,7 +27,7 @@ jobs:
run: echo "nightly=true" >> $GITHUB_ENV
- name: Check version changes
- uses: EndBug/version-check@d4be4219408b50d1bbbfd350a47cbcb126878692 # v2.1.4
+ uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5
if: env.nightly != 'true'
id: version
with:
@@ -69,7 +69,7 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Cache pnpm modules
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
diff --git a/.github/workflows/release_knope.yml b/.github/workflows/release_knope.yml
index 05cdd44840d1..e6cdab75a98b 100644
--- a/.github/workflows/release_knope.yml
+++ b/.github/workflows/release_knope.yml
@@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Check version changes
- uses: EndBug/version-check@d4be4219408b50d1bbbfd350a47cbcb126878692 # v2.1.4
+ uses: EndBug/version-check@36ff30f37c7deabe56a30caa043d127be658c425 # v2.1.5
id: version
with:
diff-search: true
diff --git a/.github/workflows/repository_dispatch.yml b/.github/workflows/repository_dispatch.yml
index 333eb8ee3f9f..092f122a52b5 100644
--- a/.github/workflows/repository_dispatch.yml
+++ b/.github/workflows/repository_dispatch.yml
@@ -28,7 +28,7 @@ jobs:
- name: Warm up wasm-pack cache
id: cache-restore
- uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
with:
path: |
./target
@@ -52,7 +52,7 @@ jobs:
continue-on-error: true
- name: Save new wasm-pack cache
- uses: actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache/save@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # v4.1.0
with:
path: |
./target
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 09de48b76e0d..d452c5f77492 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,8 +13,16 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
### Analyzer
+#### Bug fixes
+
+- Improved the message for unused suppression comments. Contributed by @dyc3
+
### CLI
+#### Enhancements
+
+- The `--summary` reporter now reports parsing diagnostics too. Contributed by @ematipico
+
### Configuration
#### Bug fixes
@@ -29,8 +37,94 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
### Linter
+#### Bug Fixes
+
+- Biome no longer crashes when it encounters a string that contain a multibyte character ([#4181](https://github.com/biomejs/biome/issues/4181)).
+
+ This fixes a regression introduced in Biome 1.9.3
+ The regression affected the following linter rules:
+
+ - nursery/useSortedClasses
+ - nursery/useTrimStartEnd
+ - style/useTemplate
+ - suspicious/noMisleadingCharacterClass
+
+ Contributed by @Conaclos
+
+- Fix [#4190](https://github.com/biomejs/biome/issues/4190), where the rule `noMissingVarFunction` wrongly reported a variable as missing when used inside a `var()` function that was a newline. Contributed by @ematipico
+
+- Fix [#4041](https://github.com/biomejs/biome/issues/4041). Now the rule `useSortedClasses` won't be triggered if `className` is composed only by inlined variables. Contributed by @ematipico
+
+- [useImportType](https://biomejs.dev/linter/rules/use-import-type/) and [useExportType](https://biomejs.dev/linter/rules/use-export-type/) now report useless inline type qualifiers ([#4178](https://github.com/biomejs/biome/issues/4178)).
+
+ The following fix is now proposed:
+
+ ```diff
+ - import type { type A, B } from "";
+ + import type { A, B } from "";
+
+ - export type { type C, D };
+ + export type { C, D };
+ ```
+
+ Contributed by @Conaclos
+
+- [useExportType](https://biomejs.dev/linter/rules/use-export-type/) now reports ungrouped `export from`.
+
+ The following fix is now proposed:
+
+ ```diff
+ - export { type A, type B } from "";
+ + export type { A, B } from "";
+ ```
+
+ Contributed by @Conaclos
+
+- [noVoidTypeReturn](https://biomejs.dev/linter/rules/no-void-type-return/) now accepts `void` expressions in return position ([#4173](https://github.com/biomejs/biome/issues/4173)).
+
+ The following code is now accepted:
+
+ ```ts
+ function f(): void {
+ return void 0;
+ }
+ ```
+
+ Contributed by @Conaclos
+
+- Fixes [#4059](https://github.com/biomejs/biome/issues/4059), the rule [noUselessFragments](https://biomejs.dev/linter/rules/no-useless-fragments/) now correctly handles fragments containing HTML escapes (e.g. ` `) inside expression escapes `{ ... }`.
+The following code is no longer reported:
+
+```jsx
+function Component() {
+ return (
+ {line || <> >}
+ )
+}
+```
+
+Contributed by @fireairforce
+
### Parser
+#### Bug Fixes
+
+- The CSS parser now accepts more emoji in identifiers ([#3627](https://github.com/biomejs/biome/issues/3627#issuecomment-2392388022)).
+
+ Browsers accept more emoji than the standard allows.
+ Biome now accepts these additional emoji.
+
+ The following code is now correctly parsed:
+
+ ```css
+ p {
+ --β¨-color: red;
+ color: var(--β¨-color);
+ }
+ ```
+
+ Contributed by @Conaclos
+
## v1.9.3 (2024-10-01)
### CLI
diff --git a/Cargo.lock b/Cargo.lock
index 39ec0ebcfdd2..934ffc8f3c79 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -602,6 +602,7 @@ dependencies = [
"biome_configuration",
"biome_formatter",
"biome_formatter_test",
+ "biome_fs",
"biome_grit_factory",
"biome_grit_parser",
"biome_grit_syntax",
diff --git a/benchmark/package.json b/benchmark/package.json
index db8ddaf88ff5..f45249440307 100644
--- a/benchmark/package.json
+++ b/benchmark/package.json
@@ -11,9 +11,9 @@
"node": ">20.0.0"
},
"devDependencies": {
- "@typescript-eslint/eslint-plugin": "8.7.0",
+ "@typescript-eslint/eslint-plugin": "8.8.0",
"dprint": "0.47.2",
- "eslint": "9.11.1",
+ "eslint": "9.12.0",
"prettier": "3.3.3"
}
}
diff --git a/crates/biome_analyze/CONTRIBUTING.md b/crates/biome_analyze/CONTRIBUTING.md
index 4635735b98c2..5a4af4740a06 100644
--- a/crates/biome_analyze/CONTRIBUTING.md
+++ b/crates/biome_analyze/CONTRIBUTING.md
@@ -170,7 +170,7 @@ use biome_deserialize_macros::Deserializable;
pub struct MyRuleOptions {
behavior: Behavior,
threshold: u8,
- behavior_exceptions: Vec
+ behavior_exceptions: Box<[Box]>
}
#[derive(Clone, Debug, Default, Deserializable)]
@@ -182,6 +182,9 @@ pub enum Behavior {
}
```
+Note that we use a boxed slice `Box<[Box]>` instead of `Vec`.
+This allows saving memory: [boxed slices and boxed str use 2 words instead of three words](https://nnethercote.github.io/perf-book/type-sizes.html#boxed-slices).
+
To allow deserializing instances of the types `MyRuleOptions` and `Behavior`,
they have to implement the `Deserializable` trait from the `biome_deserialize` crate.
This is what the `Deserializable` keyword in the `#[derive]` statements above did.
@@ -432,8 +435,10 @@ impl Rule for ForLoopCountReferences {
#### Multiple signals
Some rules require you to find all possible cases upfront in `run` function.
-To achieve that you can change Signals type from `Option<()>` to `Vec<()>`.
-This will call the diagnostic/action function for every item of the vec.
+To achieve that you can change Signals type from `Option` to an iterable data structure such as `Vec` or `Box<[Self::State]>`.
+This will call the diagnostic/action function for every item of the data structure.
+We prefer to use `Box<[_]>` over `Vec<_>` because it takes less memory.
+You can easily convert a `Vec<_>` into a `Box<[_]>` using the `Vec::into_boxed_slice()` method.
Taking previous example and modifying it a bit we can apply diagnostic for each item easily.
@@ -441,7 +446,7 @@ Taking previous example and modifying it a bit we can apply diagnostic for each
impl Rule for ForLoopCountReferences {
type Query = Semantic;
type State = TextRange;
- type Signals = Vec; // Replaced Option with Vec
+ type Signals = Box<[Self::State]>;
type Options = ();
fn run(ctx: &RuleContext) -> Self::Signals {
@@ -462,7 +467,7 @@ impl Rule for ForLoopCountReferences {
Some(range)
}).collect::>();
- write_ranges
+ write_ranges.into_boxed_slice()
}
fn diagnostic(_: &RuleContext, range: &Self::State) -> Option {
diff --git a/crates/biome_analyze/src/lib.rs b/crates/biome_analyze/src/lib.rs
index ed5e3e2ad914..e20f13b34f1f 100644
--- a/crates/biome_analyze/src/lib.rs
+++ b/crates/biome_analyze/src/lib.rs
@@ -182,7 +182,7 @@ where
SuppressionDiagnostic::new(
category!("suppressions/unused"),
suppression.comment_span,
- "Suppression comment is not being used",
+ "Suppression comment has no effect. Remove the suppression or make sure you are suppressing the correct rule.",
)
});
@@ -424,7 +424,7 @@ where
suppression
.suppressed_instances
.iter()
- .any(|(filter, v)| *filter == entry.rule && v == value)
+ .any(|(filter, v)| *filter == entry.rule && v == value.as_ref())
})
});
diff --git a/crates/biome_analyze/src/matcher.rs b/crates/biome_analyze/src/matcher.rs
index f1a101e22fa8..a88858bcb53d 100644
--- a/crates/biome_analyze/src/matcher.rs
+++ b/crates/biome_analyze/src/matcher.rs
@@ -138,7 +138,7 @@ pub struct SignalEntry<'phase, L: Language> {
/// Unique identifier for the rule that emitted this signal
pub rule: RuleKey,
/// Optional rule instances being suppressed
- pub instances: Vec,
+ pub instances: Box<[Box]>,
/// Text range in the document this signal covers
pub text_range: TextRange,
}
diff --git a/crates/biome_analyze/src/rule.rs b/crates/biome_analyze/src/rule.rs
index 4e7b3cc83588..594c75327733 100644
--- a/crates/biome_analyze/src/rule.rs
+++ b/crates/biome_analyze/src/rule.rs
@@ -85,7 +85,7 @@ impl TryFrom for Applicability {
}
#[derive(Debug, Clone, Eq)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, schemars::JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum RuleSource {
/// Rules from [Rust Clippy](https://rust-lang.github.io/rust-clippy/master/index.html)
@@ -118,7 +118,7 @@ pub enum RuleSource {
EslintTypeScript(&'static str),
/// Rules from [Eslint Plugin Unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn)
EslintUnicorn(&'static str),
- /// Rules from [Eslint Plugin Unused Imports](https://github.com/sweepline/eslint-plugin-unused-imports)
+ /// Rules from [Eslint Plugin Unused Imports](https://github.com/sweepline/eslint-plugin-unused-imports)
EslintUnusedImports(&'static str),
/// Rules from [Eslint Plugin Mysticatea](https://github.com/mysticatea/eslint-plugin)
EslintMysticatea(&'static str),
@@ -126,8 +126,12 @@ pub enum RuleSource {
EslintBarrelFiles(&'static str),
/// Rules from [Eslint Plugin N](https://github.com/eslint-community/eslint-plugin-n)
EslintN(&'static str),
+ /// Rules from [Eslint Plugin Next](https://github.com/vercel/next.js/tree/canary/packages/eslint-plugin-next)
+ EslintNext(&'static str),
/// Rules from [Stylelint](https://github.com/stylelint/stylelint)
Stylelint(&'static str),
+ /// Rules from [Eslint Plugin No Secrets](https://github.com/nickdeis/eslint-plugin-no-secrets)
+ EslintNoSecrets(&'static str),
}
impl PartialEq for RuleSource {
@@ -158,7 +162,9 @@ impl std::fmt::Display for RuleSource {
Self::EslintMysticatea(_) => write!(f, "@mysticatea/eslint-plugin"),
Self::EslintBarrelFiles(_) => write!(f, "eslint-plugin-barrel-files"),
Self::EslintN(_) => write!(f, "eslint-plugin-n"),
+ Self::EslintNext(_) => write!(f, "@next/eslint-plugin-next"),
Self::Stylelint(_) => write!(f, "Stylelint"),
+ Self::EslintNoSecrets(_) => write!(f, "eslint-plugin-no-secrets"),
}
}
}
@@ -207,6 +213,8 @@ impl RuleSource {
| Self::EslintMysticatea(rule_name)
| Self::EslintBarrelFiles(rule_name)
| Self::EslintN(rule_name)
+ | Self::EslintNext(rule_name)
+ | Self::EslintNoSecrets(rule_name)
| Self::Stylelint(rule_name) => rule_name,
}
}
@@ -231,7 +239,9 @@ impl RuleSource {
Self::EslintMysticatea(rule_name) => format!("@mysticatea/{rule_name}"),
Self::EslintBarrelFiles(rule_name) => format!("barrel-files/{rule_name}"),
Self::EslintN(rule_name) => format!("n/{rule_name}"),
+ Self::EslintNext(rule_name) => format!("@next/{rule_name}"),
Self::Stylelint(rule_name) => format!("stylelint/{rule_name}"),
+ Self::EslintNoSecrets(rule_name) => format!("no-secrets/{rule_name}"),
}
}
@@ -256,7 +266,9 @@ impl RuleSource {
Self::EslintMysticatea(rule_name) => format!("https://github.com/mysticatea/eslint-plugin/blob/master/docs/rules/{rule_name}.md"),
Self::EslintBarrelFiles(rule_name) => format!("https://github.com/thepassle/eslint-plugin-barrel-files/blob/main/docs/rules/{rule_name}.md"),
Self::EslintN(rule_name) => format!("https://github.com/eslint-community/eslint-plugin-n/blob/master/docs/rules/{rule_name}.md"),
+ Self::EslintNext(rule_name) => format!("https://nextjs.org/docs/messages/{rule_name}"),
Self::Stylelint(rule_name) => format!("https://github.com/stylelint/stylelint/blob/main/lib/rules/{rule_name}/README.md"),
+ Self::EslintNoSecrets(_) => "https://github.com/nickdeis/eslint-plugin-no-secrets/blob/master/README.md".to_string(),
}
}
@@ -280,7 +292,7 @@ impl RuleSource {
}
#[derive(Debug, Default, Clone, Copy)]
-#[cfg_attr(feature = "serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, schemars::JsonSchema))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum RuleSourceKind {
/// The rule implements the same logic of the source
@@ -788,8 +800,8 @@ pub trait Rule: RuleMeta + Sized {
/// *Note: For `noUnusedVariables` the above may not seem very useful (and
/// indeed it's not implemented), but for rules such as
/// `useExhaustiveDependencies` this is actually desirable.*
- fn instances_for_signal(_signal: &Self::State) -> Vec {
- Vec::new()
+ fn instances_for_signal(_signal: &Self::State) -> Box<[Box]> {
+ Vec::new().into_boxed_slice()
}
/// Used by the analyzer to associate a range of source text to a signal in
diff --git a/crates/biome_cli/src/changed.rs b/crates/biome_cli/src/changed.rs
index b3eae9f37621..7b101ae841f1 100644
--- a/crates/biome_cli/src/changed.rs
+++ b/crates/biome_cli/src/changed.rs
@@ -7,14 +7,14 @@ use std::ffi::OsString;
pub(crate) fn get_changed_files(
fs: &DynRef<'_, dyn FileSystem>,
configuration: &PartialConfiguration,
- since: Option,
+ since: Option<&str>,
) -> Result, CliDiagnostic> {
let default_branch = configuration
.vcs
.as_ref()
.and_then(|v| v.default_branch.as_ref());
- let base = match (since.as_ref(), default_branch) {
+ let base = match (since, default_branch) {
(Some(since), Some(_)) => since,
(Some(since), None) => since,
(None, Some(branch)) => branch,
diff --git a/crates/biome_cli/src/commands/check.rs b/crates/biome_cli/src/commands/check.rs
index d0584dd2e26f..616f9013e7f6 100644
--- a/crates/biome_cli/src/commands/check.rs
+++ b/crates/biome_cli/src/commands/check.rs
@@ -1,36 +1,24 @@
+use super::{determine_fix_file_mode, FixFileModeOptions, LoadEditorConfig};
use crate::cli_options::CliOptions;
-use crate::commands::{
- get_files_to_process, get_stdin, resolve_manifest, validate_configuration_diagnostics,
-};
-use crate::execute::VcsTargeted;
-use crate::{
- execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution, TraversalMode,
-};
+use crate::commands::{get_files_to_process_with_cli_options, CommandRunner};
+use crate::{CliDiagnostic, Execution, TraversalMode};
use biome_configuration::analyzer::assists::PartialAssistsConfiguration;
use biome_configuration::{
organize_imports::PartialOrganizeImports, PartialConfiguration, PartialFormatterConfiguration,
PartialLinterConfiguration,
};
-use biome_console::{markup, ConsoleExt};
+use biome_console::Console;
use biome_deserialize::Merge;
-use biome_diagnostics::PrintDiagnostic;
-use biome_service::configuration::{load_editorconfig, PartialConfigurationExt};
-use biome_service::workspace::RegisterProjectFolderParams;
-use biome_service::{
- configuration::{load_configuration, LoadedConfiguration},
- workspace::UpdateSettingsParams,
-};
+use biome_fs::FileSystem;
+use biome_service::{configuration::LoadedConfiguration, DynRef, Workspace, WorkspaceError};
use std::ffi::OsString;
-use super::{determine_fix_file_mode, FixFileModeOptions};
-
pub(crate) struct CheckCommandPayload {
pub(crate) apply: bool,
pub(crate) apply_unsafe: bool,
pub(crate) write: bool,
pub(crate) fix: bool,
pub(crate) unsafe_: bool,
- pub(crate) cli_options: CliOptions,
pub(crate) configuration: Option,
pub(crate) paths: Vec,
pub(crate) stdin_file_path: Option,
@@ -43,169 +31,127 @@ pub(crate) struct CheckCommandPayload {
pub(crate) since: Option,
}
-/// Handler for the "check" command of the Biome CLI
-pub(crate) fn check(
- session: CliSession,
- payload: CheckCommandPayload,
-) -> Result<(), CliDiagnostic> {
- let CheckCommandPayload {
- apply,
- apply_unsafe,
- write,
- fix,
- unsafe_,
- cli_options,
- configuration,
- paths,
- stdin_file_path,
- linter_enabled,
- organize_imports_enabled,
- formatter_enabled,
- since,
- assists_enabled,
- staged,
- changed,
- } = payload;
- setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
-
- let fix_file_mode = determine_fix_file_mode(
- FixFileModeOptions {
- apply,
- apply_unsafe,
- write,
- fix,
- unsafe_,
- },
- session.app.console,
- )?;
-
- let loaded_configuration =
- load_configuration(&session.app.fs, cli_options.as_configuration_path_hint())?;
- validate_configuration_diagnostics(
- &loaded_configuration,
- session.app.console,
- cli_options.verbose,
- )?;
-
- let editorconfig_search_path = loaded_configuration.directory_path.clone();
- let LoadedConfiguration {
- configuration: biome_configuration,
- directory_path: configuration_path,
- ..
- } = loaded_configuration;
-
- let should_use_editorconfig = configuration
- .as_ref()
- .and_then(|c| c.use_editorconfig())
- .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default());
- let mut fs_configuration = if should_use_editorconfig {
- let (editorconfig, editorconfig_diagnostics) = {
- let search_path = editorconfig_search_path.unwrap_or_else(|| {
- let fs = &session.app.fs;
- fs.working_directory().unwrap_or_default()
- });
- load_editorconfig(&session.app.fs, search_path)?
- };
- for diagnostic in editorconfig_diagnostics {
- session.app.console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- })
- }
- editorconfig.unwrap_or_default()
- } else {
- Default::default()
- };
- // this makes biome configuration take precedence over editorconfig configuration
- fs_configuration.merge_with(biome_configuration);
-
- let formatter = fs_configuration
- .formatter
- .get_or_insert_with(PartialFormatterConfiguration::default);
-
- if formatter_enabled.is_some() {
- formatter.enabled = formatter_enabled;
+impl LoadEditorConfig for CheckCommandPayload {
+ fn should_load_editor_config(&self, fs_configuration: &PartialConfiguration) -> bool {
+ self.configuration
+ .as_ref()
+ .and_then(|c| c.use_editorconfig())
+ .unwrap_or(fs_configuration.use_editorconfig().unwrap_or_default())
}
+}
- let linter = fs_configuration
- .linter
- .get_or_insert_with(PartialLinterConfiguration::default);
+impl CommandRunner for CheckCommandPayload {
+ const COMMAND_NAME: &'static str = "check";
+
+ fn merge_configuration(
+ &mut self,
+ loaded_configuration: LoadedConfiguration,
+ fs: &DynRef<'_, dyn FileSystem>,
+ console: &mut dyn Console,
+ ) -> Result {
+ let editorconfig_search_path = loaded_configuration.directory_path.clone();
+ let LoadedConfiguration {
+ configuration: biome_configuration,
+ ..
+ } = loaded_configuration;
+ let mut fs_configuration =
+ self.load_editor_config(editorconfig_search_path, &biome_configuration, fs, console)?;
+ // this makes biome configuration take precedence over editorconfig configuration
+ fs_configuration.merge_with(biome_configuration);
+
+ let formatter = fs_configuration
+ .formatter
+ .get_or_insert_with(PartialFormatterConfiguration::default);
+
+ if self.formatter_enabled.is_some() {
+ formatter.enabled = self.formatter_enabled;
+ }
- if linter_enabled.is_some() {
- linter.enabled = linter_enabled;
- }
+ let linter = fs_configuration
+ .linter
+ .get_or_insert_with(PartialLinterConfiguration::default);
- let organize_imports = fs_configuration
- .organize_imports
- .get_or_insert_with(PartialOrganizeImports::default);
+ if self.linter_enabled.is_some() {
+ linter.enabled = self.linter_enabled;
+ }
- if organize_imports_enabled.is_some() {
- organize_imports.enabled = organize_imports_enabled;
- }
+ let organize_imports = fs_configuration
+ .organize_imports
+ .get_or_insert_with(PartialOrganizeImports::default);
- let assists = fs_configuration
- .assists
- .get_or_insert_with(PartialAssistsConfiguration::default);
+ if self.organize_imports_enabled.is_some() {
+ organize_imports.enabled = self.organize_imports_enabled;
+ }
- if assists_enabled.is_some() {
- assists.enabled = assists_enabled;
- }
+ let assists = fs_configuration
+ .assists
+ .get_or_insert_with(PartialAssistsConfiguration::default);
- if let Some(mut configuration) = configuration {
- if let Some(linter) = configuration.linter.as_mut() {
- // Don't overwrite rules from the CLI configuration.
- // Otherwise, rules that are disabled in the config file might
- // become re-enabled due to the defaults included in the CLI
- // configuration.
- linter.rules = None;
+ if self.assists_enabled.is_some() {
+ assists.enabled = self.assists_enabled;
}
- fs_configuration.merge_with(configuration);
+
+ if let Some(mut configuration) = self.configuration.clone() {
+ if let Some(linter) = configuration.linter.as_mut() {
+ // Don't overwrite rules from the CLI configuration.
+ // Otherwise, rules that are disabled in the config file might
+ // become re-enabled due to the defaults included in the CLI
+ // configuration.
+ linter.rules = None;
+ }
+ fs_configuration.merge_with(configuration);
+ }
+
+ Ok(fs_configuration)
}
- // check if support of git ignore files is enabled
- let vcs_base_path = configuration_path.or(session.app.fs.working_directory());
- let (vcs_base_path, gitignore_matches) =
- fs_configuration.retrieve_gitignore_matches(&session.app.fs, vcs_base_path.as_deref())?;
-
- let stdin = get_stdin(stdin_file_path, &mut *session.app.console, "check")?;
-
- let vcs_targeted_paths =
- get_files_to_process(since, changed, staged, &session.app.fs, &fs_configuration)?;
-
- session
- .app
- .workspace
- .register_project_folder(RegisterProjectFolderParams {
- path: session.app.fs.working_directory(),
- set_as_current_workspace: true,
- })?;
- let manifest_data = resolve_manifest(&session.app.fs)?;
-
- if let Some(manifest_data) = manifest_data {
- session
- .app
- .workspace
- .set_manifest_for_project(manifest_data.into())?;
+ fn get_files_to_process(
+ &self,
+ fs: &DynRef<'_, dyn FileSystem>,
+ configuration: &PartialConfiguration,
+ ) -> Result, CliDiagnostic> {
+ let paths = get_files_to_process_with_cli_options(
+ self.since.as_deref(),
+ self.changed,
+ self.staged,
+ fs,
+ configuration,
+ )?
+ .unwrap_or(self.paths.clone());
+
+ Ok(paths)
}
- session
- .app
- .workspace
- .update_settings(UpdateSettingsParams {
- workspace_directory: session.app.fs.working_directory(),
- configuration: fs_configuration,
- vcs_base_path,
- gitignore_matches,
- })?;
-
- execute_mode(
- Execution::new(TraversalMode::Check {
+ fn get_stdin_file_path(&self) -> Option<&str> {
+ self.stdin_file_path.as_deref()
+ }
+
+ fn should_write(&self) -> bool {
+ self.write || self.fix
+ }
+
+ fn get_execution(
+ &self,
+ cli_options: &CliOptions,
+ console: &mut dyn Console,
+ _workspace: &dyn Workspace,
+ ) -> Result {
+ let fix_file_mode = determine_fix_file_mode(
+ FixFileModeOptions {
+ apply: self.apply,
+ apply_unsafe: self.apply_unsafe,
+ write: self.write,
+ fix: self.fix,
+ unsafe_: self.unsafe_,
+ },
+ console,
+ )?;
+
+ Ok(Execution::new(TraversalMode::Check {
fix_file_mode,
- stdin,
- vcs_targeted: VcsTargeted { staged, changed },
+ stdin: self.get_stdin(console)?,
+ vcs_targeted: (self.staged, self.changed).into(),
})
- .set_report(&cli_options),
- session,
- &cli_options,
- vcs_targeted_paths.unwrap_or(paths),
- )
+ .set_report(cli_options))
+ }
}
diff --git a/crates/biome_cli/src/commands/ci.rs b/crates/biome_cli/src/commands/ci.rs
index 29b909276813..be4689fcf04c 100644
--- a/crates/biome_cli/src/commands/ci.rs
+++ b/crates/biome_cli/src/commands/ci.rs
@@ -1,18 +1,15 @@
use crate::changed::get_changed_files;
use crate::cli_options::CliOptions;
-use crate::commands::{resolve_manifest, validate_configuration_diagnostics};
-use crate::execute::VcsTargeted;
-use crate::{execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution};
+use crate::commands::{CommandRunner, LoadEditorConfig};
+use crate::{CliDiagnostic, Execution};
use biome_configuration::analyzer::assists::PartialAssistsConfiguration;
use biome_configuration::{organize_imports::PartialOrganizeImports, PartialConfiguration};
use biome_configuration::{PartialFormatterConfiguration, PartialLinterConfiguration};
-use biome_console::{markup, ConsoleExt};
+use biome_console::Console;
use biome_deserialize::Merge;
-use biome_diagnostics::PrintDiagnostic;
-use biome_service::configuration::{
- load_configuration, load_editorconfig, LoadedConfiguration, PartialConfigurationExt,
-};
-use biome_service::workspace::{RegisterProjectFolderParams, UpdateSettingsParams};
+use biome_fs::FileSystem;
+use biome_service::configuration::LoadedConfiguration;
+use biome_service::{DynRef, Workspace, WorkspaceError};
use std::ffi::OsString;
pub(crate) struct CiCommandPayload {
@@ -22,163 +19,124 @@ pub(crate) struct CiCommandPayload {
pub(crate) assists_enabled: Option,
pub(crate) paths: Vec,
pub(crate) configuration: Option,
- pub(crate) cli_options: CliOptions,
pub(crate) changed: bool,
pub(crate) since: Option,
}
-/// Handler for the "ci" command of the Biome CLI
-pub(crate) fn ci(session: CliSession, payload: CiCommandPayload) -> Result<(), CliDiagnostic> {
- let CiCommandPayload {
- cli_options,
- formatter_enabled,
- linter_enabled,
- organize_imports_enabled,
- assists_enabled,
- configuration,
- mut paths,
- since,
- changed,
- } = payload;
- setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
-
- let loaded_configuration =
- load_configuration(&session.app.fs, cli_options.as_configuration_path_hint())?;
-
- validate_configuration_diagnostics(
- &loaded_configuration,
- session.app.console,
- cli_options.verbose,
- )?;
-
- let LoadedConfiguration {
- configuration: biome_configuration,
- directory_path: configuration_path,
- ..
- } = loaded_configuration;
-
- let should_use_editorconfig = configuration
- .as_ref()
- .and_then(|c| c.use_editorconfig())
- .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default());
- let mut fs_configuration = if should_use_editorconfig {
- let (editorconfig, editorconfig_diagnostics) = {
- let search_path = configuration_path.clone().unwrap_or_else(|| {
- let fs = &session.app.fs;
- fs.working_directory().unwrap_or_default()
- });
- load_editorconfig(&session.app.fs, search_path)?
- };
- for diagnostic in editorconfig_diagnostics {
- session.app.console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- })
- }
- editorconfig.unwrap_or_default()
- } else {
- Default::default()
- };
- // this makes biome configuration take precedence over editorconfig configuration
- fs_configuration.merge_with(biome_configuration);
-
- let formatter = fs_configuration
- .formatter
- .get_or_insert_with(PartialFormatterConfiguration::default);
-
- if formatter_enabled.is_some() {
- formatter.enabled = formatter_enabled;
+impl LoadEditorConfig for CiCommandPayload {
+ fn should_load_editor_config(&self, fs_configuration: &PartialConfiguration) -> bool {
+ self.configuration
+ .as_ref()
+ .and_then(|c| c.use_editorconfig())
+ .unwrap_or(fs_configuration.use_editorconfig().unwrap_or_default())
}
+}
- let linter = fs_configuration
- .linter
- .get_or_insert_with(PartialLinterConfiguration::default);
+impl CommandRunner for CiCommandPayload {
+ const COMMAND_NAME: &'static str = "ci";
+
+ fn merge_configuration(
+ &mut self,
+ loaded_configuration: LoadedConfiguration,
+ fs: &DynRef<'_, dyn FileSystem>,
+ console: &mut dyn Console,
+ ) -> Result {
+ let LoadedConfiguration {
+ configuration: biome_configuration,
+ directory_path: configuration_path,
+ ..
+ } = loaded_configuration;
+
+ let mut fs_configuration =
+ self.load_editor_config(configuration_path, &biome_configuration, fs, console)?;
+ // this makes biome configuration take precedence over editorconfig configuration
+ fs_configuration.merge_with(biome_configuration);
+
+ let formatter = fs_configuration
+ .formatter
+ .get_or_insert_with(PartialFormatterConfiguration::default);
+
+ if self.formatter_enabled.is_some() {
+ formatter.enabled = self.formatter_enabled;
+ }
- if linter_enabled.is_some() {
- linter.enabled = linter_enabled;
- }
+ let linter = fs_configuration
+ .linter
+ .get_or_insert_with(PartialLinterConfiguration::default);
- let organize_imports = fs_configuration
- .organize_imports
- .get_or_insert_with(PartialOrganizeImports::default);
+ if self.linter_enabled.is_some() {
+ linter.enabled = self.linter_enabled;
+ }
- if organize_imports_enabled.is_some() {
- organize_imports.enabled = organize_imports_enabled;
- }
+ let organize_imports = fs_configuration
+ .organize_imports
+ .get_or_insert_with(PartialOrganizeImports::default);
- let assists = fs_configuration
- .assists
- .get_or_insert_with(PartialAssistsConfiguration::default);
+ if self.organize_imports_enabled.is_some() {
+ organize_imports.enabled = self.organize_imports_enabled;
+ }
- if assists_enabled.is_some() {
- assists.enabled = assists_enabled;
- }
+ let assists = fs_configuration
+ .assists
+ .get_or_insert_with(PartialAssistsConfiguration::default);
- // no point in doing the traversal if all the checks have been disabled
- if fs_configuration.is_formatter_disabled()
- && fs_configuration.is_linter_disabled()
- && fs_configuration.is_organize_imports_disabled()
- {
- return Err(CliDiagnostic::incompatible_end_configuration("Formatter, linter and organize imports are disabled, can't perform the command. This is probably and error."));
+ if self.assists_enabled.is_some() {
+ assists.enabled = self.assists_enabled;
+ }
+
+ if let Some(mut configuration) = self.configuration.clone() {
+ if let Some(linter) = configuration.linter.as_mut() {
+ // Don't overwrite rules from the CLI configuration.
+ // Otherwise, rules that are disabled in the config file might
+ // become re-enabled due to the defaults included in the CLI
+ // configuration.
+ linter.rules = None;
+ }
+ fs_configuration.merge_with(configuration);
+ }
+
+ Ok(fs_configuration)
}
- if let Some(mut configuration) = configuration {
- if let Some(linter) = configuration.linter.as_mut() {
- // Don't overwrite rules from the CLI configuration.
- // Otherwise, rules that are disabled in the config file might
- // become re-enabled due to the defaults included in the CLI
- // configuration.
- linter.rules = None;
+ fn get_files_to_process(
+ &self,
+ fs: &DynRef<'_, dyn FileSystem>,
+ configuration: &PartialConfiguration,
+ ) -> Result, CliDiagnostic> {
+ if self.changed {
+ get_changed_files(fs, configuration, self.since.as_deref())
+ } else {
+ Ok(self.paths.clone())
}
- fs_configuration.merge_with(configuration);
}
- // check if support of git ignore files is enabled
- let vcs_base_path = configuration_path.or(session.app.fs.working_directory());
- let (vcs_base_path, gitignore_matches) =
- fs_configuration.retrieve_gitignore_matches(&session.app.fs, vcs_base_path.as_deref())?;
+ fn get_stdin_file_path(&self) -> Option<&str> {
+ None
+ }
- if since.is_some() && !changed {
- return Err(CliDiagnostic::incompatible_arguments("since", "changed"));
+ fn should_write(&self) -> bool {
+ false
}
- if changed {
- paths = get_changed_files(&session.app.fs, &fs_configuration, since)?;
+ fn get_execution(
+ &self,
+ cli_options: &CliOptions,
+ _console: &mut dyn Console,
+ _workspace: &dyn Workspace,
+ ) -> Result {
+ Ok(Execution::new_ci((false, self.changed).into()).set_report(cli_options))
}
- session
- .app
- .workspace
- .register_project_folder(RegisterProjectFolderParams {
- path: session.app.fs.working_directory(),
- set_as_current_workspace: true,
- })?;
-
- let manifest_data = resolve_manifest(&session.app.fs)?;
-
- if let Some(manifest_data) = manifest_data {
- session
- .app
- .workspace
- .set_manifest_for_project(manifest_data.into())?;
+ fn check_incompatible_arguments(&self) -> Result<(), CliDiagnostic> {
+ if matches!(self.formatter_enabled, Some(false))
+ && matches!(self.linter_enabled, Some(false))
+ && matches!(self.organize_imports_enabled, Some(false))
+ {
+ return Err(CliDiagnostic::incompatible_end_configuration("Formatter, linter and organize imports are disabled, can't perform the command. At least one feature needs to be enabled. This is probably and error."));
+ }
+ if self.since.is_some() && !self.changed {
+ return Err(CliDiagnostic::incompatible_arguments("since", "changed"));
+ }
+ Ok(())
}
- session
- .app
- .workspace
- .update_settings(UpdateSettingsParams {
- configuration: fs_configuration,
- workspace_directory: session.app.fs.working_directory(),
- vcs_base_path,
- gitignore_matches,
- })?;
-
- execute_mode(
- Execution::new_ci(VcsTargeted {
- staged: false,
- changed,
- })
- .set_report(&cli_options),
- session,
- &cli_options,
- paths,
- )
}
diff --git a/crates/biome_cli/src/commands/format.rs b/crates/biome_cli/src/commands/format.rs
index 0438e60f5706..6b876ff5691b 100644
--- a/crates/biome_cli/src/commands/format.rs
+++ b/crates/biome_cli/src/commands/format.rs
@@ -1,28 +1,21 @@
use crate::cli_options::CliOptions;
-use crate::commands::{
- get_files_to_process, get_stdin, resolve_manifest, validate_configuration_diagnostics,
-};
+use crate::commands::{get_files_to_process_with_cli_options, CommandRunner, LoadEditorConfig};
use crate::diagnostics::DeprecatedArgument;
-use crate::execute::VcsTargeted;
-use crate::{
- execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution, TraversalMode,
-};
+use crate::{CliDiagnostic, Execution, TraversalMode};
use biome_configuration::vcs::PartialVcsConfiguration;
use biome_configuration::{
- PartialCssFormatter, PartialFilesConfiguration, PartialFormatterConfiguration,
- PartialGraphqlFormatter, PartialJavascriptFormatter, PartialJsonFormatter,
+ PartialConfiguration, PartialCssFormatter, PartialFilesConfiguration,
+ PartialFormatterConfiguration, PartialGraphqlFormatter, PartialJavascriptFormatter,
+ PartialJsonFormatter,
};
-use biome_console::{markup, ConsoleExt};
+use biome_console::{markup, Console, ConsoleExt};
use biome_deserialize::Merge;
use biome_diagnostics::PrintDiagnostic;
-use biome_service::configuration::{
- load_configuration, load_editorconfig, LoadedConfiguration, PartialConfigurationExt,
-};
-use biome_service::workspace::{RegisterProjectFolderParams, UpdateSettingsParams};
+use biome_fs::FileSystem;
+use biome_service::configuration::LoadedConfiguration;
+use biome_service::{DynRef, Workspace, WorkspaceError};
use std::ffi::OsString;
-use super::check_fix_incompatible_arguments;
-
pub(crate) struct FormatCommandPayload {
pub(crate) javascript_formatter: Option,
pub(crate) json_formatter: Option,
@@ -34,227 +27,181 @@ pub(crate) struct FormatCommandPayload {
pub(crate) stdin_file_path: Option,
pub(crate) write: bool,
pub(crate) fix: bool,
- pub(crate) cli_options: CliOptions,
pub(crate) paths: Vec,
pub(crate) staged: bool,
pub(crate) changed: bool,
pub(crate) since: Option,
}
-/// Handler for the "format" command of the Biome CLI
-pub(crate) fn format(
- session: CliSession,
- payload: FormatCommandPayload,
-) -> Result<(), CliDiagnostic> {
- let FormatCommandPayload {
- mut javascript_formatter,
- mut formatter_configuration,
- vcs_configuration,
- mut paths,
- cli_options,
- stdin_file_path,
- files_configuration,
- write,
- fix,
- mut json_formatter,
- css_formatter,
- graphql_formatter,
- since,
- staged,
- changed,
- } = payload;
- setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
-
- check_fix_incompatible_arguments(super::FixFileModeOptions {
- apply: false,
- apply_unsafe: false,
- write,
- fix,
- unsafe_: false,
- })?;
-
- let loaded_configuration =
- load_configuration(&session.app.fs, cli_options.as_configuration_path_hint())?;
- validate_configuration_diagnostics(
- &loaded_configuration,
- session.app.console,
- cli_options.verbose,
- )?;
-
- let editorconfig_search_path = loaded_configuration.directory_path.clone();
- let LoadedConfiguration {
- configuration: biome_configuration,
- directory_path: configuration_path,
- ..
- } = loaded_configuration;
-
- let should_use_editorconfig = formatter_configuration
- .as_ref()
- .and_then(|c| c.use_editorconfig)
- .unwrap_or(biome_configuration.use_editorconfig().unwrap_or_default());
- let mut fs_configuration = if should_use_editorconfig {
- let (editorconfig, editorconfig_diagnostics) = {
- let search_path = editorconfig_search_path.unwrap_or_else(|| {
- let fs = &session.app.fs;
- fs.working_directory().unwrap_or_default()
- });
- load_editorconfig(&session.app.fs, search_path)?
- };
- for diagnostic in editorconfig_diagnostics {
- session.app.console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- })
- }
- editorconfig.unwrap_or_default()
- } else {
- Default::default()
- };
- // this makes biome configuration take precedence over editorconfig configuration
- fs_configuration.merge_with(biome_configuration);
- let mut configuration = fs_configuration;
-
- // TODO: remove in biome 2.0
- let console = &mut *session.app.console;
- if let Some(config) = formatter_configuration.as_mut() {
- if let Some(indent_size) = config.indent_size {
- let diagnostic = DeprecatedArgument::new(markup! {
- "The argument ""--indent-size"" is deprecated, it will be removed in the next major release. Use ""--indent-width"" instead."
- });
- console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- });
+impl LoadEditorConfig for FormatCommandPayload {
+ fn should_load_editor_config(&self, fs_configuration: &PartialConfiguration) -> bool {
+ self.formatter_configuration
+ .as_ref()
+ .and_then(|c| c.use_editorconfig)
+ .unwrap_or(fs_configuration.use_editorconfig().unwrap_or_default())
+ }
+}
- if config.indent_width.is_none() {
- config.indent_width = Some(indent_size);
+impl CommandRunner for FormatCommandPayload {
+ const COMMAND_NAME: &'static str = "format";
+
+ fn merge_configuration(
+ &mut self,
+ loaded_configuration: LoadedConfiguration,
+ fs: &DynRef<'_, dyn FileSystem>,
+ console: &mut dyn Console,
+ ) -> Result {
+ let LoadedConfiguration {
+ configuration: biome_configuration,
+ directory_path: configuration_path,
+ ..
+ } = loaded_configuration;
+ let editorconfig_search_path = configuration_path.clone();
+ let mut fs_configuration =
+ self.load_editor_config(editorconfig_search_path, &biome_configuration, fs, console)?;
+ // this makes biome configuration take precedence over editorconfig configuration
+ fs_configuration.merge_with(biome_configuration);
+ let mut configuration = fs_configuration;
+
+ // TODO: remove in biome 2.0
+ if let Some(config) = self.formatter_configuration.as_mut() {
+ if let Some(indent_size) = config.indent_size {
+ let diagnostic = DeprecatedArgument::new(markup! {
+ "The argument ""--indent-size"" is deprecated, it will be removed in the next major release. Use ""--indent-width"" instead."
+ });
+ console.error(markup! {
+ {PrintDiagnostic::simple(&diagnostic)}
+ });
+
+ if config.indent_width.is_none() {
+ config.indent_width = Some(indent_size);
+ }
}
}
- }
- // TODO: remove in biome 2.0
- if let Some(js_formatter) = javascript_formatter.as_mut() {
- if let Some(indent_size) = js_formatter.indent_size {
- let diagnostic = DeprecatedArgument::new(markup! {
- "The argument ""--javascript-formatter-indent-size"" is deprecated, it will be removed in the next major release. Use ""--javascript-formatter-indent-width"" instead."
- });
- console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- });
+ // TODO: remove in biome 2.0
+ if let Some(js_formatter) = self.javascript_formatter.as_mut() {
+ if let Some(indent_size) = js_formatter.indent_size {
+ let diagnostic = DeprecatedArgument::new(markup! {
+ "The argument ""--javascript-formatter-indent-size"" is deprecated, it will be removed in the next major release. Use ""--javascript-formatter-indent-width"" instead."
+ });
+ console.error(markup! {
+ {PrintDiagnostic::simple(&diagnostic)}
+ });
+
+ if js_formatter.indent_width.is_none() {
+ js_formatter.indent_width = Some(indent_size);
+ }
+ }
- if js_formatter.indent_width.is_none() {
- js_formatter.indent_width = Some(indent_size);
+ if let Some(trailing_comma) = js_formatter.trailing_comma {
+ let diagnostic = DeprecatedArgument::new(markup! {
+ "The argument ""--trailing-comma"" is deprecated, it will be removed in the next major release. Use ""--trailing-commas"" instead."
+ });
+ console.error(markup! {
+ {PrintDiagnostic::simple(&diagnostic)}
+ });
+
+ if js_formatter.trailing_commas.is_none() {
+ js_formatter.trailing_commas = Some(trailing_comma);
+ }
}
}
-
- if let Some(trailing_comma) = js_formatter.trailing_comma {
- let diagnostic = DeprecatedArgument::new(markup! {
- "The argument ""--trailing-comma"" is deprecated, it will be removed in the next major release. Use ""--trailing-commas"" instead."
- });
- console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- });
-
- if js_formatter.trailing_commas.is_none() {
- js_formatter.trailing_commas = Some(trailing_comma);
+ // TODO: remove in biome 2.0
+ if let Some(json_formatter) = self.json_formatter.as_mut() {
+ if let Some(indent_size) = json_formatter.indent_size {
+ let diagnostic = DeprecatedArgument::new(markup! {
+ "The argument ""--json-formatter-indent-size"" is deprecated, it will be removed in the next major release. Use ""--json-formatter-indent-width"" instead."
+ });
+ console.error(markup! {
+ {PrintDiagnostic::simple(&diagnostic)}
+ });
+
+ if json_formatter.indent_width.is_none() {
+ json_formatter.indent_width = Some(indent_size);
+ }
}
}
- }
- // TODO: remove in biome 2.0
- if let Some(json_formatter) = json_formatter.as_mut() {
- if let Some(indent_size) = json_formatter.indent_size {
- let diagnostic = DeprecatedArgument::new(markup! {
- "The argument ""--json-formatter-indent-size"" is deprecated, it will be removed in the next major release. Use ""--json-formatter-indent-width"" instead."
- });
- console.error(markup! {
- {PrintDiagnostic::simple(&diagnostic)}
- });
- if json_formatter.indent_width.is_none() {
- json_formatter.indent_width = Some(indent_size);
+ // merge formatter options
+ if !configuration
+ .formatter
+ .as_ref()
+ .is_some_and(PartialFormatterConfiguration::is_disabled)
+ {
+ let formatter = configuration.formatter.get_or_insert_with(Default::default);
+ if let Some(formatter_configuration) = self.formatter_configuration.clone() {
+ formatter.merge_with(formatter_configuration);
}
+
+ formatter.enabled = Some(true);
+ }
+ if self.css_formatter.is_some() {
+ let css = configuration.css.get_or_insert_with(Default::default);
+ css.formatter.merge_with(self.css_formatter.clone());
+ }
+ if self.graphql_formatter.is_some() {
+ let graphql = configuration.graphql.get_or_insert_with(Default::default);
+ graphql.formatter.merge_with(self.graphql_formatter.clone());
}
- }
- // merge formatter options
- if !configuration
- .formatter
- .as_ref()
- .is_some_and(PartialFormatterConfiguration::is_disabled)
- {
- let formatter = configuration.formatter.get_or_insert_with(Default::default);
- if let Some(formatter_configuration) = formatter_configuration {
- formatter.merge_with(formatter_configuration);
+ if self.javascript_formatter.is_some() {
+ let javascript = configuration
+ .javascript
+ .get_or_insert_with(Default::default);
+ javascript
+ .formatter
+ .merge_with(self.javascript_formatter.clone());
+ }
+ if self.json_formatter.is_some() {
+ let json = configuration.json.get_or_insert_with(Default::default);
+ json.formatter.merge_with(self.json_formatter.clone());
}
- formatter.enabled = Some(true);
- }
- if css_formatter.is_some() {
- let css = configuration.css.get_or_insert_with(Default::default);
- css.formatter.merge_with(css_formatter);
- }
- if graphql_formatter.is_some() {
- let graphql = configuration.graphql.get_or_insert_with(Default::default);
- graphql.formatter.merge_with(graphql_formatter);
- }
+ configuration
+ .files
+ .merge_with(self.files_configuration.clone());
+ configuration.vcs.merge_with(self.vcs_configuration.clone());
- if javascript_formatter.is_some() {
- let javascript = configuration
- .javascript
- .get_or_insert_with(Default::default);
- javascript.formatter.merge_with(javascript_formatter);
- }
- if json_formatter.is_some() {
- let json = configuration.json.get_or_insert_with(Default::default);
- json.formatter.merge_with(json_formatter);
+ Ok(configuration)
}
- configuration.files.merge_with(files_configuration);
- configuration.vcs.merge_with(vcs_configuration);
-
- // check if support of git ignore files is enabled
- let vcs_base_path = configuration_path.or(session.app.fs.working_directory());
- let (vcs_base_path, gitignore_matches) =
- configuration.retrieve_gitignore_matches(&session.app.fs, vcs_base_path.as_deref())?;
+ fn get_files_to_process(
+ &self,
+ fs: &DynRef<'_, dyn FileSystem>,
+ configuration: &PartialConfiguration,
+ ) -> Result, CliDiagnostic> {
+ let paths = get_files_to_process_with_cli_options(
+ self.since.as_deref(),
+ self.changed,
+ self.staged,
+ fs,
+ configuration,
+ )?
+ .unwrap_or(self.paths.clone());
- if let Some(_paths) =
- get_files_to_process(since, changed, staged, &session.app.fs, &configuration)?
- {
- paths = _paths;
+ Ok(paths)
}
- session
- .app
- .workspace
- .register_project_folder(RegisterProjectFolderParams {
- path: session.app.fs.working_directory(),
- set_as_current_workspace: true,
- })?;
-
- let manifest_data = resolve_manifest(&session.app.fs)?;
-
- if let Some(manifest_data) = manifest_data {
- session
- .app
- .workspace
- .set_manifest_for_project(manifest_data.into())?;
+ fn get_stdin_file_path(&self) -> Option<&str> {
+ self.stdin_file_path.as_deref()
}
- session
- .app
- .workspace
- .update_settings(UpdateSettingsParams {
- workspace_directory: session.app.fs.working_directory(),
- configuration,
- vcs_base_path,
- gitignore_matches,
- })?;
-
- let stdin = get_stdin(stdin_file_path, console, "format")?;
- let execution = Execution::new(TraversalMode::Format {
- ignore_errors: cli_options.skip_errors,
- write: write || fix,
- stdin,
- vcs_targeted: VcsTargeted { staged, changed },
- })
- .set_report(&cli_options);
+ fn should_write(&self) -> bool {
+ self.write || self.fix
+ }
- execute_mode(execution, session, &cli_options, paths)
+ fn get_execution(
+ &self,
+ cli_options: &CliOptions,
+ console: &mut dyn Console,
+ _workspace: &dyn Workspace,
+ ) -> Result {
+ Ok(Execution::new(TraversalMode::Format {
+ ignore_errors: cli_options.skip_errors,
+ write: self.should_write(),
+ stdin: self.get_stdin(console)?,
+ vcs_targeted: (self.staged, self.changed).into(),
+ })
+ .set_report(cli_options))
+ }
}
diff --git a/crates/biome_cli/src/commands/lint.rs b/crates/biome_cli/src/commands/lint.rs
index 84ab59f0f440..065674b2e4c8 100644
--- a/crates/biome_cli/src/commands/lint.rs
+++ b/crates/biome_cli/src/commands/lint.rs
@@ -1,11 +1,7 @@
+use super::{determine_fix_file_mode, FixFileModeOptions};
use crate::cli_options::CliOptions;
-use crate::commands::{
- get_files_to_process, get_stdin, resolve_manifest, validate_configuration_diagnostics,
-};
-use crate::execute::VcsTargeted;
-use crate::{
- execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution, TraversalMode,
-};
+use crate::commands::{get_files_to_process_with_cli_options, CommandRunner};
+use crate::{CliDiagnostic, Execution, TraversalMode};
use biome_configuration::analyzer::RuleSelector;
use biome_configuration::css::PartialCssLinter;
use biome_configuration::javascript::PartialJavascriptLinter;
@@ -15,22 +11,19 @@ use biome_configuration::{
PartialConfiguration, PartialFilesConfiguration, PartialGraphqlLinter,
PartialLinterConfiguration,
};
+use biome_console::Console;
use biome_deserialize::Merge;
-use biome_service::configuration::{
- load_configuration, LoadedConfiguration, PartialConfigurationExt,
-};
-use biome_service::workspace::{RegisterProjectFolderParams, UpdateSettingsParams};
+use biome_fs::FileSystem;
+use biome_service::configuration::LoadedConfiguration;
+use biome_service::{DynRef, Workspace, WorkspaceError};
use std::ffi::OsString;
-use super::{determine_fix_file_mode, FixFileModeOptions};
-
pub(crate) struct LintCommandPayload {
pub(crate) apply: bool,
pub(crate) apply_unsafe: bool,
pub(crate) write: bool,
pub(crate) fix: bool,
pub(crate) unsafe_: bool,
- pub(crate) cli_options: CliOptions,
pub(crate) linter_configuration: Option,
pub(crate) vcs_configuration: Option,
pub(crate) files_configuration: Option,
@@ -47,144 +40,112 @@ pub(crate) struct LintCommandPayload {
pub(crate) graphql_linter: Option,
}
-/// Handler for the "lint" command of the Biome CLI
-pub(crate) fn lint(session: CliSession, payload: LintCommandPayload) -> Result<(), CliDiagnostic> {
- let LintCommandPayload {
- apply,
- apply_unsafe,
- write,
- fix,
- unsafe_,
- cli_options,
- mut linter_configuration,
- paths,
- only,
- skip,
- stdin_file_path,
- vcs_configuration,
- files_configuration,
- staged,
- changed,
- since,
- javascript_linter,
- css_linter,
- json_linter,
- graphql_linter,
- } = payload;
- setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
+impl CommandRunner for LintCommandPayload {
+ const COMMAND_NAME: &'static str = "lint";
- let fix_file_mode = determine_fix_file_mode(
- FixFileModeOptions {
- apply,
- apply_unsafe,
- write,
- fix,
- unsafe_,
- },
- session.app.console,
- )?;
+ fn merge_configuration(
+ &mut self,
+ loaded_configuration: LoadedConfiguration,
+ _fs: &DynRef<'_, dyn FileSystem>,
+ _console: &mut dyn Console,
+ ) -> Result {
+ let LoadedConfiguration {
+ configuration: mut fs_configuration,
+ ..
+ } = loaded_configuration;
- let loaded_configuration =
- load_configuration(&session.app.fs, cli_options.as_configuration_path_hint())?;
- validate_configuration_diagnostics(
- &loaded_configuration,
- session.app.console,
- cli_options.verbose,
- )?;
+ fs_configuration.merge_with(PartialConfiguration {
+ linter: if fs_configuration
+ .linter
+ .as_ref()
+ .is_some_and(PartialLinterConfiguration::is_disabled)
+ {
+ None
+ } else {
+ if let Some(linter) = self.linter_configuration.as_mut() {
+ // Don't overwrite rules from the CLI configuration.
+ linter.rules = None;
+ }
+ self.linter_configuration.clone()
+ },
+ files: self.files_configuration.clone(),
+ vcs: self.vcs_configuration.clone(),
+ ..Default::default()
+ });
- let LoadedConfiguration {
- configuration: mut fs_configuration,
- directory_path: configuration_path,
- ..
- } = loaded_configuration;
- fs_configuration.merge_with(PartialConfiguration {
- linter: if fs_configuration
- .linter
- .as_ref()
- .is_some_and(PartialLinterConfiguration::is_disabled)
- {
- None
- } else {
- if let Some(linter) = linter_configuration.as_mut() {
- // Don't overwrite rules from the CLI configuration.
- linter.rules = None;
- }
- linter_configuration
- },
- files: files_configuration,
- vcs: vcs_configuration,
- ..Default::default()
- });
+ if self.css_linter.is_some() {
+ let css = fs_configuration.css.get_or_insert_with(Default::default);
+ css.linter.merge_with(self.css_linter.clone());
+ }
- if css_linter.is_some() {
- let css = fs_configuration.css.get_or_insert_with(Default::default);
- css.linter.merge_with(css_linter);
- }
+ if self.graphql_linter.is_some() {
+ let graphql = fs_configuration
+ .graphql
+ .get_or_insert_with(Default::default);
+ graphql.linter.merge_with(self.graphql_linter.clone());
+ }
+ if self.javascript_linter.is_some() {
+ let javascript = fs_configuration
+ .javascript
+ .get_or_insert_with(Default::default);
+ javascript.linter.merge_with(self.javascript_linter.clone());
+ }
+ if self.json_linter.is_some() {
+ let json = fs_configuration.json.get_or_insert_with(Default::default);
+ json.linter.merge_with(self.json_linter.clone());
+ }
- if graphql_linter.is_some() {
- let graphql = fs_configuration
- .graphql
- .get_or_insert_with(Default::default);
- graphql.linter.merge_with(graphql_linter);
+ Ok(fs_configuration)
}
- if javascript_linter.is_some() {
- let javascript = fs_configuration
- .javascript
- .get_or_insert_with(Default::default);
- javascript.linter.merge_with(javascript_linter);
- }
- if json_linter.is_some() {
- let json = fs_configuration.json.get_or_insert_with(Default::default);
- json.linter.merge_with(json_linter);
- }
-
- let vcs_targeted_paths =
- get_files_to_process(since, changed, staged, &session.app.fs, &fs_configuration)?;
-
- // check if support of git ignore files is enabled
- let vcs_base_path = configuration_path.or(session.app.fs.working_directory());
- let (vcs_base_path, gitignore_matches) =
- fs_configuration.retrieve_gitignore_matches(&session.app.fs, vcs_base_path.as_deref())?;
- let stdin = get_stdin(stdin_file_path, &mut *session.app.console, "lint")?;
+ fn get_files_to_process(
+ &self,
+ fs: &DynRef<'_, dyn FileSystem>,
+ configuration: &PartialConfiguration,
+ ) -> Result, CliDiagnostic> {
+ let paths = get_files_to_process_with_cli_options(
+ self.since.as_deref(),
+ self.changed,
+ self.staged,
+ fs,
+ configuration,
+ )?
+ .unwrap_or(self.paths.clone());
- session
- .app
- .workspace
- .register_project_folder(RegisterProjectFolderParams {
- path: session.app.fs.working_directory(),
- set_as_current_workspace: true,
- })?;
- let manifest_data = resolve_manifest(&session.app.fs)?;
+ Ok(paths)
+ }
- if let Some(manifest_data) = manifest_data {
- session
- .app
- .workspace
- .set_manifest_for_project(manifest_data.into())?;
+ fn get_stdin_file_path(&self) -> Option<&str> {
+ self.stdin_file_path.as_deref()
}
- session
- .app
- .workspace
- .update_settings(UpdateSettingsParams {
- workspace_directory: session.app.fs.working_directory(),
- configuration: fs_configuration,
- vcs_base_path,
- gitignore_matches,
- })?;
+ fn should_write(&self) -> bool {
+ self.write || self.fix
+ }
- execute_mode(
- Execution::new(TraversalMode::Lint {
+ fn get_execution(
+ &self,
+ cli_options: &CliOptions,
+ console: &mut dyn Console,
+ _workspace: &dyn Workspace,
+ ) -> Result {
+ let fix_file_mode = determine_fix_file_mode(
+ FixFileModeOptions {
+ apply: self.apply,
+ apply_unsafe: self.apply_unsafe,
+ write: self.write,
+ fix: self.fix,
+ unsafe_: self.unsafe_,
+ },
+ console,
+ )?;
+ Ok(Execution::new(TraversalMode::Lint {
fix_file_mode,
- stdin,
- only,
- skip,
- vcs_targeted: VcsTargeted { staged, changed },
+ stdin: self.get_stdin(console)?,
+ only: self.only.clone(),
+ skip: self.skip.clone(),
+ vcs_targeted: (self.staged, self.changed).into(),
})
- .set_report(&cli_options),
- session,
- &cli_options,
- vcs_targeted_paths.unwrap_or(paths),
- )
+ .set_report(cli_options))
+ }
}
diff --git a/crates/biome_cli/src/commands/migrate.rs b/crates/biome_cli/src/commands/migrate.rs
index 7f0362f3ab90..ed29e23eb737 100644
--- a/crates/biome_cli/src/commands/migrate.rs
+++ b/crates/biome_cli/src/commands/migrate.rs
@@ -1,65 +1,93 @@
+use super::{
+ check_fix_incompatible_arguments, CommandRunner, FixFileModeOptions, MigrateSubCommand,
+};
use crate::cli_options::CliOptions;
use crate::diagnostics::MigrationDiagnostic;
-use crate::execute::{execute_mode, Execution, TraversalMode};
-use crate::{setup_cli_subscriber, CliDiagnostic, CliSession};
-use biome_console::{markup, ConsoleExt};
-use biome_service::configuration::{load_configuration, LoadedConfiguration};
-use biome_service::workspace::RegisterProjectFolderParams;
+use crate::execute::{Execution, TraversalMode};
+use crate::CliDiagnostic;
+use biome_configuration::PartialConfiguration;
+use biome_console::{markup, Console, ConsoleExt};
+use biome_fs::FileSystem;
+use biome_service::configuration::LoadedConfiguration;
+use biome_service::{DynRef, Workspace, WorkspaceError};
+use std::ffi::OsString;
+use std::path::PathBuf;
-use super::{check_fix_incompatible_arguments, FixFileModeOptions, MigrateSubCommand};
+pub(crate) struct MigrateCommandPayload {
+ pub(crate) write: bool,
+ pub(crate) fix: bool,
+ pub(crate) sub_command: Option,
+ pub(crate) configuration_file_path: Option,
+ pub(crate) configuration_directory_path: Option,
+}
+
+impl CommandRunner for MigrateCommandPayload {
+ const COMMAND_NAME: &'static str = "migrate";
-/// Handler for the "migrate" command of the Biome CLI
-pub(crate) fn migrate(
- session: CliSession,
- cli_options: CliOptions,
- write: bool,
- fix: bool,
- sub_command: Option,
-) -> Result<(), CliDiagnostic> {
- let base_path = cli_options.as_configuration_path_hint();
- let LoadedConfiguration {
- configuration: _,
- diagnostics: _,
- directory_path,
- file_path,
- } = load_configuration(&session.app.fs, base_path)?;
- setup_cli_subscriber(cli_options.log_level, cli_options.log_kind);
+ fn merge_configuration(
+ &mut self,
+ loaded_configuration: LoadedConfiguration,
+ _fs: &DynRef<'_, dyn FileSystem>,
+ _console: &mut dyn Console,
+ ) -> Result {
+ self.configuration_file_path = loaded_configuration.file_path;
+ self.configuration_directory_path = loaded_configuration.directory_path;
+ Ok(loaded_configuration.configuration)
+ }
- check_fix_incompatible_arguments(FixFileModeOptions {
- apply: false,
- apply_unsafe: false,
- write,
- fix,
- unsafe_: false,
- })?;
+ fn get_files_to_process(
+ &self,
+ _fs: &DynRef<'_, dyn FileSystem>,
+ _configuration: &PartialConfiguration,
+ ) -> Result, CliDiagnostic> {
+ Ok(vec![])
+ }
- session
- .app
- .workspace
- .register_project_folder(RegisterProjectFolderParams {
- path: session.app.fs.working_directory(),
- set_as_current_workspace: true,
- })?;
+ fn get_stdin_file_path(&self) -> Option<&str> {
+ None
+ }
- if let (Some(path), Some(directory_path)) = (file_path, directory_path) {
- execute_mode(
- Execution::new(TraversalMode::Migrate {
- write: write || fix,
+ fn should_write(&self) -> bool {
+ self.write || self.fix
+ }
+
+ fn get_execution(
+ &self,
+ _cli_options: &CliOptions,
+ console: &mut dyn Console,
+ _workspace: &dyn Workspace,
+ ) -> Result {
+ if let (Some(path), Some(directory_path)) = (
+ self.configuration_file_path.clone(),
+ self.configuration_directory_path.clone(),
+ ) {
+ Ok(Execution::new(TraversalMode::Migrate {
+ write: self.should_write(),
configuration_file_path: path,
configuration_directory_path: directory_path,
- sub_command,
- }),
- session,
- &cli_options,
- vec![],
- )
- } else {
- let console = session.app.console;
- console.log(markup! {
+ sub_command: self.sub_command.clone(),
+ }))
+ } else {
+ console.log(markup! {
"If this project has not yet been set up with Biome yet, please follow the ""Getting Started guide"" first."
});
- Err(CliDiagnostic::MigrateError(MigrationDiagnostic {
- reason: "Biome couldn't find the Biome configuration file.".to_string(),
- }))
+ Err(CliDiagnostic::MigrateError(MigrationDiagnostic {
+ reason: "Biome couldn't find the Biome configuration file.".to_string(),
+ }))
+ }
+ }
+
+ fn check_incompatible_arguments(&self) -> Result<(), CliDiagnostic> {
+ check_fix_incompatible_arguments(FixFileModeOptions {
+ apply: false,
+ apply_unsafe: false,
+ write: self.write,
+ fix: self.fix,
+ unsafe_: false,
+ })
+ }
+
+ fn should_validate_configuration_diagnostics(&self) -> bool {
+ false
}
}
diff --git a/crates/biome_cli/src/commands/mod.rs b/crates/biome_cli/src/commands/mod.rs
index 5e68992a2456..0101d9545236 100644
--- a/crates/biome_cli/src/commands/mod.rs
+++ b/crates/biome_cli/src/commands/mod.rs
@@ -3,7 +3,9 @@ use crate::cli_options::{cli_options, CliOptions, CliReporter, ColorsArg};
use crate::diagnostics::{DeprecatedArgument, DeprecatedConfigurationFile};
use crate::execute::Stdin;
use crate::logging::LoggingKind;
-use crate::{CliDiagnostic, LoggingLevel, VERSION};
+use crate::{
+ execute_mode, setup_cli_subscriber, CliDiagnostic, CliSession, Execution, LoggingLevel, VERSION,
+};
use biome_configuration::analyzer::RuleSelector;
use biome_configuration::css::PartialCssLinter;
use biome_configuration::javascript::PartialJavascriptLinter;
@@ -22,10 +24,12 @@ use biome_configuration::{BiomeDiagnostic, PartialConfiguration};
use biome_console::{markup, Console, ConsoleExt};
use biome_diagnostics::{Diagnostic, PrintDiagnostic};
use biome_fs::{BiomePath, FileSystem};
-use biome_service::configuration::LoadedConfiguration;
+use biome_service::configuration::{
+ load_configuration, load_editorconfig, LoadedConfiguration, PartialConfigurationExt,
+};
use biome_service::documentation::Doc;
-use biome_service::workspace::FixFileMode;
-use biome_service::{DynRef, WorkspaceError};
+use biome_service::workspace::{FixFileMode, RegisterProjectFolderParams, UpdateSettingsParams};
+use biome_service::{DynRef, Workspace, WorkspaceError};
use bpaf::Bpaf;
use std::ffi::OsString;
use std::path::PathBuf;
@@ -608,7 +612,7 @@ impl BiomeCommand {
/// It accepts a [LoadedPartialConfiguration] and it prints the diagnostics emitted during parsing and deserialization.
///
-/// If it contains errors, it return an error.
+/// If it contains [errors](Severity::Error) or higher, it returns an error.
pub(crate) fn validate_configuration_diagnostics(
loaded_configuration: &LoadedConfiguration,
console: &mut dyn Console,
@@ -666,33 +670,8 @@ fn resolve_manifest(
Ok(None)
}
-/// Computes [Stdin] if the CLI has the necessary information.
-///
-/// ## Errors
-/// - If the user didn't provide anything via `stdin` but the option `--stdin-file-path` is passed.
-pub(crate) fn get_stdin(
- stdin_file_path: Option,
- console: &mut dyn Console,
- command_name: &str,
-) -> Result