From ee5a2053bbd2a771ce5f160bf2b940bd8d51b01c Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Wed, 31 Jan 2024 16:01:41 -0500 Subject: [PATCH 1/2] feat(lint/noRestrictedImports): add rule --- .../src/categories.rs | 1 + .../biome_js_analyze/src/analyzers/nursery.rs | 2 + .../nursery/no_restricted_imports.rs | 72 ++++++++++++ crates/biome_js_analyze/src/options.rs | 12 ++ .../nursery/noRestrictedImports/invalid.js | 2 + .../noRestrictedImports/invalid.js.snap | 39 +++++++ .../noRestrictedImports/invalid.options.json | 19 ++++ .../nursery/noRestrictedImports/valid.js | 1 + .../nursery/noRestrictedImports/valid.js.snap | 11 ++ .../noRestrictedImports/valid.options.json | 15 +++ .../src/configuration/linter/rules.rs | 106 ++++++++++-------- .../invalid/hooks_missing_name.json.snap | 1 + .../@biomejs/backend-jsonrpc/src/workspace.ts | 15 +++ .../@biomejs/biome/configuration_schema.json | 24 ++++ .../components/generated/NumberOfRules.astro | 2 +- .../src/content/docs/linter/rules/index.mdx | 1 + .../linter/rules/no-restricted-imports.md | 39 +++++++ 17 files changed, 316 insertions(+), 46 deletions(-) create mode 100644 crates/biome_js_analyze/src/analyzers/nursery/no_restricted_imports.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.options.json create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.options.json create mode 100644 website/src/content/docs/linter/rules/no-restricted-imports.md diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 5be1a68f0f6b..876669e747fd 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -111,6 +111,7 @@ define_categories! { "lint/nursery/noInvalidUseBeforeDeclaration": "https://biomejs.dev/linter/rules/no-invalid-use-before-declaration", "lint/nursery/noMisleadingCharacterClass": "https://biomejs.dev/linter/rules/no-misleading-character-class", "lint/nursery/noNodejsModules": "https://biomejs.dev/linter/rules/no-nodejs-modules", + "lint/nursery/noRestrictedImports": "https://biomejs.dev/linter/rules/no-restricted-imports", "lint/nursery/noSkippedTests": "https://biomejs.dev/linter/rules/no-skipped-tests", "lint/nursery/noThenProperty": "https://biomejs.dev/linter/rules/no-then-property", "lint/nursery/noTypeOnlyImportAttributes": "https://biomejs.dev/linter/rules/no-type-only-import-attributes", diff --git a/crates/biome_js_analyze/src/analyzers/nursery.rs b/crates/biome_js_analyze/src/analyzers/nursery.rs index ccff9dbbfe84..956e83d1001c 100644 --- a/crates/biome_js_analyze/src/analyzers/nursery.rs +++ b/crates/biome_js_analyze/src/analyzers/nursery.rs @@ -6,6 +6,7 @@ pub(crate) mod no_empty_block_statements; pub(crate) mod no_empty_type_parameters; pub(crate) mod no_focused_tests; pub(crate) mod no_nodejs_modules; +pub(crate) mod no_restricted_imports; pub(crate) mod no_skipped_tests; pub(crate) mod no_unused_private_class_members; pub(crate) mod no_useless_lone_block_statements; @@ -26,6 +27,7 @@ declare_group! { self :: no_empty_type_parameters :: NoEmptyTypeParameters , self :: no_focused_tests :: NoFocusedTests , self :: no_nodejs_modules :: NoNodejsModules , + self :: no_restricted_imports :: NoRestrictedImports , self :: no_skipped_tests :: NoSkippedTests , self :: no_unused_private_class_members :: NoUnusedPrivateClassMembers , self :: no_useless_lone_block_statements :: NoUselessLoneBlockStatements , diff --git a/crates/biome_js_analyze/src/analyzers/nursery/no_restricted_imports.rs b/crates/biome_js_analyze/src/analyzers/nursery/no_restricted_imports.rs new file mode 100644 index 000000000000..78c13fd63a65 --- /dev/null +++ b/crates/biome_js_analyze/src/analyzers/nursery/no_restricted_imports.rs @@ -0,0 +1,72 @@ +use biome_analyze::context::RuleContext; +use biome_analyze::{declare_rule, Ast, Rule, RuleDiagnostic, RuleSource}; +use biome_console::markup; +use biome_deserialize_macros::Deserializable; +use biome_js_syntax::inner_string_text; +use biome_rowan::TextRange; +use rustc_hash::FxHashMap; +use serde::{Deserialize, Serialize}; + +use super::use_nodejs_import_protocol::AnyJsImportLike; + +declare_rule! { + /// Disallow specified modules when loaded by import or require. + /// + /// ## Options + /// + /// ```json + /// { + /// "noRestrictedImports": { + /// "options": { + /// "paths": { + /// "lodash": "Using lodash is not encouraged", + /// "underscore": "Using underscore is not encouraged" + /// } + /// } + /// } + /// } + /// ``` + pub(crate) NoRestrictedImports { + version: "next", + name: "noRestrictedImports", + source: RuleSource::Eslint("no-restricted-imports"), + recommended: false, + } +} + +/// Options for the rule `noRestrictedImports`. +#[derive(Clone, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct RestrictedImportsOptions { + /// A list of names that should trigger the rule + #[serde(skip_serializing_if = "FxHashMap::is_empty")] + paths: FxHashMap, +} + +impl Rule for NoRestrictedImports { + type Query = Ast; + type State = (TextRange, String); + type Signals = Option; + type Options = RestrictedImportsOptions; + + fn run(ctx: &RuleContext) -> Self::Signals { + let module_name = ctx.query().module_name_token()?; + let inner_text = inner_string_text(&module_name); + + ctx.options() + .paths + .get(inner_text.text()) + .map(|message| (module_name.text_trimmed_range(), message.to_string())) + } + + fn diagnostic(_ctx: &RuleContext, (span, text): &Self::State) -> Option { + Some(RuleDiagnostic::new( + rule_category!(), + *span, + markup! { + {text} + }, + )) + } +} diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 5b6af44b806e..d6d415d4a24b 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -1,5 +1,6 @@ //! This module contains the rules that have options +use crate::analyzers::nursery::no_restricted_imports::RestrictedImportsOptions; use crate::analyzers::nursery::use_consistent_array_type::ConsistentArrayTypeOptions; use crate::analyzers::nursery::use_filenaming_convention::FilenamingConventionOptions; use crate::semantic_analyzers::correctness::use_exhaustive_dependencies::HooksOptions; @@ -37,6 +38,8 @@ pub enum PossibleOptions { NamingConvention(NamingConventionOptions), /// Options for `noRestrictedGlobals` rule RestrictedGlobals(RestrictedGlobalsOptions), + /// Options for `noRestrictedImports` rule + RestrictedImports(RestrictedImportsOptions), /// Options for `useValidAriaRole` rule ValidAriaRole(ValidAriaRoleOptions), /// Options for `useSortedClasses` rule @@ -101,6 +104,13 @@ impl PossibleOptions { }; RuleOptions::new(options) } + "noRestrictedImports" => { + let options = match self { + PossibleOptions::RestrictedImports(options) => options.clone(), + _ => RestrictedImportsOptions::default(), + }; + RuleOptions::new(options) + } "useValidAriaRole" => { let options = match self { PossibleOptions::ValidAriaRole(options) => options.clone(), @@ -133,6 +143,8 @@ impl Deserializable for PossibleOptions { } "noRestrictedGlobals" => Deserializable::deserialize(value, "options", diagnostics) .map(Self::RestrictedGlobals), + "noRestrictedImports" => Deserializable::deserialize(value, "options", diagnostics) + .map(Self::RestrictedImports), "useConsistentArrayType" => Deserializable::deserialize(value, "options", diagnostics) .map(Self::ConsistentArrayType), "useExhaustiveDependencies" => { diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js new file mode 100644 index 000000000000..e22cad3c6d99 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js @@ -0,0 +1,2 @@ +import eslint from 'eslint'; +const l = require('lodash'); diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js.snap new file mode 100644 index 000000000000..8835361d9dba --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.js.snap @@ -0,0 +1,39 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.js +--- +# Input +```jsx +import eslint from 'eslint'; +const l = require('lodash'); + +``` + +# Diagnostics +``` +invalid.js:1:20 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Importing Eslint is forbidden + + > 1 │ import eslint from 'eslint'; + │ ^^^^^^^^ + 2 │ const l = require('lodash'); + 3 │ + + +``` + +``` +invalid.js:2:19 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! It's not recommended to use lodash + + 1 │ import eslint from 'eslint'; + > 2 │ const l = require('lodash'); + │ ^^^^^^^^ + 3 │ + + +``` + + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.options.json b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.options.json new file mode 100644 index 000000000000..be28e75b490a --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalid.options.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "enabled": true, + "rules": { + "nursery": { + "noRestrictedImports": { + "level": "error", + "options": { + "paths": { + "eslint": "Importing Eslint is forbidden", + "lodash": "It's not recommended to use lodash" + } + } + } + } + } + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js new file mode 100644 index 000000000000..b01e0318dda5 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js @@ -0,0 +1 @@ +const path = require('lodash'); diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js.snap new file mode 100644 index 000000000000..0729d1d3be89 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.js.snap @@ -0,0 +1,11 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid.js +--- +# Input +```jsx +const path = require('lodash'); + +``` + + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.options.json b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.options.json new file mode 100644 index 000000000000..e3229e50e688 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/valid.options.json @@ -0,0 +1,15 @@ +{ + "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", + "linter": { + "rules": { + "nursery": { + "noRestrictedImports": { + "level": "error", + "options": { + "paths": {} + } + } + } + } + } +} diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 90f5b3170622..3ad2e2c304b4 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2248,6 +2248,9 @@ pub struct Nursery { #[doc = "Forbid the use of Node.js builtin modules."] #[serde(skip_serializing_if = "Option::is_none")] pub no_nodejs_modules: Option, + #[doc = "Disallow specified modules when loaded by import or require."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_restricted_imports: Option, #[doc = "Disallow disabled tests."] #[serde(skip_serializing_if = "Option::is_none")] pub no_skipped_tests: Option, @@ -2319,7 +2322,7 @@ impl DeserializableValidator for Nursery { } impl Nursery { const GROUP_NAME: &'static str = "nursery"; - pub(crate) const GROUP_RULES: [&'static str; 27] = [ + pub(crate) const GROUP_RULES: [&'static str; 28] = [ "noDuplicateJsonKeys", "noEmptyBlockStatements", "noEmptyTypeParameters", @@ -2329,6 +2332,7 @@ impl Nursery { "noInvalidUseBeforeDeclaration", "noMisleadingCharacterClass", "noNodejsModules", + "noRestrictedImports", "noSkippedTests", "noThenProperty", "noUnusedImports", @@ -2368,15 +2372,15 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), ]; - const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 27] = [ + const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 28] = [ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[1]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]), @@ -2404,6 +2408,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended(&self) -> bool { @@ -2465,96 +2470,101 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_skipped_tests.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_then_property.as_ref() { + if let Some(rule) = self.no_skipped_tests.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_then_property.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_useless_ternary.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_ternary.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_consistent_array_type.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_export_type.as_ref() { + if let Some(rule) = self.use_consistent_array_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.use_filenaming_convention.as_ref() { + if let Some(rule) = self.use_export_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_filenaming_convention.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_import_type.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { + if let Some(rule) = self.use_import_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_number_namespace.as_ref() { + if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if let Some(rule) = self.use_number_namespace.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_shorthand_function_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -2604,96 +2614,101 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.no_skipped_tests.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_then_property.as_ref() { + if let Some(rule) = self.no_skipped_tests.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_then_property.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_useless_ternary.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_ternary.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_consistent_array_type.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_export_type.as_ref() { + if let Some(rule) = self.use_consistent_array_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.use_filenaming_convention.as_ref() { + if let Some(rule) = self.use_export_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_filenaming_convention.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_import_type.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { + if let Some(rule) = self.use_import_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_number_namespace.as_ref() { + if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if let Some(rule) = self.use_number_namespace.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_shorthand_function_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -2707,7 +2722,7 @@ impl Nursery { pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 12] { Self::RECOMMENDED_RULES_AS_FILTERS } - pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 27] { + pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 28] { Self::ALL_RULES_AS_FILTERS } #[doc = r" Select preset rules"] @@ -2739,6 +2754,7 @@ impl Nursery { "noInvalidUseBeforeDeclaration" => self.no_invalid_use_before_declaration.as_ref(), "noMisleadingCharacterClass" => self.no_misleading_character_class.as_ref(), "noNodejsModules" => self.no_nodejs_modules.as_ref(), + "noRestrictedImports" => self.no_restricted_imports.as_ref(), "noSkippedTests" => self.no_skipped_tests.as_ref(), "noThenProperty" => self.no_then_property.as_ref(), "noUnusedImports" => self.no_unused_imports.as_ref(), diff --git a/crates/biome_service/tests/invalid/hooks_missing_name.json.snap b/crates/biome_service/tests/invalid/hooks_missing_name.json.snap index 2493bbaaee7e..5b6c9b21f839 100644 --- a/crates/biome_service/tests/invalid/hooks_missing_name.json.snap +++ b/crates/biome_service/tests/invalid/hooks_missing_name.json.snap @@ -26,6 +26,7 @@ hooks_missing_name.json:6:5 deserialize ━━━━━━━━━━━━━ - noInvalidUseBeforeDeclaration - noMisleadingCharacterClass - noNodejsModules + - noRestrictedImports - noSkippedTests - noThenProperty - noUnusedImports diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8ac63316784b..7f75a69e976b 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -890,6 +890,10 @@ export interface Nursery { * Forbid the use of Node.js builtin modules. */ noNodejsModules?: RuleConfiguration; + /** + * Disallow specified modules when loaded by import or require. + */ + noRestrictedImports?: RuleConfiguration; /** * Disallow disabled tests. */ @@ -1404,6 +1408,7 @@ export type PossibleOptions = | DeprecatedHooksOptions | NamingConventionOptions | RestrictedGlobalsOptions + | RestrictedImportsOptions | ValidAriaRoleOptions | UtilityClassSortingOptions; /** @@ -1466,6 +1471,15 @@ export interface RestrictedGlobalsOptions { */ deniedGlobals: string[]; } +/** + * Options for the rule `noRestrictedImports`. + */ +export interface RestrictedImportsOptions { + /** + * A list of names that should trigger the rule + */ + paths: {}; +} export interface ValidAriaRoleOptions { allowInvalidRoles: string[]; ignoreNonDom: boolean; @@ -1696,6 +1710,7 @@ export type Category = | "lint/nursery/noInvalidUseBeforeDeclaration" | "lint/nursery/noMisleadingCharacterClass" | "lint/nursery/noNodejsModules" + | "lint/nursery/noRestrictedImports" | "lint/nursery/noSkippedTests" | "lint/nursery/noThenProperty" | "lint/nursery/noTypeOnlyImportAttributes" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 619e15453d48..def0d34c40b9 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1336,6 +1336,13 @@ { "type": "null" } ] }, + "noRestrictedImports": { + "description": "Disallow specified modules when loaded by import or require.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noSkippedTests": { "description": "Disallow disabled tests.", "anyOf": [ @@ -1674,6 +1681,10 @@ "description": "Options for `noRestrictedGlobals` rule", "allOf": [{ "$ref": "#/definitions/RestrictedGlobalsOptions" }] }, + { + "description": "Options for `noRestrictedImports` rule", + "allOf": [{ "$ref": "#/definitions/RestrictedImportsOptions" }] + }, { "description": "Options for `useValidAriaRole` rule", "allOf": [{ "$ref": "#/definitions/ValidAriaRoleOptions" }] @@ -1699,6 +1710,19 @@ }, "additionalProperties": false }, + "RestrictedImportsOptions": { + "description": "Options for the rule `noRestrictedImports`.", + "type": "object", + "required": ["paths"], + "properties": { + "paths": { + "description": "A list of names that should trigger the rule", + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "additionalProperties": false + }, "RuleConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" }, diff --git a/website/src/components/generated/NumberOfRules.astro b/website/src/components/generated/NumberOfRules.astro index 365b543aa4b5..136102735741 100644 --- a/website/src/components/generated/NumberOfRules.astro +++ b/website/src/components/generated/NumberOfRules.astro @@ -1,2 +1,2 @@ -

Biome's linter has a total of 197 rules

\ No newline at end of file +

Biome's linter has a total of 198 rules

\ No newline at end of file diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index a47e33bfe666..1db23d7f3118 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -241,6 +241,7 @@ Rules that belong to this group are not subject to semantic version🔧 | | [noNodejsModules](/linter/rules/no-nodejs-modules) | Forbid the use of Node.js builtin modules. | | +| [noRestrictedImports](/linter/rules/no-restricted-imports) | Disallow specified modules when loaded by import or require. | | | [noSkippedTests](/linter/rules/no-skipped-tests) | Disallow disabled tests. | | | [noThenProperty](/linter/rules/no-then-property) | Disallow then property. | | | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | 🔧 | diff --git a/website/src/content/docs/linter/rules/no-restricted-imports.md b/website/src/content/docs/linter/rules/no-restricted-imports.md new file mode 100644 index 000000000000..71c2ef293169 --- /dev/null +++ b/website/src/content/docs/linter/rules/no-restricted-imports.md @@ -0,0 +1,39 @@ +--- +title: noRestrictedImports (not released) +--- + +**Diagnostic Category: `lint/nursery/noRestrictedImports`** + +:::danger +This rule hasn't been released yet. +::: + +:::caution +This rule is part of the [nursery](/linter/rules/#nursery) group. +::: + +Source: no-restricted-imports + +Disallow specified modules when loaded by import or require. + +https://eslint.org/docs/latest/rules/no-restricted-imports + +## Options + +```json +{ + "noRestrictedImports": { + "options": { + "paths": { + "lodash": "Using lodash is not encouraged", + "underscore": "Using underscore is not encouraged" + } + } + } +} +``` + +## Related links + +- [Disable a rule](/linter/#disable-a-lint-rule) +- [Rule options](/linter/#rule-options) From 38d464c0f6924522563ad3b82ad165fbfbc26f14 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 6 Feb 2024 15:16:04 +0000 Subject: [PATCH 2/2] rebase --- .../src/configuration/linter/rules.rs | 106 ++++++++++-------- .../@biomejs/backend-jsonrpc/src/workspace.ts | 15 +++ .../@biomejs/biome/configuration_schema.json | 24 ++++ .../components/generated/NumberOfRules.astro | 2 +- .../src/content/docs/linter/rules/index.mdx | 2 +- .../linter/rules/no-restricted-imports.md | 2 - 6 files changed, 102 insertions(+), 49 deletions(-) diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 233a8e464f82..d7584355546f 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2251,6 +2251,9 @@ pub struct Nursery { #[doc = "Avoid re-export all"] #[serde(skip_serializing_if = "Option::is_none")] pub no_re_export_all: Option, + #[doc = "Disallow specified modules when loaded by import or require."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_restricted_imports: Option, #[doc = "Disallow disabled tests."] #[serde(skip_serializing_if = "Option::is_none")] pub no_skipped_tests: Option, @@ -2322,7 +2325,7 @@ impl DeserializableValidator for Nursery { } impl Nursery { const GROUP_NAME: &'static str = "nursery"; - pub(crate) const GROUP_RULES: [&'static str; 28] = [ + pub(crate) const GROUP_RULES: [&'static str; 29] = [ "noDuplicateJsonKeys", "noEmptyBlockStatements", "noEmptyTypeParameters", @@ -2333,6 +2336,7 @@ impl Nursery { "noMisleadingCharacterClass", "noNodejsModules", "noReExportAll", + "noRestrictedImports", "noSkippedTests", "noThenProperty", "noUnusedImports", @@ -2372,15 +2376,15 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[3]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), ]; - const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 28] = [ + const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 29] = [ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[1]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]), @@ -2409,6 +2413,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended(&self) -> bool { @@ -2475,96 +2480,101 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_skipped_tests.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_then_property.as_ref() { + if let Some(rule) = self.no_skipped_tests.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_then_property.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_useless_ternary.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_ternary.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_consistent_array_type.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.use_export_type.as_ref() { + if let Some(rule) = self.use_consistent_array_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.use_filenaming_convention.as_ref() { + if let Some(rule) = self.use_export_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_filenaming_convention.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_import_type.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { + if let Some(rule) = self.use_import_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_number_namespace.as_ref() { + if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if let Some(rule) = self.use_number_namespace.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_shorthand_function_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -2619,96 +2629,101 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.no_skipped_tests.as_ref() { + if let Some(rule) = self.no_restricted_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.no_then_property.as_ref() { + if let Some(rule) = self.no_skipped_tests.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_then_property.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); } } - if let Some(rule) = self.no_useless_ternary.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_ternary.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_consistent_array_type.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } - if let Some(rule) = self.use_export_type.as_ref() { + if let Some(rule) = self.use_consistent_array_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.use_filenaming_convention.as_ref() { + if let Some(rule) = self.use_export_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_filenaming_convention.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[21])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.use_import_type.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { + if let Some(rule) = self.use_import_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.use_number_namespace.as_ref() { + if let Some(rule) = self.use_nodejs_import_protocol.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.use_shorthand_function_type.as_ref() { + if let Some(rule) = self.use_number_namespace.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_shorthand_function_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -2722,7 +2737,7 @@ impl Nursery { pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 12] { Self::RECOMMENDED_RULES_AS_FILTERS } - pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 28] { + pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 29] { Self::ALL_RULES_AS_FILTERS } #[doc = r" Select preset rules"] @@ -2755,6 +2770,7 @@ impl Nursery { "noMisleadingCharacterClass" => self.no_misleading_character_class.as_ref(), "noNodejsModules" => self.no_nodejs_modules.as_ref(), "noReExportAll" => self.no_re_export_all.as_ref(), + "noRestrictedImports" => self.no_restricted_imports.as_ref(), "noSkippedTests" => self.no_skipped_tests.as_ref(), "noThenProperty" => self.no_then_property.as_ref(), "noUnusedImports" => self.no_unused_imports.as_ref(), diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 0ec1319f28d6..67d42dc45b83 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -903,6 +903,10 @@ export interface Nursery { * Avoid re-export all */ noReExportAll?: RuleConfiguration; + /** + * Disallow specified modules when loaded by import or require. + */ + noRestrictedImports?: RuleConfiguration; /** * Disallow disabled tests. */ @@ -1421,6 +1425,7 @@ export type PossibleOptions = | DeprecatedHooksOptions | NamingConventionOptions | RestrictedGlobalsOptions + | RestrictedImportsOptions | ValidAriaRoleOptions | UtilityClassSortingOptions; /** @@ -1483,6 +1488,15 @@ export interface RestrictedGlobalsOptions { */ deniedGlobals: string[]; } +/** + * Options for the rule `noRestrictedImports`. + */ +export interface RestrictedImportsOptions { + /** + * A list of names that should trigger the rule + */ + paths: {}; +} export interface ValidAriaRoleOptions { allowInvalidRoles: string[]; ignoreNonDom: boolean; @@ -1714,6 +1728,7 @@ export type Category = | "lint/nursery/noMisleadingCharacterClass" | "lint/nursery/noNodejsModules" | "lint/nursery/noReExportAll" + | "lint/nursery/noRestrictedImports" | "lint/nursery/noSkippedTests" | "lint/nursery/noThenProperty" | "lint/nursery/noTypeOnlyImportAttributes" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index f7a70f73dbb7..542fbeee1310 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1358,6 +1358,13 @@ { "type": "null" } ] }, + "noRestrictedImports": { + "description": "Disallow specified modules when loaded by import or require.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noSkippedTests": { "description": "Disallow disabled tests.", "anyOf": [ @@ -1704,6 +1711,10 @@ "description": "Options for `noRestrictedGlobals` rule", "allOf": [{ "$ref": "#/definitions/RestrictedGlobalsOptions" }] }, + { + "description": "Options for `noRestrictedImports` rule", + "allOf": [{ "$ref": "#/definitions/RestrictedImportsOptions" }] + }, { "description": "Options for `useValidAriaRole` rule", "allOf": [{ "$ref": "#/definitions/ValidAriaRoleOptions" }] @@ -1729,6 +1740,19 @@ }, "additionalProperties": false }, + "RestrictedImportsOptions": { + "description": "Options for the rule `noRestrictedImports`.", + "type": "object", + "required": ["paths"], + "properties": { + "paths": { + "description": "A list of names that should trigger the rule", + "type": "object", + "additionalProperties": { "type": "string" } + } + }, + "additionalProperties": false + }, "RuleConfiguration": { "anyOf": [ { "$ref": "#/definitions/RulePlainConfiguration" }, diff --git a/website/src/components/generated/NumberOfRules.astro b/website/src/components/generated/NumberOfRules.astro index 136102735741..901fe345df2e 100644 --- a/website/src/components/generated/NumberOfRules.astro +++ b/website/src/components/generated/NumberOfRules.astro @@ -1,2 +1,2 @@ -

Biome's linter has a total of 198 rules

\ No newline at end of file +

Biome's linter has a total of 199 rules

\ No newline at end of file diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index 17474aaecf4b..93d12b48cef2 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -241,8 +241,8 @@ Rules that belong to this group are not subject to semantic version🔧 | | [noNodejsModules](/linter/rules/no-nodejs-modules) | Forbid the use of Node.js builtin modules. | | -| [noRestrictedImports](/linter/rules/no-restricted-imports) | Disallow specified modules when loaded by import or require. | | | [noReExportAll](/linter/rules/no-re-export-all) | Avoid re-export all | | +| [noRestrictedImports](/linter/rules/no-restricted-imports) | Disallow specified modules when loaded by import or require. | | | [noSkippedTests](/linter/rules/no-skipped-tests) | Disallow disabled tests. | | | [noThenProperty](/linter/rules/no-then-property) | Disallow then property. | | | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | 🔧 | diff --git a/website/src/content/docs/linter/rules/no-restricted-imports.md b/website/src/content/docs/linter/rules/no-restricted-imports.md index 71c2ef293169..241866bfe2eb 100644 --- a/website/src/content/docs/linter/rules/no-restricted-imports.md +++ b/website/src/content/docs/linter/rules/no-restricted-imports.md @@ -16,8 +16,6 @@ Source: