From b6cff4b5921ab68672499acd5693ee95c58fa0e7 Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Wed, 27 Nov 2024 20:42:41 +0100 Subject: [PATCH] chore: extract noPackagePrivateImport rule --- ...restrictions_to_nopackageprivateimports.md | 8 + ...has_been_moved_into_norestrictedimports.md | 26 -- .../src/analyzer/linter/rules.rs | 154 +++++---- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 2 + .../nursery/no_package_private_imports.rs | 199 +++++++++++ .../src/lint/nursery/no_restricted_imports.rs | 308 +++--------------- crates/biome_js_analyze/src/options.rs | 1 + .../invalid.js} | 0 .../invalid.js.snap} | 10 +- .../valid.js} | 0 .../valid.js.snap} | 0 .../invalidPackagePrivateImports.options.json | 16 - .../validPackagePrivateImports.options.json | 16 - .../@biomejs/backend-jsonrpc/src/workspace.ts | 5 + .../@biomejs/biome/configuration_schema.json | 7 + 16 files changed, 366 insertions(+), 387 deletions(-) create mode 100644 .changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md delete mode 100644 .changeset/the_rule_useimportrestrictions_has_been_moved_into_norestrictedimports.md create mode 100644 crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs rename crates/biome_js_analyze/tests/specs/nursery/{noRestrictedImports/invalidPackagePrivateImports.js => noPackagePrivateImports/invalid.js} (100%) rename crates/biome_js_analyze/tests/specs/nursery/{noRestrictedImports/invalidPackagePrivateImports.js.snap => noPackagePrivateImports/invalid.js.snap} (75%) rename crates/biome_js_analyze/tests/specs/nursery/{noRestrictedImports/validPackagePrivateImports.js => noPackagePrivateImports/valid.js} (100%) rename crates/biome_js_analyze/tests/specs/nursery/{noRestrictedImports/validPackagePrivateImports.js.snap => noPackagePrivateImports/valid.js.snap} (100%) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.options.json delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.options.json diff --git a/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md b/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md new file mode 100644 index 000000000000..d19b15054667 --- /dev/null +++ b/.changeset/renamed_useimportrestrictions_to_nopackageprivateimports.md @@ -0,0 +1,8 @@ +--- +cli: major +--- + +# The rule `useImportRestrictions` has been renamed to `noPackagePrivateImports` + +To avoid confusion with `noRestrictedImports`, `useImportRestrictions` has been +renamed to `noPackagePrivateImports`. diff --git a/.changeset/the_rule_useimportrestrictions_has_been_moved_into_norestrictedimports.md b/.changeset/the_rule_useimportrestrictions_has_been_moved_into_norestrictedimports.md deleted file mode 100644 index 608190d6f561..000000000000 --- a/.changeset/the_rule_useimportrestrictions_has_been_moved_into_norestrictedimports.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -cli: major ---- - -# The rule `useImportRestrictions` has been removed and replaced with an option in `noRestrictedImports` - -To avoid confusion between the two similarly named rules, `useImportRestrictions` has been removed -and replaced with the option `restrictPackagePrivate` in the `noRestrictedImports` rule: - -```jsonc -// biome.json -{ - "linter": { - "rules": { - "nursury": { - "noRestrictedImports": { - "level": "error", - "options": { - "restrictPackagePrivate": "all" - } - } - } - } - } -} -``` diff --git a/crates/biome_configuration/src/analyzer/linter/rules.rs b/crates/biome_configuration/src/analyzer/linter/rules.rs index 156d692da5ef..cef83e05a2fa 100644 --- a/crates/biome_configuration/src/analyzer/linter/rules.rs +++ b/crates/biome_configuration/src/analyzer/linter/rules.rs @@ -3366,6 +3366,10 @@ pub struct Nursery { #[doc = "Disallow octal escape sequences in string literals"] #[serde(skip_serializing_if = "Option::is_none")] pub no_octal_escape: Option>, + #[doc = "Succinct description of the rule."] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_package_private_imports: + Option>, #[doc = "Disallow the use of process.env."] #[serde(skip_serializing_if = "Option::is_none")] pub no_process_env: Option>, @@ -3518,6 +3522,7 @@ impl Nursery { "noMissingVarFunction", "noNestedTernary", "noOctalEscape", + "noPackagePrivateImports", "noProcessEnv", "noRestrictedImports", "noRestrictedTypes", @@ -3574,15 +3579,15 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), - 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]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), ]; const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), @@ -3635,6 +3640,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[47]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -3746,161 +3752,166 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_process_env.as_ref() { + if let Some(rule) = self.no_package_private_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_process_env.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_restricted_types.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[21])); } } - if let Some(rule) = self.no_secrets.as_ref() { + if let Some(rule) = self.no_restricted_types.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_static_element_interactions.as_ref() { + if let Some(rule) = self.no_secrets.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_static_element_interactions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_template_curly_in_string.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unknown_pseudo_class.as_ref() { + if let Some(rule) = self.no_template_curly_in_string.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unknown_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_unknown_type_selector.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_element.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_useless_escape_in_regex.as_ref() { + if let Some(rule) = self.no_unknown_type_selector.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_useless_string_raw.as_ref() { + if let Some(rule) = self.no_useless_escape_in_regex.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_useless_undefined.as_ref() { + if let Some(rule) = self.no_useless_string_raw.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_value_at_rule.as_ref() { + if let Some(rule) = self.no_useless_undefined.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_value_at_rule.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_at_index.as_ref() { + if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_collapsed_if.as_ref() { + if let Some(rule) = self.use_at_index.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_component_export_only_modules.as_ref() { + if let Some(rule) = self.use_collapsed_if.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_consistent_curly_braces.as_ref() { + if let Some(rule) = self.use_component_export_only_modules.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { + if let Some(rule) = self.use_consistent_curly_braces.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_google_font_display.as_ref() { + if let Some(rule) = self.use_explicit_type.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_guard_for_in.as_ref() { + if let Some(rule) = self.use_google_font_preconnect.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_named_operation.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_strict_mode.as_ref() { + 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[47])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> FxHashSet> { @@ -4000,161 +4011,166 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); } } - if let Some(rule) = self.no_process_env.as_ref() { + if let Some(rule) = self.no_package_private_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[19])); } } - if let Some(rule) = self.no_restricted_imports.as_ref() { + if let Some(rule) = self.no_process_env.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[20])); } } - if let Some(rule) = self.no_restricted_types.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[21])); } } - if let Some(rule) = self.no_secrets.as_ref() { + if let Some(rule) = self.no_restricted_types.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[22])); } } - if let Some(rule) = self.no_static_element_interactions.as_ref() { + if let Some(rule) = self.no_secrets.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[23])); } } - if let Some(rule) = self.no_substr.as_ref() { + if let Some(rule) = self.no_static_element_interactions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[24])); } } - if let Some(rule) = self.no_template_curly_in_string.as_ref() { + if let Some(rule) = self.no_substr.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[25])); } } - if let Some(rule) = self.no_unknown_pseudo_class.as_ref() { + if let Some(rule) = self.no_template_curly_in_string.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[26])); } } - if let Some(rule) = self.no_unknown_pseudo_element.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_class.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[27])); } } - if let Some(rule) = self.no_unknown_type_selector.as_ref() { + if let Some(rule) = self.no_unknown_pseudo_element.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[28])); } } - if let Some(rule) = self.no_useless_escape_in_regex.as_ref() { + if let Some(rule) = self.no_unknown_type_selector.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[29])); } } - if let Some(rule) = self.no_useless_string_raw.as_ref() { + if let Some(rule) = self.no_useless_escape_in_regex.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[30])); } } - if let Some(rule) = self.no_useless_undefined.as_ref() { + if let Some(rule) = self.no_useless_string_raw.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[31])); } } - if let Some(rule) = self.no_value_at_rule.as_ref() { + if let Some(rule) = self.no_useless_undefined.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[32])); } } - if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { + if let Some(rule) = self.no_value_at_rule.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[33])); } } - if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() { + if let Some(rule) = self.use_adjacent_overload_signatures.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[34])); } } - if let Some(rule) = self.use_at_index.as_ref() { + if let Some(rule) = self.use_aria_props_supported_by_role.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[35])); } } - if let Some(rule) = self.use_collapsed_if.as_ref() { + if let Some(rule) = self.use_at_index.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[36])); } } - if let Some(rule) = self.use_component_export_only_modules.as_ref() { + if let Some(rule) = self.use_collapsed_if.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[37])); } } - if let Some(rule) = self.use_consistent_curly_braces.as_ref() { + if let Some(rule) = self.use_component_export_only_modules.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[38])); } } - if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { + if let Some(rule) = self.use_consistent_curly_braces.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[39])); } } - if let Some(rule) = self.use_deprecated_reason.as_ref() { + if let Some(rule) = self.use_consistent_member_accessibility.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[40])); } } - if let Some(rule) = self.use_explicit_type.as_ref() { + if let Some(rule) = self.use_deprecated_reason.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[41])); } } - if let Some(rule) = self.use_google_font_display.as_ref() { + if let Some(rule) = self.use_explicit_type.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[42])); } } - if let Some(rule) = self.use_google_font_preconnect.as_ref() { + if let Some(rule) = self.use_google_font_display.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[43])); } } - if let Some(rule) = self.use_guard_for_in.as_ref() { + if let Some(rule) = self.use_google_font_preconnect.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[44])); } } - if let Some(rule) = self.use_named_operation.as_ref() { + if let Some(rule) = self.use_guard_for_in.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[45])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_named_operation.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[46])); } } - if let Some(rule) = self.use_strict_mode.as_ref() { + 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[47])); } } - if let Some(rule) = self.use_trim_start_end.as_ref() { + if let Some(rule) = self.use_strict_mode.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[48])); } } - if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if let Some(rule) = self.use_trim_start_end.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[49])); } } + if let Some(rule) = self.use_valid_autocomplete.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[50])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -4267,6 +4283,10 @@ impl Nursery { .no_octal_escape .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "noPackagePrivateImports" => self + .no_package_private_imports + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "noProcessEnv" => self .no_process_env .as_ref() diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index bf1fa75568c4..b0cce49bded5 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -161,6 +161,7 @@ define_categories! { "lint/nursery/noMissingVarFunction": "https://biomejs.dev/linter/rules/no-missing-var-function", "lint/nursery/noNestedTernary": "https://biomejs.dev/linter/rules/no-nested-ternary", "lint/nursery/noOctalEscape": "https://biomejs.dev/linter/rules/no-octal-escape", + "lint/nursery/noPackagePrivateImports": "https://biomejs.dev/linter/rules/no-package-private-imports", "lint/nursery/noProcessEnv": "https://biomejs.dev/linter/rules/no-process-env", "lint/nursery/noReactSpecificProps": "https://biomejs.dev/linter/rules/no-react-specific-props", "lint/nursery/noRestrictedImports": "https://biomejs.dev/linter/rules/no-restricted-imports", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 944ed077ee40..168a7462f36c 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -16,6 +16,7 @@ pub mod no_img_element; pub mod no_irregular_whitespace; pub mod no_nested_ternary; pub mod no_octal_escape; +pub mod no_package_private_imports; pub mod no_process_env; pub mod no_restricted_imports; pub mod no_restricted_types; @@ -60,6 +61,7 @@ declare_lint_group! { self :: no_irregular_whitespace :: NoIrregularWhitespace , self :: no_nested_ternary :: NoNestedTernary , self :: no_octal_escape :: NoOctalEscape , + self :: no_package_private_imports :: NoPackagePrivateImports , self :: no_process_env :: NoProcessEnv , self :: no_restricted_imports :: NoRestrictedImports , self :: no_restricted_types :: NoRestrictedTypes , diff --git a/crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs b/crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs new file mode 100644 index 000000000000..757316e9969e --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/no_package_private_imports.rs @@ -0,0 +1,199 @@ +use biome_analyze::{ + context::RuleContext, declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource, +}; +use biome_console::markup; +use biome_js_syntax::{inner_string_text, AnyJsImportLike}; +use biome_rowan::{TextRange, TokenText}; + +const INDEX_BASENAMES: &[&str] = &["index", "mod"]; + +const SOURCE_EXTENSIONS: &[&str] = &["js", "ts", "cjs", "cts", "mjs", "mts", "jsx", "tsx"]; + +declare_lint_rule! { + /// Restricts imports of "package private" exports. + /// + /// By enabling this rule, all exported symbols, such as types, functions + /// or other things that may be exported, are considered to be "package + /// private". This means that modules that reside in the same directory, as + /// well as submodules of those "sibling" modules, are allowed to import + /// them, while any other modules that are further away in the file system + /// are restricted from importing them. A symbol's visibility may be + /// extended by re-exporting from an index file. + /// + /// Notes: + /// + /// * This rule only applies to relative imports. External dependencies + /// as well as TypeScript aliases are exempted. + /// * This rule only applies to imports for JavaScript and TypeScript + /// files. Imports for resources such as images or CSS files are exempted. + /// + /// Source: https://github.com/uhyo/eslint-plugin-import-access + /// + /// #### Examples (Invalid) + /// + /// ```js,expect_diagnostic + /// // Attempt to import from `foo.js` from outside its `sub` module. + /// import { fooPackageVariable } from "./sub/foo.js"; + /// ``` + /// ```js,expect_diagnostic + /// // Attempt to import from `bar.ts` from outside its `aunt` module. + /// import { barPackageVariable } from "../aunt/bar.ts"; + /// ``` + /// + /// ```js,expect_diagnostic + /// // Assumed to resolve to a JS/TS file. + /// import { fooPackageVariable } from "./sub/foo"; + /// ``` + /// + /// ```js,expect_diagnostic + /// // If the `sub/foo` module is inaccessible, so is its index file. + /// import { fooPackageVariable } from "./sub/foo/index.js"; + /// ``` + /// + /// #### Examples (Valid) + /// + /// ```js + /// // Imports within the same module are always allowed. + /// import { fooPackageVariable } from "./foo.js"; + /// + /// // Resources (anything other than JS/TS files) are exempt. + /// import { barResource } from "../aunt/bar.png"; + /// + /// // A parent index file is accessible like other modules. + /// import { internal } from "../../index.js"; + /// + /// // If the `sub` module is accessible, so is its index file. + /// import { subPackageVariable } from "./sub/index.js"; + /// + /// // Library imports are exempt. + /// import useAsync from "react-use/lib/useAsync"; + /// ``` + /// + pub NoPackagePrivateImports { + version: "next", + name: "noPackagePrivateImports", + language: "js", + sources: &[ + RuleSource::EslintImportAccess("eslint-plugin-import-access") + ], + recommended: false, + } +} + +pub struct NoPackagePrivateImportsState { + range: TextRange, + + /// The path that is being restricted. + path: String, + + /// Suggestion from which to import instead. + suggestion: String, +} + +impl Rule for NoPackagePrivateImports { + type Query = Ast; + type State = NoPackagePrivateImportsState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + if node.is_in_ts_module_declaration() { + return None; + } + + let module_name = node.module_name_token()?; + let import_source_text = inner_string_text(&module_name); + + get_restricted_import(module_name.text_trimmed_range(), &import_source_text) + } + + fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { + let diagnostic = RuleDiagnostic::new( + rule_category!(), + state.range, + markup! { + "Importing package private symbols is disallowed from outside the module directory." + }, + ) + .note(markup! { + "Please import from "{state.suggestion}" instead " + "(you may need to re-export the symbol(s) from "{state.path}")." + }); + + Some(diagnostic) + } +} + +fn get_restricted_import( + range: TextRange, + module_path: &TokenText, +) -> Option { + if !module_path.starts_with('.') { + return None; + } + + let mut path_parts: Vec<_> = module_path.text().split('/').collect(); + let mut index_filename = None; + + // TODO. The implementation could be optimized further by using + // `Path::new(module_path.text())` for further inspiration see `use_import_extensions` rule. + if let Some(extension) = get_extension(&path_parts) { + if !SOURCE_EXTENSIONS.contains(&extension) { + return None; // Resource files are exempt. + } + + if let Some(basename) = get_basename(&path_parts) { + if INDEX_BASENAMES.contains(&basename) { + // We pop the index file because it shouldn't count as a path, + // component, but we store the file name so we can add it to + // both the reported path and the suggestion. + index_filename = path_parts.last().copied(); + path_parts.pop(); + } + } + } + + let is_restricted = path_parts + .iter() + .filter(|&&part| part != "." && part != "..") + .count() + > 1; + if !is_restricted { + return None; + } + + let mut suggestion_parts = path_parts[..path_parts.len() - 1].to_vec(); + + // Push the index file if it exists. This makes sure the reported path + // matches the import path exactly. + if let Some(index_filename) = index_filename { + path_parts.push(index_filename); + + // Assumes the user probably wants to use an index file that has the + // same name as the original. + suggestion_parts.push(index_filename); + } + + Some(NoPackagePrivateImportsState { + range, + path: path_parts.join("/"), + suggestion: suggestion_parts.join("/"), + }) +} + +fn get_basename<'a>(path_parts: &'_ [&'a str]) -> Option<&'a str> { + path_parts.last().map(|&part| match part.find('.') { + Some(dot_index) if dot_index > 0 && dot_index < part.len() - 1 => &part[..dot_index], + _ => part, + }) +} + +fn get_extension<'a>(path_parts: &'_ [&'a str]) -> Option<&'a str> { + path_parts.last().and_then(|part| match part.find('.') { + Some(dot_index) if dot_index > 0 && dot_index < part.len() - 1 => { + Some(&part[dot_index + 1..]) + } + _ => None, + }) +} diff --git a/crates/biome_js_analyze/src/lint/nursery/no_restricted_imports.rs b/crates/biome_js_analyze/src/lint/nursery/no_restricted_imports.rs index ff345de284c1..9c63cbc81b5d 100644 --- a/crates/biome_js_analyze/src/lint/nursery/no_restricted_imports.rs +++ b/crates/biome_js_analyze/src/lint/nursery/no_restricted_imports.rs @@ -1,11 +1,9 @@ -use crate::lint::nursery::no_restricted_imports::NoRestrictedImportsState::RestrictedImportMessage; use biome_analyze::context::RuleContext; use biome_analyze::{declare_lint_rule, Ast, Rule, RuleDiagnostic, RuleSource}; use biome_console::markup; use biome_deserialize::{ Deserializable, DeserializableType, DeserializableValue, DeserializationContext, }; -use biome_deserialize_macros::Deserializable; use biome_js_syntax::{ inner_string_text, AnyJsArrowFunctionParameters, AnyJsBindingPattern, AnyJsCombinedSpecifier, AnyJsExpression, AnyJsImportLike, AnyJsNamedImportSpecifier, AnyJsObjectBindingPatternMember, @@ -18,16 +16,10 @@ use biome_js_syntax::{ JsObjectBindingPatternShorthandProperty, JsShorthandNamedImportSpecifier, JsStaticMemberExpression, JsSyntaxKind, JsVariableDeclarator, }; -use biome_rowan::{ - AstNode, AstSeparatedList, SyntaxNode, SyntaxNodeCast, SyntaxToken, TextRange, TokenText, -}; +use biome_rowan::{AstNode, AstSeparatedList, SyntaxNode, SyntaxNodeCast, SyntaxToken, TextRange}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -const INDEX_BASENAMES: &[&str] = &["index", "mod"]; - -const SOURCE_EXTENSIONS: &[&str] = &["js", "ts", "cjs", "cts", "mjs", "mts", "jsx", "tsx"]; - declare_lint_rule! { /// Disallow specified modules when loaded by import or require. /// @@ -285,77 +277,6 @@ declare_lint_rule! { /// } /// ``` /// - /// ### `paths` - /// - /// A map where every key represents a path that is not allowed to be - /// imported. The corresponding value is the message to use when reporting - /// violations. - /// - /// ### `restrictPackagePrivate` - /// - /// Whether imports of "package private" exports should be restricted. - /// - /// Allowed values are `"all"` and `"none"`: - /// - /// If `"all"` is specified, all exported symbols, such as types, functions - /// or other things that may be exported, are considered to be "package - /// private". This means that modules that reside in the same directory, as - /// well as submodules of those "sibling" modules, are allowed to import - /// them, while any other modules that are further away in the file system - /// are restricted from importing them. A symbol's visibility may be - /// extended by re-exporting from an index file. - /// - /// If `"none"` is used (the default), no restrictions are applied. - /// - /// Notes: - /// - /// * This option only applies to relative imports. External dependencies - /// as well as TypeScript aliases are exempted. - /// * This option only applies to imports for JavaScript and TypeScript - /// files. Imports for resources such as images or CSS files are exempted. - /// - /// Source: https://github.com/uhyo/eslint-plugin-import-access - /// - /// #### Examples (Invalid) - /// - /// ```js,expect_diagnostic - /// // Attempt to import from `foo.js` from outside its `sub` module. - /// import { fooPackageVariable } from "./sub/foo.js"; - /// ``` - /// ```js,expect_diagnostic - /// // Attempt to import from `bar.ts` from outside its `aunt` module. - /// import { barPackageVariable } from "../aunt/bar.ts"; - /// ``` - /// - /// ```js,expect_diagnostic - /// // Assumed to resolve to a JS/TS file. - /// import { fooPackageVariable } from "./sub/foo"; - /// ``` - /// - /// ```js,expect_diagnostic - /// // If the `sub/foo` module is inaccessible, so is its index file. - /// import { fooPackageVariable } from "./sub/foo/index.js"; - /// ``` - /// - /// #### Examples (Valid) - /// - /// ```js - /// // Imports within the same module are always allowed. - /// import { fooPackageVariable } from "./foo.js"; - /// - /// // Resources (anything other than JS/TS files) are exempt. - /// import { barResource } from "../aunt/bar.png"; - /// - /// // A parent index file is accessible like other modules. - /// import { internal } from "../../index.js"; - /// - /// // If the `sub` module is accessible, so is its index file. - /// import { subPackageVariable } from "./sub/index.js"; - /// - /// // Library imports are exempt. - /// import useAsync from "react-use/lib/useAsync"; - /// ``` - /// /// #### Invalid /// /// ```js,expect_diagnostic,use_options @@ -373,8 +294,7 @@ declare_lint_rule! { language: "js", sources: &[ RuleSource::Eslint("no-restricted-imports"), - RuleSource::EslintTypeScript("no-restricted-imports"), - RuleSource::EslintImportAccess("eslint-plugin-import-access") + RuleSource::EslintTypeScript("no-restricted-imports") ], recommended: false, } @@ -397,10 +317,6 @@ pub struct RestrictedImportsOptions { /// A list of import paths that should trigger the rule. #[serde(skip_serializing_if = "FxHashMap::is_empty")] paths: FxHashMap, CustomRestrictedImport>, - - /// Whether to place restrictions on the importing of "package private" - /// symbols. - restrict_package_private: PackagePrivateRestriction, } /// Specifies why a specific import is allowed or disallowed. @@ -551,7 +467,7 @@ impl Deserializable for CustomRestrictedImport { struct RestrictedImportVisitor<'a> { import_source: &'a str, restricted_import: CustomRestrictedImportOptions, - results: Vec, + results: Vec, } impl<'a> RestrictedImportVisitor<'a> { @@ -966,17 +882,16 @@ impl<'a> RestrictedImportVisitor<'a> { if status.is_allowed() { return None; } - self.results - .push(NoRestrictedImportsState::RestrictedImportMessage { - location: import_node.text_trimmed_range(), - message: self.restricted_import.get_message_for_restriction( - self.import_source, - name_or_alias, - status.reason(), - ), - import_source: self.import_source.to_string(), - allowed_import_names: self.restricted_import.allow_import_names.clone(), - }); + self.results.push(RestrictedImportMessage { + location: import_node.text_trimmed_range(), + message: self.restricted_import.get_message_for_restriction( + self.import_source, + name_or_alias, + status.reason(), + ), + import_source: self.import_source.to_string(), + allowed_import_names: self.restricted_import.allow_import_names.clone(), + }); Some(()) } @@ -1005,68 +920,41 @@ impl<'a> RestrictedImportVisitor<'a> { } } -/// Allowed values for the `restrictPackagePrivate` option. -#[derive(Clone, Copy, Debug, Default, Deserialize, Deserializable, Eq, PartialEq, Serialize)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub enum PackagePrivateRestriction { - /// All exported symbols are assumed to be package private and may only be - /// imported from sibling modules and their submodules. - All, - - /// No restrictions are applied on importing package private symbols. - #[default] - None, -} - -pub enum NoRestrictedImportsState { - RestrictedImportMessage { - location: TextRange, - message: String, - import_source: String, - allowed_import_names: Box<[Box]>, - }, - PackagePrivate { - range: TextRange, - - /// The path that is being restricted. - path: String, - - /// Suggestion from which to import instead. - suggestion: String, - }, +pub struct RestrictedImportMessage { + location: TextRange, + message: String, + import_source: String, + allowed_import_names: Box<[Box]>, } impl Rule for NoRestrictedImports { type Query = Ast; - type State = NoRestrictedImportsState; + type State = RestrictedImportMessage; type Signals = Vec; type Options = Box; fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); if node.is_in_ts_module_declaration() { - return [].into(); + return Vec::new(); } let Some(module_name) = node.module_name_token() else { - return vec![]; + return Vec::new(); }; let import_source_text = inner_string_text(&module_name); let import_source = import_source_text.text(); let Some(restricted_import_settings) = ctx.options().paths.get(import_source) else { - return vec![]; + return Vec::new(); }; let restricted_import: CustomRestrictedImportOptions = restricted_import_settings.clone().into(); - get_restricted_import(module_name.text_trimmed_range(), &import_source_text); - match node { AnyJsImportLike::JsModuleSource(module_source_node) => { if !restricted_import.has_import_name_patterns() { // All imports disallowed, add diagnostic to the import source - vec![NoRestrictedImportsState::RestrictedImportMessage { + vec![RestrictedImportMessage { location: module_name.text_trimmed_range(), message: restricted_import.get_message_for_restriction( import_source, @@ -1094,7 +982,7 @@ impl Rule for NoRestrictedImports { // be difficult to distinguish) or a collection of named imports. if !restricted_import.has_import_name_patterns() { // All imports disallowed, add diagnostic to the import source - vec![NoRestrictedImportsState::RestrictedImportMessage { + vec![RestrictedImportMessage { location: module_name.text_trimmed_range(), message: restricted_import.get_message_for_restriction( import_source, @@ -1122,7 +1010,7 @@ impl Rule for NoRestrictedImports { if status.is_forbidden() { // require() calls can only import the default import, so // there are no individual import names to check or report on. - vec![NoRestrictedImportsState::RestrictedImportMessage { + vec![RestrictedImportMessage { location: module_name.text_trimmed_range(), message: restricted_import.get_message_for_restriction( import_source, @@ -1140,130 +1028,36 @@ impl Rule for NoRestrictedImports { } fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { - match state { - NoRestrictedImportsState::RestrictedImportMessage { - import_source, - allowed_import_names, - location, - message, - } => { - let mut rule_diagnostic = RuleDiagnostic::new( - rule_category!(), - location, - markup! { - {message} - }, - ); - if !allowed_import_names.is_empty() { - let mut sorted = allowed_import_names.to_vec(); - sorted.sort(); - let allowed_import_names = sorted.into_iter().map(|name| { - if &*name == RestrictedImportVisitor::BARE_IMPORT_ALIAS { - "Side-effect only import".into() - } else { - name - } - }); + let RestrictedImportMessage { + import_source, + allowed_import_names, + location, + message, + } = state; + + let mut rule_diagnostic = RuleDiagnostic::new( + rule_category!(), + location, + markup! { + {message} + }, + ); + if !allowed_import_names.is_empty() { + let mut sorted = allowed_import_names.to_vec(); + sorted.sort(); + let allowed_import_names = sorted.into_iter().map(|name| { + if &*name == RestrictedImportVisitor::BARE_IMPORT_ALIAS { + "Side-effect only import".into() + } else { + name + } + }); - rule_diagnostic = rule_diagnostic.footer_list( + rule_diagnostic = rule_diagnostic.footer_list( markup! { "Only the following imports from ""'"{import_source}"'"" are allowed:" }, allowed_import_names, ); - } - Some(rule_diagnostic) - } - NoRestrictedImportsState::PackagePrivate { - range, - path, - suggestion, - } => { - let diagnostic = RuleDiagnostic::new( - rule_category!(), - *range, - markup! { - "Importing package private symbols is disallowed from outside the module directory." - }, - ) - .note(markup! { - "Please import from "{suggestion}" instead " - "(you may need to re-export the symbol(s) from "{path}")." - }); - - Some(diagnostic) - } - } - } -} - -fn get_restricted_import( - range: TextRange, - module_path: &TokenText, -) -> Option { - if !module_path.starts_with('.') { - return None; - } - - let mut path_parts: Vec<_> = module_path.text().split('/').collect(); - let mut index_filename = None; - - // TODO. The implementation could be optimized further by using - // `Path::new(module_path.text())` for further inspiration see `use_import_extensions` rule. - if let Some(extension) = get_extension(&path_parts) { - if !SOURCE_EXTENSIONS.contains(&extension) { - return None; // Resource files are exempt. } - - if let Some(basename) = get_basename(&path_parts) { - if INDEX_BASENAMES.contains(&basename) { - // We pop the index file because it shouldn't count as a path, - // component, but we store the file name so we can add it to - // both the reported path and the suggestion. - index_filename = path_parts.last().copied(); - path_parts.pop(); - } - } - } - - let is_restricted = path_parts - .iter() - .filter(|&&part| part != "." && part != "..") - .count() - > 1; - if !is_restricted { - return None; + Some(rule_diagnostic) } - - let mut suggestion_parts = path_parts[..path_parts.len() - 1].to_vec(); - - // Push the index file if it exists. This makes sure the reported path - // matches the import path exactly. - if let Some(index_filename) = index_filename { - path_parts.push(index_filename); - - // Assumes the user probably wants to use an index file that has the - // same name as the original. - suggestion_parts.push(index_filename); - } - - Some(NoRestrictedImportsState::PackagePrivate { - range, - path: path_parts.join("/"), - suggestion: suggestion_parts.join("/"), - }) -} - -fn get_basename<'a>(path_parts: &'_ [&'a str]) -> Option<&'a str> { - path_parts.last().map(|&part| match part.find('.') { - Some(dot_index) if dot_index > 0 && dot_index < part.len() - 1 => &part[..dot_index], - _ => part, - }) -} - -fn get_extension<'a>(path_parts: &'_ [&'a str]) -> Option<&'a str> { - path_parts.last().and_then(|part| match part.find('.') { - Some(dot_index) if dot_index > 0 && dot_index < part.len() - 1 => { - Some(&part[dot_index + 1..]) - } - _ => None, - }) } diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 2ed59dc37f9d..eda882c7bdd7 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -165,6 +165,7 @@ pub type NoNoninteractiveTabindex = < lint :: a11y :: no_noninteractive_tabindex pub type NoNonoctalDecimalEscape = < lint :: correctness :: no_nonoctal_decimal_escape :: NoNonoctalDecimalEscape as biome_analyze :: Rule > :: Options ; pub type NoOctalEscape = ::Options; +pub type NoPackagePrivateImports = < lint :: nursery :: no_package_private_imports :: NoPackagePrivateImports as biome_analyze :: Rule > :: Options ; pub type NoParameterAssign = ::Options; pub type NoParameterProperties = diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.js b/crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/invalid.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.js rename to crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/invalid.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/invalid.js.snap similarity index 75% rename from crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/invalid.js.snap index e4ccfc18efee..d4d964ac649f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/invalid.js.snap @@ -1,6 +1,6 @@ --- source: crates/biome_js_analyze/tests/spec_tests.rs -expression: invalidPackagePrivateImports.js +expression: invalid.js --- # Input ```jsx @@ -20,7 +20,7 @@ import { fooPackageVariable } from "./sub/foo/index.js"; # Diagnostics ``` -invalidPackagePrivateImports.js:2:36 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:2:36 lint/nursery/noPackagePrivateImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Importing package private symbols is disallowed from outside the module directory. @@ -36,7 +36,7 @@ invalidPackagePrivateImports.js:2:36 lint/nursery/noRestrictedImports ━━━ ``` ``` -invalidPackagePrivateImports.js:5:36 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:5:36 lint/nursery/noPackagePrivateImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Importing package private symbols is disallowed from outside the module directory. @@ -52,7 +52,7 @@ invalidPackagePrivateImports.js:5:36 lint/nursery/noRestrictedImports ━━━ ``` ``` -invalidPackagePrivateImports.js:8:36 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:8:36 lint/nursery/noPackagePrivateImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Importing package private symbols is disallowed from outside the module directory. @@ -68,7 +68,7 @@ invalidPackagePrivateImports.js:8:36 lint/nursery/noRestrictedImports ━━━ ``` ``` -invalidPackagePrivateImports.js:11:36 lint/nursery/noRestrictedImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:11:36 lint/nursery/noPackagePrivateImports ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Importing package private symbols is disallowed from outside the module directory. diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.js b/crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/valid.js similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.js rename to crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/valid.js diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/valid.js.snap similarity index 100% rename from crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/noPackagePrivateImports/valid.js.snap diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.options.json b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.options.json deleted file mode 100644 index 318ec0690a75..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/invalidPackagePrivateImports.options.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", - "linter": { - "enabled": true, - "rules": { - "nursery": { - "noRestrictedImports": { - "level": "error", - "options": { - "restrictPackagePrivate": "all" - } - } - } - } - } -} diff --git a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.options.json b/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.options.json deleted file mode 100644 index 318ec0690a75..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/noRestrictedImports/validPackagePrivateImports.options.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "../../../../../../packages/@biomejs/biome/configuration_schema.json", - "linter": { - "enabled": true, - "rules": { - "nursery": { - "noRestrictedImports": { - "level": "error", - "options": { - "restrictPackagePrivate": "all" - } - } - } - } - } -} diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 657b52a30892..89cf29c87c86 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -1291,6 +1291,10 @@ export interface Nursery { * Disallow octal escape sequences in string literals */ noOctalEscape?: RuleConfiguration_for_Null; + /** + * Succinct description of the rule. + */ + noPackagePrivateImports?: RuleConfiguration_for_Null; /** * Disallow the use of process.env. */ @@ -3074,6 +3078,7 @@ export type Category = | "lint/nursery/noMissingVarFunction" | "lint/nursery/noNestedTernary" | "lint/nursery/noOctalEscape" + | "lint/nursery/noPackagePrivateImports" | "lint/nursery/noProcessEnv" | "lint/nursery/noReactSpecificProps" | "lint/nursery/noRestrictedImports" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 8266817fde5d..6cedca2c8c53 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -2302,6 +2302,13 @@ { "type": "null" } ] }, + "noPackagePrivateImports": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noProcessEnv": { "description": "Disallow the use of process.env.", "anyOf": [