Skip to content

Version History

2.6.2 Latest

Minor Changes

  • #11479 5c60ba8 Thanks @nhedger! - Added a --git flag to biome init. The flag enables Git VCS integration and sets vcs.useIgnoreFile to true in the generated configuration, even when the command runs outside a Git repository.

  • #11742 3a1356d Thanks @Netail! - Added the biome migrate stylelint command. It imports a Stylelint configuration and its ignore settings into your Biome configuration, mapping Stylelint rules to their Biome equivalents. Pass --write to apply the changes, and --include-inspired or --include-nursery to migrate additional rules.

  • #12318 774b5b5 Thanks @Netail! - Added the new assist action useGraphqlSortedDefinitions, which sorts the named definitions of a GraphQL document, such as types, type extensions, directives, fragments, and operations.

    Invalid:

    type User {
    id: ID!
    }
    enum Role {
    ADMIN
    USER
    }
    directive @auth on FIELD_DEFINITION
  • #6886 3479d15 Thanks @christymcgrory! - biome rage now shows the resolved files.includes and files.ignoreUnknown configuration.

  • #11102 0874b7f Thanks @gameroman! - Added support for useTailwindSortedClasses rule in HTML-ish files.

  • #10711 40de5a0 Thanks @soconnor-seeq! - Adds a new resolutionKind option for object-syntax plugin entries.

    This allows shared Biome configs in monorepos to load local Grit plugins from the package that declares them instead of resolving plugin paths from the consuming project.

    {
    "plugins": [
    { "path": "./grit/no-debugger.grit", "resolutionKind": "config" },
    { "path": "local-plugin.grit", "resolutionKind": "project" },
    { "path": "local-plugin.grit" }
    ]
    }

    Use resolutionKind: "config" to resolve the plugin from the configuration file that declares it. Use resolutionKind: "project" to resolve the plugin from the consuming project. When omitted, resolutionKind defaults to project. This only affects plugin resolution; includes still apply to end-user project files as usual.

  • #11432 4befd08 Thanks @ematipico! - Added biome inspect config to show resolved configuration values and their origins as human-readable diagnostics, including matching overrides.

    Configuration files can also extend other configurations up to ten levels deep. Repeated references to the same resolved file are ignored with an information diagnostic, while resolving multiple versions of one extended package produces an error.

  • #11477 ab05554 Thanks @nhedger! - Added automatic Git VCS integration when running biome init inside a Git repository, including from nested directories and Git worktrees. The generated configuration sets vcs.enabled and vcs.useIgnoreFile to true, with vcs.clientKind set to "git".

  • #11451 1ac4b09 Thanks @ematipico! - #### YAML is now supported

    Added support for formatting YAML files, both .yaml and .yml extensions

  • #11432 4befd08 Thanks @ematipico! - Configuration files can now recursively extend local files and npm package configurations up to ten levels. Every repeated reference participates in the merge, while resolving multiple versions of one extended package produces an error.

  • #11713 ac6a640 Thanks @dyc3! - Added support for Grit range() patterns, addressing #8454. Plugins can filter syntax nodes by optional start_line, start_column, end_line, and end_column bounds.

    For example, this pattern matches function calls contained entirely on line 2.

    range(start_line=2, end_line=2) as $call where {
    $call <: call_expression()
    }
  • #11244 0628b53 Thanks @ematipico! - Added the new CLI option --profile-type-inference to the commands lint and check.

    The profiler is designed to track the execution time of internal queries and operations, group them by file, and sort them from the most expensive to the least expensive.

    Terminal window
    biome lint --profile-type-inference
    profiler ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    i Type inference profile (Biome <version>)
    src/index.ts
    Requests (top 5 of 12 groups; 28 source records; 46 executions)
    1. Promise classification <- nursery/noFloatingPromises
    ...
    Queries (top 8 of 25 groups; 61 source records; 94 executions)
    1. Promises / infer_expression_is_promise
    ...
    Whole-module inference (top 1 of 1 group; 1 source record; 1 execution)
    1. Import depth limit
    ...
    68 files omitted.
    ℹ To show all the information, use the --verbose option. The output might be very verbose, so it's advised to analyze a single file.
    $ biome lint --profile-type-inference --verbose ./path/to/file.ts

    Use --verbose to include detailed timings, attribution, breadth, and implementation references for the top source records in each file.

  • #12108 4fcb880 Thanks @ematipico! - Promoted 59 nursery rules to stable groups.

    Correctness

    Promoted the following rules to the correctness group with error severity. All of them are recommended:

    Suspicious

    Promoted the following rules to the suspicious group:

    Security

    Promoted useIframeSandbox to the security group with error severity.

    Performance

    Promoted the recommended rule useDomNodeTextContent to the performance group with warning severity.

    Complexity

    Promoted the following rules to the complexity group:

    Style

    Promoted the following rules to the style group with information severity:

  • #12206 0f984fb Thanks @dyc3! - Added the ignoreUsingDeclarations option to noUnusedVariables. When enabled, the rule no longer reports unused variables declared with using or await using, because these declarations clean up their value automatically even when the code never reads it.

    {
    "linter": {
    "rules": {
    "correctness": {
    "noUnusedVariables": {
    "options": { "ignoreUsingDeclarations": true }
    }
    }
    }
    }
    }
  • #12424 6a41490 Thanks @siketyan! - Removed the javascript.parser.gritMetavariables option. It was intended for internal use only and had no effect on how Biome parses JavaScript and TypeScript files. Biome now reports it as an unknown key and exits with a configuration error, so remove it from your configuration file.

  • #12055 54870c0 Thanks @ff1451! - Added the new assist action useGraphqlSortedVariables, which sorts the variable definitions of GraphQL operations.

    Invalid:

    query GetUser($userId: ID!, $includePosts: Boolean = false, $after: String) {
    user(id: $userId) {
    name
    }
    }
  • #12149 83df808 Thanks @dyc3! - Added the tailwind.stylesheet option, which points Biome at the CSS file that holds your Tailwind CSS configuration. useTailwindSortedClasses reads the @theme, @utility, and @custom-variant rules in that file and in the local files it imports, so your own theme values, utilities, and variants sort correctly.

    biome.json
    {
    "tailwind": { "stylesheet": "./src/app.css" }
    }
    src/app.css
    @import "tailwindcss";
    @theme {
    --color-brand: #3b82f6;
    }
    @utility content-auto {
    content-visibility: auto;
    }
    @custom-variant theme-midnight (&:where([data-theme="midnight"] *));
  • #12206 0f984fb Thanks @dyc3! - Added migration of the ignoreRestSiblings and ignoreUsingDeclarations options of no-unused-vars, @typescript-eslint/no-unused-vars, and unused-imports/no-unused-vars to noUnusedVariables in biome migrate eslint. Options that the ESLint configuration omits are set to ESLint’s defaults, so the migrated rule reports the same variables. For example, "no-unused-vars": "error" now migrates ignoreRestSiblings as false.

  • #11709 32d12cb Thanks @ematipico! - Added the new command biome inspect file, which returns developer information for the given file.

    Terminal window
    biome inspect file path/to/file.ts

    By default, the command returns the CST of the file. The command accepts the following arguments:

    • --ast, which returns the AST of a file.
    • --ir, which returns the formatting IR of a file.
    • --semantic, which returns the semantic model information of a file, if available.
  • #12221 cb571af Thanks @dyc3! - The rule noEmptySource now supports Markdown files. It reports files that contain only blank lines and HTML comments. For example, the following file is now reported:

    <!-- TODO -->
  • #12214 8955b56 Thanks @dyc3! - The rule noEmptySource now supports YAML files. It reports files with no documents and each document that has no content. For example, the second document in the following code is now reported:

    name: foo
    ---
  • #12033 8669e53 Thanks @dyc3! - Added the ignoreElements option to noRedundantRoles. Elements listed in this option are not checked by the rule.

    {
    "linter": {
    "rules": {
    "a11y": {
    "noRedundantRoles": {
    "options": {
    "ignoreElements": ["button"]
    }
    }
    }
    }
    }
    }
  • #12084 04e3750 Thanks @dyc3! - The rule noRestrictedElements now supports HTML, Vue, Svelte, and Astro files. For example, with { "elements": { "marquee": "Use CSS animations instead." } }, the following code is now reported:

    <marquee>Breaking news</marquee>
  • #11408 5a870f2 Thanks @ematipico! - Added support for distributing named Grit rules, rule presets, and configurations through packages that have a manifest file.

    The field plugins is used to export named Grit plugins, and configs is used to export named configurations. Package entries must select a named export: package/rule, package/presets/preset, or package/configs/config. Bare package entries aren’t supported. Rules consumed from another package are exposed under the consuming package’s name, while package authors continue to reference them by their source package inside the manifest. Configurations consumed from another package are also exposed under the consuming package’s name.

    biome-manifest.json
    {
    "$schema": "./node_modules/@biomejs/biome/manifest_schema.json",
    "version": 1,
    "plugins": {
    "rules": [
    "@org/shared-rules/noDeprecatedApi",
    "@org/strict-rules/presets/recommended",
    { "useCompanyLogger": "./rules/useCompanyLogger.grit" }
    ],
    "presets": {
    "recommended": [
    "useCompanyLogger",
    "@org/shared-rules/noDeprecatedApi"
    ]
    }
    },
    "configs": [
    "@org/shared-configs/configs/recommended",
    { "base": "./configs/base.json" }
    ]
    }

    The package must publish the manifest and every referenced file, for example, through the files field in package.json. Plugin authors can expose their manifest through the biome condition with ./biome-manifest.json as its value.

    package.json
    {
    "name": "@org/biome-plugin",
    "exports": {
    "biome": "./biome-manifest.json",
    "default": "./index.js"
    },
    "files": ["biome-manifest.json", "rules", "configs"]
    }

    And then, you can load the rules in your biome.json:

    biome.json
    {
    "plugins": [
    "@org/biome-plugin/useCompanyLogger",
    "@org/biome-plugin/presets/recommended"
    ]
    }

    For more information on how to create a plugin to distribute via npm, check the relative guide.

  • #12264 90f2eaf Thanks @dyc3! - Fixed #11082: glob patterns in the includes option of a plugin are now matched against file paths relative to the directory of the configuration file, the same as files.includes and overrides[].includes.

    Previously, the patterns were matched against the absolute file path, so a pattern such as packages/**/src/** matched no files and the plugin never ran.

    {
    "plugins": [
    { "path": "./my-plugin.grit", "includes": ["packages/**/src/**"] }
    ]
    }

    Patterns that start with **/ keep working. Patterns that contain the absolute path of the project no longer match, and must be rewritten as relative patterns.

  • #11133 1df00de Thanks @dlecan! - Added Android (Termux) support to the npm distribution (#1340). npm install @biomejs/biome now works on Termux through the new @biomejs/cli-android-arm64 and @biomejs/cli-android-x64 packages, which reuse the static musl binaries.

  • #12132 c5fadc4 Thanks @ematipico! - Added SCSS support throughout the toolchain. Biome can now format and lint .scss files, and recognizes .module.scss files as CSS Modules.

  • #10837 754432c Thanks @JamBalaya56562! - Added support for Google Apps Script (.gs) files.

    They are parsed as JavaScript scripts, so ES module import/export statements are reported as errors (Apps Script has no module system). Common Apps Script service globals (e.g. SpreadsheetApp, DriveApp, Logger) are recognized by noUndeclaredVariables and noGlobalAssign, and the list can be extended through the javascript.globals option.

  • #11126 fa63050 Thanks @Punisheroot! - Biome now recognizes files with the .htm extension as HTML.

  • #12291 6c6619e Thanks @dyc3! - Changed the dependency that enables the tailwind domain automatically. The domain is now enabled when package.json declares tailwindcss v4 or later, instead of v3 or later, because we are currently targeting Tailwind v4 support. The domain can always be enabled manually.

  • #11394 de1ad4c Thanks @ematipico! - #### Markdown is now supported

    Added support for linting and formatting Markdown files. The first version of Biome Markdown parser has 100% compatibility against CommonMark.

    Other variants of Markdown, such as GitHub Flavored Markdown (GFM) aren’t currently supported; however, we plan to support them soon.

    Formatter and Linter are automatically enabled by default.

    Markdown parser

    The parser comes with an opt-in option to enable frontmatter parsing. When enabled, the first thematic break --- is considered the opening fence of the frontmatter:

    biome.json
    {
    "markdown": {
    "parser": {
    "frontmatter": true
    }
    }
    }
    ---
    title: Lorem ipsum
    ---

    The parser also comes with an opt-in option called cjkFriendlyEmphasis. When enabled, the parser follows the CommonMark CJK-friendly amendments and recognizes emphasis markers, such as **, placed directly next to Chinese, Japanese, or Korean text. Enable it only when the tool that renders your Markdown supports the same amendments, because the option changes which text Biome formats and lints as emphasis:

    biome.json
    {
    "markdown": {
    "parser": {
    "cjkFriendlyEmphasis": true
    }
    }
    }

    With the option enabled, the following text is parsed as bold, instead of as literal **:

    **このアスタリスクは強調記号として認識されず、そのまま表示されます。**この文のせいで。
    Markdown formatter

    The formatter has great compatibility with Prettier formatting, more than 90% detected by our infrastructure. The formatter ships with a new option called proseWrap, that allows controlling how the Biome formatter should wrap paragraphs.

    For example, when proseWrap is set to always, the formatter will wrap the paragraph to match the configured lineWidth:

    biome.json
    {
    "markdown": {
    "formatter": {
    "lineWidth": 20,
    "proseWrap": "always"
    }
    }
    }
    Very tiny line width, so
    the paragraph is more
    compact.
    Markdown linter

    The linter ships with a few rules inspired by markdownlint. We plan to ship more rules in the upcoming releases.

    Some rules you can start using already:

    Markdown snippets

    Biome recognizes the code inside fenced code blocks, frontmatter, and HTML blocks as embedded languages, and formats it with the formatter of its language and your configuration:

    ```js
    const value = {a:1,b:2}
    const value = { a: 1, b: 2 };
    ```

    HTML blocks are formatted only when the HTML formatter is enabled. A block with a syntax error stays as written, and Biome still formats the rest of the file.

    The option markdown.formatter.formatEmbeds controls this behavior, and it’s enabled by default. Set it to false to keep the code of these blocks as written:

    biome.json
    {
    "markdown": {
    "formatter": {
    "formatEmbeds": false
    }
    }
    }

    The command biome migrate prettier migrates the Prettier option embeddedLanguageFormatting to markdown.formatter.formatEmbeds.

    The option markdown.analyzeEmbeds runs the linter and assist actions on the same blocks, with the rules and actions of their language, and reports their syntax errors. It’s disabled by default, because fenced code blocks usually contain examples or partial code:

    biome.json
    {
    "markdown": {
    "analyzeEmbeds": true
    }
    }
  • #11634 04c3f5d Thanks @ematipico! - Added biome inspect plugins to list resolved rules by plugin and importing preset without loading or executing plugin code. Include globs show which files each import targets, and --path selects a target file. Fixed Markdown linter overrides and their configuration inspection.

  • #12282 a3fa0d6 Thanks @ff1451! - Added the new assist action useGraphqlSortedArguments, which sorts the arguments of GraphQL fields and directives, and the arguments declared by field and directive definitions.

    Invalid:

    query {
    users(orderBy: NAME, first: 10, after: "cursor") {
    name
    }
    }
  • #10631 1077238 Thanks @thibaut-pro! - Added the sortFirst option to the useSortedAttributes action (JSX and HTML). It takes a list of attribute names that are sorted before all other attributes, in the order given, while the remaining attributes keep their usual sort. This is useful to keep attributes such as key first.

    With the following configuration:

    {
    "assist": {
    "actions": {
    "source": {
    "useSortedAttributes": {
    "level": "on",
    "options": { "sortFirst": ["key"] }
    }
    }
    }
    }
    }

    The attributes are reordered so that key comes first:

    // Before
    <div className={styles.investment} key={investment.id} />;
    // After
    <div key={investment.id} className={styles.investment} />;
  • #10424 189b7ba Thanks @LinusU! - Added the partitionByNewLine option to the useSortedInterfaceMembers assist. When enabled, members separated by a blank line are kept in their own section and sorted only within that section, preserving the logical groupings the author intentionally introduced with empty lines.

Patch Changes

  • #11953 354f74c Thanks @Th3S4mur41! - Enhanced the nursery rule useLogicalProperties to check physical CSS values in addition to physical property names.

    The rule recognizes physical values in frame-sizing, anchor-size(), and anchor(), with direction-aware replacements controlled by the existing direction option.

    For justify-content: left and justify-content: right, the rule reports a diagnostic without an automatic replacement because the correct logical alignment depends on the layout axis.

    This is a second part of the rule covering parts of #9034

  • #12175 3730b32 Thanks @dyc3! - Added the nursery rule noDoubleComparison, which reports two comparisons of the same values that can be combined into one. For example, x === y || x < y can be written as x <= y.

  • #12166 25eb10c Thanks @dyc3! - Added the new nursery rule noNestedSwitch, which disallows switch statements inside other switch statements.

    switch (n) {
    case 0:
    switch (m) {} // reported
    }
  • #12170 872ac9f Thanks @dyc3! - Added the new nursery rule noNestedTemplateLiterals, which disallows template literals written inside other template literals.

    const message = `I have ${color ? `${count} ${color}` : count} apples`;
  • #12111 f72375f Thanks @dyc3! - Added the nursery rule noObjectConstructor, which disallows calling the Object constructor without arguments, such as new Object() or Object(). Biome can replace these calls with the object literal {}.

  • #12254 cb88f07 Thanks @dyc3! - Added the nursery rule noSvelteClassDirective, which reports Svelte’s class: directive. Since Svelte 5.16, the class attribute accepts objects and arrays, and Svelte recommends them over the directive.

    <div class:active={isActive}></div>
  • #12186 30aa7d2 Thanks @dyc3! - Added the new nursery rule noUselessDateGetTime, which reports dates converted with .getTime() before being passed to the Date constructor. Passing the date directly, as in new Date(date), already creates a copy.

    const copy = new Date(date.getTime());
  • #12229 5f8ea6c Thanks @dyc3! - Added the nursery rule noVueLifecycleAfterAwait, which reports Vue lifecycle hooks, such as onMounted(), that are registered after an await in a component’s setup() function. Vue doesn’t run these hooks.

    <script>
    import { onMounted } from "vue";
    export default {
    async setup() {
    await fetchUser();
    onMounted(() => {});
    },
    };
    </script>
  • #12077 3dd096f Thanks @dyc3! - Added the nursery rule noVueRootVIf, which disallows v-if on the only root element of a Vue component template.

    <template>
    <div v-if="visible">Content</div>
    </template>
  • #12136 b92638b Thanks @dyc3! - Added the nursery rule noVueShadowNativeEvents, which reports Vue component events named after built-in browser events, such as click or keydown.

    <script setup>
    // Invalid
    const emit = defineEmits(["click"]);
    emit("click");
    </script>
  • #12135 31a29b6 Thanks @dyc3! - Added the nursery rule noVueVHtml, which disallows Vue’s v-html directive. v-html inserts its value into the page as HTML, so text from users can run code on your page (cross-site scripting, or XSS).

    <template>
    <div v-html="content"></div>
    </template>
  • #12134 60329af Thanks @dyc3! - Added the new nursery rule noVueVText, which disallows Vue’s v-text directive in favor of text interpolation.

    For example, the following snippet triggers the rule:

    <div v-text="message"></div>
  • #9817 241b861 Thanks @dyc3! - Added the new nursery rule noZeroFractions, which disallows numeric literals with redundant zero fractions or dangling dots such as 1.0 and 1.. Biome can now simplify these literals to shorter equivalents.

  • #12109 b45d426 Thanks @dyc3! - Added the nursery rule useConsistentJsonFileRead, which enforces a consistent way of reading JSON files before passing them to JSON.parse(). By default, it reports files read as a Buffer; set readAs to "buffer" to report files read as a UTF-8 string instead.

    const packageJson = JSON.parse(await fs.readFile("./package.json"));
  • #12129 b57f874 Thanks @dyc3! - Added the new nursery rule useMatchingFunctionName, which requires a named function expression to have the same name as the variable or property it’s assigned to.

    // The function name `bar` doesn't match the variable name `foo`.
    const foo = function bar() {};
  • #12169 0f1ae53 Thanks @dyc3! - Added the new nursery rule useSimplifiedBooleanReturn, which reports if statements that only return true or false and can be replaced by returning the condition directly.

    function isPositive(value) {
    if (value > 0) {
    return true;
    }
    return false;
    }
  • #11957 b2db9f2 Thanks @dyc3! - Added the nursery rule useSvelteKitResolve, which requires internal links and calls to goto(), pushState(), and replaceState() to use paths built with SvelteKit’s resolve().

    <a href="/about">About</a>
  • #12235 b7c1870 Thanks @dyc3! - Added the nursery rule useSvelteReactiveClasses, which reports Date, Map, Set, URL, and URLSearchParams objects that are modified in Svelte files or exported from Svelte modules. Svelte doesn’t detect changes made inside these built-in objects, so the rule suggests the matching classes from svelte/reactivity.

    <script>
    const tags = new Set();
    function addTag(tag) {
    tags.add(tag);
    }
    </script>
  • #12172 b47674d Thanks @dyc3! - Added the nursery rule useSvelteShorthandDirective, which enforces the shorthand syntax for Svelte bind:, class:, and style: directives. For example, <input bind:value={value} /> is now reported and can be fixed to <input bind:value />.

  • #12137 4aeca96 Thanks @dyc3! - Added the nursery rule useVueMatchingComponentFileName, which requires the name of a Vue component to match its file name. The following component in MyComponent.vue is now reported:

    <script>
    export default {
    name: "MyButton",
    };
    </script>
  • #12075 de453c0 Thanks @tionway! - Fixed #11902: biome migrate --write now successfully migrates empty nested configuration files without introducing an invalid trailing comma.

  • #12149 83df808 Thanks @dyc3! - Switched useTailwindSortedClasses to the Tailwind CSS v4 class order. The rule now checks the attributes and functions set in the top-level tailwind configuration, and its own attributes and functions options were removed. Run biome migrate --write to move them to tailwind.attributes, tailwind.mergeFunctions, and tailwind.variantFunctions.

    The rule is now part of the project domain, so enabling it makes Biome scan your project.

  • #12202 9f1cfde Thanks @dyc3! - Fixed #12184: useImportExtensions no longer misses relative imports with .jsx or .tsx extensions when using extensionMappings or forceJsExtensions. The rule reports these imports when the configured replacement resolves to an existing file.

  • #12081 fd4df34 Thanks @dyc3! - Added the nursery rule useCapitalizedConstructors, a port of ESLint’s new-cap. It reports new expressions whose callee name starts with a lowercase letter, and calls without new whose callee name starts with an uppercase letter.

    const friend = new person();
    const other = Person();
  • #12061 36aa8c3 Thanks @dyc3! - Added the nursery rule noExcessiveStatementsPerFunction, a port of ESLint’s max-statements. It reports functions that contain more statements than the configured max (default: 10).

  • #12053 b91a445 Thanks @dyc3! - Added the nursery rule useVueConsistentEventHyphenation, a port of vue/v-on-event-hyphenation. It enforces a consistent hyphenation style for event names in v-on directives on custom components. By default, hyphenated names are required, so <MyComponent @customEvent="handler" /> is reported.

  • #12327 2266df2 Thanks @dyc3! - Improved the performance of the Biome CLI on projects with many files during cleanup.

  • #12110 cf408d5 Thanks @dyc3! - Added the recommended nursery rule noOctal, which disallows legacy octal literals such as 071 and decimal literals with leading zeros such as 08. Use 0o71 for explicit octal notation.

  • #12066 d13d1ba Thanks @dyc3! - Updated rule source metadata.

  • #12393 c80f28c Thanks @ematipico! - Fixed #12389: the Svelte formatter no longer duplicates binding names when converting redundant bind: initializers to shorthand syntax.

    <input bind:valuevalue>
    <input bind:value>
  • #12325 31a2fb0 Thanks @dyc3! - Improved the performance of the formatter in files with little or no comments.

  • #12223 9f246d7 Thanks @dyc3! - Added the nursery rule noAccidentalBitwiseOperators, which reports the bitwise operators &, |, and |= where the logical operators &&, ||, and ||= were likely intended.

    if (obj & obj.prop) {}
    options = options | {};
  • #12052 1a6fb41 Thanks @dyc3! - Added the nursery rule noVueBooleanDefault, which disallows default values for Boolean props in Vue components. Vue already defaults an absent Boolean prop to false.

    <script setup lang="ts">
    withDefaults(defineProps<{ disabled?: boolean }>(), {
    disabled: true,
    });
    </script>
  • #12177 215288b Thanks @MustafaKemal0146! - Fixed #4517: noConfusingVoidType no longer reports void in union types that contain only void and never, such as void | never.

  • #12461 259a84e Thanks @ematipico! - Fixed GritQL JSON snippets so patterns such as { $key: $value } can now bind metavariables in object keys and values.

  • #12185 08cfbd2 Thanks @dyc3! - Fixed the Tailwind CSS parser splitting dashed utility names. Classes such as drop-shadow!, flex-grow-0, and max-w-screen-lg now keep drop-shadow, flex-grow, and max-w-screen as their utility names.

  • #12444 ef61338 Thanks @ematipico! - Fixed the match count reported by biome search. The summary and the matches field of the JSON reporters now count every match, instead of the number of searched files.

  • #12292 4eadd57 Thanks @ematipico! - Fixed withastro/compiler-rs#199: processing instructions such as <?xml version="1.0" encoding="UTF-8"?> no longer cause parse errors in Astro and Vue files.

  • #12152 7a573de Thanks @dyc3! - Fixed the Tailwind class parser rejecting arbitrary variants whose selector starts with an attribute selector. Classes like [[data-variant=legend]+&]:-mt-1.5 and group-has-[[data-slot=item-description]]/item:self-start now parse correctly.

  • #12155 851d803 Thanks @erenbati! - biomejs/biome#11949: Fixed noSelfAssign to detect self-assignments when equivalent property names use dot and bracket notation, including numeric and string-literal keys.

  • #12112 db7ede6 Thanks @dyc3! - Added the nursery rule noPromiseExecutorReturn, which disallows returning values from the function passed to new Promise(), because new Promise() ignores them.

    new Promise((resolve) => resolve(1));
  • #12404 fcd3d74 Thanks @dyc3! - Fixed #12374: biome migrate eslint now preserves eqeqeq’s null comparison options when configuring noDoubleEquals. Migrating the default ESLint configuration now sets ignoreNull: false, so comparisons such as x == null remain reported.

  • #12426 0fe3d2b Thanks @dyc3! - Improved the performance of linting by computing the range of a syntax node without allocating its edge tokens.

  • #12441 c4199d7 Thanks @dyc3! - Improved the performance of commands that run with --use-server, such as biome check --use-server. The Biome daemon now processes the files of a command on all available CPU cores, instead of at most four files at a time.

  • #12183 c08e3c3 Thanks @dyc3! - Fixed the Tailwind class parser rejecting CSS variable shorthands whose fallback contains parentheses. Classes like bg-(--a,var(--b)), bg-(--a,calc(1px)), and after:bg-(--drawer-bleed-background,var(--color-popover)) now parse correctly, including in variants (max-(--a,var(--b)):flex) and modifiers (bg-red-500/(--a,var(--b))).

  • #12441 c4199d7 Thanks @dyc3! - Fixed biome stop waiting 15 seconds and then failing with “the request to the remote workspace timed out”, even though the Biome daemon had stopped. The command now returns as soon as the daemon shuts down.

  • #12117 ae0cabd Thanks @dyc3! - Fixed noUnreachable, noUnreachableSuper, useGetterReturn, useIterableCallbackReturn and noFallthroughSwitchClause skipping functions that contain an unlabeled break or continue inside a labeled loop or switch.

    For example, noUnreachable now reports afterBreak() here:

    function f(xs) {
    outer: for (const x of xs) {
    break;
    afterBreak();
    }
    }
  • #11093 78c992f Thanks @dyc3! - Fixed CSS formatting for multiline function arguments preceded by comments:

    .example {
    value: outer(
    1,
    /* comment */
    nested(
    first,
    second
    )
    first,
    second
    )
    );
    }
  • #12474 e55a750 Thanks @dyc3! - Fixed #12471: a comment above a CSS rule no longer removes the indentation of the rule’s wrapped selector.

    /* comment */
    .sidebar-navigation
    .sidebar-navigation-item-with-a-long-descriptive-class-name {
    color: red;
    }
  • #12180 6ddce62 Thanks @dyc3! - Fixed #12179: the CSS parser now accepts Tailwind @custom-variant names that start with a digit, such as the 2xl breakpoint.

    @custom-variant 2xl (@container page (width >= 96rem));
  • #12440 6636760 Thanks @dyc3! - Fixed #12433: with html.experimentalFullSupportEnabled, the editor now offers code actions for diagnostics inside embedded code, such as expressions and scripts in Astro, Vue, Svelte, and HTML files.

  • #12480 00771b4 Thanks @vidigoat! - Fixed the formatter duplicating comments inside an empty object, array, or class that is spread in JSX. Each formatting pass added another copy of the comment.

    <div
    {
    ...{
    /* comment */
    }/* comment */
    }
    }
    />
  • #11093 78c992f Thanks @dyc3! - Fixed #10131: Biome now correctly parses curried arrow functions in ternary consequents when the inner arrow’s parameters use a destructuring pattern, e.g. cond ? (x) => ({ a, b }) => body : alt.

  • #11563 a4e6528 Thanks @santichausis! - Fixed #11419: noAdjacentSpacesInRegex no longer applies its fix inside a regex character class ([...]), where it was changing what the class matches instead of just clarifying the spacing.

    For example, Biome used to “fix” this regex into matching different characters:

    const before = /[\s ]/;
    // Biome used to rewrite this to /[\s {3}]/, which is a different regex:
    // {3} isn't a quantifier inside a character class, it's three more
    // characters to match — the fixed regex now also matches "1", "2", "{", "}".

    Adjacent spaces outside a character class are still merged into a quantifier as before.

  • #12451 c71fe6a Thanks @dyc3! - Fixed #12445: noTsIgnore no longer reports comments that merely mention @ts-ignore, such as // see the note on @ts-ignore below or JSDoc prose. The rule now follows TypeScript and only reports @ts-ignore at the start of a comment, or at the start of the last line of a block comment. The fix now rewrites only the directive.

  • #12266 33fef85 Thanks @JamBalaya56562! - Fixed #12265: Biome now uses the correct defaults for semi and singleQuote when migrating a Prettier configuration that doesn’t set them. The migrated configuration uses "semicolons": "always" and "quoteStyle": "double", matching Prettier’s defaults.

  • #11093 78c992f Thanks @dyc3! - Fixed #10515: biome check --write was not idempotent on Svelte files — multi-line template literals in <script> blocks and block comments in <style> blocks gained an extra indent level on every run.

  • #12246 b073c2f Thanks @dyc3! - Fixed the parsing of Svelte class: and style: directive names. Modifiers such as |important in style:color|important are now parsed as modifiers instead of being part of the name, and / in <input class:invalid/> now closes the tag.

  • #12246 b073c2f Thanks @dyc3! - Fixed #11215: noUnusedVariables no longer reports variables in Svelte components that are only used through the class: and style: directive shorthands, such as <div class:active> and <div style:color>.

  • #12104 2bb4838 Thanks @dyc3! - Fixed #12098: useImportExtensions now reports relative imports without extensions in Vue, Svelte, and Astro files when html.experimentalFullSupportEnabled is enabled.

  • #12441 c4199d7 Thanks @dyc3! - Fixed commands that run with --use-server reporting cancelled because of pending write for some files instead of checking them. Biome now sends a cancelled request again, so the command reports the same diagnostics as a run without --use-server.

  • #12242 0ec0a46 Thanks @dyc3! - Fixed a false positive in useVueConsistentDefinePropsDeclaration. The rule no longer reports calls such as a.b.c(0) or calls to a locally imported defineProps function. It now only checks calls to Vue’s defineProps macro.

  • #12244 fe217c6 Thanks @dyc3! - Fixed a false positive in noVueDuplicateKeys where variables created from props wrapped in withDefaults() were reported as duplicates of those props. The following code is now valid:

    <script setup lang="ts">
    import { toRefs } from "vue";
    const props = withDefaults(defineProps<{ text: string }>(), {});
    const { text } = toRefs(props);
    </script>
  • #12240 e26a76b Thanks @latent-9! - Fixed a false negative in noUnreachable and useGetterReturn: for loops whose test is a truthy literal, such as for (; true ;), are now handled as infinite loops, consistent with while and do...while.

  • #12212 513f7db Thanks @siketyan! - Added support for formatting CSS-in-JS templates with interpolations, such as the ones of styled-components and Emotion, when javascript.experimentalEmbeddedSnippetsEnabled is enabled. The interpolations are formatted as JavaScript, and the text glued to them is kept as is.

    const Button = styled.button`
    color:${ (props)=>props.color };
    width : ${({width})=>width}px;
    color: ${(props) => props.color};
    width: ${({ width }) => width}px;
    `;
  • #12131 05e3036 Thanks @dyc3! - The HTML formatter now formats the JavaScript inside quoted Vue directive values, such as :prop, v-bind, v-if, @event handlers that reference a function, and v-slot. These values and v-for values are written between double quotes, and the strings inside them use single quotes.

    <div :bar="foo + 1" v-if='isVisible&&ready'></div>
    <li v-for='item in ["a","b"]'></li>
    <div :bar="foo + 1" v-if="isVisible && ready"></div>
    <li v-for="item in ['a', 'b']"></li>
  • #12218 73e169e Thanks @dyc3! - The HTML formatter now formats Vue @event handlers that contain statements. A single expression stays on one line without a semicolon, and multiple statements are written on lines of their own inside the quotes, like Prettier does.

    <button @click="count += 1"></button>
    <button @click="save( );close( )"></button>
    <button @click="count += 1"></button>
    <button
    @click="
    save();
    close();
    "
    ></button>

    The parser also accepts handlers that start with a statement, such as @click="if (ok) save()". The formatter adds the ; that Vue needs to compile them as the body of a function:

    <button @click="if (ok) save()"></button>
    <button @click="if (ok) save();"></button>

    Handlers that Vue compiles as the body of a function keep their semicolons with "semicolons": "asNeeded", because Vue compiles a handler without a ; as an expression.

  • #12192 4d056a5 Thanks @dyc3! - Fixed CSS package imports that use the style export condition, such as tw-animate-css. With tailwind.stylesheet configured, useTailwindSortedClasses now reads utilities and theme values from imported packages, including their transitive CSS imports.

  • #12217 8a253f8 Thanks @dyc3! - Updated biome migrate eslint to migrate tailwindcss/enforces-shorthand to useTailwindShorthandClasses when nursery rules are included, and @html-eslint/no-target-blank to noBlankTarget. Corrected the reported reasons for unsupported legacy, deprecated, formatter-covered, and compiler-dependent rules.

  • #12047 4db3f58 Thanks @denbezrukov! - Fixed parsing of bare if identifiers in CSS if() branch values, such as if(style(--enabled: true): if; else: serif).

  • #12401 e295f02 Thanks @dyc3! - Fixed useTailwindShorthandClasses suggesting invalid or behavior-changing shorthands for rotations, dividers, content alignment, and custom width and height tokens. Shorthand fixes now preserve !important with either leading or trailing ! markers.

  • #11854 2917939 Thanks @dyc3! - Added support for mapped types such as { [K in keyof T]: T[K] } to type inference. Type-aware rules now resolve the properties of mapped types, including generic aliases instantiated with concrete type arguments:

    type Optional<T> = { [K in keyof T]?: T[K] };
    declare const config: Optional<{ retries: number }>;
    // `config.retries` is now inferred as `number | undefined`.

    Mapped types that remap keys with an as clause remain unsupported.

  • #12251 7d55c42 Thanks @dyc3! - Fixed #12233: the biome command installed from npm now exits with a non-zero code when the Biome binary is killed by a signal, such as a crash from SIGABRT or SIGSEGV. Previously, it exited with code 0 and reported the run as successful.

  • #12157 a1320b1 Thanks @dyc3! - Renamed the nursery rule useSortedClasses to useTailwindSortedClasses. Run biome migrate --write to update your configuration.

  • #12124 d5ace64 Thanks @levrik! - Biome now parses and formats the generic attribute of Vue <script setup lang="ts"> blocks, such as generic="TData extends RowData". The declared type parameters are no longer reported by noUndeclaredVariables, noUnusedVariables and noUnusedImports also take the parameters into account now.

    <script setup lang="ts" generic="T extends Item,U">
    <script setup lang="ts" generic="T extends Item, U">
  • #12350 97c51a8 Thanks @Netail! - Improved the diagnostics of the GraphQL rules noDuplicateArgumentNames, noDuplicateEnumValueNames, noDuplicateFieldDefinitionNames, noDuplicateGraphqlOperationName, noDuplicateInputFieldNames and noDuplicateVariableNames. Each diagnostic now names the duplicated identifier, highlights its first occurrence and points to every repeated occurrence, instead of highlighting the whole parent node or only the repeated occurrences. Names that are duplicated more than once in the same list are reported separately.

  • #12178 8c4469c Thanks @MustafaKemal0146! - Fixed #10212: useConsistentObjectDefinitions no longer reports named function expressions such as { b: function c() {} }, because converting them to methods can change the function’s name or how its name is resolved inside the body.

  • 9361d23 Thanks @florian-lefebvre! - The JavaScript formatter now wraps a sequence expression returned from a return or throw statement in parentheses, matching Prettier.

    return a, b;
    return (a, b);
  • #12362 ac71f2b Thanks @dyc3! - Improved the performance of the linter and assists, most of all when few rules are enabled, for example with --only.

  • #12185 08cfbd2 Thanks @dyc3! - Added the nursery rule noTailwindLegacyUtilities for JavaScript and HTML. It reports Tailwind CSS classes that Tailwind CSS only keeps for backward compatibility, and fixes them to their current names.

    <div className="flex-grow bg-gradient-to-r" />; // use `grow` and `bg-linear-to-r`
  • #11093 78c992f Thanks @dyc3! - Improved the accuracy of type-aware lint rules by resolving more inferred types. For example, noFloatingPromises now detects floating Promises returned by aliased callbacks and arrays of Promises created by async mapping callbacks.

    The following statements are now reported:

    type AsyncCallback = () => Promise<void>;
    declare const callback: AsyncCallback;
    callback();
    [1, 2, 3].map(async value => value);
  • #12063 8724a5d Thanks @dyc3! - Fixed #9749: in GritQL plugins and biome search, a contains followed by further conditions now tries every node it matches, instead of failing when the first match doesn’t satisfy the remaining conditions.

  • #12154 79c1643 Thanks @dyc3! - Added the new nursery rule useLowercaseTestTitle, which requires test, test suite, and benchmark titles to start with a lowercase letter.

    // Invalid
    it("Returns the cached value", () => {});
  • #12086 27a34f9 Thanks @dyc3! - biome migrate eslint now preserves the element lists of react/forbid-elements, svelte/no-restricted-html-elements, and vue/no-restricted-html-elements when migrating them to noRestrictedElements. When several of these rules are configured, their lists are merged.

  • #12174 bee1e54 Thanks @dyc3! - Fixed the Tailwind class parser producing an invalid syntax tree for parenthesized and unary expressions inside arbitrary values. Classes like w-[calc(1px+(2px))], w-[calc((100%_-_2rem)/3)], and w-[calc(1px*-(2px))] now parse correctly.

  • #12203 b1e35fa Thanks @dyc3! - Fixed parsing of Tailwind @theme at-rules with more than one option. Biome now accepts any number of options, including prefix(...), such as the @theme default inline reference { ... } rule in Tailwind’s own tailwindcss/index.css.

  • #11888 02e038f Thanks @dfedoryshchev! - Fixed useNamingConvention reporting a declaration nested in a namespace that is itself inside declare global or inside an external module declaration. Both positions are documented as always ignored, and the rule offered a safe fix, so biome check --write renamed the declaration. This follows up #11827.

    export {}
    declare global {
    namespace Outer {
    // no longer reported
    namespace my_NAMESPACE {}
    }
    }
  • #12068 dd96b11 Thanks @GreedyC! - Fixed #12004: noDuplicateFontNames allows font-family: monospace, monospace, which preserves the inherited font size in browsers.

  • #12411 4de5d3d Thanks @latin-1! - Fixed #12410: the useSortedProperties assist now preserves the relative order of Tailwind CSS wildcard resets such as --font-*: initial and other custom properties.

  • #12211 8d0f080 Thanks @Athish2002! - Fixed #11944: noUselessStringConcat safe fix now accurately stringifies numbers according to ECMAScript Number::toString semantics, properly handling scientific notation (such as 1e21 and 0.1e-6), alternate radixes, and separators.

  • #12392 60211cb Thanks @ematipico! - Fixed #12390: rules from the project and types domains that are enabled in an override now run for files matched by that override.

  • #12399 cd735e9 Thanks @dyc3! - Fixed #12373: biome migrate eslint no longer migrates one-var to useSingleVarDeclarator unless --include-inspired is passed.

  • #12141 4efe06d Thanks @dyc3! - Updated biome migrate eslint to recognize more upstream rules covered by existing Biome rules and explain why additional unsupported rules are skipped.

  • #11093 78c992f Thanks @dyc3! - The HTML parser now accepts mixed-case doctype declarations.

  • #12288 39d7595 Thanks @dyc3! - Fixed false positives in noProto for computed identifiers, property names containing quotes, and tagged templates. Expressions such as obj[__proto__] and obj[`"__proto__"`] no longer trigger the rule.

    Fixed missed diagnostics for __proto__ accesses through parentheses, TypeScript wrappers, and comma expressions, such as obj[('__proto__')].

  • #12341 9a44363 Thanks @dyc3! - Improved the performance of noUnusedVariables, noUnusedImports, and the other lint rules that check whether a variable is used in another language of the same file.

  • #11432 4befd08 Thanks @ematipico! - Fixed JavaScript assist overrides and HTML parser, linter, and assist overrides so they are applied to matching files.

  • #12339 8fa3b1a Thanks @dyc3! - Improved the performance of the linter and assists by up to 50% on some files.

  • #12322 b822e49 Thanks @dyc3! - Improved the performance of biome format --write for files without syntax errors. Biome no longer parses the formatted code of these files a second time. JSON files are not affected.

  • #12362 ac71f2b Thanks @dyc3! - Improved the performance of the lint rules that analyze control flow, such as noUnreachable and useGetterReturn.

  • #12361 f447e08 Thanks @dyc3! - Improved the performance of noIrregularWhitespace. The rule now spends much less time in files that contain no irregular whitespace.

  • #12094 4e50a11 Thanks @dyc3! - Added the nursery rule useConsistentBlockLang, which enforces the languages that the top-level blocks of Vue and Svelte components use. The blocks option configures the allowed languages per block, and the rule doesn’t check anything until it’s configured.

    For example, with { "blocks": { "script": { "lang": ["ts"] } } }, the following is reported:

    <script lang="js"></script>
  • #12114 3743a53 Thanks @dyc3! - Added the recommended nursery rule noArgumentsCallerOrCallee, which disallows arguments.caller and arguments.callee. For example, arguments.callee() now produces a diagnostic; refer to the function by name for recursion.

  • #12360 c2cc18d Thanks @dyc3! - Improved the performance of noTsIgnore. The rule now spends much less time in files that contain no @ts-ignore comment.

  • #12092 d7cf132 Thanks @dyc3! - Added the nursery rule noSvelteUselessDerivedBy, which reports $derived.by() calls whose function only returns a single expression, such as $derived.by(() => count * 2), and suggests $derived(count * 2) instead.

  • #12133 2405c76 Thanks @dyc3! - Added the nursery rule useNamedFunction, which requires functions written with the function keyword to have a name. For example, Foo.prototype.bar = function () {}; is now reported.

  • #12116 b52fee2 Thanks @dyc3! - Added the new nursery rule noUnsafeUnaryMinus, which uses type information to report unary - applied to values that aren’t a number or bigint.

    declare const value: string;
    -value;
  • #12072 c35b067 Thanks @dyc3! - Added the nursery rule noSvelteObjectInTextMustaches, which disallows object, array, function, and class literals in Svelte text mustaches, where they are rendered as strings such as [object Object].

    <!-- Invalid: renders "[object Object]" -->
    {{ name }}
  • #12082 914dca7 Thanks @dyc3! - Added the nursery rule noUselessCall, which reports .call() and .apply() calls that can be replaced with a regular function call.

    obj.foo.call(obj, 1, 2);
    foo.apply(null, [1, 2]);
  • #12067 21fb018 Thanks @dyc3! - Added the nursery rule noSvelteInspect, which reports uses of the Svelte $inspect rune, such as $inspect(count) or $inspect.trace(), left over from debugging.

  • #12199 1004290 Thanks @siketyan! - Fixed CSS metavariables in compound selectors, complex selectors, and property names in GritQL plugins and biome search. Patterns such as `$tag:hover { color: red; }`, `$parent .child {}` and `$property: red` now match the intended rules and declarations, whereas they previously failed to parse or matched the wrong nodes.

  • #12093 3c6331f Thanks @dyc3! - Added the nursery rule useUnaryMinus, which reports multiplying or dividing by -1 instead of using the unary minus operator.

    const inverted = value * -1;
  • #12107 81773ad Thanks @dyc3! - Added the nursery rule noUselessAssignment, which reports assignments whose value is never read before the variable is reassigned or stops being used.

    let id = "x1234"; // this value is never read
    id = generateId();
    doSomethingWith(id);
  • #12115 47c3084 Thanks @dyc3! - Improved type inference for template literals. Biome now infers untagged template literals as strings, so type-aware rules such as noUselessTypeConversion now report code like String(`hello ${name}`).

  • #12468 62d69dc Thanks @dyc3! - Improved performance of the biome search command.

  • #12272 84f62f2 Thanks @dyc3! - The noTailwindRawColors diagnostic now lists up to three classes to use instead. Each one sets a color from your Tailwind CSS theme that looks similar to the reported palette color. Set tailwind.stylesheet to the CSS file that holds your theme to get the list. Biome now scans your project when this rule is enabled, so that it can read that stylesheet.

    For example, with --color-danger: oklch(0.58 0.24 27) in the theme, the diagnostic for bg-red-600/50 lists bg-danger/50.

  • #12402 cf6e36c Thanks @dyc3! - Fixed useVueDefineMacrosOrder missing misplaced bare macro calls and reporting the wrong macro for assigned calls. Its unsafe fix now orders all macro statements in one application and preserves their comments and statement boundaries.

  • #12353 505e2b4 Thanks @Netail! - Improved the quality of diagnostics emitted by the following rules:

  • #11093 78c992f Thanks @dyc3! - Fixed a performance regression in type-aware JavaScript lint rules by inferring only requested types and memoizing export resolution.

  • #12280 80a9204 Thanks @dyc3! - Fixed #12278: noUnusedImports now recognizes Svelte imports used only through $store subscriptions in markup or another script block.

  • #11093 78c992f Thanks @dyc3! - Added support for Svelte declaration tags using let and const. Biome can now parse, format, and lint bindings declared in these tags.

  • #12394 eca05e8 Thanks @florian-lefebvre! - Fixes a parser error for Astro shorthand attributes that use an expression instead of a bare identifier, such as {object?.something}:

    <div {object?.something}></div>
  • #12171 271d9b2 Thanks @dyc3! - Improved useBetterDomTraversing to offer its fix when the reported expression is followed by a comment, such as element.childNodes[0] /* first */. The fix keeps the comment.

  • #11961 6d419d4 Thanks @dyc3! - Added the nursery rule useTailwindStaticClassStrings to the Tailwind domain. The rule reports class names assembled from fragments, such as className={"text-" + color} in JSX and class="bg-{color}" in Svelte.

  • #11801 81cccb0 Thanks @dyc3! - Added the nursery rule noTailwindRestyledComponents, which reports Tailwind appearance overrides on JSX and HTML components, such as <Button className="rounded-none" />. No appearance categories are allowed by default. Layout utilities without a category, such as w-full and mt-4, are ignored.

  • #12122 d87ecd3 Thanks @dyc3! - Added the nursery rule noProcessExit, which disallows calling process.exit(). For example, process.exit(1); is now reported.

  • #12249 5fa646c Thanks @dyc3! - Fixed #12247: type inference now infers array literals without as const as arrays, as TypeScript does. For example, [1, "a"] is inferred as (string | number)[] instead of a tuple. As a result, useArraySortCompare now reports sort() and toSorted() calls on arrays without a type annotation.

    const numbers = [1, 2, 3, 10, 20, 30];
    numbers.sort();
  • #12434 8cfb294 Thanks @fe-dudu! - Fixed #12187: noIrregularWhitespace no longer reports irregular whitespace inside CSS and SCSS strings. It avoids duplicate diagnostics in nested rules and checks embedded CSS snippets.

  • #12014 915dc9d Thanks @ematipico! - Fixed noDescendingSpecificity computing the wrong specificity for nested selectors, and ignoring nested selectors inside @container and @starting-style blocks. The following code is now reported, because html body & .d resolves to html body .x .y .d, which is more specific than .x .y .d:

    .x {
    .y {
    html body & .d {}
    }
    }
    .x .y .d {}
  • #12392 60211cb Thanks @ematipico! - Fixed #12358: rules from a domain set to none now remain disabled when an override configures another rule in the same group.

  • #12070 6a8fb01 Thanks @dyc3! - Added the nursery rule noSvelteAddEventListener, which reports addEventListener calls in Svelte files and suggests the on function from svelte/events instead.

    <script>
    window.addEventListener("resize", handler);
    </script>
  • #12089 dba2a37 Thanks @ternaus! - Fixed #12088: false positives in noUnknownAttribute for React 19 transition event handlers. onTransitionCancel, onTransitionRun, onTransitionStart, and their capture variants are recognized when the React dependency range allows React 19 or later.

  • #11093 78c992f Thanks @dyc3! - Improved the performance of Biome formatter up to ~7% across the board.

  • #12194 a3944f7 Thanks @dyc3! - Added the allowedCategories and allowedClasses options to the nursery rule noTailwindArbitraryValue. They allow arbitrary values in categories of utilities, such as layout, or in specific classes, such as p-[13px].

    The rule no longer reports arbitrary modifiers, so bg-black/[0.5] and text-sm/[18px] are now valid.

  • #12120 a1197ac Thanks @dyc3! - Added the nursery rule noInvalidThis, which reports this where it is undefined: at the top level of a module, and in functions that aren’t methods or constructors.

    function foo() {
    this.a = 0;
    }
  • #11093 78c992f Thanks @dyc3! - Added the nursery rule noJsRestrictedProperties, which ports ESLint’s no-restricted-properties rule. Biome now flags restricted member access and object destructuring, and biome migrate eslint preserves the rule’s options.

  • #12052 1a6fb41 Thanks @dyc3! - Vue rules that check props, such as noVueReservedProps, now detect props declared inside withDefaults(), for example withDefaults(defineProps<{ key?: string }>(), {}).

  • #12292 4eadd57 Thanks @ematipico! - Fixed a parse error in HTML and Svelte files when an attribute named ? came right before the end of a tag. <div a ?> now has the attributes a and ?, as it already did in Astro and Vue files.

  • #12080 6e2ee70 Thanks @dyc3! - Fixed #12079: the formatter no longer puts the parameters of a Svelte {#snippet} block on separate lines when they fit on one line.

    {#snippet children(
    item,
    )}
    {#snippet children(item)}
  • #12346 ab77fa8 Thanks @ematipico! - Fixed errors that the Biome language server sent to the editor with an empty message. For example, when a nested configuration file sets "root": true, the editor logs now show “Found a nested root configuration, but there’s already a root configuration.” instead of an empty line.

  • #12090 79ed984 Thanks @dyc3! - Added the nursery rule noVueRequiredPropWithDefault, which reports Vue props that are marked as required but also have a default value.

    <script setup lang="ts">
    const props = withDefaults(defineProps<{ name: string }>(), {
    name: "World",
    });
    </script>
  • #11967 4c1612b Thanks @dyc3! - Added the nursery rule useBigintLiterals, which enforces bigint literals over BigInt() calls with a literal argument.

    // Invalid
    const bigint = BigInt(1);
    const hex = BigInt("0xFF");
    // Valid
    const bigint = 1n;
    const hex = 0xFFn;
    const computed = BigInt(getSomeNumber());
  • #12014 915dc9d Thanks @ematipico! - Fixed noDuplicateSelectors ignoring nested selectors inside @container and @starting-style blocks. The following duplicate is now reported:

    .card {
    @container (min-width: 1px) {
    & > p {}
    & > p {}
    }
    }
  • #12340 edbe9d7 Thanks @maroKanatani! - Fixed #12320: useReactCompiler now analyzes files that contain an exported type alias, interface, or enum declaration, such as export type Props = { id: string };. Previously, the rule silently skipped these files.

2.5.15

Patch Changes

  • #10634 b436ba0 Thanks @subaru-hello! - Added the new nursery rule noReactObjectTypeAsDefaultProp, which disallows array, object, and function values as default props in React components.

    For example, the following snippet triggers the rule.

    function Component({ items = [] }) {
    return items;
    }
  • #11956 faa8b37 Thanks @dyc3! - Added the nursery rule noSvelteExportLet, which disallows declaring Svelte component props with the legacy export let syntax. Use the $props() rune instead.

    <script>
    export let name;
    </script>
  • #10816 1b9479e Thanks @Th3S4mur41! - Added a new nursery rule useLogicalProperties that enforces the use of logical properties in CSS, promoting better internationalization and accessibility practices. The rule supports a direction option with "ltr" as the default and "rtl" as the alternative. This is a first rule covering parts of #9034

    {
    "linter": {
    "rules": {
    "nursery": {
    "useLogicalProperties": {
    "level": "warn",
    "options": {
    "direction": "rtl"
    }
    }
    }
    }
    }
    }
  • #11960 1fdb5c2 Thanks @dyc3! - Added the nursery rule useSvelteKitRuneImports, which reports imports from the deprecated $app/stores module and suggests $app/state instead.

    import { page } from "$app/stores";
  • #11723 3b429d1 Thanks @m1handr! - Fixed #11656: noAstroSetHtmlDirective now correctly reports set:html directives inside Astro template expressions.

  • #12023 874d5ae Thanks @codspeed! - Improved the performance of the HTML formatter up to 4x.

  • #11761 a3462fe Thanks @saberoueslati! - Fixed #11351: useSimplifiedLogicExpression no longer reports boolean literals on the right side of || and && outside boolean contexts, because removing them can change the result of the expression. For example, y = x || false is no longer reported, while if (x || false) still is.

  • #11732 ff4c4dd Thanks @dyc3! - Added the nursery rule noMeaninglessVoidOperator, which reports unnecessary uses of void, such as void log() when log returns void. The rule allows discarded call results, thenables, void 0, and calls returning never.

  • #11975 40dd3fb Thanks @ematipico! - Fixed the indentation of multiline Astro expressions, in templates and attribute values, when running biome check --write. Biome now formats Astro expressions with biome format too, and places them at the column of the surrounding markup.

    <div>
    {items.map((item) => (
    <span>{item}</span>
    ))}
    <span>{item}</span>
    ))}
    </div>
  • #12016 09d4000 Thanks @codspeed! - Improved the performance of indexing and analysis of big files up to 2x. The improvements are mostly visible in projects that make use of project and types lint rules.

  • #11750 089bde0 Thanks @dyc3! - Added the nursery rule useStrictBooleanExpressions, which reports ambiguous truthiness checks such as if (value) when value has type number | undefined. Non-nullable strings and numbers and nullable objects are allowed; the rule has no options.

  • #11494 ad5b362 Thanks @jp-knj! - Added the nursery rule noAstroConflictingSetDirectives, which reports Astro elements with multiple content sources, such as set:html, set:text, and child content.

    For example, <div set:html={html}>content</div> triggers the rule.

  • #11878 84d1b3b Thanks @dyc3! - Fixed #11748: useExhaustiveSwitchCases now reports missing cases for values created with the mapping overload of Array.from, including arrays imported from another module.

  • #11802 7d1f37e Thanks @dyc3! - Tailwind classes will now be detected in Svelte, Vue, and Astro class attribute expressions that don’t use a class merging function.

  • #11910 9e50ea2 Thanks @ematipico! - Fixed #11504, a regression where Biome would silently ignore errors in the configuration file. Now errors are correctly retained and checked before executing any command.

  • #11921 d568632 Thanks @hirehamir! - Fixed #10846: when plugins fail to load, Biome now prints each failing plugin’s path on its own line, instead of concatenating bare messages like Cannot read file.Cannot read file..

  • #11930 82ea5a6 Thanks @dyc3! - Fixed #11927: The HTML formatter no longer duplicates comments around Svelte blocks. This affected a comment after a block such as {#if} or {#each} at the end of an element, and a comment on the same line as the last element inside a block, before {:else}, {/if}, or a similar tag.

  • #11931 0cc46d8 Thanks @dyc3! - Fixed the indentation of comments at the end of a Svelte block’s contents. A comment before {:else}, {:then}, {/if}, or a similar tag is now indented with the block’s contents instead of with the tag.

    {#if condition}
    <span>Text</span>
    <!-- comment -->
    <!-- comment -->
    {/if}
  • #12031 ef0edd4 Thanks @dyc3! - Fixed #12027: Biome’s test rules no longer mistake regular method calls named test, it, or describe for tests. For example, useValidTestTitle used to report the following regular expression check as a test with an invalid title:

    const isComment = /^\s*#/.test(line);
  • #11959 8477e61 Thanks @dyc3! - Fixed #11950: noUnusedVariables now reports arrow functions with expression bodies that only reference themselves, such as let h = () => h();.

  • #11915 3260602 Thanks @ematipico! - Fixed #11841, where suppression comments had no effect on some parts of HTML-ish files and on snippets embedded in JavaScript files.

    Now the following suppression works as expected:

    <!-- biome-ignore lint/correctness/noUndeclaredVariables: intentionally external -->
    <div :title="missingValue"></div>
  • #11952 b51040e Thanks @dyc3! - Fixed #11951: the GritQL formatter no longer inserts a space after a within pattern without an until clause.

    $arg <: within `bar($_)` ,
    $arg <: within `bar($_)`,
  • #11794 429cf95 Thanks @dyc3! - Added the nursery rule noTailwindRawColors for JavaScript and HTML. It disallows Tailwind palette colors such as bg-pink-500 and text-white, encouraging design system color utilities such as bg-primary.

  • #11837 f5e249b Thanks @dyc3! - Fixed #11836: biome check --write no longer adds invalid parentheses around Svelte {@const} declarations when experimental HTML support and formatting are enabled.

  • #11978 8be0b9e Thanks @dyc3! - Fixed #11817: Biome no longer crashes when its output is piped to a program that exits early, such as head. Output to the closed pipe is now discarded and Biome exits normally.

  • #11868 3841c26 Thanks @ematipico! - Fixed #8986: Biome’s language server now scopes all watched-file patterns to each workspace folder, falling back to the deprecated rootUri when no workspace folders are provided. Clients without relative-pattern support receive compatible absolute glob patterns.

  • #11928 0015681 Thanks @dyc3! - Restricted access to the Unix daemon socket to the user running Biome. The socket now lives in a biome-daemon directory inside Biome’s cache directory that only this user can access, and the socket itself has mode 0600.

  • #11835 589ca1a Thanks @dyc3! - Updated useReactCompiler: Biome now reports React Compiler diagnostics regardless of the React version declared in package.json.

  • #11907 b7d9037 Thanks @posido! - Fixed withastro/compiler-rs#194: the HTML parser no longer treats a regex literal that starts with > as the end of a self-closing tag. Astro frontmatter such as const escaped = s.replace(/>/g, "&gt;"); no longer swallows the closing --- fence, and the same regex inside a template expression no longer runs past its closing }. A regex literal after a keyword such as return, as in return />'/.test(s), is now recognized too.

  • #12021 59cc595 Thanks @dyc3! - Type inference performance has been significantly improved. Some popular libraries like Zod, Valibot, Arktype, Effect, Kysely, and Drizzle have gained a ~2-240x speedup in our benchmarks. This improvement affects all rules that use type information.

  • #12044 c73fb91 Thanks @dyc3! - Fixed #12042: the HTML formatter no longer removes the space between text and an inline element on the next line when it joins the lines.

    <p>a <em>b</em> c<u>d</u></p>
    <p>a <em>b</em> c <u>d</u></p>
  • #11965 f90bf38 Thanks @ematipico! - Fixed module resolution in long-running workspaces so imports reflect package manifest and TypeScript path-mapping changes without requiring the importing file to be edited.

  • #11860 0884e29 Thanks @dyc3! - Removed the attributes and functions options from the nursery rule noTailwindArbitraryValue. The rule now uses the same Tailwind detection as useTailwindShorthandClasses.

  • #11969 865cd30 Thanks @AlbinoGeek! - Fixed #11962: noUnknownTypeSelector no longer reports view transition names inside view transition pseudo-elements, such as page in ::view-transition-group(page).

  • #11914 06ff47b Thanks @dyc3! - Fixed #11897: the safe fix for noUselessStringConcat now escapes embedded double quotes when combining literals, preserving valid JavaScript and existing escape sequences.

  • #11997 af7825f Thanks @github-actions! - The noRestrictedDependencies rule has been updated with new module replacement data, it should now detect for more relevant replacements.

  • #11827 31bb662 Thanks @dfedoryshchev! - Fixed #11566: useNamingConvention no longer reports a namespace declared inside declare global or inside an external module declaration. Both positions are documented as always ignored, and the rule offered a safe fix, so biome check --write renamed the declaration:

    export {}
    declare global {
    // no longer renamed to `Jsx`
    namespace JSX {}
    }
  • #11814 23ba25f Thanks @siketyan! - Fixed type inference through generic type aliases that instantiate another generic type with a nested generic argument, such as type Nested<T> = Box<Wrapper<T>>. Type-aware rules now resolve members of such types:

    declare const nested: Nested<number>;
    // noUnnecessaryConditions now reports that `??` is unnecessary.
    const inner = nested.value.inner ?? 1;
  • #11908 dd5a5ce Thanks @siketyan! - Fixed #11880: Biome no longer misparses a << expression followed by a later >>> as TypeScript type arguments. For example, const mask = 1 << bits followed by const m = mask >>> 0 on the next line now parses correctly.

  • #11882 28c817d Thanks @mikehasa! - Fixed a bug in noOctalEscape where the safe fix could silently change a string’s value. A legacy octal escape is at most two digits when the leading digit is 4-7, so "\751" is "\75" + "1" (i.e. "=1") but was rewritten to the single character ǩ; it is now rewritten to "\x3d1".

  • #11861 81b0adb Thanks @Netail! - Added the new nursery rule noSelfImport, which forbids a module from importing itself.

    foo.js
    import foo from "./foo.js";
  • #12041 5e14509 Thanks @ematipico! - Fixed a stack overflow in type-aware lint rules, such as noMisusedPromises and noFloatingPromises, when generic types in files that import each other reference one another in their type parameters.

    entity.ts
    import type { Repository } from "./repository";
    export interface Entity<R extends Repository<any> = Repository<any>> {}
    // repository.ts
    import type { Entity } from "./entity";
    export interface Repository<E extends Entity<any> = Entity<any>> {}
  • #11838 9bbff0c Thanks @dyc3! - Added the nursery rule usePromiseRejectErrors, which requires Error objects as Promise rejection reasons.

    Promise.reject("Request failed");
    new Promise((resolve, reject) => reject(42));
  • #11917 717db8c Thanks @dyc3! - Added the nursery rule noMisplacedListElements for HTML and JSX, which requires <li> elements with an HTML element parent to be children of <ul>, <ol>, or <menu>. For example, <div><li>Item</li></div> is invalid.

  • #11919 8d0b990 Thanks @dyc3! - Fixed #11899: disabling a domain no longer disables rules that also belong to another enabled domain. For example, with "domains": { "react": "all", "next": "none" }, useExhaustiveDependencies and useHookAtTopLevel are now enabled. Rules enabled explicitly in the configuration also stay enabled when one of their domains is set to "none".

  • #11814 23ba25f Thanks @siketyan! - Fixed #11810 and #11813: type-aware rules such as noUnnecessaryConditions and noFloatingPromises no longer take several seconds when a member is accessed on a recursive generic type alias, such as react-hook-form’s FieldPathValue or zustand’s Mutate.

  • #11983 cc39794 Thanks @AlbinoGeek! - Fixed #11939: the fix of useRegexLiterals no longer escapes a slash that is already escaped. new RegExp("\\/") is now fixed to /\// instead of the invalid /\\//.

  • #11869 3a21c80 Thanks @ematipico! - Fixed #9105: vcs.useIgnoreFile now evaluates parent directory patterns when matching child paths, preserving re-included directories such as !/src while ignoring their excluded siblings.

  • #11875 2cdd220 Thanks @dyc3! - Fixed #11867: noUndeclaredVariables incorrectly reported Vue slot props declared with v-slot or its # shorthand, including destructured props.

  • #12021 59cc595 Thanks @dyc3! - Type inference accuracy has been improved significantly. More global types, like Array, Map, Set, etc., are now fully defined. This should decrease false positives on all typed rules, since less types will be unknown.

  • #11929 aef690d Thanks @Th3S4mur41! - Fixed noUnknownProperty to recognize the frame-sizing CSS property.

  • #12022 6233eb2 Thanks @dyc3! - Fixed #12020: the HTML parser now reports an error for a mismatched closing tag like the </span> in <p>two</span></p>. Previously, it accepted </span> as the end of <p> because span contains the letter p, and the formatter deleted everything after it. HTML tag names are matched case-insensitively, so <DIV></div> is no longer reported as mismatched.

    A closing tag with no opening tag at the top level of a file, like the second </p> in <p>a</p></p><p>b</p>, is now also reported as an error, instead of the formatter deleting it and everything after it.

  • #12037 48cdbb8 Thanks @ff1451! - Fixed #11898: noUselessReturn no longer offers a safe fix for a return; that is the body of an unbraced if, else, or label, because removing it produced invalid code. The safe fix now also keeps comments placed before or after the removed return;.

    function foo() {
    // Still reported, but the return is no longer removed
    if (aborted) return;
    }
  • #12030 4ff83dd Thanks @ematipico! - Fixed #9155: Biome no longer reports a parse error for typed slot props in Vue files, such as v-slot="{ value }: { value: ValueType }". Types used in slot props annotations are now correctly detected as used by noUnusedVariables.

  • #11955 088164f Thanks @dyc3! - Fixed #11946: noUnreachable and useGetterReturn now recognize while and do...while loops with truthy literal conditions as infinite unless control flow exits the loop.

  • #11958 6964bfc Thanks @erenbati! - Fixed #11924: noFocusedTests no longer reports chained method calls such as builder.image(url).fit("max") as focused tests.

  • #11908 dd5a5ce Thanks @siketyan! - Fixed parsing of left shifts between TypeScript instantiation expressions. Biome now parses f<T> << f<T> as a left shift of two instantiation expressions, matching TypeScript, instead of reporting a parse error.

  • #11877 5a53b2c Thanks @dyc3! - Fixed #11278: noUnnecessaryConditions no longer incorrectly reports optional chaining on RegExp.exec() results, including patterns created with new RegExp(). Biome now infers the nullable RegExpExecArray | null return type, preserving necessary checks such as new RegExp(pattern).exec(input)?.[1].

  • #11906 b87822d Thanks @dyc3! - Fixed #11900: useForOf no longer reports loops that update their index inside the body, including i += 1 and argv[++i].

  • #11834 f89dd4f Thanks @dyc3! - Fixed incorrect type inference when generic parameters are reused across inherited types or swapped in recursive types.

2.5.14

Patch Changes

  • #9022 0d49e24 Thanks @dyc3! - Added the nursery rule noReturnInFinally. This rule disallows return statements in Promise.prototype.finally() callbacks, including inside nested blocks and conditional branches. Returns in nested functions are ignored by the rule.

    // Invalid: return in finally callback
    Promise.resolve(1).finally(() => { return 2 })
    // Valid: no return in finally callback
    Promise.resolve(1).finally(() => { console.log(2) })

    Returning a value from a Promise.prototype.finally() callback does not replace the original promise’s fulfillment value, which can be confusing. Returned promises and thenables are awaited, and their rejection rejects the resulting promise.

  • #11754 71eaa0d Thanks @griff-rees! - Added the nursery rule noSvelteAtDebugTags, which disallows Svelte’s {@debug} tag.

    <!-- Invalid: leftover debugging tag -->
    {@debug user}

    The {@debug} tag is a debugging aid and should be removed once you no longer need it, as it should not remain in production code. The rule provides a safe fix that removes the tag.

  • #11725 5eb5f09 Thanks @m1handr! - Added the nursery rule useValidTestTitle, which enforces valid titles for unit test cases and suites.

  • #11735 9bd70c7 Thanks @ematipico! - Fixed #8471: source.fixAll.biome ignored formatter.formatWithErrors. It now applies safe fixes without formatting files that have parse errors when the option is disabled.

  • #11715 f05a3c3 Thanks @ematipico! - Fixed #7771: Grit plugins that use sequential no longer panic when Biome processes files.

  • #11766 c2542c6 Thanks @dyc3! - Fixed validation of readonly and accessor modifiers: combining them in either order now reports that they cannot be used together.

  • #11461 22e9966 Thanks @FoundDream! - Fixed #11423: Multiline template interpolations now preserve the indentation of their closing brace when the source indentation is not a multiple of tabWidth.

    const value = `
    ${
    condition
    ? "yes"
    : "no"
    }
    }
    `;
  • #11766 c2542c6 Thanks @dyc3! - Fixed #11763: TypeScript class members using override accessor, such as override accessor value = 1, now parse correctly. The reversed order, accessor override, now reports that override must precede accessor.

  • #11790 17d0ff0 Thanks @ematipico! - Fixed #10248: noUselessFragments now allows fragments with props in Astro files, such as <Fragment slot="name">{text}</Fragment> inside template expressions.

  • #11777 7ee3a6c Thanks @ematipico! - Fixed #7573: added the requireExplicitCase option to useExhaustiveSwitchCases. When set to true, the rule reports missing cases even when the switch has a default clause, so you can keep a runtime fallback while checking that every value in the union has its own case. The option defaults to false.

  • #11751 d37f24b Thanks @ematipico! - Fixed #8347: the fix from useConsistentArrowReturn now parenthesizes returned expressions that begin with object literals before removing the arrow function body braces, preventing invalid output for expressions such as object property access.

  • #11784 46e8912 Thanks @dyc3! - Fixed #11782: noUndeclaredCustomProperties could hang while checking stylesheets imported by JavaScript modules with many shared dependencies.

  • #11731 1534885 Thanks @ematipico! - Fixed #7984: The fix from useSimplifiedLogicExpression now preserves line breaks in multiline conditions with line comments, preventing the right-hand side condition from being commented out.

  • #11735 9bd70c7 Thanks @ematipico! - Fixed #7304: the HTML formatter now preserves authored segment breaks between CJK characters, and next to CJK punctuation, instead of replacing them with spaces.

    <div lang="zh-Hant-TW">
    這個段落是那麼長, 在一行寫不行。
    這個段落是那麼長,
    在一行寫不行。
    </div>
  • #11749 ff992a1 Thanks @ematipico! - Fixed #11747: formatting and checking large parenthesized object expressions no longer exhibit quadratic slowdowns.

  • #11736 1dd1fc4 Thanks @dyc3! - Fixed #8177: code actions no longer modify the wrong part of Vue, Svelte, or Astro files when experimental full HTML support is disabled.

  • #11743 3835945 Thanks @santichausis! - Fixed #10247: biome check --write/biome lint --write now correctly writes fixes for code inside an HTML attribute expression (for example a Svelte onclick={...} handler, or a mustache expression like {count}), instead of silently reporting the diagnostic as fixable and applying nothing.

    For example, running biome lint --write --unsafe for useBlockStatements (an unsafe fix) on this Svelte component used to leave the file unchanged:

    <button onclick={() => { if (open) close(); }}>Close</button>
  • #11740 8ea8b4a Thanks @dyc3! - Fixed #11453: useConsistentTestIt now updates imports alongside calls, preserving the original export through an alias. The rule ignores locally declared functions and withholds fixes when the preferred name would conflict with another binding or global reference.

  • #11355 27177ca Thanks @dyc3! - Fixed the HTML formatter incorrectly applying native HTML element formatting to PascalCase component names such as <Ul> and <Body> in Vue, Svelte, and Astro files.

    <Body>
    <div>content</div>
    </Body>
    <Body><div>content</div></Body>
  • #11355 27177ca Thanks @dyc3! - Fixed the HTML formatter incorrectly applying SVG block formatting to unknown elements whose names matched SVG element names.

    <foreignobject>
    <div>content</div>
    </foreignobject>
    <foreignobject><div>content</div></foreignobject>
  • #11741 fc69047 Thanks @dyc3! - Fixed #8893: useImportExtensions no longer suggests adding .ts to .jsx imports when a colocated .d.ts file provides type declarations.

  • #11642 c87341c Thanks @dyc3! - Added the nursery rule useConsistentFunctionStyle, which requires a consistent style for defining functions.

    By default, the rule reports the following declaration because it requires a function expression assigned to a variable:

    function greet() {
    return "Hello";
    }
  • #11770 ddfd622 Thanks @dyc3! - Fixed #8980: suppression comments targeting the entire assist category are now respected, including biome-ignore-all assist when running check.

  • #11792 7a4b895 Thanks @dyc3! - Fixed dashed utility base names in the Tailwind parser, including border-bs, font-features, and scrollbar-thumb. Classes such as min-inline-[12rem] now preserve the complete base name and parse the arbitrary value separately.

  • #11739 1fc17e3 Thanks @Netail! - The rule useIncludes now also reports lastIndexOf() comparisons and some() calls with a strict-equality callback.

    arr.lastIndexOf(x) !== -1
    arr.some(item => item === x)
  • #11735 9bd70c7 Thanks @ematipico! - Fixed #6888. GritQL plugins can now use contains on import-clause metavariables such as $clause in import $clause from "module" patterns.

  • #11790 17d0ff0 Thanks @ematipico! - Fixed #11786: useAnchorContent now reports anchors without accessible content in HTML, Astro, Vue, and Svelte even when they have an aria-label, aria-labelledby, or title attribute, matching JSX behavior.

  • #11651 a9c4aa0 Thanks @saberoueslati! - Added the new nursery rule noVueUndeclaredDirectives, which reports custom Vue directives that are not declared by a <script setup> binding, the component’s directives option, or the rule’s globals option. Closes #11478.

    <template>
    <!-- v-highlight is not declared anywhere -->
    <div v-highlight></div>
    </template>

    Aliased named imports in single-file components are now tracked under their local name, so noUndeclaredVariables recognizes vHighlight in import { highlight as vHighlight } from "./directives".

  • #11715 f05a3c3 Thanks @ematipico! - Fixed #7795. The noJsxLiterals rule now ignores surrounding whitespace when matching literals against allowedStrings.

  • #11780 99c7049 Thanks @ematipico! - Fixed false positives in useExhaustiveSwitchCases when numeric cases use different spellings of the same value. For example, case 0x1 now covers the numeric literal type 1.

  • #11720 c7c4e2b Thanks @ematipico! - Fixed #7880: noUselessStringConcat no longer reports literal concatenations split across multiple lines when a numeric literal ends the chain.

  • #11355 27177ca Thanks @dyc3! - Improved performance of the HTML formatter for documents that contain many HTML-native or SVG-native tags.

  • #11720 c7c4e2b Thanks @ematipico! - Fixed #7949: useReadonlyClassProperties now reports static class properties that are never reassigned.

  • #11751 d37f24b Thanks @ematipico! - Fixed #7644: useImportExtensions now resolves path aliases declared by referenced TypeScript project configurations.

  • #11791 f88793c Thanks @dyc3! - Fixed a false positive in useTailwindShorthandClasses for strings in conditional tests, such as cn(m === "w-2 h-2" ? "bg-red-800" : "bg-red-400").

  • #11720 c7c4e2b Thanks @ematipico! - Fixed #7783: noNoninteractiveElementInteractions no longer reports event handlers on native <dialog> elements.

  • #11733 7030068 Thanks @dyc3! - Fixed #11730: useExhaustiveSwitchCases reports missing cases when iterating over a class property with for...of.

  • #11717 2107dae Thanks @ternaus! - Fixed #11716: the noUnknownAttribute rule now accepts fullscreen event handlers, the credentialless iframe property, and the SVG maskType property when the React dependency range allows React 19.3 or later. The credentialless and maskType properties are restricted to <iframe> and <mask> elements, respectively.

  • #11737 b7e3559 Thanks @dyc3! - Fixed #11692: noFloatingPromises now detects unhandled promises returned through generic method signatures, including Playwright fixtures.

  • #11780 99c7049 Thanks @ematipico! - Fixed #7747: useExhaustiveSwitchCases now reports missing cases for literal unions derived from const tuples with (typeof values)[number] and objects with keyof typeof object.

    Other type-aware rules, including noFloatingPromises and noUselessTypeConversion, also recognize supported indexed-access results.

  • #11724 a9a5e9a Thanks @dyc3! - Fixed redundant parentheses around binary and logical unary operands with leading line comments.

    !(
    // leading
    (a || b)
    a || b
    );
  • #11715 f05a3c3 Thanks @ematipico! - Fixed #7722: noUnusedImports no longer reports type-only imports used in computed names of declared class properties.

  • #11731 1534885 Thanks @ematipico! - Fixed #6390: Biome now offers suppression actions for noDynamicNamespaceImportAccess in editors.

  • #11751 d37f24b Thanks @ematipico! - Fixed #7533: noDescendingSpecificity no longer compares selector specificity across separate cascade layer blocks.

  • #11735 9bd70c7 Thanks @ematipico! - Fixed #6206: useUniqueElementIds no longer reports static IDs on elements in SVG contexts.

    <svg>
    <defs>
    <pattern id="dots" width="10" height="10" />
    </defs>
    <rect fill="url(#dots)" width="100%" height="100%" />
    </svg>
  • #11715 f05a3c3 Thanks @ematipico! - Fixed #5447, so the GitHub reporter now associates annotations with the correct files when Biome runs from a nested directory.

  • #11720 c7c4e2b Thanks @ematipico! - Fixed #7816: useHookAtTopLevel no longer reports methods named like hooks when called on another function’s result, such as Reactotron.configure(...).useReactNative(...).

  • #11355 27177ca Thanks @dyc3! - Removed special HTML formatter handling for the obsolete <listing> element.

  • #11731 1534885 Thanks @ematipico! - Fixed an issue where Grit plugin code fixes weren’t available as editor code actions.

  • #11726 dea163f Thanks @dyc3! - Fixed #11722: the JavaScript formatter inserts a newline before the closing angle bracket when a leading comment forces type arguments onto multiple lines.

    type Foo = Record<
    // comment
    string,
    number>;
    number
    >;
  • #9758 02ea438 Thanks @Netail! - Added the nursery rule noJsonUnsafeValues, which disallows JSON values that are unsafe to use between different tools or languages.

    Invalid:

    [
    2e308, // Number evaluating to Infinity
    -2e308, // Number evaluating to -Infinity
    "\ud83d", // String with lone surrogate
    1e-400, // Unsafe zero (too small, will evaluate to 0)
    9007199254740992, // Unsafe integer (outside safe integer range)
    2.2250738585072009e-308, // Subnormal number
    ]
  • #11790 17d0ff0 Thanks @ematipico! - Fixed #8574: the JavaScript formatter sometimes added extra parentheses and moved comments when formatting multiline expressions after operators such as !. Comments now stay beside the values they describe, without an extra pair of parentheses.

    !(
    (
    cond1 || // force this to be multi line
    cond3
    ) // comment
    cond1 || // force this to be multi line
    cond3 // comment
    );
  • #11715 f05a3c3 Thanks @ematipico! - Fixed #7711: biome lint --suppress no longer fails with conflicting rule fixes when multiple diagnostics target a declaration preceded by a multiline comment.

  • #11700 0e9fe53 Thanks @dyc3! - Added the nursery rule noObsoleteTags, which reports obsolete HTML elements in HTML and JSX, such as <font color="red">Text</font>.

  • #11735 9bd70c7 Thanks @ematipico! - Fixed #7363: Biome GritQL plugins now match TypeScript interface snippets such as interface $name { $body }.

  • #11729 f047985 Thanks @m1handr! - Added support for suite() as an alias of describe() across test analysis rules and formatter. Rules now recognize suite, fsuite, xsuite, and test.suite blocks. The formatter recognises them as test declarations.

  • #11778 4b7aa1f Thanks @ematipico! - Fixed #7727: GritQL snippets such as import $what from $where now match namespace imports, including type-only imports. Explicit import type $what from $where patterns also match type-only named and namespace imports.

  • #11715 f05a3c3 Thanks @ematipico! - Fixed #7603: useSingleJsDocAsterisk no longer reports asterisks that are part of JSDoc comment content, such as italic text, as extra line markers.

  • #11706 e19512a Thanks @dyc3! - Fixed #11704: files re-included by negation patterns in a nested .gitignore are processed when vcs.useIgnoreFile is enabled, even when the ignore file contains *.

  • #11718 76a302a Thanks @dyc3! - Fixed #8573: own-line comments before binary operators stay above the operator when javascript.formatter.operatorLinebreak is "before".

    foo
    || // comment
    bar;
    // comment
    || bar;
  • #9797 64fd314 Thanks @Netail! - Added the nursery rule useConsistentObjectKeys, which requires JSON object keys to follow a consistent Unicode representation.

2.5.13

Patch Changes

2.5.12

Patch Changes

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed Astro attribute expressions rejecting TypeScript and JSX syntax that is accepted in text expressions.

    <Component icon={<Icon />} count={total as number} onSelect={(e: Event) => e} />
  • #11440 b88f1ea Thanks @Princesseuh! - Fixed Astro attribute names being split on : and . inside an expression, such as {x && <button x-on:keyup.enter={go} client:load.foo />}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed a bare > in the children of an Astro expression being treated as markup, such as {x && <div>a > b</div>}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed HTML comments inside an Astro expression failing to parse. They are now read as trivia, wherever they appear among the children.

    {x && <div><!-- first -->text<!-- last --></div>}
    {cond && <a></a><!-- c --><b></b>}
  • #11440 b88f1ea Thanks @Princesseuh! - Fixed is:raw children inside an Astro expression being read as JSX, such as {x && <div is:raw>{not js} < & text</div>}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed an apostrophe or quote in the text of a JSX element inside an Astro expression ending the expression early, such as {items.map((i) => <li>it's {i}</li>)}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed the children of a <script> or <style> inside an Astro expression being read as JSX. Their contents are text, so braces and comparisons no longer have to be escaped.

    {cond && <style>a { color: red }</style>}
    {cond && <script>let x = {a: 1};</script>}
  • #11440 b88f1ea Thanks @Princesseuh! - Added support for template literal attribute values inside an Astro expression, such as {x && <C data-x=`t${x}` />}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed unquoted attribute values being rejected inside an Astro expression, such as {x && <a class=foo maxlength=255 href=/about>go</a>}.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed a template literal nested inside ${} breaking the rest of an Astro file, such as const href = `/blog${page === 0 ? '' : `/${page + 1}`}`;.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed a quote inside a regex character class breaking the rest of an Astro file, such as const unsafe = /[/"]/;.

  • #11508 54f3a2e Thanks @dyc3! - Added the nursery rule useFlatMathMinMax. Because Math.min() and Math.max() accept any number of arguments, the rule reports unnecessary nested calls to the same method:

    Math.max(Math.max(a, b), c);

    The fix flattens this expression to Math.max(a, b, c).

  • #11585 c5c8315 Thanks @Netail! - Fixed #11475: noUnresolvedImports no longer reports Bun runtime built-in modules (bun, bun:bundle, bun:ffi, bun:jsc, bun:sqlite, bun:test).

  • #11368 52a57b3 Thanks @Austin1serb! - Fixed #6830: Biome now reports a diagnostic for excessively deep syntax instead of overflowing the native stack while releasing the parsed tree.

  • #11596 1fc42ed Thanks @dyc3! - Added the nursery rule noThisOutsideOfClass. The rule reports this outside class members and TypeScript functions with an explicit this parameter.

    function Person(name) {
    this.name = name;
    }
  • #11555 2516335 Thanks @dyc3! - Fixed #11529, where noFloatingPromises missed unhandled Promise chains when the imported function’s module belonged to an import cycle. Cyclic modules now preserve types for exports that do not participate in recursive type dependencies.

  • #11518 0fee70c Thanks @HarperZ9! - Fixed #11500: the formatter now prints the declare modifier before accessibility modifiers on class properties. private declare readonly name: string is now formatted as declare private readonly name: string, matching Prettier and TypeScript’s canonical modifier order.

  • #11580 1277af2 Thanks @ematipico! - Fixed #5091: Biome no longer moves comments next to the < of a generic, which causes invalid TypeScript syntax:

    Generic<// a comment
    Generic<
    // a comment
  • #11577 42995d2 Thanks @ematipico! - Fixed #4592. Biome no longer crashes while parsing malformed delete expressions.

  • #11590 67963b4 Thanks @ematipico! - Fixed #6427 so Grit plugins can use function = ... as a node argument.

  • #11600 a689cb5 Thanks @ematipico! - Fixed #6644: noUnusedVariables now recognizes all interface declarations in a TypeScript declaration-merging group when the interface is referenced.

    The following snippet no longer triggers the rule.

    interface Things {
    foo: string;
    }
    interface Things {
    bar: string;
    }
    export type Key = keyof Things;
    interface Things {
    baz: string;
    }
  • #11591 d4a0716 Thanks @ematipico! - Fixed #6615. noDuplicateProperties no longer reports declarations nested in block at-rules as duplicates of declarations in their parent block.

  • #11492 f2a07aa Thanks @santichausis! - Fixed #11454: noMisplacedAssertion now recognises @fast-check/vitest’s test.prop(...) (and .concurrent.prop, .skip.prop, etc.) as a test function, the same way it already recognises test.each. The JS formatter picks up the same recognition, so a curried test.prop(...)(...) call is now formatted with the regular breakable argument layout used for test.each/test.for, instead of the single-line-hugging layout used for plain it/test calls.

    For example, Biome no longer reports the assertion below as misplaced:

    import { fc, test } from "@fast-check/vitest";
    test.prop([fc.string()])("round-trips", (s) => {
    expect(s).toBe(s);
    });
  • #11589 65742b3 Thanks @ematipico! - Fixed #4928: noUnusedVariables no longer reports a value declaration as unused when its merged namespace is referenced.

  • #11559 472dbc2 Thanks @levrik! - Fixed a false positive in noVueDuplicateKeys where a <script setup> variable initialized from props was reported as a duplicate of the prop it derives from. Biome now exempts any variable whose initializer references props, instead of only recognizing defineProps() and toRefs(props).

    For example, Biome no longer reports foo below as a duplicate key:

    <script setup>
    import { toRef } from 'vue';
    const props = defineProps(['foo']);
    const foo = toRef(props, 'foo');
    </script>
  • #11594 6586ceb Thanks @ematipico! - Fixed #6640. Biome no longer crashes when linting malformed for...of statements.

  • #11571 85b197d Thanks @ematipico! - Fixed #10838: useSortedAttributes no longer corrupts JSX attributes when nested JSX elements also require sorting.

  • #11533 97e76c0 Thanks @ematipico! - Fixed #11520, where the Biome scanner would start analysing dependencies multiple times, leading to long and unresponsive sessions.

  • #11564 18a0e1f Thanks @Netail! - Fixed the diagnostic range of noInferrableTypes so it now highlights only the type instead of including the leading : colon, spaces and comments.

  • #11540 124fdaa Thanks @ematipico! - Fixed #11537: noShorthandPropertyOverrides now compares declarations only within the same block. The rule no longer reports @supports feature queries and correctly checks nested, @keyframes, and @page blocks.

  • #11532 7ceb0ee Thanks @dyc3! - Fixed #11528: noFloatingPromises no longer reports statement-level await expressions that handle Promise values, including overloaded calls returning Promise aliases. Awaited values that resolve to arrays of Promises remain reported because their element Promises are not handled by await.

  • #11474 3c6412e Thanks @dyc3! - Fixed #10241. Biome no longer reports unsupported text expression diagnostics for double-curly text in vanilla HTML, and the formatter preserves adjacent curly-brace text.

  • #11593 6c7fd27 Thanks @dyc3! - Added the nursery rule noVueDeprecatedScopedSlots. It reports deprecated $scopedSlots references in Vue templates and component objects, and offers an unsafe replacement with $slots. For example, Biome now reports this.$scopedSlots.default inside a Vue component.

  • #11440 b88f1ea Thanks @Princesseuh! - Fixed the formatter crashing on an Astro or Svelte expression spanning several lines in a file with CRLF line endings, such as <p>{a +\r\n b}</p>.

  • #11581 f4e5ebb Thanks @dyc3! - Added the nursery rule useModernMathApis. The rule reports legacy mathematical patterns that have direct modern Math equivalents.

    Math.sqrt(a * a + b * b);
  • #11597 a20f44a Thanks @Netail! - Added the nursery rule noBunModules, which forbids the use of Bun builtin modules (e.g. bun:sqlite, bun:ffi).

  • #11545 7d54688 Thanks @dyc3! - Fixed #11542: Biome now reports HTML comments between Svelte tag attributes as parse errors.

  • #11582 b6611dd Thanks @ematipico! - Fixed #3862. Biome now parses legacy Internet Explorer filter and -ms-filter values such as progid:DXImageTransform... and alpha(opacity=40).

  • #11575 65da251 Thanks @dyc3! - Improved the Tailwind parser’s ability to recover from parsing failures. Whitespace now always allows the parser to recover and start parsing a new class.

  • #11576 0f78499 Thanks @ematipico! - Fixed #3515 and #10395, where Biome could corrupt Unicode characters while writing source received through standard input to standard output. Characters such as ⚠ and ✔ are now preserved.

  • #11539 0fca643 Thanks @ematipico! - Fixed #11512, where style/noDescendingSpecificity missed lower-specificity selectors after a later higher-specificity selector with the same tail selector.

  • #11544 040f867 Thanks @dyc3! - Fixed #11541: formatting a Svelte render tag followed by an HTML comment no longer duplicates the comment.

    <div>
    {@render children?.()}
    <!-- comment -->
    <!-- comment -->
    </div>
  • #11565 ee69e0e Thanks @ematipico! - Fixed #11525. Now the configuration schema correctly provides auto-completion for linter domains.

  • #11583 b19390c Thanks @dyc3! - Fixed #11352: useExplicitLengthCheck no longer reports length-like properties used as value-producing || fallbacks or optional chains, and it no longer offers fixes for value-producing && checks or unsafe negations.

  • #11562 753e955 Thanks @ematipico! - Fixed an issue where the Biome Language Server would start with logging level set to debug. This would cause logs to grow exponentially in long sessions.

  • #11217 7d3ee9c Thanks @dyc3! - Fixed handling of biome-ignore format suppression comments on TypeScript declared class properties with string literal names.

    class A {
    declare /* biome-ignore format: exercise suppression checking */ 'a-b': 0;
    }
  • #11497 f5d7896 Thanks @dyc3! - Added the noInvalidFileInputAccept nursery rule. The rule reports invalid literal accept values on file inputs in JSX and HTML, and normalizes common mistakes.

    <input type="file" accept="image/jpg" />
  • #11345 ac58958 Thanks @jakeleventhal! - Improved type inference performance by avoiding resolution of unused members in object arguments.

  • #11554 2d55931 Thanks @Netail! - Added the new nursery rule useReactNamingConvention, which enforces naming conventions for React values assigned from createContext, useId, and useRef. A value from createContext must be a PascalCase component name ending with Context, a value from useId must be named id or end with Id, and a value from useRef must be named ref or end with Ref.

  • #11491 1d6210b Thanks @dyc3! - Added the nursery rule noUnmodifiedLoopCondition, which reports variables in loop conditions that are never modified in the loop.

    let node = getNode();
    while (node) {
    process(node);
    }

2.5.11

Patch Changes

  • #11499 9743d0c Thanks @scs0209! - Fixed #11496: useValidAnchor now treats Astro JSX shorthand attributes like <a {href}> as a valid href.

  • #11437 88f805e Thanks @Princesseuh! - Fixed #9944: adjacent elements inside an Astro expression now parse as an implicit fragment instead of raising an error.

    {options.map(() =>
    <div />
    <div />
    )}
  • #11437 88f805e Thanks @Princesseuh! - Fixed Astro templates rejecting unclosed HTML void elements, such as {cond && <br>}.

  • #11507 e2fc036 Thanks @dyc3! - Fixed #11157: noUnusedVariables no longer reports Vue <script setup> bindings used by CSS v-bind() as unused.

  • #11398 afc4615 Thanks @dyc3! - Fixed #11389: Files passed through --stdin-file-path now use full HTML support for Astro, Svelte, and Vue when it is enabled.

  • #11526 372cd68 Thanks @dyc3! - Fixed noVueRefAsOperand to track Vue refs through declaration aliases and toRefs() properties, and to recognize useTemplateRef() results. The rule no longer reports false positives such as plain ref transfers, plain toRefs() property access, defineModel() modifiers, or the supported .effect member as operands.

    The refactor enabling these fixes also improves the performance of the rule.

  • #11458 a7cd286 Thanks @dyc3! - Fixed #11436: GritQL snippets such as export { $specifiers } from $source now match named re-exports with aliases, inline type modifiers, and multiple specifiers.

  • #11515 382b15d Thanks @dyc3! - Fixed #11390, where noFloatingPromises performed expensive full type inference for calls to non-Promise methods declared on third-party TypeScript classes. The rule now classifies those calls using targeted type information.

  • #11516 6f40e82 Thanks @levrik! - Fixed noVueRefAsOperand so it no longer reports a callback parameter (e.g. from .find(), .map()) as an unwrapped ref value just because it’s nested inside a ref(), computed(), or similar call.

    const result = computed(() => list.find((item) => item.label === "a"));

    Previously, item here was incorrectly treated as a ref value because the rule attributed it to the outer computed() call.

  • #11495 496268d Thanks @Netail! - Fixed useGraphqlNamingConvention so it no longer reports GraphQL enum value definitions with comments & descriptions and now displays a more accurate diagnostic range.

  • #11407 6ef52b0 Thanks @1678092075! - Fixed #11214: noUnusedVariables no longer reports type parameters declared by non-default function overload signatures that have an implementation.

  • #11322 5c353e6 Thanks @jp-knj! - Added a new nursery rule noAstroSetHtmlDirective, which disallows Astro’s set:html directive because untrusted content can introduce cross-site scripting vulnerabilities.

    For example, the following snippet triggers the rule:

    <div set:html={content} />
  • #11462 18883b7 Thanks @dyc3! - Fixed #10776: useVueHyphenatedAttributes no longer reports lowercase attribute names containing punctuation, such as pt:header:data-test-id and some_attr.

  • #11476 3270ca4 Thanks @dyc3! - Fixed #10330: Vue interpolation delimiters now stay attached to whitespace-sensitive element boundaries and adjacent inline siblings, wrapping their expression when needed to fit the configured line width. Interpolations followed by text now also converge after one formatting pass.

    <v-btn v-if="store.state.user" variant="text" to="/my-rooms"
    >{{ $t("nav.my-rooms") }}</v-btn
    >
    <v-btn v-if="store.state.user" variant="text" to="/my-rooms">{{
    $t("nav.my-rooms")
    }}</v-btn>
  • #11191 3e5367f Thanks @ematipico! - Added the nursery rule noUndeclaredCustomProperties, which reports references to custom properties that are not defined in available CSS, static HTML-like style attributes, or JSX string style attributes.

    For example, the following snippet triggers the rule:

    a { color: var(--undefined-color); }
  • #11435 7754894 Thanks @levrik! - Fixed: Variables and imports used as custom Vue directives are no longer reported as unused.

    For example:

    <script setup>
    const vHighlight = {
    mounted: (element) => {
    element.style.color = "red";
    },
    };
    </script>
    <template>
    <p v-highlight>Hello</p>
    </template>
  • #11501 e6acded Thanks @aminya! - Improved the performance of useArraySortCompare by skipping type inference for calls to unrelated methods.

  • #11467 66b282c Thanks @dyc3! - Fixed #11464: Biome now parses parenthesized object literals returned from arrow functions when they contain a conditional expression and a nested arrow function.

  • #11456 db9aa2a Thanks @dyc3! - Fixed #10278: Marked the fix for noThisInStatic as unsafe by default.

  • #11502 652aedb Thanks @levrik! - noGlobalAssign no longer reports assignments to a Vue <script setup> binding from a template expression, when the binding’s name happens to match a built-in global (e.g. open, parent, top).

    For example, this no longer triggers a diagnostic:

    <script setup>
    const open = defineModel();
    </script>
    <template>
    <button @click="open = !open">Toggle</button>
    </template>

2.5.10

Patch Changes

  • #11403 8f7786f Thanks @Princesseuh! - Fixed Astro rejecting JavaScript comments between attributes.

    <div /* block comment */ class="something"></div>
    <Component /* c */ client:load />
  • #11403 8f7786f Thanks @Princesseuh! - Fixed a bare < in Astro text being treated as the start of a tag, such as <p>5 < 6 and 7 > 6</p>. As in HTML, a < that cannot open a tag is text and needs no escaping.

  • #11438 3133ffa Thanks @Princesseuh! - Fixed #8294: an Astro expression holding only a comment is no longer reported as a parse error, which also stopped the whole file from being formatted.

    <div>{/* a note */}</div>
    <div class={/* a note */}>x</div>
  • #11403 8f7786f Thanks @Princesseuh! - Fixed #9165: an empty Astro expression such as <div>{}</div> no longer fails to parse. Astro renders {} as nothing.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed Astro expressions containing a comment failing to parse.

    <div>{/* block comment */ x}</div>
    <div>{/* only a comment */}</div>
  • #11403 8f7786f Thanks @Princesseuh! - Added support for Astro’s fragment shorthand.

    <>
    <p>a</p>
    </>
  • #11403 8f7786f Thanks @Princesseuh! - Fixed an Astro frontmatter block being cut short by a closing tag inside a string or comment.

    ---
    const a = "</script>";
    // </script> in a comment
    ---
  • #11403 8f7786f Thanks @Princesseuh! - Fixed --- being read as an Astro frontmatter fence when markup precedes it. Astro only recognizes frontmatter at the very start of a file, so a file opening with a comment now has no frontmatter, and its --- lines are content.

    <!-- c -->
    ---
    this is text, not frontmatter
    ---
  • #11403 8f7786f Thanks @Princesseuh! - Fixed an Astro frontmatter block ending early on a line that merely starts with a dash.

    ---
    --count;
    ---
  • #11403 8f7786f Thanks @Princesseuh! - Fixed the children of an Astro element carrying is:raw being parsed as markup instead of raw text. This now also covers <script> and <style>, whose contents Astro emits verbatim rather than processing, so they are no longer linted as JavaScript or CSS.

    <article is:raw><% awesome %></article>
    <script is:raw>{{ mustache }}</script>
  • #11403 8f7786f Thanks @Princesseuh! - Fixed Astro rejecting attribute names that start with a colon, such as :href.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed the Astro parser failing to recover from a malformed closing tag such as <div></{<//, so that a later mistake is reported where it happens rather than cascading.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed { inside an Astro <math> element opening an expression. MathML is foreign content where Astro parses no expressions, so LaTeX such as R^{2x} now survives as text. <svg> is unaffected.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed {{ at the start of an Astro expression being read as an interpolation. Astro has no {{ }} syntax, so {{ a: 1 }} and <Comp a={{ b: 1 }} /> are object literals.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed expressions inside an Astro <pre> or <textarea> being read as raw text. Astro parses both as ordinary elements, so their markup and interpolations are now parsed, and a variable used only inside one is no longer reported as unused.

    <pre>{value}</pre>
    <textarea><div>{value}</div></textarea>
  • #11403 8f7786f Thanks @Princesseuh! - Added support for template literal attribute values in Astro, such as <div class=`a ${b} c`>.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed Astro rejecting HTML5 unquoted attribute values that contain `, =, ' or ", such as <a href=a=b> and <a href=a'b>.

  • #11393 dec5a8f Thanks @1678092075! - Fixed #11207: useStrictMode no longer reports Vue event handlers such as @click="count++".

  • #11431 c065f99 Thanks @levrik! - Fixed #11429: Variables and imports used by Vue same-name bindings such as :disabled or v-bind:disabled are no longer reported as unused.

  • #11409 405dedb Thanks @ematipico! - Fixed a memory leak in the LSP server where memory usage kept growing over long editor sessions.

  • #11422 a51eff7 Thanks @dyc3! - Fixed #11416: Biome no longer crashes when parsing incomplete {let} or {const} declarations in Svelte files.

  • #11378 34b715c Thanks @Netail! - Added extra rule sources from @eslint/css. biome migrate eslint detects rules in your eslint configurations more reliably.

  • #11403 8f7786f Thanks @Princesseuh! - Fixed {#, {/, {: and {@ being read as Svelte block openings in every HTML-like file. They are now Svelte-only, so in HTML, Vue and Angular files a sequence such as {#if x} is ordinary text instead of a parse error.

  • #11443 8d45229 Thanks @ematipico! - Fixed #11390: noFloatingPromises no longer performs unnecessary type inference on call arguments when checking methods of non-generic class instances created with new.

  • #11425 9c2667b Thanks @dyc3! - Fixed #6426: GritQL plugins now match and rewrite metavariables embedded in quoted strings.

  • #11441 00317c3 Thanks @dyc3! - Improved performance of useNamedCaptureGroup, noMisplacedAssertion, noSkippedTests, noExportsInTest, noDuplicateTestHooks, noIdenticalTestTitle, useTestHooksInOrder, and useTestHooksOnTop.

2.5.9

Patch Changes

  • #11321 41386f3 Thanks @dyc3! - Fixed #11315: The CSS parser now recovers at declaration boundaries after bogus declarations, allowing subsequent valid declarations to be parsed.

  • #11248 57b197e Thanks @yanthomasdev! - Expanded the environment variable metadata used by biome rage to include BIOME_BINARY, BIOME_LOG_FILE, and RUST_BACKTRACE as well as reworded explanations for better readability.

  • #11377 a8798ea Thanks @Netail! - Added a new nursery rule useNamedLayer which disallows anonymous cascade layers.

    @layer {
    a {
    color: red;
    }
    }
  • #11327 6771cf5 Thanks @dyc3! - The HTML formatter now preserves meaningful blank lines in HTML, including spacing after elements with trailing spaces and blank lines between comment groups.

    <div>
    <!-- first group -->
    <!-- second group -->
    </div>
  • #10312 ba8aa18 Thanks @dyc3! - Added the nursery rule useTailwindShorthandClasses, which suggests shorter Tailwind utility classes. For example, the rule suggests replacing w-4 h-4 with size-4.

  • #11333 715e0cd Thanks @kkkhs! - Fixed #11328: lint/nursery/useExpect now recognizes Vitest Browser Mode expect.element() calls as assertions.

  • #11343 9b98211 Thanks @johncarmack1984! - Fixed #11311: the CSS parser now accepts Tailwind container-query variant names in @variant, such as @xl and @max-xl. These previously produced a parse error and a noUnknownAtRules diagnostic.

    @variant @xl {
    div {
    background: red;
    }
    }
  • #11220 3e8c488 Thanks @santichausis! - Fixed #9541: noUndeclaredVariables, noUnusedImports, and noUnusedVariables now correctly recognise exported variables and functions declared in one embedded <script> block as usable from a sibling <script> block, in Svelte’s <script module>/<script> pair and Vue’s non-setup <script> blocks.

    For example, Biome no longer reports greet as undeclared in the following Svelte component:

    <script module>
    export function greet() {
    console.log("Hello!");
    }
    </script>
    <script>
    greet();
    </script>
  • #11300 36430eb Thanks @dyc3! - Fixed the HTML formatter’s whitespace handling for marquee, noscript, video, audio, and object elements.

    <marquee behavior="alternate"> This text will bounce </marquee>
    <marquee behavior="alternate">This text will bounce</marquee>
  • #11299 6559e6c Thanks @jp-knj! - Added the nursery rule useAstroClientOnlyDirectiveValue, which reports Astro client:only directives without an initializer.

    For example, <Component client:only /> triggers the rule.

  • #11365 7529811 Thanks @MHJahanbakhsh! - Fixed #11229: The useGenericFontNames rule now treats math as a valid generic font family.

  • #11346 674f5f4 Thanks @Jayllyz! - Fixed #11335: noComponentHookFactories now reports a use-prefixed variable only when a function is assigned to it directly.

    function factory() {
    const useColors = true; // no longer reported
    const useStore = createStore({ count: 0 }); // no longer reported
    const useData = () => useState(null); // still reported
    return useColors;
    }
  • #11334 c87c46a Thanks @zkasuran! - Fixed #11317: noSvgWithoutTitle no longer reports an svg that uses the boolean shorthand aria-hidden (equivalent to aria-hidden={true} in React).

  • #11364 13853b1 Thanks @ematipico! - Fixed a bug where useJsxKeyInIterable incorrectly flagged Astro files.

  • #11321 41386f3 Thanks @dyc3! - Fixed #11315: Invalid CSS declarations in HTML style attributes now produce parser diagnostics instead of causing a panic.

  • #11325 67c3bf0 Thanks @dyc3! - Fixed HTML text wrapping to account for the width of an adjacent closing tag, avoiding lines that exceed the configured width when the final word and tag must move together.

    <a-long-long-long-element
    >foo bar foo bar foo bar foo bar foo bar foo bar foo bar</a-long-long-long-element
    >foo bar foo bar foo bar foo bar foo bar foo
    bar</a-long-long-long-element
    >
  • #11367 fe5b5d4 Thanks @ematipico! - Fixed TypeScript compilerOptions.paths resolution when mapping targets omit ./. Biome now resolves these targets relative to their configured path base.

  • #11316 17e48d6 Thanks @wanxiankai! - Fixed #11289: the safe fix for noExtraBooleanCast now preserves parentheses around nested conditional expressions.

  • #11254 d25d113 Thanks @dyc3! - Fixed #11242: Biome no longer crashes with an access violation when analysing files on Windows ARM64.

  • #11221 85aac73 Thanks @freeatnet! - Added the nursery rule noUnsafeTypeAssertion, which disallows TypeScript type assertions while allowing const assertions.

    const value = input as SomeType;
  • #11314 7ffb677 Thanks @ematipico! - Fixed #11310: Restored the performance of noMisusedPromises and noFloatingPromises when analyzed expressions share deep imported type paths.

  • #11356 6cd3263 Thanks @johncarmack1984! - The Tailwind parser now understands modifiers on bare utilities (@container/sidebar, shadow/50).

  • #11318 76059e9 Thanks @johncarmack1984! - The Tailwind parser now understands container-query variants (@sm:, @max-lg:, @min-[400px]:) and child and descendant variants (*:, **:).

  • #11357 faa2074 Thanks @johncarmack1984! - The Tailwind parser now accepts the legacy leading ! important marker (!flex, hover:!p-4).

  • #11344 f34e15c Thanks @johncarmack1984! - The Tailwind parser now understands combinator selectors in arbitrary variants (has-[>svg]:, has-[+p]:), modifiers on variants (group-hover/menu:, @sm/main:), and arbitrary container-query sizes (@[400px]:).

  • #11324 2f5d452 Thanks @dyc3! - Fixed HTML formatting that inserted rendered whitespace between an element and touching text when the line wrapped.

    <div>
    before<meter value=".5"></meter>
    after
    before<meter value=".5"></meter
    >after
    </div>
  • #11312 e65f07e Thanks @xosnos! - Added a new nursery rule useControlLabel for both HTML and JSX, which reports interactive control elements (button, menuitem) without an accessible label.

    <button />
  • #11364 13853b1 Thanks @ematipico! - Fixed SVG parsing for files with an XML declaration followed by a PUBLIC doctype, such as <?xml version="1.0"?><!DOCTYPE svg PUBLIC "a" "b">.

  • #11301 610ee28 Thanks @dyc3! - Fixed parent tag wrapping when an HTML element starts or ends with a block-like or hidden child such as source, track, or param.

    <video src="brave.webm"><track kind="subtitles" src="brave.en.vtt"></video>
    <video src="brave.webm">
    <track kind="subtitles" src="brave.en.vtt">
    </video>

2.5.8

Patch Changes

  • #10710 0a0fbc1 Thanks @dyc3! - Added a new nursery rule useReactCompiler, which reports diagnostics from React Compiler lint mode.

  • #11251 ea9dd8a Thanks @dyc3! - Improved performance of noImportCycles.

  • #11247 52b44d6 Thanks @dyc3! - Added the nursery rule noSvelteLegacyConst, which disallows legacy Svelte {@const} tags and recommends declaration tags with $derived().

    Invalid:

    {#each boxes as box}
    {@const area = box.width * box.height}
    <p>{area}</p>
    {/each}

    Valid:

    {#each boxes as box}
    {const area = $derived(box.width * box.height)}
    <p>{area}</p>
    {/each}
  • #11252 d5f5704 Thanks @Turtle-Hwan! - Fixed #11250: useAwait no longer reports async functions that contain an await using declaration.

  • #11143 6be7be1 Thanks @vznh! - Fixed #11017: noUselessUndefined no longer reports return undefined when the enclosing function has a return type annotation other than undefined or void.

  • #11234 caefe39 Thanks @subotac! - Fixed #11228: CSS block comments between a declaration colon and value now preserve their source indentation.

    :root {
    --font-stack:
    /* comment */
    /* comment */
    system-ui;
    }
  • #11285 bca1f73 Thanks @denbezrukov! - Fixed #11280: CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.

    :/* comment */ where(div) {}
    :where(/* comment */ div) {}
  • #11080 af16a0b Thanks @dyc3! - HTML style attribute values are now parsed as CSS. All Biome CSS lint rules are applied to the style attributes.

  • #11195 6a85588 Thanks @dyc3! - Fixed Svelte files failing to parse when an expression begins with an object literal.

    Now the following snippet is correctly parsed:

    <p>{{ a: true }}</p>
    <div class={{ active: isActive }}></div>
  • #11173 481d008 Thanks @Austin1serb! - Fixed #10242: JavaScript GritQL patterns with multiple metavariables now match snippets consistently in WebAssembly.

  • #11187 23c0369 Thanks @ematipico! - Added the nursery rule noInvalidPropertyInitValue, which reports an @property whose initial-value does not match its syntax descriptor. For example, the following declaration triggers the rule because red is not a <length>:

    @property --size {
    syntax: "<length>";
    inherits: false;
    initial-value: red;
    }
  • #11272 73896e6 Thanks @ematipico! - Improved the diagnostic emitted by noRootType.

  • #11240 bd0b68d Thanks @ematipico! - Fixed #11223: Improved the performance of noMisusedPromises when analyzing async class methods that call other methods through this.

  • #11172 4a0bc5c Thanks @saberoueslati! - Fixed #10806: noUselessFragments no longer causes Biome to panic when its unsafe fix removes a fragment used as a JSX attribute value.

  • #11227 4d603b0 Thanks @saberoueslati! - Fixed #11178: noUndeclaredVariables no longer reports Vue’s built-in instance properties, such as $slots and $attrs, in template expressions or $event in inline event-handler expressions. The instance properties are still reported inside <script setup>, where they are not defined.

  • #11187 23c0369 Thanks @ematipico! - Fixed CSS parsing of registered custom properties: Biome now correctly validates the syntax descriptor of @property rules.

2.5.7

Patch Changes

  • #10822 c171b3b Thanks @pkallos! - Added the option ignoreIfStatements to useNullishCoalescing. Biome now flags if statements that only assign to a nullish variable (such as if (!a) { a = b }) and can rewrite them to ??=. When enabled, Biome ignores those if statements.

  • #11136 e63354c Thanks @AkashNaickar! - Added a new nursery rule noExtendNative, which reports extending the prototype of a built-in object.

  • #10094 e007143 Thanks @THEjacob1000! - Added the nursery rule noTailwindArbitraryValue. Biome now reports Tailwind CSS arbitrary values such as w-[400px], including in HTML/JSX class attributes, configured utility functions, and tagged templates.

  • #11184 135f476 Thanks @subotac! - Fixed #11176: noUnknownPseudoClass now recognizes Vue’s :deep() pseudo-class inside .vue style blocks.

  • #8239 a519f9d Thanks @cormacrelf! - Fixed #8233, where Biome CLI in stdin mode didn’t work correctly when handling files in projects with nested configurations. For example, with the following structure, --stdin-file-path=subdirectory/... would not use the nested configuration in subdirectory/biome.json:

    ├── biome.json
    └── subdirectory
    ├── biome.json
    └── lib.js
    Terminal window
    biome format --write --stdin-file-path=subdirectory/lib.js < subdirectory/lib.js

    Now, the nested configuration is correctly picked up and applied.

    In addition, Biome now shows a warning if --stdin-file-path is provided but that path is ignored and therefore not formatted or fixed.

  • #11138 8c2c6bd Thanks @ematipico! - Fixed noUnnecessaryConditions: Biome now chooses the same function overload as TypeScript when an argument is a callback, so conditions that were previously missed are reported.

    The following code is now invalid, because a parameter typed () => void accepts an async callback and schedule therefore returns string:

    declare function schedule(handler: () => void): string;
    declare function schedule(handler: () => Promise<void>): string | undefined;
    schedule(async () => {}) ?? "fallback";

    The following code is also now invalid, because map(() => 42) returns 42:

    type Mapper<T> = () => T;
    declare function map<T>(mapper: Mapper<T>): T;
    map(() => 42) || flag;
  • #11138 8c2c6bd Thanks @ematipico! - Fixed #11087: noUnnecessaryConditions no longer reports optional chains and nullish coalescing whose receiver can be nullish.

    For example, the optional chain and fallback in the following code are no longer reported:

    declare const usage: { range: { startDate: string } } | null;
    const startDate = usage?.range.startDate ?? "N/A";
  • #11118 9c16840 Thanks @subotac! - Fixed #11098: The HTML formatter now preserves the configured trailing newline when a file ends with a comment.

    <!-- trailing comment -->
    \ No newline at end of file
    <!-- trailing comment -->
  • #11201 0e80610 Thanks @Bishwas-py! - Fixed #11182: suppression comments for noPositiveTabindex now suppress the rule in HTML files when the attributes of the element span multiple lines.

  • #11079 607afd2 Thanks @dyc3! - The HTML formatter now lays out the srcset attribute of <img> and <source> as the list of candidates it is. Runs of whitespace between candidates collapse, and once the list no longer fits on one line each candidate goes on its own line with the descriptors aligned:

    <img srcset="/visual@0.5.png 400w, /visual.png 805w, /visual@2x.png 1610w, /visual@3x.png 2415w" />
    <img
    srcset="
    /visual@0.5.png 400w,
    /visual.png 805w,
    /visual@2x.png 1610w,
    /visual@3x.png 2415w
    "
    />
  • #11156 fed72c7 Thanks @saberoueslati! - Fixed #11129: noUnusedVariables no longer reports Vue bindings as unused when they are assigned through automatically unwrapped template refs.

  • #11124 d890b39 Thanks @denbezrukov! - Fixed CSS formatting of line comments between a declaration colon and value to preserve their source indentation.

    .test {
    background:
    /////// foo
    // bar
    /////// foo
    // bar
    radial-gradient(circle, #000, transparent);
    }
  • #11113 3d8ab73 Thanks @denbezrukov! - Fixed CSS formatting of long block comments between comma-separated property values:

    .foo {
    box-shadow:
    1000px /* long long long long long long long long long long long long comment */ 1000px /* long long long long long long long long long comment */ 2px color(srgb 0.555555555 0.555555555 0.555555555),
    1000px
    /* long long long long long long long long long long long long comment */
    1000px /* long long long long long long long long long comment */ 2px
    color(srgb 0.555555555 0.555555555 0.555555555),
    1px 1px black;
    }
  • #11127 da5c1a5 Thanks @dyc3! - The HTML formatter now picks the quote character for an attribute by counting the quotes in the value rather than looking only for a double quote. &apos; and &quot; count as the characters they stand for, and only the character that ends up as the delimiter stays escaped:

    <div title='123 &apos;&quot; 456'></div>
    <div title="123 '&quot; 456"></div>

    Entities that are not quotes, such as &amp; or &[#39](https://github.com/biomejs/biome/issues/39);, are left exactly as written.

  • #11193 77035bb Thanks @dyc3! - Fixed the HTML formatter collapsing the blank line between an element and the text that follows it. A blank line before text is now kept, the way one before another element already was:

    <div>foo</div>
    text
  • #11106 ad80f57 Thanks @dyc3! - The HTML formatter now writes the HTML5 doctype in lowercase, matching Prettier:

    <!DOCTYPE html>
    <!doctype html>

    This only applies to a plain .html file whose doctype stands alone. A doctype that names a DTD keeps the case it was written with, since the rest of the declaration is not lowercased either:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

    A .vue, .svelte, or .astro file keeps whatever the author wrote.

  • #11188 60679db Thanks @dyc3! - Fixed the HTML formatter printing a comment twice when it ended the line of the last element in a document:

    text<!-- a --><!-- a -->
    text<!-- a -->
  • #11077 4dcd0d9 Thanks @dyc3! - Fixed a bug where the HTML formatter collapsed the whitespace inside <textarea>, <xmp> and <plaintext>, changing what the page renders.

    <textarea>
    line one
    line two </textarea>
    <textarea>line one line two</textarea>

    Biome now prints the content of these elements exactly as it appears in the source, matching the existing behavior for <pre>.

  • #11194 abfbb11 Thanks @dyc3! - Fixed the HTML formatter refusing to format a Svelte file containing an array pattern that skips a position:

    {#each animals as [, value]}
    <p>{value}</p>
    {/each}
  • #10094 e007143 Thanks @THEjacob1000! - Fixed useSortedClasses to correctly detect unsorted classes in static member expression tagged templates (e.g. tw.div\…“). Previously, these were silently skipped due to surrounding whitespace trivia not being stripped from the tag name.

  • #11078 10da30e Thanks @dyc3! - Fixed Vue single-file components failing to parse when they contain a custom block such as <i18n> or <docs>, or a <template> written in another language. Their content is no longer read as HTML, so a block may hold whatever its own tooling expects:

    <docs>
    This block is prose, and it may mention a `<my-component>` without closing it.
    </docs>
    <template lang="pug">
    .test
    #foo
    </template>

    Previously both blocks produced a parse error and the whole file was left unformatted. Biome now prints their content unchanged while still formatting the opening tag.

  • #11231 4afd901 Thanks @ematipico! - Improved the performance of the following lint rules:

  • #11134 2fa0a62 Thanks @yanthomasdev! - Clarified the warning emitted when using the experimental json and json-pretty reporters.

  • #11198 ed88b13 Thanks @saberoueslati! - Fixed #11171: variables referenced only inside a Svelte attachment ({@attach ...}) are no longer reported as unused by noUnusedVariables and noUnusedImports.

  • #11155 6ee17ea Thanks @dyc3! - Improved performance when printing diagnostics to the console.

  • #11160 217f8ad Thanks @dyc3! - Improved the performance of noFloatingPromises by skipping type inference for assignment statements, which are always considered handled.

  • #11159 26c23d9 Thanks @saberoueslati! - Fixed #11144: noFloatingPromises no longer reports already-awaited optional Promise values.

  • #11138 8c2c6bd Thanks @ematipico! - Fixed #11121: noUnnecessaryConditions no longer reports conditions based on an inapplicable function overload.

    For example, the condition in the following code is no longer reported because query({}) selects the overload that returns boolean:

    declare function query(options: { initial: string }): { isPending: false };
    declare function query(options: { initial?: string }): { isPending: boolean };
    const { isPending } = query({});
    isPending || fallback;
  • #11152 c4fc6a9 Thanks @dyc3! - Improved the performance of collecting rule timings with --profile-rules in heavily multithreaded environments.

  • #11128 4d3ff76 Thanks @ematipico! - Fixed #7635: noDeprecatedImports now detects deprecated ambient declarations that are exported separately.

  • #11117 01f7ef5 Thanks @subotac! - Fixed #11014: noDelete no longer reports process.env["FOO"] style property deletions.

  • #11168 9847e68 Thanks @saberoueslati! - Added the nursery rule noNonScalableViewport, which reports viewport metadata that disables user scaling with user-scalable=no.

    For example:

    <meta name="viewport" content="width=device-width, user-scalable=no" />
  • #11154 a1d6b1f Thanks @dyc3! - Improved the performance of noImportCycles by skipping graph traversals for imports that cannot be part of a cycle.

  • #11175 d96d6dd Thanks @ematipico! - Fixed CSS parsing of registered custom properties: Biome now correctly validates the syntax descriptor of @property rules.