Skip to content

Biome v2.5—500 Lint Rules, Plugin Code Fix, and Cross-File Linting

The brand of the project. It says "Biome, toolchain of the web" The brand of the project. It says "Biome, toolchain of the web"

With this release, Biome has surpassed 500 rules! It took us a very long time, but we’re thrilled about this milestone. The release also promotes over 70 rules spread among the Vue framework, type-aware rules, and more.

However, I want to provide the spotlight to three particular rules.

The rules noUndeclaredClasses and noUnusedClasses are two new nursery rules that use the powerful Biome module graph.

The rule noUnusedClasses analyses all the CSS classes defined in your project, and emits a diagnostic if the class isn’t used in any HTML-ish files or JSX files.

The rule noUndeclaredClasses analyses the class and className attributes of all your HTML-ish files and JSX files, and emits a diagnostic if a class hasn’t been defined in any of the CSS files or <style> tags. It also detects the :global() pseudo-selector.

What’s really informative about this rule is the detailed level of information provided by the diagnostic. The message provides a printed version of the imported files with their relative CSS files. Here’s an example, taken from our internal tests:

Button.jsx:4:28 lint/nursery/noUndeclaredClasses ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
i The CSS class btn-invalid is not defined in any imported stylesheet.
3 │ export function Button() {
> 4 │ return <button className="btn-invalid">This class does not exist</button>;
│ ^^^^^^^^^^^
5 │ }
6 │
i Referencing undefined classes often indicates a typo or a missing stylesheet, and will result in elements not being styled as intended.
i Checked import tree:
Button.jsx (this file)
└─ imported by:
├─ • Block.jsx
│ └─ imported by:
│ └─ • Page.jsx
│ └─ imported by:
│ └─ • App.jsx -> which imports app.css
└─ • Page.jsx
└─ imported by:
└─ • App.jsx -> which imports app.css

This rule noRestrictedDependencies is an homage to the e18e initiative and their https://replacements.fyi/ website & ban-dependencies lint rule.

This rule uses their data to detect dependencies that are deemed replaceable due to being deprecated, heavy, or because there’s a native alternative.

Promoted 73 nursery rules to stable groups.

Four rules were renamed as part of the promotion:

Promoted the following rules to the correctness group:

Promoted the following rules to the suspicious group:

Promoted the following rules to the style group:

Promoted the following rules to the complexity group:

Promoted the following rules to the performance group:

Promoted the following rules to the security group:

Promoted the following rules to the a11y group:

This release ships two important enhancements for GritQL plugins inside Biome, thanks to contributor @chocky335 .

You can now specify which paths a plugin should be applied to, using the includes setting. The option accepts globs and negative globs, so you can whitelist and denylist paths based on your needs:

In the following example, the first plugin only runs on files inside src/components/. The second plugin runs on all files that have the extension .ts under the src/ folder, except for files that end with .test.ts:

biome.json
{
"plugins": [
{
"path": "./react-plugin.grit",
"includes": ["src/components/**"]
},
{
"path": "./ts-only-plugin.grit",
"includes": ["src/**/*.ts", "!src/**/*.test.ts"]
}
]
}

Plugins can now declare a code fix, where appropriate. To register a code fix, you can use the => symbol and specify the new code.

The register_diagnostic function now accepts a fix_kind option where you can decide the entity of the fix.

By default, code fixes of plugins are marked as "unsafe", and they are applied when you run lint or check with --write --unsafe. You can explicitly use "safe", and the code fix is applied when you use --write via CLI.

The following example shows how to change a $call of a console.log function with a “safe” fix:

no-console-log.grit
`console.log($msg)` as $call where {
register_diagnostic(
span = $call,
message = "Use console.info instead of console.log.",
severity = "warn",
fix_kind = "safe"
),
$call => `console.info($msg)`
}

We are very excited about this new option. The team felt this option was worth having because it will help people with dyslexia. However, at the time of acceptance, the team didn’t have the resources to implement it.

Contributors @sirreal and @luisherranz are the real heroes here; they implemented the option for JavaScript, CSS, JSON, and GraphQL!

When enabled, the formatter adds spaces to language-delimiters:

biome.json
{
"formatter": {
"delimiterSpacing": true
}
}
function name(param) {
function name( param )
return [1, 3, 4]
return [ 1, 3, 4 ]
}

The option behaves differently based on the language, so make sure to consult the documentation of each language.

Since version v2.0, Biome has shipped with its own file watcher. Biome uses the file watcher to provide real-time diagnostics for type-aware lint rules and project lint rules. When a file changes, types and the module graph are updated.

With v2.5, we now expose the watcher to the lint, format, and check commands with the --watch option. When in watch mode, Biome listens to file changes that belong to the project and emits diagnostics while typing. Watch mode works only in read mode, which means you can’t use --fix or --write.

You can use BIOME_WATCHER_KIND and BIOME_WATCHER_POLLING to change the behavior of the watcher.

The Biome Language server now supports the “go-to definition” feature.

When the mouse cursor is hovering an entity (variable, CSS class, type, etc.), and the command CTRL + click is triggered, the editor jumps to where this entity is defined, if the language server can find it.

Here’s what Biome can resolve:

  • Variables and types used in JavaScript modules, defined in the same file or imported from another module.
  • JSX Components used in JavaScript modules, defined in the same file or imported from another module.
  • CSS classes used in JSX and HTML-ish files (Vue, Svelte and Astro), and defined in CSS files.
  • Components used in HTML-ish files and defined in other HTML-ish.
  • Variables used in HTML-ish files and defined in the same file or imported from another module (JavaScript or HTML-ish).

We added the linter.rules.preset option, giving you better control over which rules should be enabled by default.

Among the available presets, there’s the "all" preset that allows enabling all rules, except for the nursery rules.

As a result, the option recommended has been deprecated. Run biome migrate --write to update the configuration:

biome.json
{
"linter": {
"rules": {
"recommended": true
"preset": "recommended"
}
}
}

Added a biome upgrade command for standalone installations. It upgrades Homebrew installs with brew upgrade biome, updates manually installed binaries from the latest GitHub release, and tells npm users to upgrade with their package manager instead.

The CLI option --reporter now accepts a new concise, which prints a shorter version of Biome diagnostics. If you use a coding agent, this is the perfect reporter because it will save you tokens.

! index.ts:2:10: lint/correctness/noUnusedImports: Several of these imports are unused.
! main.ts:9:7: lint/correctness/noUnusedVariables: This variable f is unused.
× index.ts:8:5: lint/suspicious/noImplicitAnyLet: This variable implicitly has the any type.
× main.ts:2:10: lint/suspicious/noRedeclare: Shouldn't redeclare 'z'. Consider to delete it or rename it.

This release also includes several smaller features that are worth mentioning:

  • Biome lint and check are now ~13% faster.
  • Biome can now format and lint .svg files.
  • The organizeImports assist action can now sort bare imports, match imports by syntactic kind with kind: "bare" and kind: "!bare", and group style imports with the new :STYLE: matcher.
  • New sorting assist actions are available for package.json fields, HTML attributes, TypeScript and GraphQL enum members, GraphQL selection sets, and GraphQL type fields.
  • Biome can now resolve dependencies from default and named pnpm catalogs when javascript.resolver.experimentalPnpmCatalogs is enabled. This feature uses our internal YAML parser, which is soon to be stable. Thank you @apple-yagi
  • When VCS ignore files are enabled, Biome now respects .git/info/exclude in the same way as .gitignore, including in linked worktrees. Thank you @jongwan56
  • The new html.parser.vue option enables Vue syntax parsing in regular .html files for projects that embed Vue syntax outside .vue files.
  • The JavaScript API now exposes spanInBytesToSpanInCodeUnits, which converts byte-based diagnostic spans into UTF-16 code unit spans for JavaScript strings.
  • Several rules received new options, including noUnusedVariables.ignore, useIterableCallbackReturn.allowImplicit, useNumericSeparators grouping options, noUndeclaredDependencies.bundleDependencies, and noImplicitCoercions.allowDoubleNegation.

I like where this is going, how can I help?

Section titled “I like where this is going, how can I help?”

I want to remind you that Biome is a project led by volunteers who like programming, open-source, and embrace the Biome philosophy, so any help is welcome.

If you are familiar with Biome and would like to contribute to its outreach, you can assist us by translating the website into your native language. In this dashboard, you can check the supported languages and whether they are up-to-date.

Join our Discord server, and engage with the community. Chatting with the community and being part of the community is a form of contribution.

If you like the technical aspects of the project, and you want to make your way into the Rust language, or practice your knowledge around parsers, compilers, analysers, etc., Biome is the project for you!

There are numerous aspects to explore; I assure you that you won’t get bored. Here is a small list of the things you can start with:

If you believe in the future of the project, you can also help with a financial contribution via Open Collective or GitHub Sponsors.

Additionally, the project provides an enterprise support program where you as a company can employ one of the core contributors to work on a specific aspect of the Biome toolchain.