diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3bb41438e71b..743dcc59597a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,7 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
#### Bug fixes
- `useConsistentArrayType` and `useShorthandArrayType` now ignore `Array` in the `extends` and `implements` clauses. Fix [#3247](https://github.com/biomejs/biome/issues/3247). Contributed by @Conaclos
+- Fixes [#3066](https://github.com/biomejs/biome/issues/3066) by taking into account the dependencies declared in the `package.json`. Contributed by @ematipico
## v1.8.2 (2024-06-20)
diff --git a/crates/biome_js_analyze/src/lib.rs b/crates/biome_js_analyze/src/lib.rs
index 2baff312c935..902b48340419 100644
--- a/crates/biome_js_analyze/src/lib.rs
+++ b/crates/biome_js_analyze/src/lib.rs
@@ -131,9 +131,7 @@ where
services.insert_service(Arc::new(AriaRoles));
services.insert_service(Arc::new(AriaProperties));
- if let Some(manifest) = manifest {
- services.insert_service(Arc::new(manifest));
- }
+ services.insert_service(Arc::new(manifest));
services.insert_service(source_type);
(
analyzer.run(AnalyzerContext {
@@ -238,6 +236,7 @@ mod tests {
use biome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic, Severity};
use biome_js_parser::{parse, JsParserOptions};
use biome_js_syntax::{JsFileSource, TextRange, TextSize};
+ use biome_project::{Dependencies, PackageJson};
use std::slice;
use crate::lint::correctness::use_exhaustive_dependencies::{Hook, HooksOptions};
@@ -256,7 +255,7 @@ mod tests {
String::from_utf8(buffer).unwrap()
}
- const SOURCE: &str = r#"
"#;
+ const SOURCE: &str = r#"import buffer from "buffer"; "#;
let parsed = parse(SOURCE, JsFileSource::tsx(), JsParserOptions::default());
@@ -268,13 +267,15 @@ mod tests {
dependencies_index: Some(1),
stable_result: StableHookResult::None,
};
- let rule_filter = RuleFilter::Rule("nursery", "useSortedClasses");
+ let rule_filter = RuleFilter::Rule("style", "useNodejsImportProtocol");
options.configuration.rules.push_rule(
RuleKey::new("nursery", "useHookAtTopLevel"),
RuleOptions::new(HooksOptions { hooks: vec![hook] }, None),
);
+ let mut dependencies = Dependencies::default();
+ dependencies.add("buffer", "latest");
analyze(
&parsed.tree(),
AnalysisFilter {
@@ -283,7 +284,10 @@ mod tests {
},
&options,
JsFileSource::tsx(),
- None,
+ Some(PackageJson {
+ dependencies,
+ ..Default::default()
+ }),
|signal| {
if let Some(diag) = signal.diagnostic() {
error_ranges.push(diag.location().span.unwrap());
diff --git a/crates/biome_js_analyze/src/lint/style/use_nodejs_import_protocol.rs b/crates/biome_js_analyze/src/lint/style/use_nodejs_import_protocol.rs
index 4310d1e7d88d..1d44683bd877 100644
--- a/crates/biome_js_analyze/src/lint/style/use_nodejs_import_protocol.rs
+++ b/crates/biome_js_analyze/src/lint/style/use_nodejs_import_protocol.rs
@@ -1,11 +1,11 @@
use biome_analyze::{
- context::RuleContext, declare_rule, ActionCategory, Ast, FixKind, Rule, RuleDiagnostic,
- RuleSource,
+ context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, RuleSource,
};
use biome_console::markup;
use biome_js_syntax::{inner_string_text, AnyJsImportLike, JsSyntaxKind, JsSyntaxToken};
use biome_rowan::BatchMutationExt;
+use crate::services::manifest::Manifest;
use crate::{globals::is_node_builtin_module, JsRuleAction};
declare_rule! {
@@ -14,6 +14,13 @@ declare_rule! {
/// The rule marks traditional imports like `import fs from "fs";` as invalid,
/// suggesting the format `import fs from "node:fs";` instead.
///
+ /// The rule also isn't triggered if there are dependencies declared in the `package.json` that match
+ /// the name of a built-in Node.js module.
+ ///
+ /// :::caution
+ /// The rule doesn't support dependencies installed inside a monorepo.
+ /// :::
+ ///
/// ## Examples
///
/// ### Invalid
@@ -51,7 +58,7 @@ declare_rule! {
}
impl Rule for UseNodejsImportProtocol {
- type Query = Ast;
+ type Query = Manifest;
type State = JsSyntaxToken;
type Signals = Option;
type Options = ();
@@ -62,6 +69,13 @@ impl Rule for UseNodejsImportProtocol {
return None;
}
let module_name = node.module_name_token()?;
+ if ctx.is_dependency(&inner_string_text(&module_name))
+ || ctx.is_dev_dependency(&inner_string_text(&module_name))
+ || ctx.is_peer_dependency(&inner_string_text(&module_name))
+ || ctx.is_optional_dependency(&inner_string_text(&module_name))
+ {
+ return None;
+ }
is_node_module_without_protocol(&inner_string_text(&module_name)).then_some(module_name)
}
diff --git a/crates/biome_js_analyze/src/services/manifest.rs b/crates/biome_js_analyze/src/services/manifest.rs
index 47be6e8d1603..f8aa3b3dc238 100644
--- a/crates/biome_js_analyze/src/services/manifest.rs
+++ b/crates/biome_js_analyze/src/services/manifest.rs
@@ -9,24 +9,40 @@ use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ManifestServices {
- pub(crate) manifest: Arc,
+ pub(crate) manifest: Arc