Skip to content

feat(organizeImports): add kind field to import matcher#10074

Merged
Conaclos merged 3 commits into
biomejs:nextfrom
georgephillips:feat/organize-imports-bare-matcher
Apr 27, 2026
Merged

feat(organizeImports): add kind field to import matcher#10074
Conaclos merged 3 commits into
biomejs:nextfrom
georgephillips:feat/organize-imports-bare-matcher

Conversation

@georgephillips

Copy link
Copy Markdown
Contributor

Summary

Adds a :BARE: predefined group matcher to the organizeImports assist action. It selects bare (side-effect) imports such as import "polyfill" and composes with the existing groups option and sortBareImports: true introduced in #9384 and discussed at #7811.

I originally started with a top-level option that would skip the chunking of the imports entirely. This seemed easier but then I came across the sortBareImports on the current branch and found the discussion about adding the BARE matcher. This will be useful for our lit.dev project which doesn't care too much about the order of the imports.

{
    "sortBareImports": true,
    "groups": [
        ["**", "!:BARE:"],
        ":BLANK_LINE:",
        [":BARE:"]
    ]
}

Hope this matches what you were planning to add for this. Thanks for building a great tool.

Test Plan

  • Three new snapshot scenarios under crates/biome_js_analyze/tests/specs/source/organizeImports/:
    • bare-grouping.js — mixed bare/binding imports, bare group placed last with a blank-line separator.
    • bare-grouping-only.js — file containing only bare imports, sorted naturally within the bare group.
    • bare-grouping-with-predefined.js:BARE: combined with :NODE: and the default package group.
  • All existing organizeImports snapshots unchanged: cargo test -p biome_js_analyze --test spec_tests -- organize_imports → 61 passed.
  • Full suite: cargo test -p biome_js_analyze --test spec_tests → 2477 passed.
  • cargo fmt, cargo run -p rules_check, cargo codegen-schema, and bindings regen are all clean.

Docs

Documentation is inline in the declare_source_rule! macro for OrganizeImports in crates/biome_js_analyze/src/assist/source/organize_imports.rs:

  • New :BARE: bullet in the predefined-groups list under ### groups.
  • New ### Place side-effect imports last worked example under ## Common configurations.
  • Reference to :BARE: in the ### Chunks note explaining how sortBareImports + :BARE: interact.

AI assistance disclosure: I used Claude Code to explore the codebase and scaffold the tests; the design and the final diff were chosen and reviewed by me.

Introduce a `:BARE:` predefined group matcher for the `organizeImports`
assist action. It selects bare (side-effect) imports such as
`import "polyfill"` and composes with the existing `groups` option and
`sortBareImports: true` to express orderings such as "place all
side-effect imports at the end of the import list":

    {
      "sortBareImports": true,
      "groups": [
        ["**", "!:BARE:"],
        ":BLANK_LINE:",
        [":BARE:"]
      ]
    }

It can also be combined with other predefined matchers (`:NODE:`,
`:PACKAGE:`, `:STYLE:`, etc.) to express richer orderings in a single
`groups` array, without introducing a dedicated top-level option.

Implementation mirrors the recently-added `:STYLE:` matcher: a new
`PredefinedSourceMatcher::Bare` variant plus an `is_bare` flag threaded
through `ImportSourceCandidate`. Bare imports only participate in group
matching when `sortBareImports` is enabled, preserving today's default
chunk-preserving behavior.
@changeset-bot

changeset-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 92e7f51

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@biomejs/biome Minor
@biomejs/cli-win32-x64 Minor
@biomejs/cli-win32-arm64 Minor
@biomejs/cli-darwin-x64 Minor
@biomejs/cli-darwin-arm64 Minor
@biomejs/cli-linux-x64 Minor
@biomejs/cli-linux-arm64 Minor
@biomejs/cli-linux-x64-musl Minor
@biomejs/cli-linux-arm64-musl Minor
@biomejs/wasm-web Minor
@biomejs/wasm-bundler Minor
@biomejs/wasm-nodejs Minor
@biomejs/backend-jsonrpc Patch
@biomejs/js-api Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added A-Linter Area: linter L-JavaScript Language: JavaScript and super languages labels Apr 21, 2026
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR extends the organizeImports assist action with a new kind field in the ImportMatcher to filter imports by syntactic kind. It introduces support for bare imports (side-effect imports like import "polyfill") with negation via !bare. The implementation adds an is_bare boolean field to ImportCandidate, creates NegatableImportKindMatcher and ImportKindMatcher types, and updates matching logic to compose the new constraint with existing type and source fields. Multiple test cases demonstrate grouping scenarios, including placing bare imports last with blank-line separation.

Possibly related PRs

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly relates to the changeset, explaining the motivation, implementation approach, test coverage, and documentation updates for the new bare import grouping feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately describes the main feature addition: introducing a kind field to the import matcher for the organizeImports assist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_rule_options/src/no_restricted_imports.rs (1)

250-255: ⚠️ Potential issue | 🟠 Major

Preserve :BARE: semantics for restricted-import groups.

Line 254 always passes false, so patterns[].group: [":BARE:"] can never match a real static side-effect import in noRestrictedImports, even though this path uses the shared SourcesMatcher. Please either derive bare-ness here, or reject :BARE: for this rule if it is meant to stay organise-imports-only.

🐛 Proposed fix
         if let Some(group) = &self.group {
             let source = ImportSource::from(ComparableToken {
                 token: import_source_text.clone(),
             });
-            let candidate = ImportSourceCandidate::new(&source, false);
+            let is_bare = matches!(
+                node,
+                AnyJsImportLike::JsModuleSource(module_source_node)
+                    if matches!(
+                        module_source_node.syntax().parent().map(|clause| clause.kind()),
+                        Some(JsSyntaxKind::JS_IMPORT_BARE_CLAUSE)
+                    )
+            );
+            let candidate = ImportSourceCandidate::new(&source, is_bare);
             if group.is_match(&candidate) {

Please add a noRestrictedImports fixture covering group: [":BARE:"] as well; the organise-import snapshots will not catch this shared matcher path. As per coding guidelines, “All code changes MUST include appropriate tests”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_rule_options/src/no_restricted_imports.rs` around lines 250 -
255, The call to ImportSourceCandidate::new(&source, false) always sets the
"bare" flag to false so groups containing ":BARE:" can never match; change this
to derive the bare-ness from the import token (e.g. detect a side-effect/bare
import from ComparableToken/import_source_text or existing helpers) and pass the
correct boolean into ImportSourceCandidate::new (or alternatively explicitly
reject ":BARE:" for this rule if intended), and add a new noRestrictedImports
fixture that includes group: [":BARE:"] to cover this path; update references:
ImportSource, ComparableToken, ImportSourceCandidate, and group.is_match
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@crates/biome_rule_options/src/no_restricted_imports.rs`:
- Around line 250-255: The call to ImportSourceCandidate::new(&source, false)
always sets the "bare" flag to false so groups containing ":BARE:" can never
match; change this to derive the bare-ness from the import token (e.g. detect a
side-effect/bare import from ComparableToken/import_source_text or existing
helpers) and pass the correct boolean into ImportSourceCandidate::new (or
alternatively explicitly reject ":BARE:" for this rule if intended), and add a
new noRestrictedImports fixture that includes group: [":BARE:"] to cover this
path; update references: ImportSource, ComparableToken, ImportSourceCandidate,
and group.is_match accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e98fa38d-92bd-4608-b03d-3cd3d1a91073

📥 Commits

Reviewing files that changed from the base of the PR and between bae0710 and e993f57.

⛔ Files ignored due to path filters (5)
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-only.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-predefined.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (11)
  • .changeset/organize-imports-bare-matcher.md
  • crates/biome_js_analyze/src/assist/source/organize_imports.rs
  • crates/biome_js_analyze/src/assist/source/organize_imports/import_key.rs
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-only.js
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-only.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-predefined.js
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-predefined.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping.js
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping.options.json
  • crates/biome_rule_options/src/no_restricted_imports.rs
  • crates/biome_rule_options/src/organize_imports/import_groups.rs

@ematipico ematipico closed this Apr 21, 2026
@ematipico

Copy link
Copy Markdown
Member

It seems that OP isn't a bot, despite the heavy use of AI technologies.

@Conaclos what do think of this proposal?

@Conaclos

Conaclos commented Apr 22, 2026

Copy link
Copy Markdown
Member

Hi @georgephillips, thanks for your interest for improving Biome!

I see an issue in the current implementation : :BARE: is implemented as a predefined source matcher.
Thus, this currently allows this configuration:

{
    "sortBareImports": true,
    "groups": [
        { "source": ":BARE:" }
    ]
}

This doesn't make sense to me.
Instead, we should define a GroupMatcher. We could add the GroupMatcher::Bare variant.
This could disable the previous configuration.

Actually, I wonder if we should extend the ImportMatcher (adding a new optional kind field) instead of introducing a :BARE: group.
This could allow us to provide more power to users.
For example a user could match against bare imports that import css files:

{
    "sortBareImports": true,
    "groups": [
        { "kind": "bare",  "source": "**/*.css" }
    ]
}

What do you think?

Your example could so be written as:

{
    "sortBareImports": true,
    "groups": [
        { "kind": "!bare" },
        ":BLANK_LINE:",
        { "kind": "bare" }
    ]
}

Added a `kind` field to the `ImportMatcher` in the `organizeImports` assist action, allowing users to filter imports by their syntactic kind. The new matcher supports `bare` imports (e.g., `import "polyfill"`) and can be negated with `!bare`. This enhancement enables more flexible grouping configurations, such as combining with `source` and `type` fields for complex patterns. Updated documentation and tests to reflect these changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/biome_rule_options/src/organize_imports/import_groups.rs (1)

196-317: Consider collapsing the Negatable… boilerplate.

NegatableImportKindMatcher mirrors NegatablePredefinedSourceMatcher almost line-for-line: same { is_negated, inner } shape, same Display/Debug/From<_> for String/TryFrom<String>/FromStr scaffolding, same !-prefixed JsonSchema trick. A generic Negatable<T: Display + FromStr> (or a small impl_negatable! macro) would pay for itself the next time a kind matcher is added — and there's clearly appetite to add more (type, etc.).

Non-blocking; happy to see it landed as-is and cleaned up in a follow-up, since it matches the pattern already in this file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_rule_options/src/organize_imports/import_groups.rs` around lines
196 - 317, NegatableImportKindMatcher duplicates
NegatablePredefinedSourceMatcher boilerplate; refactor by introducing a generic
Negatable<T> (or impl_negatable! macro) that wraps T with is_negated and
implements Display, Debug, FromStr/TryFrom<String>, From<Negatable<T>> for
String, biome_deserialize::Deserializable, and the schemars json_schema
enum-extension; then replace NegatableImportKindMatcher with a type alias or
newtype using Negatable<ImportKindMatcher> and keep ImportKindMatcher unchanged
(look for NegatableImportKindMatcher, NegatablePredefinedSourceMatcher,
ImportKindMatcher, the Deserializable impl, Display/Debug impls,
TryFrom<String>, FromStr, and the #[cfg(feature = "schema")] json_schema method
to update).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@crates/biome_rule_options/src/organize_imports/import_groups.rs`:
- Around line 196-317: NegatableImportKindMatcher duplicates
NegatablePredefinedSourceMatcher boilerplate; refactor by introducing a generic
Negatable<T> (or impl_negatable! macro) that wraps T with is_negated and
implements Display, Debug, FromStr/TryFrom<String>, From<Negatable<T>> for
String, biome_deserialize::Deserializable, and the schemars json_schema
enum-extension; then replace NegatableImportKindMatcher with a type alias or
newtype using Negatable<ImportKindMatcher> and keep ImportKindMatcher unchanged
(look for NegatableImportKindMatcher, NegatablePredefinedSourceMatcher,
ImportKindMatcher, the Deserializable impl, Display/Debug impls,
TryFrom<String>, FromStr, and the #[cfg(feature = "schema")] json_schema method
to update).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 75b555bb-9ce5-480f-a318-5e6697ff1b54

📥 Commits

Reviewing files that changed from the base of the PR and between 04109e2 and 92e7f51.

⛔ Files ignored due to path filters (3)
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-source.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (9)
  • .changeset/organize-imports-bare-matcher.md
  • crates/biome_js_analyze/src/assist/source/organize_imports.rs
  • crates/biome_js_analyze/src/assist/source/organize_imports/import_key.rs
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-only.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-predefined.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-source.js
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-source.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping.options.json
  • crates/biome_rule_options/src/organize_imports/import_groups.rs
✅ Files skipped from review due to trivial changes (5)
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-source.js
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-only.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-source.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping.options.json
  • crates/biome_js_analyze/tests/specs/source/organizeImports/bare-grouping-with-predefined.options.json

@Conaclos Conaclos left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your changes! Looks awesome to me.

@Conaclos Conaclos changed the title feat(organizeImports): add :BARE: group matcher feat(organizeImports): add kind field to import matcher Apr 27, 2026
@codspeed-hq

codspeed-hq Bot commented Apr 27, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 196 skipped benchmarks1


Comparing georgephillips:feat/organize-imports-bare-matcher (92e7f51) with next (bae0710)

Open in CodSpeed

Footnotes

  1. 196 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@Conaclos Conaclos merged commit 9c7c6eb into biomejs:next Apr 27, 2026
29 of 30 checks passed
@georgephillips

Copy link
Copy Markdown
Contributor Author

@Conaclos thanks for merging! Any idea when this will be released?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants