Biome v2.5—500 Lint Rules, Plugin Code Fix, and Cross-File Linting
500 lint rules
Section titled “500 lint rules”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.
Cross-language lint rules
Section titled “Cross-language lint 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.cssnoRestrictedDependencies
Section titled “noRestrictedDependencies”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 rules
Section titled “Promoted rules”Promoted 73 nursery rules to stable groups.
Four rules were renamed as part of the promotion:
noFloatingClassesis nownoUnusedInstantiation, because the rule checks any discardednewexpression, not only classes.noMultiStris nownoMultilineString.useFindis nowuseArrayFind.useSpreadis nowuseSpreadOverApply, because the rule enforces spread call arguments overFunction.apply(), not array or object spread.
Correctness
Section titled “Correctness”Promoted the following rules to the correctness group:
noBeforeInteractiveScriptOutsideDocumentnoUnusedInstantiationuseInlineScriptId(recommended, Next.js domain)noVueVIfWithVFor(recommended, Vue domain)useVueValidVBind(recommended, Vue domain)useVueValidVElse(recommended, Vue domain)useVueValidVElseIf(recommended, Vue domain)useVueValidVHtml(recommended, Vue domain)useVueValidVIf(recommended, Vue domain)useVueValidVOn(recommended, Vue domain)useVueValidVText(recommended, Vue domain)useVueValidTemplateRoot(recommended, Vue domain)useVueValidVCloak(recommended, Vue domain)useVueValidVOnce(recommended, Vue domain)useVueValidVPre(recommended, Vue domain)useVueVForKey(recommended, Vue domain)noDuplicateAttributes(recommended)noDuplicateArgumentNames(recommended)noDuplicateInputFieldNames(recommended)noDuplicateVariableNames(recommended)noDuplicateEnumValueNames(recommended)useLoneAnonymousOperation(recommended)
Suspicious
Section titled “Suspicious”Promoted the following rules to the suspicious group:
noShadownoUnnecessaryConditionsnoParametersOnlyUsedInRecursionnoUnknownAttributeuseArraySortComparenoForInnoDuplicatedSpreadPropsnoEqualsToNullnoProto(recommended)noUndeclaredEnvVars(recommended, Turborepo domain)noReturnAssignnoDuplicateEnumValues(recommended)noVueArrowFuncInWatch(recommended, Vue domain)noNestedPromisesnoLeakedRendernoDeprecatedMediaType(recommended)noDuplicateGraphqlOperationNameuseRequiredScripts
Promoted the following rules to the style group:
useVueMultiWordComponentNames(recommended, Vue domain)useVueDefineMacrosOrdernoIncrementDecrementnoContinueuseSpreadOverApplynoTernarynoMultilineStringnoMultiAssignnoExcessiveClassesPerFilenoExcessiveLinesPerFilenoVueOptionsApiuseErrorCauseuseConsistentEnumValueTypeuseConsistentMethodSignaturesuseGlobalThisuseDestructuringuseVueHyphenatedAttributes(recommended, Vue domain)useVueConsistentVBindStyle(recommended, Vue domain)useVueConsistentVOnStyle(recommended, Vue domain)noHexColorsuseConsistentGraphqlDescriptionsnoRootTypeuseLoneExecutableDefinitionuseInputName
Complexity
Section titled “Complexity”Promoted the following rules to the complexity group:
Performance
Section titled “Performance”Promoted the following rules to the performance group:
Security
Section titled “Security”Promoted the following rules to the security group:
noScriptUrl(recommended)
Promoted the following rules to the a11y group:
noAmbiguousAnchorText(recommended)
Plugin improvements
Section titled “Plugin improvements”This release ships two important enhancements for GritQL plugins inside Biome, thanks to contributor @chocky335 .
Apply plugins to specific paths
Section titled “Apply plugins to specific paths”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:
{ "plugins": [ { "path": "./react-plugin.grit", "includes": ["src/components/**"] }, { "path": "./ts-only-plugin.grit", "includes": ["src/**/*.ts", "!src/**/*.test.ts"] } ]}Code fix for plugins
Section titled “Code fix for plugins”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:
`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)`}New delimiterSpacing formatter option
Section titled “New delimiterSpacing formatter option”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:
{ "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.
Watch mode
Section titled “Watch mode”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.
Go-to definition
Section titled “Go-to definition”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).
Linter preset option
Section titled “Linter preset option”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:
{ "linter": { "rules": { "recommended": true "preset": "recommended" } }}The biome upgrade command
Section titled “The biome upgrade command”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.
New concise reporter
Section titled “New concise reporter”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.Other new features and notable changes
Section titled “Other new features and notable changes”This release also includes several smaller features that are worth mentioning:
- Biome
lintandcheckare now ~13% faster. - Biome can now format and lint
.svgfiles. - The
organizeImportsassist action can now sort bare imports, match imports by syntactic kind withkind: "bare"andkind: "!bare", and group style imports with the new:STYLE:matcher. - New sorting assist actions are available for
package.jsonfields, 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.experimentalPnpmCatalogsis 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/excludein the same way as.gitignore, including in linked worktrees. Thank you @jongwan56 - The new
html.parser.vueoption enables Vue syntax parsing in regular.htmlfiles for projects that embed Vue syntax outside.vuefiles. - 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,useNumericSeparatorsgrouping options,noUndeclaredDependencies.bundleDependencies, andnoImplicitCoercions.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.
Translations
Section titled “Translations”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.
Chat with us
Section titled “Chat with us”Join our Discord server, and engage with the community. Chatting with the community and being part of the community is a form of contribution.
Code contributions
Section titled “Code contributions”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:
- Create new lint rules! We have so many rules that we haven’t implemented yet (ESLint, ESLint plugins, Next.js, Solid, etc.). We have a very extensive technical guide.
- Help us build Biome’s parsers for languages such as YAML, HTML, and Markdown. One interesting fact about Biome parsers is that they are recoverable parsers (error-resilient), which emit a CST instead of a classic AST.
- Implement new capabilities in our LSP (Language Server Protocol), or add new features in one of our editor extensions: VS Code, Zed, and JetBrains IntelliJ.
Financial help
Section titled “Financial help”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.
Copyright (c) 2023-present Biome Developers and Contributors.