diff --git a/.changeset/nasty-cities-check.md b/.changeset/nasty-cities-check.md new file mode 100644 index 000000000000..e1df3bbdee4a --- /dev/null +++ b/.changeset/nasty-cities-check.md @@ -0,0 +1,5 @@ +--- +"@biomejs/biome": minor +--- + +Biome now supports pnpm catalogs (default and named) when resolving dependencies for linting. This behavior is opt-in and requires setting `javascript.resolver.experimentalPnpmCatalogs` to `true`. diff --git a/Cargo.lock b/Cargo.lock index 79ce5bceaa3d..b7175e23b04a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1439,6 +1439,8 @@ dependencies = [ "biome_parser", "biome_rowan", "biome_text_size", + "biome_yaml_parser", + "biome_yaml_syntax", "camino", "codspeed-divan-compat", "indexmap", diff --git a/crates/biome_cli/tests/cases/diagnostics.rs b/crates/biome_cli/tests/cases/diagnostics.rs index 31a5937cd6ab..d188f131de39 100644 --- a/crates/biome_cli/tests/cases/diagnostics.rs +++ b/crates/biome_cli/tests/cases/diagnostics.rs @@ -98,6 +98,86 @@ fn max_diagnostics_no_verbose() { )); } +#[test] +fn reads_pnpm_workspace_catalog() { + let fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + fs.insert( + Utf8Path::new("pnpm-workspace.yaml").into(), + r#" +packages: + - "src" +catalogs: + react19: + react: 19.0.0 +"# + .as_bytes(), + ); + + fs.insert( + Utf8Path::new("package.json").into(), + r#" +{ + "name": "app", + "dependencies": { + "react": "catalog:react19" + } +} +"# + .as_bytes(), + ); + + fs.insert( + Utf8Path::new("biome.json").into(), + r#" +{ + "javascript": { + "resolver": { + "experimentalPnpmCatalogs": true + } + }, + "linter": { + "rules": { + "suspicious": { + "noReactForwardRef": "error" + } + } + } +} +"# + .as_bytes(), + ); + + fs.insert( + Utf8Path::new("src/input.jsx").into(), + r#" +import { forwardRef } from "react"; + +export const Component = forwardRef((props, ref) => { + return
; +}); +"# + .as_bytes(), + ); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["check", "src/input.jsx"].as_slice()), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "reads_pnpm_workspace_catalog", + fs, + console, + result, + )); +} + #[test] fn should_fail_when_max_diagnostics_is_zero() { let fs = MemoryFileSystem::default(); diff --git a/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap b/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap new file mode 100644 index 000000000000..6d3afc515c27 --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_cases_diagnostics/reads_pnpm_workspace_catalog.snap @@ -0,0 +1,126 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: redactor(content) +--- +## `biome.json` + +```json +{ + "javascript": { + "resolver": { + "experimentalPnpmCatalogs": true + } + }, + "linter": { + "rules": { + "suspicious": { + "noReactForwardRef": "error" + } + } + } +} +``` + +## `package.json` + +```json + +{ + "name": "app", + "dependencies": { + "react": "catalog:react19" + } +} + +``` + +## `pnpm-workspace.yaml` + +```yaml + +packages: + - "src" +catalogs: + react19: + react: 19.0.0 + +``` + +## `src/input.jsx` + +```jsx + +import { forwardRef } from "react"; + +export const Component = forwardRef((props, ref) => { + return
; +}); + +``` + +# Termination Message + +```block +check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Some errors were emitted while running checks. + + + +``` + +# Emitted Messages + +```block +src/input.jsx:4:26 lint/suspicious/noReactForwardRef FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Use of forwardRef is detected, which is deprecated. + + 2 │ import { forwardRef } from "react"; + 3 │ + > 4 │ export const Component = forwardRef((props, ref) => { + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + > 5 │ return
; + > 6 │ }); + │ ^^ + 7 │ + + i In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead. + + i Replace the use of forwardRef with passing ref as a prop. + + i Unsafe fix: Remove the forwardRef() call and receive the ref as a prop. + + 2 2 │ import { forwardRef } from "react"; + 3 3 │ + 4 │ - export·const·Component·=·forwardRef((props,·ref)·=>·{ + 4 │ + export·const·Component·=·({·ref,·...props·})·=>·{ + 5 5 │ return
; + 6 │ - }); + 6 │ + }; + 7 7 │ + + +``` + +```block +src/input.jsx format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Formatter would have printed the following content: + + 1 │ - + 2 1 │ import { forwardRef } from "react"; + 3 2 │ + 4 3 │ export const Component = forwardRef((props, ref) => { + 5 │ - ··return·; + 4 │ + → return·; + 6 5 │ }); + 7 6 │ + + +``` + +```block +Checked 1 file in