#1137907a0073 Thanks @Netail! - Added the nursery rule useLayeredStyles, which enforces that style rules are defined within a cascade layer and import rules to import its styles into a cascade layer.
#11667e997900 Thanks @devtechedge! - Added the nursery rule useBetterDomTraversing, which prefers .firstChild, .firstElementChild, .closest(), and merged .querySelector() calls over positional DOM traversal.
#11657e322040 Thanks @ematipico! - Fixed #7495: noUselessConstructor now ignores TypeScript constructors that forward at least one argument to super, preserving constructors that narrow the subclass's accepted parameter types. The exemption also applies when the parent and child signatures are identical; JavaScript and zero-argument forwarding behavior are unchanged.
#1163100dbd3a Thanks @ematipico! - Reduced unnecessary type inference when type-aware lint rules inspect members of namespace imports from libraries such as Zod. Fixed type inference so blanket re-exports do not expose default exports.
#11628a2f8ff7 Thanks @dyc3! - Added the nursery rule noXorAsExponentiation, which reports the bitwise XOR operator ^ between two decimal integer literals, where the exponentiation operator ** was likely intended.
#11676840a52a Thanks @dyc3! - Fixed #11672 and #11671 by disabling the experimental capitalized-call and effect-dependency checks in useReactCompiler, matching their exclusion from upstream's recommended lint preset. Valid calls such as Intl.NumberFormat() and captures of variables declared inside effects no longer produce these diagnostics.
#1166049485ed Thanks @ematipico! - Fixed #11653: Astro template suppression comments ({/* biome-ignore lint: reason */}) now suppress matching HTML lint diagnostics on the following line when full HTML support is enabled.
#1163062e1fc5 Thanks @dyc3! - Fixed the HTML formatter inserting whitespace between adjacent Svelte expressions when their combined length exceeds the line width.
#11658ed4bfa4 Thanks @fredrikblau! - Fixed #11644: useHeadingContent no longer reports headings that render their text with a directive: set:html and set:text in Astro files, v-html and v-text in Vue files.
interfaceContext{doSomething():Promise<void>;}declarefunctiontest(callback:(ctx: Context)=>Promise<void>):void;test(async(ctx)=>{ctx.doSomething();// now reported as a floating promise
});
#11698b019982 Thanks @denbezrukov! - Fixed parsing of unquoted CSS URLs beginning with @ or !, such as url(@/assets/icon.svg) and url(!font.woff2). Preserved escaped and non-ASCII whitespace in raw URLs during formatting.
#11622c23e4c7 Thanks @Netail! - Added the nursery rule noUnsafeIframeSandbox, which reports iframe elements whose sandbox attribute combines allow-scripts and allow-same-origin, since that combination lets the embedded document remove its own sandboxing.
#116704969ee1 Thanks @ematipico! - Fixed #7527: suppression actions for diagnostics emitted on comments are now inserted before the diagnostic comment. In particular, suppressing noTsIgnore now places the biome-ignore comment before @ts-ignore.
#11440b88f1ea 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 />}.
#11440b88f1ea Thanks @Princesseuh! - Fixed a bare > in the children of an Astro expression being treated as markup, such as {x && <div>a > b</div>}.
#11440b88f1ea 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>}
#11440b88f1ea Thanks @Princesseuh! - Fixed is:raw children inside an Astro expression being read as JSX, such as {x && <div is:raw>{not js} < & text</div>}.
#11440b88f1ea 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>)}.
#11440b88f1ea 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>}
#11440b88f1ea Thanks @Princesseuh! - Added support for template literal attribute values inside an Astro expression, such as {x && <C data-x=`t${x}` />}.
#11440b88f1ea Thanks @Princesseuh! - Fixed unquoted attribute values being rejected inside an Astro expression, such as {x && <a class=foo maxlength=255 href=/about>go</a>}.
#11440b88f1ea Thanks @Princesseuh! - Fixed a template literal nested inside ${} breaking the rest of an Astro file, such as const href = `/blog${page === 0 ? '' : `/${page + 1}`}`;.
#11440b88f1ea Thanks @Princesseuh! - Fixed a quote inside a regex character class breaking the rest of an Astro file, such as const unsafe = /[/"]/;.
#1150854f3a2e 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).
#11585c5c8315 Thanks @Netail! - Fixed #11475: noUnresolvedImports no longer reports Bun runtime built-in modules (bun, bun:bundle, bun:ffi, bun:jsc, bun:sqlite, bun:test).
#1136852a57b3 Thanks @Austin1serb! - Fixed #6830: Biome now reports a diagnostic for excessively deep syntax instead of overflowing the native stack while releasing the parsed tree.
#115961fc42ed Thanks @dyc3! - Added the nursery rule noThisOutsideOfClass. The rule reports this outside class members and TypeScript functions with an explicit this parameter.
functionPerson(name){this.name=name;}
#115552516335 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.
#115180fee70c 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.
#115801277af2 Thanks @ematipico! - Fixed #5091: Biome no longer moves comments next to the < of a generic, which causes invalid TypeScript syntax:
#11492f2a07aa 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:
#11559472dbc2 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:
#1153397e76c0 Thanks @ematipico! - Fixed #11520, where the Biome scanner would start analysing dependencies multiple times, leading to long and unresponsive sessions.
#1156418a0e1f Thanks @Netail! - Fixed the diagnostic range of noInferrableTypes so it now highlights only the type instead of including the leading : colon, spaces and comments.
#11540124fdaa 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.
#115327ceb0ee 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.
#114743c6412e 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.
#115936c7fd27 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.
#11440b88f1ea 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>.
#11581f4e5ebb 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);
#11597a20f44a Thanks @Netail! - Added the nursery rule noBunModules, which forbids the use of Bun builtin modules (e.g. bun:sqlite, bun:ffi).
#115457d54688 Thanks @dyc3! - Fixed #11542: Biome now reports HTML comments between Svelte tag attributes as parse errors.
#11582b6611dd Thanks @ematipico! - Fixed #3862. Biome now parses legacy Internet Explorer filter and -ms-filter values such as progid:DXImageTransform... and alpha(opacity=40).
#1157565da251 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.
#115760f78499 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.
#11565ee69e0e Thanks @ematipico! - Fixed #11525. Now the configuration schema correctly provides auto-completion for linter domains.
#11583b19390c 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.
#11562753e955 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.
#112177d3ee9c Thanks @dyc3! - Fixed handling of biome-ignore format suppression comments on TypeScript declared class properties with string literal names.
#11497f5d7896 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.
<inputtype="file"accept="image/jpg"/>
#11345ac58958 Thanks @jakeleventhal! - Improved type inference performance by avoiding resolution of unused members in object arguments.
#115542d55931 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.
#1143788f805e 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 />
)}
#1143788f805e Thanks @Princesseuh! - Fixed Astro templates rejecting unclosed HTML void elements, such as {cond && <br>}.
#11398afc4615 Thanks @dyc3! - Fixed #11389: Files passed through --stdin-file-path now use full HTML support for Astro, Svelte, and Vue when it is enabled.
#11526372cd68 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.
#11458a7cd286 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.
#11515382b15d 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.
#115166f40e82 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.
Previously, item here was incorrectly treated as a ref value because the rule attributed it to the outer computed() call.
#11495496268d Thanks @Netail! - Fixed useGraphqlNamingConvention so it no longer reports GraphQL enum value definitions with comments & descriptions and now displays a more accurate diagnostic range.
#113225c353e6 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:
#114763270ca4 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.
#111913e5367f 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);}
#114357754894 Thanks @levrik! - Fixed: Variables and imports used as custom Vue directives are no longer reported as unused.
#1146766b282c 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.
#11502652aedb 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:
#114038f7786f 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.
#114383133ffa 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>
#114038f7786f Thanks @Princesseuh! - Fixed #9165: an empty Astro expression such as <div>{}</div> no longer fails to parse. Astro renders {} as nothing.
#114038f7786f 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
---
#114038f7786f 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
---
#114038f7786f Thanks @Princesseuh! - Fixed an Astro frontmatter block ending early on a line that merely starts with a dash.
---
--count;
---
#114038f7786f 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.
#114038f7786f Thanks @Princesseuh! - Fixed Astro rejecting attribute names that start with a colon, such as :href.
#114038f7786f 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.
#114038f7786f 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.
#114038f7786f 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.
#114038f7786f 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.
#114038f7786f Thanks @Princesseuh! - Added support for template literal attribute values in Astro, such as <div class=`a ${b} c`>.
#114038f7786f Thanks @Princesseuh! - Fixed Astro rejecting HTML5 unquoted attribute values that contain `, =, ' or ", such as <a href=a=b> and <a href=a'b>.
#11431c065f99 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.
#11409405dedb Thanks @ematipico! - Fixed a memory leak in the LSP server where memory usage kept growing over long editor sessions.
#11422a51eff7 Thanks @dyc3! - Fixed #11416: Biome no longer crashes when parsing incomplete {let} or {const} declarations in Svelte files.
#1137834b715c Thanks @Netail! - Added extra rule sources from @eslint/css. biome migrate eslint detects rules in your eslint configurations more reliably.
#114038f7786f 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.
#114438d45229 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.
#114259c2667b Thanks @dyc3! - Fixed #6426: GritQL plugins now match and rewrite metavariables embedded in quoted strings.
#1132141386f3 Thanks @dyc3! - Fixed #11315: The CSS parser now recovers at declaration boundaries after bogus declarations, allowing subsequent valid declarations to be parsed.
#1124857b197e 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.
#113276771cf5 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>
#10312ba8aa18 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.
#11333715e0cd Thanks @kkkhs! - Fixed #11328: lint/nursery/useExpect now recognizes Vitest Browser Mode expect.element() calls as assertions.
#113439b98211 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.
#1130036430eb 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>
#112996559e6c 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.
functionfactory(){constuseColors=true;// no longer reported
constuseStore=createStore({count:0});// no longer reported
constuseData=()=>useState(null);// still reported
returnuseColors;}
#1132141386f3 Thanks @dyc3! - Fixed #11315: Invalid CSS declarations in HTML style attributes now produce parser diagnostics instead of causing a panic.
#1132567c3bf0 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
>
#11367fe5b5d4 Thanks @ematipico! - Fixed TypeScript compilerOptions.paths resolution when mapping targets omit ./. Biome now resolves these targets relative to their configured path base.
#11312e65f07e 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/>
#1136413853b1 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">.
#11301610ee28 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.
#111436be7be1 Thanks @vznh! - Fixed #11017: noUselessUndefined no longer reports return undefined when the enclosing function has a return type annotation other than undefined or void.
#11234caefe39 Thanks @subotac! - Fixed #11228: CSS block comments between a declaration colon and value now preserve their source indentation.
#11285bca1f73 Thanks @denbezrukov! - Fixed #11280: CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.
#11173481d008 Thanks @Austin1serb! - Fixed #10242: JavaScript GritQL patterns with multiple metavariables now match snippets consistently in WebAssembly.
#1118723c0369 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>:
#112274d603b0 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.
#1118723c0369 Thanks @ematipico! - Fixed CSS parsing of registered custom properties: Biome now correctly validates the syntax descriptor of @property rules.
#10822c171b3b 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.
#10094e007143 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.
#8239a519f9d 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 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.
#111388c2c6bd 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:
#11079607afd2 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:
#11124d890b39 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);
}
#111133d8ab73 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;
}
#11127da5c1a5 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. ' and " count as the characters they stand for, and only the character that ends up as the delimiter stays escaped:
Entities that are not quotes, such as & or &[#​39](https://github.com/biomejs/biome/issues/39);, are left exactly as written.
#1119377035bb 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
#11106ad80f57 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.
#1118860679db 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 -->
#110774dcd0d9 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>.
#11194abfbb11 Thanks @dyc3! - Fixed the HTML formatter refusing to format a Svelte file containing an array pattern that skips a position:
{#eachanimalsas[,value]}<p>{value}</p>{/each}
#10094e007143 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.
#1107810da30e 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:
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.
#111556ee17ea Thanks @dyc3! - Improved performance when printing diagnostics to the console.
#11160217f8ad Thanks @dyc3! - Improved the performance of noFloatingPromises by skipping type inference for assignment statements, which are always considered handled.
#1015250a9bd8 Thanks @Zelys-DFKH! - 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.
#105335809875 Thanks @Mokto! - 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.
#110400abb620 Thanks @Mokto! - Fixed an issue where the HTML formatter would duplicate a comment placed directly before a Svelte {@const ...} or {@debug ...} block. The duplication compounded on every subsequent --write, causing the file to grow exponentially.
#108586d18204 Thanks @ruidosujeira! - Fixed #10839: Svelte {#each} array destructuring no longer includes spaces inside square brackets, and multiline bind function expressions now indent their getter, setter, and function body correctly.
#110092c36626 Thanks @ematipico! - 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.
#109739cb044c Thanks @ematipico! - Fixed false positives in noMisleadingReturnType when generic-constraint, normalization, substitution, or structural return-type comparison cannot complete. The rule now suppresses diagnostics rather than suggesting a return type derived from partial information. For example, this unresolved return type is no longer reported:
#1107115047a2 Thanks @dyc3! - The HTML parser now accepts mixed-case doctype declarations.
#11030cc90e65 Thanks @marschattha! - The rdjson reporter now populates the severity field of each diagnostic (ERROR, WARNING, or INFO), so tools consuming Reviewdog Diagnostic Format output no longer need to assume a default severity.
#110092c36626 Thanks @ematipico! - Fixed a performance regression in type-aware JavaScript lint rules by inferring only requested types and memoizing export resolution.
#11056903b177 Thanks @dyc3! - Added support for Svelte declaration tags using let and const. Biome can now parse, format, and lint bindings declared in these tags.
#1104589c27c6 Thanks @ematipico! - Improved the performance of Biome formatter up to ~7% across the board.
#9806781d68d 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.
#10972ab8c21b Thanks @ematipico! - Fixed useExhaustiveSwitchCases for unions of bigint literals. The rule now reports missing bigint cases and compares bigint literals by value, including binary, octal, hexadecimal, and separator-containing spellings. For example, this switch now reports the missing 2n case:
#10972ab8c21b Thanks @ematipico! - Fixed false positives in noBaseToString and useNullishCoalescing when member, stringification, or nullish inference cannot complete. These rules now suppress diagnostics instead of reporting from partial type information. For example, neither expression is reported when a recursive type cannot be fully resolved:
#10957cf263c4 Thanks @dyc3! - Fixed noThenProperty failing to detect Object.fromEntries, Object.defineProperty, and Reflect.defineProperty calls with comments between their tokens.
#10983edc0ed7 Thanks @ayaangazali! - Fixed #10980: useAriaPropsSupportedByRole no longer reports false positives when the attribute that determines an element's implicit ARIA role is written as a shorthand attribute, such as <a {href} aria-label="..."> in Astro and Svelte files.
Shorthand attributes are now taken into account when computing the implicit role, so the anchor above correctly resolves to the link role instead of generic.
#1088989526e3 Thanks @denbezrukov! - Fixed CSS formatter casing for syntax-owned names while preserving author-defined names, including scoped keyframes and container scroll-state queries.
#10964794ccd0 Thanks @denbezrukov! - Fixed CSS formatting for comments between declaration values and !important.
-a { color: /* before */ /* after */ red !important; }
+a { color: /* before */ red /* after */ !important; }
#10993b7a9694 Thanks @denbezrukov! - Fixed the CSS formatter to preserve comments on the correct side of selector combinators and before declaration blocks.
This syntax is valid Vue and equivalent to v-bind="props", so the parser now accepts it (along with the longhand v-bind:="props") instead of crashing while building a diagnostic for a missing argument.
#109367df46f5 Thanks @ematipico! - Improved generic tuple inference for useIncludes. The rule now recognizes specialised tuple element types returned through generic aliases.
#1096972d309b Thanks @ematipico! - Fixed an issue where Biome logs became too verbose, dumping information not relevant to user's operations.
e62f6b6 Thanks @ematipico! - Fixed #10963: Biome no longer panics when a type-aware rule such as noFloatingPromises checks a call to a function with multiple call signatures imported from another module.
#10931899c60d Thanks @ematipico! - Fixed check --write command. Now the command reports code frame of the formatted code, if the formatter is enabled.
#10904ceee4f4 Thanks @qzwxsaedc! - Fixed #10892: noUnnecessaryConditions no longer reports a false positive when checking a member of a discriminated union that is accessed through a default type-only namespace import. The following code is no longer flagged:
#10962f0a67f2 Thanks @ematipico! - Biome no longer removes embedded styles and scripts in HTML files.
#110005039a1e Thanks @ematipico! - Fixed a bug where closing one editor stopped a shared Biome daemon used by other editors. LSP proxy processes now exit when either the editor or daemon disconnects.
#109924bf9b21 Thanks @ematipico! - Fixed noMisusedPromises: The rule now reports Promise-returning callbacks where a synchronous callback is expected when calls use tuple spreads or tuple rest parameters, including generic and deeply nested tuples, and when constructor signatures come from interface or object types. Recursive or excessively nested tuple spreads use a conservative fallback so analysis terminates.
For example, the following callback is now reported.
#10915b3b12b3 Thanks @Functionhx! - Added the rule noNegationInEqualityCheck. The rule flags negated expressions on the left side of strict equality checks like !foo === bar — due to operator precedence this evaluates as (!foo) === bar which is almost always a mistake for foo !== bar.
The rule provides an unsafe fix that flips the operator.
#10970bd1038b Thanks @ematipico! - Improved overload selection for noMisusedPromises. Biome now handles overloaded calls, overloaded constructors, rest parameters, union arguments, and generic constraints without selecting an incompatible signature. For example, noMisusedPromises now reports the async callback passed to the synchronous overload:
#10931899c60d Thanks @ematipico! - Fixed an orchestration issue that could lead to deadlocks when type-aware rules are enabled.
#1096972d309b Thanks @ematipico! - Hardened the Biome Language Server by improving its synchronisation logic.
#10972ab8c21b Thanks @ematipico! - Fixed false positives in noMisusedPromises and useAwaitThenable when Promise or thenable inference cannot complete. These rules now suppress diagnostics instead of treating incomplete type information as a definite result. For example, useAwaitThenable no longer reports await value when the value's thenability is unknown:
The rule's fix now wraps the comment in a real JSX expression container ({/* comment */}) instead of re-inserting the braces as plain JSX text, so the fixed code is no longer reported again by the same rule.
#10891ecca79e Thanks @ematipico! - Fixed #10885: prevented a module-inference regression introduced by a housekeeping change.
#1088660c8043 Thanks @dyc3! - Fixed #10727: Biome now breaks the arguments of curried test.each, it.each, describe.each, and test.for calls when they exceed the configured line width.
- test.each([[1, 2]])("a description that is long enough to push the hugged opening line beyond the print width", (a, b) => {
- expect(a).toBe(b);
- });
+ test.each([[1, 2]])(
+ "a description that is long enough to push the hugged opening line beyond the print width",
+ (a, b) => {
+ expect(a).toBe(b);
+ },
+ );
#1089501a85f0 Thanks @ematipico! - Biome will now remove stale Unix daemon sockets from older Biome versions when starting a newer daemon.
#1081586613d5 Thanks @WaterWhisperer! - Fixed a parser panic reported in #10708: Biome now recovers when unsupported CSS Modules @value rules or scoped @keyframes names end at EOF.
#10534da9b403 Thanks @Mokto! - Fixed noUnusedVariables false positives in Svelte files: Svelte store subscriptions ($store references in templates now keep the underlying store binding from being flagged), and $bindable() props that are only written to in the script block (write-only is intentional for bindable props) are no longer reported as unused.
#10827098ba41 Thanks @Aqu1bp! - Fixed #10698: The noUnsafeOptionalChaining rule now reports unsafe optional chains wrapped in TypeScript as, satisfies, type assertion, and instantiation expressions, such as new (value?.constructor as Constructor)().
#107733c6513d Thanks @otkrickey! - Fixed #10772: useVueValidVOn no longer reports a missing handler for v-on directives using a verb modifier (.stop / .prevent) without an expression, e.g. <div @click.stop></div>. The rule also accepts the arg-less object syntax <div v-on="$listeners"></div> instead of reporting a missing event name.
#10721d83c66b Thanks @minseong0324! - Improved type-aware lint rule inference for built-in globals and indexed function calls. Biome now resolves Error(...), new Error(...), optional Error#stack, and calls through indexed function values such as handlers[0]() more accurately.
#108656450276 Thanks @ematipico! - Fixed #10845. Biome Language Server no longer goes in deadlock when the scanner is enabled.
#1085393d8e53 Thanks @Netail! - Fixed #10840: Astro shorthand attribute syntax is now correctly being parsed from embedded nodes.
#10820bba3092 Thanks @JamBalaya56562! - Fixed #10619: noProcessEnv now also reports computed (bracket) member access. Previously only dot access was checked, so process["env"] and env["NODE_ENV"] (where env is imported from node:process) were missed. Both static and computed accesses are now reported.
#10875b12e486 Thanks @dyc3! - Fixed #10795: --profile-rules now reports timings for each plugin separately as plugin/<pluginName>, matching the naming used by plugin suppressions, instead of aggregating all plugins under a single plugin/plugin entry.
#10877d6bc447 Thanks @ematipico! - Fixed biome-zed#164: Biome no longer inserts stray whitespace when format-on-type runs after closing delimiters such as ), ], and }.
#10867a21463e Thanks @dyc3! - Fixed #10864: Biome no longer crashes when checking or linting HTML files with unquoted attribute values such as <textarea rows=4></textarea>.
0f192a7: ultracite init no longer wipes an existing Biome config it can't read. Previously a biome.json or biome.jsonc with a syntax error, or a nested monorepo config with "extends": "//", was treated as empty and replaced with just the Ultracite extends, losing every other setting. Now:
A config with a syntax error is left unchanged, with a warning asking you to fix it and re-run init.
A nested config that extends the root config ("extends": "//") is left unchanged, since the Ultracite presets belong in the root config.
A string extends is turned into a list that also includes the Ultracite presets.
Updates edit the file in place, so comments and formatting in biome.jsonc are preserved.
ultracite doctor and the check/fix resolution check now follow a nested config that extends "//" to the root config, instead of warning that the nested config doesn't extend ultracite/biome/core.
c68ba48: ultracite check and ultracite fix handle their arguments and missing tools more reliably:
check now treats explicit files the way fix does. Oxlint only gets files it can lint, Prettier runs with --ignore-unknown, and oxfmt with --no-error-on-unmatched-pattern. Before, ultracite check README.md or ultracite check Dockerfile src/index.ts failed even though nothing was wrong.
Linter flags that take a value keep it, even when the value looks like a file: --tsconfig tsconfig.json, -c .oxlintrc.json, --config-path biome.json, --only lint/suspicious/noDebugger, --since origin/main and similar. Before, the value was treated as a lint target, so ultracite fix --tsconfig tsconfig.json skipped Oxlint entirely and only formatted tsconfig.json. Ultracite's own --claude, --codex, --hook and --unsafe never take a value, so the next argument is always a target.
ultracite fix --unsafe with the ESLint toolchain no longer fails with ESLint's "Invalid option '--unsafe'". ESLint has no unsafe fixes, so the flag is dropped with a warning.
A linter that isn't installed is reported with a plain message instead of a stack trace. The other tools still run first. Stylelint is optional in the ESLint toolchain, as ultracite doctor already said, so a project without it now skips CSS linting with a warning instead of failing. "No linter configuration found" is also printed without a stack trace.
Linters installed in the project's node_modules/.bin are found even when Ultracite isn't run through a package manager script, npx or bunx, for example ./node_modules/.bin/ultracite check.
f61f393: ultracite init now looks for existing ESLint, Prettier and Stylelint configs in the same order the tools do, so when a project has more than one, init updates the one the tool actually loads. For example, Prettier reads .prettierrc.json before prettier.config.mjs, and ESLint reads eslint.config.js before eslint.config.mjs. Before, init could update a config the tool ignored and leave the active one in place. Stylelint's .stylelintrc.ts and stylelint.config.ts are now recognised too.
242cd2a: ultracite init now updates an existing .vscode/settings.json or .zed/settings.json in place, so your comments and formatting are kept. Before, the file was re-serialised as plain JSON, which stripped every comment. A settings file with a syntax error is now left unchanged with a warning. Before, it was rewritten with whatever part the parser could recover, which dropped the rest.
For the ESLint toolchain, init now also installs the Prettier VS Code extension (esbenp.prettier-vscode), since the settings it writes make Prettier the default formatter. Before, only the ESLint extension was installed, so format-on-save did nothing until you added Prettier yourself.
c273393: ultracite init now checks every flag value before it changes anything in the project. An unknown value for --linter, --pm, --frameworks, --editors, --agents, --hooks, --integrations or --js-plugins stops init with a message listing the valid values. Previously a misspelled --linter (for example --linter Biome) deleted every existing Biome, ESLint, Prettier, Stylelint, Oxlint and oxfmt config file and then crashed.
When --linter is not passed and init runs without prompts (because of --quiet, CI, or flags such as --agents or --pm), it now keeps the linter the project is already set up with and only falls back to Oxlint when there is none. Running ultracite init --agents universal on a Biome project no longer migrates it to Oxlint. The interactive linter prompt also preselects the detected linter.
ultracite init --help now lists the valid values for --pm, --linter, --frameworks, --hooks and --integrations, and explains what --type-aware does for Biome and for Oxlint.
The agent rules file says "Oxlint + Oxfmt will catch most mechanical issues automatically" instead of "Oxlint + Oxfmt's linter will catch…".
init and upgrade say "Using pnpm (detected from the project)" instead of "Detected lockfile", since the package manager can also come from packageManager.
ultracite doctor spells "unrecognized" consistently, formats commands as code, and describes warnings as "Some checks have warnings" instead of "optional improvements".
6ae8be8: The ESLint nestjs preset imports @darraghor/eslint-plugin-nestjs-typed, but ultracite init --linter eslint --frameworks nestjs never installed it, so ESLint failed to load the config with "Cannot find package". init now installs the plugin with the preset, and ultracite upgrade installs the plugins of every framework preset your eslint.config.* imports, so existing NestJS projects pick it up on their next upgrade.
a8e83bb: ultracite init now migrates an existing .oxlintrc.json, .oxfmtrc.json or .oxfmtrc.jsonc when it sets up Oxlint. Oxlint and oxfmt refuse to load any config when a JSON config sits next to oxlint.config.ts or oxfmt.config.ts, and init used to write the TS configs beside the JSON ones, so ultracite check and ultracite fix stopped working. Init now moves the JSON config's settings into the TS config and deletes the JSON file:
rules, overrides, env, plugins and other options become properties of the generated config.
ignorePatterns, settings and jsPlugins are added to the ones Ultracite generates instead of replacing them.
Ultracite extends entries become presets. Other extends paths can't be referenced from a TS config, so init names them in a warning.
A JSON config that doesn't parse is left in place, and neither file is written.
Re-running ultracite init also keeps what you added to oxlint.config.ts and oxfmt.config.ts: custom rules, overrides, ignorePatterns, settings and other properties, extra extends entries, your own imports and statements, and comments are carried over while the Ultracite parts are regenerated. Before, both files were regenerated from scratch. A config that doesn't parse is now left unchanged with a warning instead of being overwritten.
ultracite doctor now fails when a JSON config and a TS config for Oxlint or oxfmt sit side by side, and suggests running init to migrate a lone .oxfmtrc.json.
981ef2f: ultracite init --linter oxlint no longer adds "type": "module" to package.json. That field changes how Node loads every .js file in the package, so CommonJS files such as a next.config.js, postcss.config.js or jest.config.js using module.exports stopped working after init.
Instead, init writes the Oxlint and oxfmt configs as oxlint.config.mts and oxfmt.config.mts when the package isn't an ES module package (no "type" or "type": "commonjs"). A .mts file always loads as an ES module, with no MODULE_TYPELESS_PACKAGE_JSON warning on every run, and it works under "type": "commonjs". ES module packages ("type": "module") still get oxlint.config.ts and oxfmt.config.ts.
Re-running init updates an existing config under the name it already has. The one exception is a .ts config in a "type": "commonjs" package, which Node can't load: init renames it to .mts and says so. ultracite doctor, linter detection and the stale-config cleanup all recognise the .mts names. doctor fails a .ts config in a CommonJS package, and fails when a .ts and an .mts config sit side by side.
Requires oxfmt >= 0.59.0, the first release that finds oxfmt.config.mts on its own.
9a9beb0: ultracite init is gentler with package.json:
It keeps the file's key order, indentation and line endings. Before, every write moved devDependencies to the top and pushed keys like private, main and exports to the end.
It no longer overwrites a check or fix script the project already has (for example "check": "tsc --noEmit"). It prints a warning instead, and it only adds the scripts that are missing. Existing scripts that already run Ultracite with extra flags are left as they are.
When switching linters, it only removes the previous linter's packages from devDependencies. Packages in dependencies and peerDependencies stay, such as prettier used at runtime or eslint as the peer of a published plugin.
With --skip-install, the final message now says the dependencies were added to package.json instead of claiming they were installed.
c3027a5: Re-running ultracite init no longer silently discards changes to an ESLint, Prettier or Stylelint config that builds on Ultracite's:
eslint.config.*: the framework presets it already spreads are kept alongside any newly selected ones, together with your own config objects, imports and a defineConfig(...) wrapper. Before, the file was regenerated from the selected frameworks only, dropping earlier presets and every custom rule.
prettier.config.*: your options, imports and extra plugins are kept. Framework plugins are added, and prettier-plugin-tailwindcss stays last.
Stylelint: a config that already uses ultracite/stylelint, including one in package.json, is left as it is.
A config that doesn't use Ultracite's presets is still replaced, but init now prints a warning naming the file (or the package.json key) it replaced. A config that doesn't parse is left unchanged with a warning. With the ESLint toolchain, a "prettier" or "stylelint" key in package.json is now handled by these updates (and reported when replaced) instead of being deleted silently during migration.
44d6997: ultracite init no longer creates directories for agent rule files or agent hook configs (such as .claude/ or .cursor/) before checking that the path stays inside the project. When one of those directories was a symlink to somewhere outside the project, init created the missing directories there before it refused to write the file.
33a1ccd: ultracite init --integrations husky --skip-install no longer replaces an existing prepare script in package.json. It adds husky to the script, as the installing path already did (for example "prepare": "svelte-kit sync && husky"). The Lefthook setup messages now name the config file init actually updates or creates, such as .lefthook.yaml, instead of always saying lefthook.yml.
923667b: ultracite init no longer overrides a tsconfig that turns strictNullChecks off. It leaves an explicit "strictNullChecks": false, whether set in the file or in a config it extends, as it is and prints a warning. It also follows extends, whether a relative path or a package such as @tsconfig/strictest, so a tsconfig that already inherits strict or strictNullChecks is no longer given a redundant "strictNullChecks": true.
199974c: ultracite init and ultracite upgrade now work in Yarn 2+ monorepos. The root install used to pass Yarn 1's -W flag, which Yarn 2+ rejects with "Unsupported option name", whenever the Yarn version wasn't named in package.json's packageManager field. That happened with --pm yarn, with lockfile-only detection, and always in the second half of upgrade, which re-ran the new CLI with a bare --pm yarn. Ultracite now tells Yarn 2+ apart from Yarn 1 by .yarnrc.yml or the lockfile format. upgrade only forwards --pm to the newly installed CLI when you passed it.
ultracite doctor (and the check at the end of upgrade) no longer fails in Yarn Plug'n'Play projects just because it can't find Ultracite or the tools in node_modules. It warns that it can't verify them there instead.
c33158d: Agent hooks set up by ultracite init --hooks work in more places and no longer double up.
GitHub Copilot:.github/hooks/ultracite.json is now written in the format the Copilot CLI and Copilot cloud agent read: "version": 1 and a camelCase postToolUse event. VS Code reads this format too. The previous file, with a PascalCase PostToolUse and no version, was only picked up by VS Code. Re-running init moves an existing Ultracite hook to the new format instead of keeping both.
fix --hook with Copilot: the file path is now read from Copilot payloads: toolArgs.path from the Copilot CLI (including when toolArgs is a JSON string), tool_input.filePath from VS Code's edit tools, every file of a multi_replace_string_in_file edit, and the file headers of an apply_patch. So only the edited files are fixed, not the whole project. These hosts run the hook after every tool, so tools that edit nothing (such as read_file, view or bash) now skip the fix instead of fixing the whole project.
Re-running init no longer adds a second hook next to one generated by Ultracite 7.8.2 or earlier: pnpm fix …, bun fix …, yarn fix …, or npm run fix --skip=…. That older hook is updated in place, so it stops running a whole-project fix after every edit.
A hook is now added even when the settings file mentions ultracite somewhere else, such as a Claude Code permission rule like Bash(npx ultracite check). Only existing hook commands are checked.
fix --hook now fixes a file at the project root whose name starts with .., instead of treating it as outside the project.
Zed: the includePackageJsonAutoImports preference is now set for vtsls, Zed's default TypeScript and JavaScript language server (lsp.vtsls.settings), for both TypeScript and JavaScript. It was previously set under typescript-language-server, which Zed doesn't use by default.
dc5e18e: Re-running ultracite init now updates the Ultracite rules in AGENTS.md, .claude/CLAUDE.md, GEMINI.md and the other shared rule files in place, instead of appending another full copy.
Previously a copy was appended whenever the linter, the package manager or the rules text changed. For example, switching from Biome to Oxlint left instructions for both engines side by side. Duplicate copies written by earlier versions are now removed, and your own content before and after the block is kept.
Files with CRLF line endings keep them, and are no longer duplicated on every run.
The commands in the rules now run the project's installed CLI (npx ultracite fix, yarn ultracite fix, pnpm exec ultracite fix, bunx ultracite fix) instead of a dlx runner. For Deno they read deno run -A npm:ultracite fix; previously the broken deno run -A npm: ultracite fix was written.
Firebender: rules are now written to .firebender/rules/ultracite.mdc with alwaysApply: true, where Firebender reads project rules. They were previously written as Markdown into firebender.json, which made that file invalid JSON and overwrote an existing Firebender config. A firebender.json that still holds only those Markdown rules is reset to {}.
2ad7704: Fixes for ultracite fix --claude and ultracite fix --codex:
On a terminal, the ✓/✗ result for every issue is now kept once a file is done. Previously a file with more issues than the terminal had rows kept only the last screenful of results.
In CI logs and other piped output, lint messages are no longer cut off at 80 columns.
The prompt is now sent to the agent CLI on stdin (claude -p, codex exec -) instead of as a command-line argument. On Windows, an npm-installed CLI runs through a .cmd shim, which can cut an argument off at its first newline, so the agent received only the prompt's first line and never saw the file or its issues.
--codex now passes --skip-git-repo-check, so it also works in a project that isn't a git repository. Previously codex exec refused to run there and every file failed.
0b0775e: The Husky pre-commit hook written by ultracite init --integrations husky is more robust.
The hook now runs the project's installed tools (npx, yarn, pnpm exec, bunx) instead of yarn dlx / pnpm dlx. yarn dlx does not exist in Yarn 1, so every commit failed with "Ultracite found issues that could not be auto-fixed", and pnpm dlx downloaded the latest release instead of the version the project pins. Husky itself is also initialised with the installed binary.
Installing Husky now chains husky onto an existing prepare script (for example SvelteKit's svelte-kit sync || echo '') instead of replacing it. Choosing Husky and lefthook together no longer leaves only one of them in prepare.
When nothing is staged, the hook no longer exits early, so commands you keep after the Ultracite section still run.
Re-running init replaces the Ultracite section of a hook saved with CRLF line endings instead of appending a second one. It also no longer drops the last line of a hook that only mentions "# ultracite" in a comment. Commands after a lint-staged section written by an older version are kept.
3862221: ultracite init --integrations lint-staged no longer damages or shadows an existing lint-staged config.
An extension-less .lintstagedrc written in YAML (which is how lint-staged reads it) is now edited as YAML. Previously it was overwritten with a JSON file holding only the Ultracite task, deleting the user's own tasks, and single-quoted YAML was silently skipped.
YAML configs are edited through the yaml Document API, so comments and the rest of the file are kept.
TypeScript configs (.lintstagedrc.ts, .mts, .cts and lint-staged.config.ts, .mts, .cts) and the lint-staged key of package.yaml are now recognised. Previously init created a .lintstagedrc.json next to them, and because lint-staged uses only the first config in a directory, the user's config silently stopped running.
When a config can't be edited automatically (a function-based entry, a CommonJS module that can't be loaded, a syntax error), init now leaves it untouched and prints the command to add, instead of writing a second config file that would replace it.
When both exist, the dedicated config file is updated rather than the package.json key, matching the file lint-staged actually uses.
A single command already mapped to the same glob is kept alongside ultracite fix instead of being replaced.
The task now runs the project's installed Ultracite (npx, yarn, pnpm exec, bunx) instead of yarn dlx / pnpm dlx, which don't exist in Yarn 1 or download the latest release instead of the pinned one. Re-running init upgrades a dlx command written by an earlier version.
bffb8dd: Corrected the bundled Ultracite agent skill (skills/ultracite/SKILL.md):
It said Oxlint has github and sonarjs presets that init includes by default. Neither exists. The GitHub, SonarJS and React Doctor rules live in the ultracite/oxlint/js-plugins preset (plus next/js-plugins and tanstack/js-plugins), next to the anti-slop and shadcn presets, and all of them are opt-in.
The --js-plugins init flag is now documented.
The skill now tells agents to run the project's installed CLI (npx, pnpm exec, yarn, bunx) rather than pnpx or yarn dlx.
da2934a: Installing the Ultracite skill from ultracite init now works when init runs in a normal terminal.
Init runs skills add haydenbleasel/ultracite without a terminal attached. When the skills CLI needed to ask which agents to install to, or whether to install into the project or globally, it could not show the prompt:
In some cases it cancelled the prompt and exited successfully with nothing installed, so init said "Ultracite skill installed." when the skill wasn't there.
In others it failed, so the install never happened.
Init now passes --yes and names the agents to install for, so skills installs without asking. The skill always goes to the shared .agents/skills directory, which Codex, Cursor, GitHub Copilot, Gemini CLI, Amp, Cline, OpenCode and other agents read. It also goes to the skills directory of any agent whose project folder exists (for example .claude, .windsurf, .codebuddy or .roo, which init creates when you choose those agents).
Init names the agents because otherwise, in a project where skills detects no agent, --yes would install for every agent it knows. That also created a .claude/skills link and a second copy in a top-level agent/ directory (for the Eve framework). The "install it later" hint still shows the interactive skills add haydenbleasel/ultracite command.
4941883: ultracite init now edits .pre-commit-config.yaml and lefthook configs through the yaml Document API instead of splicing text, so the result is always valid YAML and comments are kept.
pre-commit: a repos: list written flush with its key (- repo:, the style pre-commit sample-config generates) or with four-space indentation no longer turns into invalid YAML that breaks every commit. The file's sequence style is kept.
lefthook: a four-space indented pre-commit: block, a compact jobs: list, or a column-0 comment inside the block no longer produce invalid YAML or a duplicate jobs: key. A hook that uses commands: gets a jobs: list next to it.
lefthook: init now finds an existing .lefthook.yml, lefthook.yaml, .config/lefthook.yml (and the other names lefthook reads) and edits it. Previously it created lefthook.yml, which lefthook picks first, so the user's own config silently stopped running. A JSON or TOML config is left alone with instructions.
lefthook: the job's globs are now *.js, *.ts, and so on. With lefthook's default matcher, the previous **/*.js form skipped root-level files such as package.json or index.ts. Projects that set glob_matcher: doublestar get **/*.js.
lefthook: re-running init with a different package manager updates the existing job instead of adding a second one.
Both now run the project's installed Ultracite (npx, yarn, pnpm exec, bunx) instead of yarn dlx / pnpm dlx. Re-running init upgrades a hook or job written by an earlier version.
lefthook install runs the project's installed lefthook, and the prepare script is chained onto an existing one (e.g. svelte-kit sync || echo '' && lefthook install) instead of replacing it.
9c30c15: Raise the oxlint peer dependency to ^1.82.0. The core Oxlint preset configures no-unmodified-loop-condition with checkConditionalExpressions, an option Oxlint 1.79 to 1.81 reject, so those releases failed with "Failed to parse oxlint configuration file" even though the old ^1.79.0 range allowed them. ultracite doctor and your package manager now flag the too-old version instead.
Requires oxlint >= 1.82.0
47303f4: The Biome jest preset now declares Jest's globals (describe, it, test, expect, jest, beforeEach and the rest of globals.jest) for test files. Jest injects them without imports, so noUndeclaredVariables from the core preset reported every one of them in idiomatic Jest tests. The ESLint jest preset already declared them.
The route-file exemptions in the Biome remix and tanstack presets (useFilenamingConvention off in routes/) now cover .js and .jsx route files as well as .ts and .tsx.
8f236ee: The ESLint core preset now lints .jsx, .tsx, .mts and .cts files. Its main block previously matched only .js, .mjs, .cjs, .ts, .json and .html, so ESLint skipped .mts and .cts files entirely ("File ignored because no matching configuration was supplied"), and .jsx and .tsx files received only the framework preset's rules. .tsx files were also parsed by espree, so any TypeScript syntax in them was a parse error. Every TypeScript extension now uses @typescript-eslint/parser with type information, and the core rules apply to all of them.
The JavaScript block no longer matches **/*.json. espree cannot parse JSON, so every package.json and tsconfig.json failed with Parsing error: Unexpected token :. The ESLint preset does not lint JSON, matching the Oxlint preset.
94722fa: The eslint.config.mjs that ultracite init writes no longer fails its own lint. Every ESLint preset exported a default named config, so import core from "ultracite/eslint/core" (and each framework import) tripped import-x/no-rename-default twice per preset. Each preset's default export is now named after the identifier the generated config imports it as (core, react, tanstack, …).
d9803a6: Fix ESLint presets that crashed the whole run or never reached the files they target.
TanStack:@tanstack/start/no-async-client-component and @tanstack/start/no-client-code-in-server-component need type information and aborted ESLint ("You have used a rule which requires type information") on the first .jsx or .tsx file. They now run only on TypeScript files. The Query, Router and Start rules also apply to plain .ts/.js modules, where query and mutation options are usually declared, instead of only to .jsx and .tsx. sort-keys is off, and no-use-before-define and unicorn/filename-case are off in routes/ directories, matching the Oxlint and Biome TanStack presets (alphabetical option keys break TanStack's type inference, and file routes are mutually recursive and encode the URL in the filename).
Qwik:qwik/valid-lexical-scope needs type information and crashed ESLint on .jsx files. It now runs only on .tsx files.
Jest:jest/no-deprecated-functions threw "Unable to detect Jest version" and crashed ESLint when the jest package was not resolvable from the plugin, for example in bun:test projects. The preset now reads the Jest version from the project and falls back to the latest major.
Cypress: the block used globals.cypress, which does not exist in the globals package, so cy, Cypress, describe and it were reported by no-undef. It now uses eslint-plugin-cypress's own globals config. It also matched only *.cy.js, and now covers *.cy.* for every JS and TS extension plus files under cypress/.
Storybook: the block matched only *.stories.js and *.stories.ts. It now covers *.stories.* and *.story.* for every JS and TS extension, including .tsx and .jsx.
d6ee272: The ESLint vue, svelte and astro presets now parse their files. They registered only the plugin and its rules, never a parser, so ESLint read every .vue, .svelte and .astro file with espree and reported a parse error (Unexpected token <) instead of linting it.
Each preset now starts from its plugin's own base config (flat/base in eslint-plugin-vue, eslint-plugin-svelte and eslint-plugin-astro), which registers vue-eslint-parser, svelte-eslint-parser or astro-eslint-parser from the plugin's dependencies, and parses <script lang="ts"> blocks with @typescript-eslint/parser. The Svelte preset also covers .svelte.js and .svelte.ts rune modules. In Astro files, client-side <script> tags are linted without type information, because those in-memory virtual files cannot be type-checked.
f7599ef: The ESLint core preset no longer re-enables formatting rules that eslint-config-prettier turns off. The unicorn rule set was spread after eslint-config-prettier, which switched unicorn/number-literal-case, unicorn/template-indent and unicorn/empty-brace-spaces back on. unicorn/number-literal-case demands 0xABCD while Prettier prints 0xabcd, so prettier/prettier and the unicorn rule reported every hex literal and eslint --fix could not satisfy both. eslint-config-prettier now applies after every plugin, so unicorn/number-literal-case and unicorn/template-indent are off. unicorn/empty-brace-spaces stays on (Prettier prints empty braces as {} too), matching the Oxlint preset.
efa80e8: The ESLint core preset no longer reports the same problem two or three times on TypeScript files, and no longer flags ambient type-only globals.
no-undef and sonarjs/no-reference-error are off for .ts, .tsx, .mts and .cts files. TypeScript already reports unresolved identifiers, and both rules flagged valid ambient types such as NodeJS.Timeout (see the typescript-eslint FAQ).
The base class-methods-use-this, consistent-return, no-unused-private-class-members, prefer-destructuring and prefer-promise-reject-errors rules are off for TypeScript files, because their @typescript-eslint extension rules are enabled there. The base rules duplicated every report and ignored the extensions' TypeScript-aware options.
unused-imports/no-unused-vars is off. It is a copy of no-unused-vars / @typescript-eslint/no-unused-vars, which stay on as in the Oxlint preset, so every unused variable was reported twice. unused-imports/no-unused-imports stays on for its autofix.
@typescript-eslint/max-params is off, matching max-params being off in the core presets. It was on with its default limit of 3 parameters for TypeScript files only.
9ae95d1: Fix false positives in the ESLint vue, svelte and astro presets that appeared once their files were actually parsed.
vue/block-lang and svelte/block-lang rejected every <script lang="ts">, because with no options they only allow blocks without a lang attribute. They now allow TypeScript scripts, and less and scss styles, as well as blocks without lang.
Astro's client-side <script> tags are linted as virtual files named 1_1.ts, 2_1.js and so on, which github/filenames-match-regex reported on every page with a script tag. Filename rules are now off for those virtual files.
The Astro preset passes @typescript-eslint/parser for the frontmatter explicitly. ESLint's config merge dropped the copy in eslint-plugin-astro's base config, so frontmatter TypeScript parsed only when @typescript-eslint/parser happened to resolve from the working directory, and failed with The keyword 'interface' is reserved otherwise.
ultracite init now installs eslint-plugin-jsx-a11y for Astro projects using ESLint. The preset enables eslint-plugin-astro's astro/jsx-a11y/* rules, which load that package and reported "you need to install eslint-plugin-jsx-a11y" on every .astro file when it was missing.
e88e76d: The Prettier preset now sets tailwindFunctions to clsx, cva, tw, twMerge, cn, twJoin and tv, so prettier-plugin-tailwindcss sorts Tailwind classes passed to those helpers anywhere in a file, for example const button = cva("p-4 flex"). Previously only class strings inside class/className attributes were sorted with Prettier, while the Oxfmt (sortTailwindcss.functions) and Biome (useSortedClasses) presets already sorted these helpers. See https://github.com/tailwindlabs/prettier-plugin-tailwindcss#sorting-classes-in-function-calls.
3a5885e: github/filenames-match-regex now accepts file-based routing filenames in the ESLint preset too, and Next.js's special pages in both presets.
The page-route regex (Oxlint js-plugins and now ESLint core) allows a leading underscore, so the Next.js pages-router files _app, _document and _error, which cannot be renamed, are no longer reported. Other names in pages/ (BadPage.ts) and camelCase route params ([postId], which unicorn/filename-case rejects too) are still reported.
The ESLint core preset now carries the same route exemptions as the Oxlint js-plugins preset (#799, #804). Previously it applied the plain kebab-case regex everywhere and reported SvelteKit's +page.ts and +server.ts, TanStack Router and React Router files such as posts.$postId.ts, and page routes such as [slug].json.ts and [...auth].ts.
The route-directory exemptions (github/filenames-match-regex in Oxlint js-plugins and ESLint core, plus unicorn/filename-case and no-use-before-define in the Oxlint and ESLint tanstack presets) now cover .js and .jsx route files as well as .ts and .tsx.
ad2adb0: The Stylelint preset now handles Tailwind CSS v4, SCSS and Less, and skips build output.
Tailwind CSS v4:@theme, @utility, @variant, @custom-variant, @slot, @plugin and @config are allowed by at-rule-no-unknown, alongside the Tailwind v3 at-rules and @source and @reference. at-rule-prelude-no-invalid no longer rejects every @apply (or the other Tailwind at-rules' preludes). custom-property-pattern accepts theme namespace resets (--color-*: initial) and sub-properties (--text-xl--line-height). declaration-property-value-no-unknown and function-no-unknown accept --spacing(), --alpha(), --value(), --modifier() and theme(). media-query-no-invalid accepts theme() breakpoints, and nesting-selector-no-missing-scoping-root allows & inside @custom-variant, @variant and @utility. See https://tailwindcss.com/docs/functions-and-directives.
import-notation is now "string". stylelint-config-standard's "url" autofix rewrote @import "tailwindcss" to @import url("tailwindcss").
SCSS and Less:.scss files are parsed with postcss-scss and .less files with postcss-less. Previously every SCSS and Less file failed with CssSyntaxError (for example on // comments or mixin calls). The CSS-only rules that cannot understand preprocessor syntax (unknown at-rules such as @use, @include and @mixin, $variable and @variable values, Sass and Less built-in functions, // comments) are relaxed for those files. ultracite init now installs postcss-scss and postcss-less with the ESLint toolchain.
.sass: the indented Sass syntax has no maintained PostCSS parser, so ultracite check and ultracite fix no longer pass .sass files to Stylelint, where every one failed to parse.
Ignores:ignoreFiles now uses the shared ignore list (dist, build, .next, coverage, storybook-static and so on). Stylelint does not read .gitignore, so it previously linted generated CSS.
Prettier:prettier/prettier is now enabled. The preset loaded stylelint-prettier but never turned its rule on.
ultracite check and ultracite fix now escape glob characters in directory arguments before passing them to Stylelint. A directory such as app/(marketing) previously matched no files.
89989df: Test-file overrides in every preset now match .mts, .cts, .mjs and .cjs test files as well as .ts, .tsx, .js and .jsx. The globs were **/*.{test,spec,test-d,spec-d}.{ts,tsx,js,jsx} and **/__tests__/**/*.{ts,tsx,js,jsx}, so a sum.test.mts file got none of the test-file relaxations and none of the Jest or Vitest rules. For example, test.only in a .test.mts file was not reported by vitest/no-focused-tests or jest/no-focused-tests. This covers the Oxlint core, js-plugins, jest and vitest presets, the Biome core preset, and the ESLint core, jest and vitest presets.
4d28cf9: The Oxlint vue preset now lists every non-nursery Vue rule Oxlint implements, like the other framework presets. It previously enabled 18 of them. Among the 27 newly enabled rules are vue/valid-define-props, vue/valid-define-emits, vue/no-export-in-script-setup, vue/no-lifecycle-after-await, vue/no-arrow-functions-in-watch, vue/no-side-effects-in-computed-properties, vue/require-typed-ref, vue/define-props-destructuring and the vue/no-deprecated-* rules. vue/max-props is off in both the Oxlint and ESLint presets, because its default allows a single prop per component.
The Oxlint vitest preset now enables vitest/consistent-test-it, the one Vitest rule it was missing (the ESLint preset already had it).
Vue files whose names Nuxt and file-based Vue Router require (pages/**, layouts/**, error.vue and app.vue) are exempt from useVueMultiWordComponentNames in the Biome preset and from vue/multi-word-component-names in the ESLint preset. Previously pages/index.vue, layouts/default.vue and error.vue were always reported.
00dd547: Agent hooks now fix only the file the agent edited. The generated hook command passes a new --hook flag, and ultracite fix --hook reads the agent's payload from stdin: Claude Code and CodeBuddy (tool_input.file_path), Cursor (file_path) and Windsurf (tool_info.file_path). A file outside the project, one that no longer exists, or one the project's linters do not handle is skipped. A payload with no readable file path keeps the previous whole-project run. Re-running ultracite init rewrites a hook from an earlier init to the new command, including one generated before this change or for a different package manager or linter.
300a814: Update @biomejs/biome to 2.5.14. Releases 2.5.13 and 2.5.14 add only nursery rules (14 of them, including noReturnInFinally, noSvelteAtHtmlTags and useValidTestTitle), with no stable-group additions, removals or promotions. The Biome presets deliberately leave nursery rules off, so they are unchanged.
b59b8aa: Update oxfmt to 0.70.0 and re-target the markdown ::: fence patch. Releases 0.68 through 0.70 change no options; they fix suppression-comment handling, comment placement around operators and as/satisfies, and several YAML and CSS edge cases. oxfmt 0.69 also ships a native markdown formatter, but .md files still go through the Prettier-based path, so the fence patch still applies.
548c8b0: Update oxlint to 1.85.0. Releases 1.83 through 1.85 add and remove no rules and change no options, so the presets are unchanged. Note that jest/prefer-strict-equal, vitest/prefer-strict-equal, vitest/prefer-to-be-truthy and vitest/prefer-to-be-falsy now offer suggestions instead of fixes, so ultracite fix reports them rather than rewriting them. unicorn/consistent-function-scoping also now catches functions that only capture variables from an outer ancestor scope.
The ESLint core preset picks up unicorn's ten new JavaScript rules automatically: no-async-iterator-callback, no-unused-builtin-method-return (which replaces the now-deprecated no-unused-array-method-return), no-unused-iterator-helper, no-useless-set-construction, no-using-resource-escape, prefer-combined-guards, prefer-iterator-zip, prefer-json-import, prefer-temporal-conversion and prefer-uint8array-hex. The CSS-only unicorn rules added in v75 stay off for JavaScript files. unicorn 76 makes no-immediate-mutation skip conditional mutations and no-break-in-nested-loop skip continue by default; the preset keeps those defaults because oxlint's no-immediate-mutation behaves the same way and checking continue would contradict prefer-continue.
0bcb2c6: Update prettier-plugin-astro to 1.1.0 (the version ultracite init installs for Astro projects with Prettier) and refresh the transitive Prettier lock to 3.9.9. Prettier 3.9.7 through 3.9.9 are formatting fixes with no option changes, so the Prettier preset is unchanged. prettier-plugin-astro 1.1 makes astroAllowShorthand tri-state. When it is unset, which is how Ultracite's generated config leaves it, attributes now stay as written instead of {value} being expanded to value={value}. Set astroAllowShorthand: false in your Prettier config to keep the 1.0 behaviour.
8b39375: Update @shadcn/lint to 0.2.0 (the version ultracite init installs for the opt-in ultracite/oxlint/shadcn preset). The release adds Vue and Svelte support and fixes theme, barrel and class-site discovery; the rules and their options are unchanged, so the preset is too. Under Oxlint the plugin reads only the <script> blocks of .vue and .svelte files, not their templates.
0bc6cd6: Update eslint-plugin-sonarjs to 4.2.1. The ESLint preset adopts its 16 new rules automatically (Vue, Cypress, Playwright, Testing Library, Vitest, Lodash, jQuery and Axios checks). The ultracite/oxlint/js-plugins preset now enables every sonarjs rule that does not need type information, adding 35 rules. These include the 15 new non-type-aware rules and nine test rules that older versions of oxlint's JS plugin bridge could not load. sonarjs 4.2.1 also raised the no-nested-functions default threshold from 4 to 5; both presets pin threshold: 4 to keep the previous strictness.
1f427d7: Add an opt-in ultracite/oxlint/shadcn preset for @shadcn/lint, shadcn's agent-first linter for Tailwind v4 design systems. All six rules (no-restyle, no-raw-colors, no-arbitrary-values, no-inline-styles, no-unknown-classes, require-static-classes) run at error with the upstream allow: ["layout"] policy, and the component-authoring rules are relaxed inside **/components/ui/**. Enable it with ultracite init --linter oxlint --js-plugins @shadcn/lint, or extend it alongside ultracite/oxlint/core after installing @shadcn/lint.
Patch Changes
600e991: Accept file-based page route filenames in github/filenames-match-regex. Files under pages/ (Astro, Next.js pages router) now match a bracket-aware pattern, so dynamic, rest and optional parameters such as [slug].astro, [...slug].ts and [[...slug]].tsx pass while non-route names like BadPage.ts are still rejected.
66f97bb: Allow top-level return in Astro frontmatter. The core preset now turns off unicorn/prefer-module for **/*.astro files (the rule cannot be configured to allow only return), and keeps require(), module.exports, __dirname and __filename flagged there via import/no-commonjs and no-restricted-globals.
24b2797: Update oxfmt to 0.67.0 and re-target the markdown ::: fence patch to the new dist chunk. The release changes no options; it fixes CSS/SCSS/Less comment preservation and a few JSDoc-cast edge cases in member chains.
24b2797: Update oxlint to 1.82.0. The release adds and removes no rules, so the presets are unchanged apart from one new option: no-unmodified-loop-condition now runs with checkConditionalExpressions: true in the oxlint and ESLint core presets, so each branch of a ternary in a loop condition must be modified inside the loop rather than only the expression as a whole.
24b2797: Update the ESLint family: @angular-eslint/eslint-plugin 22.5, @typescript-eslint/* 8.70, eslint-plugin-solid 0.18, eslint-plugin-vue 10.11, eslint-plugin-jsdoc 64.3.8, eslint-plugin-cypress 7.0.2, @darraghor/eslint-plugin-nestjs-typed 7.5.2 and prettier-plugin-astro 1.0. The dynamic presets pick up the seven new rules automatically: @typescript-eslint/no-generated-empty-object-type, @angular-eslint/reactive-context-must-read-signal, vue/no-shadow-native-events, and Solid's no-boolean-enumerated-attribute, no-store-mutation-outside-setter, no-unused-signal and no-write-in-pure-computation. Note that prettier-plugin-astro 1.0 is a rewrite on the Astro 7 Rust compiler (Node 22.12+); expect whitespace diffs on first run and mirror any custom compressHTML via its new astroCompressHTML option.
24b2797: Refresh the stylelint lock to 17.15.0 and enable its new selector-no-unmatchable rule, which flags selectors that can never match (for example label:checked, ::before:first-child or :is(::before)).
a71dda4: Stop sorting object keys across the whole TanStack presets, not just route files. TanStack Query mutation options are order-sensitive too: TypeScript infers the onMutate context from context-sensitive callbacks in source order, so moving onError/onSettled above onMutate turns the context into {} and ultracite fix broke type-correct code (TS2339: Property '...' does not exist on type '{}'). Mutation and query options live anywhere (hooks, components, colocated files), so the route-file exemption could not cover them. ultracite/oxlint/tanstack now sets sort-keys to off and ultracite/biome/tanstack now sets useSortedKeys to off for all files.
a9db61c: Disable github/filenames-match-regex for file-based route directories (**/routes/**) in the oxlint js-plugins preset, matching the existing unicorn/filename-case exemption in the TanStack preset. TanStack Router filenames such as __root.tsx, $.tsx, and posts.$postId.tsx no longer fail the GitHub filename rule. Resolves #799.
d5d241f: Add ultracite upgrade, and make version drift between Ultracite and its linter visible instead of a crash. Bumping ultracite without bumping the linter fails on the next check/fix with Biome's Found an unknown key error (and the equivalent for ESLint and Oxlint), because each release enables rules from a specific linter version.
ultracite upgrade installs the latest ultracite, prints a release-notes link for the versions you crossed, then hands off to the freshly installed CLI to reinstall your toolchain at the versions that release was verified with: Biome; ESLint, Prettier, Stylelint and the plugins the preset imports; or Oxlint and Oxfmt. Optional extras already in your package.json (framework plugins, Oxlint JS plugins, oxlint-tsgolint) are bumped as well. Config files are never rewritten, and the run ends with the same diagnostics as doctor.
ultracite doctor now checks the installed Biome, ESLint, Prettier, Stylelint, Oxlint and Oxfmt versions against the range this release supports. Too old fails the check and points at ultracite upgrade; newer than the verified range only warns.
The supported ranges are declared as optional peer dependencies so package managers warn when they drift: @biomejs/biome ^2.5.0, eslint ^10.0.0, prettier ^3.0.0, stylelint ^17.0.0, oxlint ^1.79.0, oxfmt >=0.40.0. ultracite init now installs Prettier and Stylelint from those ranges instead of latest.
d322691: Default ultracite init to Oxlint + Oxfmt when --linter is omitted in quiet or non-interactive mode
c677cf5: Recommend Oxlint + Oxfmt in the ultracite init linter prompt and list it first
c5be848: Disable no-use-before-define in TanStack Router route files for the oxlint tanstack preset. File routes are mutually recursive (Route references the component, and the component calls Route.useParams()), so no declaration order could satisfy the rule.
2a43fdd: Turn off unicorn/prefer-reflect-apply in the ultracite/oxlint/anti-slop preset. When extended after ultracite/oxlint/core, the core rule recommended rewriting Function#apply() to Reflect.apply(), which anti-slop's no-reflect-apply then rejected, leaving no way to satisfy both. Direct-call guidance for .apply() still comes from eslint/prefer-spread and eslint/no-useless-call.
03196df: Update @biomejs/biome to 2.5.12. Releases 2.5.10 through 2.5.12 only add nursery rules, which the Biome presets exclude, so the presets are unchanged. The releases also carry linter and formatter fixes, including fewer noUnusedVariables false positives on merged interfaces, namespaces, and overload type parameters, an unsafe-by-default fix for noThisInStatic, and a large batch of Astro expression parsing fixes.
031b867: Update oxfmt to 0.66.0. The 0.65 and 0.66 releases are formatter fixes only, mainly around comment placement between a statement head and its body, suppressed statements (decorators, typecast parens, semicolons), custom side-effect groups in sortImports, CommonMark list interruption in JSDoc, and trailing whitespace in YAML block scalars. The ::: container-directive fence patch is re-targeted to the new markdown bundle; the patched code itself is unchanged.
54affee: Update oxlint to 1.81.0. The 1.80 and 1.81 releases add no rules and remove none, so the presets are unchanged and the peer range stays at ^1.79.0. Notable fixes carried in: no-use-before-define now runs on JS and JSX files, object-shorthand preserves __proto__ semantics, unicorn/prefer-math-min-max no longer applies an unsafe autofix, and JS plugin diagnostics with invalid or reversed locations are clamped instead of crashing.
0b058d1: Fix ultracite fix --codex failing on recent Codex CLI releases. The Codex adapter invoked codex exec --full-auto, a flag Codex has since removed, so every attempt failed at argument parsing. It now runs codex exec --sandbox workspace-write, the equivalent non-interactive, workspace-scoped mode, which is also accepted by older Codex versions. Resolves #794.
e2346bb: Update the ESLint family to latest: eslint 10.9.1, typescript-eslint 8.69.0, eslint-plugin-unicorn 74, eslint-plugin-solid 0.17, eslint-plugin-jsdoc 64.3.5, eslint-plugin-react-doctor and oxlint-plugin-react-doctor 0.9.13, globals 17.12, and the angular, nestjs-typed, next, tanstack-query, cypress, html, jest and storybook plugins. The dynamic presets pick up the new rules automatically: the solid preset gains the eleven rules added in eslint-plugin-solid 0.16 and 0.17 (removed-api, no-single-arg-create-effect, no-accessor-as-prop, valid-use-server, require-async-server-function, no-invalid-server-capture, no-browser-globals-in-server-function, no-module-scope-reactive-primitive, no-restated-default-options, prefer-onSettled-for-side-effects, prefer-structured-class), which only report Solid 2.0 issues when settings.solid.version is set to 2 and stay silent on Solid 1.x code; the nestjs preset gains forward-ref-injection-should-use-wrapper-type, swagger-file-upload-should-be-documented and uploaded-file-should-be-validated. The curated jsdoc and react-doctor lists are unchanged. No enabled rule was removed or deprecated, and the ESLint/oxlint parity check still passes.
7b0456e: ultracite fix now runs lint fixes before the formatter: oxlint before oxfmt, and ESLint then Stylelint before Prettier. A fixer can insert unformatted code, such as the braces curly adds, the imports consistent-type-specifier-style splits, or the font names Stylelint requotes, and running the formatter first left that code unformatted until the next run. Fixer-inserted code is now formatted in the same run. The --claude and --codex agent passes follow the same order.
unicorn/no-nested-ternary is now off in the oxlint and ESLint presets: its fixer adds parentheses that oxfmt and Prettier remove, so the two tools rewrote the file on every run. The core no-nested-ternary rule still reports nested ternaries.
8ddc078: Enable three rules added in Stylelint 17 in the Stylelint preset: selector-no-deprecated and selector-no-invalid report selectors that CSS has dropped or that cannot parse, and relative-selector-nesting-notation is set to explicit, so nested relative selectors are written & > b rather than > b. The property-layout-mappings, unit-layout-mappings and value-keyword-layout-mappings rules are deliberately left off, since they reject every physical property, unit and keyword and can only autofix with a per-project languageOptions.directionality setting. The repo's own Stylelint lock is refreshed from 16.26.1 to 17.14.1, which the config packages already required; ultracite init already installs stylelint@latest so users are unaffected by the lock change.
4609d79: Update the vendored anti-slop Oxlint plugin to upstream v0.1.2 (commit e8c4880). The rule set is unchanged; this picks up a round of correctness fixes: no-known-value-widening now also reports known values passed into local unknown-typed type predicates, no-object-parameters, no-unknown-parameters, no-unknown-returns and no-unknown-type-aliases resolve scoped and transparent generic aliases (including block-scoped aliases and forward references), no-unknown-returns covers PromiseLike<unknown>, no-unsafe-dictionary-type allows generic constraints such as T extends Record<string, unknown>, no-runtime-typeof always allows existence probes against "undefined", no-shape-in-symbol-names allows static member reads such as Zod's schema.shape, and require-safety-comment-for-type-assertion rejects empty justifications and gains a markers option (default ["SAFETY"]). Upstream also added a separate opt-in anti-slop-effect plugin for Effect projects, which is not vendored.
15f7ae0: Add support for the Aube package manager: ultracite init --pm aube is now accepted and projects with an aube-lock.yaml lockfile are detected automatically
1afc8fe: Re-declare selected Oxlint JS plugins on the root config so dependency analyzers see them. ultracite init --linter oxlint --js-plugins ... now generates const jsPlugins = selectJsPlugins([...]) and adds jsPlugins: jsPlugins.jsPlugins to the root config (also when the full ultracite/oxlint/js-plugins preset is extended). Knip only resolves jsPlugins from the root Oxlint config and never walks extends, so eslint-plugin-github, eslint-plugin-sonarjs, and oxlint-plugin-react-doctor were reported as unused devDependencies. Oxlint dedupes the plugin between the root and the extended preset, so linting is unchanged.
Also fix ultracite update dropping the js-plugins preset from extends when the config used the documented import jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins" form: the import parser only matched bare default imports. The regenerated full-preset config keeps settings: jsPluginSettings on the root as well.
20b06bf: Ignore .alchemy across all linters and formatters. Alchemy (alchemy.run) writes local state and generated bindings to a .alchemy directory. It is now part of the shared ignore patterns synced into Biome's files.includes and imported by oxlint, oxfmt, and ESLint, and the Stylelint preset ignores it via ignoreFiles. Prettier needs no change: it already respects .gitignore/.prettierignore.
6e9286c: Run lefthook install during ultracite init through the same spawn adapter as every other tool invocation instead of execSync, so it no longer goes through a shell and gets the same Windows command resolution as husky, the linters, and editor extension installs.
8b24f34: Add support for the Nub package manager: ultracite init --pm nub is now accepted and projects with a nub.lock lockfile are detected automatically (requires nypm ^0.6.9)
240cb6b: Install into the workspace root correctly for Nub and Aube monorepos: nypm emits no root selector for them, so nub add ran without -w and was refused. Ultracite now hands nypm pnpm's flag set for these pnpm-compatible CLIs, producing nub add --workspace-root --save-dev ultracite
44ecbba: Add Vitest type-test files (**/*.{test-d,spec-d}.{ts,tsx,js,jsx}) to the test-file globs so the shared test relaxations and the Vitest rule overrides apply to them, and enable the Vitest plugin's typecheck setting in the ESLint preset so expectTypeOf/assertType count as assertions.
7ba6bb8: Package the reusable Ultracite agent skill with the npm distribution.
972b946: Update the oxlint presets for oxlint 1.79: replace the removed react/react-compiler rule with the 22 new React Compiler rules in the react preset, add jsdoc/no-blank-blocks and one-var to the core preset (mirrored in the ESLint presets), and require oxlint ^1.79.0
2d5bbdb: Declare the jsPluginSettings named export in the ultracite/oxlint/js-plugins type declarations, so the documented (and ultracite init-generated) import jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins" type-checks (#773)
56aef65: Refresh the toolchain versions that ultracite init installs into projects: @biomejs/biome 2.5.9, the ESLint plugin family (including eslint-plugin-cypress 7, eslint-plugin-jsdoc 64, eslint-plugin-solid 0.15, and eslint-plugin-unicorn 73 — the dynamic presets adopt their new rules automatically), and prettier-plugin-svelte 4.1.1 / prettier-plugin-tailwindcss 0.8.1
8df6ad0: Offer the vendored anti-slop Oxlint preset during ultracite init — it now appears in the JS-plugins prompt when you pick Oxlint, and non-interactive setup accepts it via --js-plugins anti-slop. Selecting it adds ultracite/oxlint/anti-slop to the generated config's extends; since the preset is vendored inside Ultracite, nothing extra is installed.
cd229e9: Bump the oxlint-plugin-react-doctor pin from ^0.7.1 to ^0.9.12, so ultracite init installs the current plugin. All react-doctor rules enabled by the js-plugins presets still exist in 0.9.12, and the ported rules run in curated mode via the settings shipped alongside this release (#771).
e1ac886: Pin React Doctor's ported rules to their framework-aware "curated" mode (#771). react-doctor 0.9.x rewrote its ported oxc/react-refresh rules — notably only-export-components — with a stripped-down default mode: no framework detection, no route-file skipping, and allowConstantExport off, so Next.js route-segment exports like export const dynamic = "force-static" or metadata were flagged as non-component exports in every route file. The ESLint react preset now sets settings["react-doctor"].portedRuleMode: "curated", and generated oxlint configs apply a new jsPluginSettings export from ultracite/oxlint/js-plugins on the root config (oxlint does not merge settings from extended configs, so the setting cannot ride along inside the preset). If you extend the js-plugins preset manually, add settings: jsPluginSettings to your root oxlint config.
c27fe36: Generate oxlint configs that enable a subset of the JS plugins via a new selectJsPlugins export from ultracite/oxlint/js-plugins, instead of inlining the filtering logic into the generated file. The inlined block contained a typeof check that user-side lint presets flagged (anti-slop/no-runtime-typeof, #770); the generated config is now a one-line extend, is emitted already formatted (including the previously missing blank line after imports), and re-running ultracite init migrates existing configs with the old inlined block automatically.
0616523: Update the vendored anti-slop Oxlint plugin to upstream commit 446268e, picking up fixes to no-object-parameters and no-unknown-returns (respect lexical type binders in alias resolution) and a new allowInTypeGuards option on no-runtime-typeof. The ultracite/oxlint/anti-slop preset enables allowInTypeGuards, so typeof checks inside type predicate functions ((x): x is T) no longer need disable comments — predicates are the named-boundary pattern the rule pushes toward (dmmulroy/anti-slop#10).
417a85a: Add an opt-in ultracite/oxlint/anti-slop preset that ships a vendored, self-contained build of the anti-slop Oxlint plugin — fifteen rules that reject low-evidence TypeScript and JavaScript patterns (unjustified type assertions, unknown leaking through signatures, Reflect-based access, module mocking, and more). Extend it alongside ultracite/oxlint/core; nothing extra to install. The preset also turns off two core rules that conflict with anti-slop's widening checks (typescript/consistent-indexed-object-style and unicorn/no-immediate-mutation) when extended after core.
4d3fab8: Move suspicious/useArraySortCompare from the Biome core config to the opt-in type-aware config. The rule is in Biome's types domain — it type-infers the receiver of every method call before checking the method name, which made ultracite check up to ~260x slower on projects with expensive library types (zod, better-auth, Prisma). It now only runs when type-aware linting is explicitly enabled, alongside the other type/project-domain rules. Fixes #768.
a1fa9c4: Replace the hand-rolled package exports map matching in the config-resolution doctor check with the resolve.exports library, which implements Node's full PACKAGE_TARGET_RESOLVE algorithm (wildcard patterns, key-order precedence, conditional exports, and array fallbacks). The manual node_modules walk is kept intentionally — it exists to avoid Bun's auto-install cache resolving specifiers the project's own node_modules can't.
414ea80: Replace the hand-rolled monorepo workspace scan in framework detection with the find-workspaces library. Workspace declarations from package.json (array and yarn-classic object form) and pnpm-workspace.yaml — including negated globs — are now resolved by the library instead of manual pattern collection and globbing, and lerna/bolt monorepos are picked up as well.
a9a1989: Replace the hand-rolled upward directory walks in findNearestFile and detectLinter with the empathic library's find.any, which checks candidate names in order within each directory before moving to the parent — the same per-directory precedence the previous implementation enforced manually.
27b2707: Use magicast to update ESM lint-staged config files during ultracite init. The config is now edited as an AST instead of being imported and re-serialized, so comments and function-valued entries elsewhere in the config survive the update, and the user's config code is no longer executed. If the Ultracite glob pattern is already owned by a non-array value, or the config isn't a mergeable object literal (e.g. defineConfig(...)), init warns and leaves the file untouched instead of rewriting it. CommonJS configs keep the previous behavior.
f2529b8: Rewrite the agent-fix progress renderer on top of log-update, cli-truncate, and string-width. log-update now owns the in-place block rewriting that was previously done with manual cursor-up/clear-line escape sequences, and line truncation is measured by display width instead of code units — so lint messages containing emoji or CJK text can no longer overflow the terminal row and corrupt the animated block.
277b9d6: Replace the hand-rolled child-process handling in the agent fix runner with execa. The timeout → SIGTERM → grace period → SIGKILL escalation, stderr capture, and spawn-failure handling now use execa's timeout and forceKillAfterDelay options, which are battle-tested across platforms (including Windows kill semantics the manual implementation didn't cover). Behavior is unchanged: agent runs still time out after 5 minutes, escalate to SIGKILL after a 10-second grace period, and report a capped stderr tail.
1614a80: Drop the direct cross-spawn dependency: all synchronous process spawning (linter runs, tool version checks, editor extension installs, skill installs) now goes through a small adapter over execa's sync API, which owns the Windows spawn semantics cross-spawn provided. The adapter preserves the spawnSync result shape (status/signal/error/stdout), always disables shell interpretation, and always decodes output as UTF-8. execa was already a dependency for the agent fix runner, so this consolidates on one process-spawning library.
ac114b4: Replace the glob dependency with fast-glob for the tsconfig.json scan during init. fast-glob was already in the dependency tree via find-workspaces, so this drops glob's transitive dependencies (minipass, path-scurry, etc.) from the install without changing behavior.
1c48c68: Enable Tailwind CSS class sorting (sortTailwindcss) in the Oxfmt preset. Classes in class/className attributes and in clsx, cva, tw, twMerge, cn, twJoin, and tv calls are now sorted using the same algorithm as prettier-plugin-tailwindcss, matching the behavior of the Prettier preset (which always loads the Tailwind plugin) and the Biome preset's useSortedClasses rule. Projects without Tailwind installed are unaffected beyond class strings being sorted against the default theme, and oxfmt versions older than 0.35.0 ignore the option.
dc78be4: Stop sorting TanStack route option keys in route files. TanStack Router's route option types are order-sensitive (head/component infer loaderData from properties declared before them), so the Biome useSortedKeys source action rewrote createFileRoute literals into an order that breaks type inference (loaderData becomes never). The Biome TanStack preset now disables useSortedKeys for route files, and the oxlint TanStack preset disables sort-keys there so route files aren't caught between it and react-doctor/tanstack-start-route-property-order.
d018b7f: Fix several agent fix mode (fix --claude/--codex) issues: runs no longer abort with ENOBUFS when linter JSON output exceeds 1MB, user-supplied format/reporter flags can no longer override the JSON reporter and break parsing, a stuck agent process is force-killed 10 seconds after the timeout instead of hanging forever, and the progress renderer no longer garbles TTY output when file paths and rule names exceed the terminal width.
d018b7f: Fix Biome config migration leaving the legacy bare "extends": ["ultracite"] form in place, which breaks Biome's module resolution since the package has no root export. It's now mapped to ultracite/biome/core.
d018b7f: Fix ultracite doctor reporting spurious failures: config checks now walk up parent directories (matching check/fix and the linters themselves) so monorepo packages inheriting a root config pass, .oxlintrc.json is accepted as a valid oxlint config (with a migration suggestion), and Prettier/Stylelint configs declared via package.json keys are recognized.
d018b7f: Fix ultracite init corrupting existing Prettier/Stylelint/ESLint configs by writing an ESM module into JSON/YAML/TOML/CJS config files (e.g. .prettierrc, eslint.config.cjs). Updates now write the default .mjs config instead and remove the incompatible file so it can't shadow the new one.
d018b7f: Fix ultracite check/fix misrouting space-separated flag values (e.g. --max-warnings 10) into the file list, which scrambled the underlying linter invocation and made formatters fail on bogus targets. Positional files listed before a -- separator are also kept as lint targets instead of being reclassified as passthrough, which could silently widen formatter runs to the whole project.
d018b7f: Fix Lefthook and pre-commit YAML updates silently doing nothing on common config shapes: the Lefthook job is now inserted correctly when jobs: isn't the first key under pre-commit: (and no longer matches a jobs: key in a different hook), repos: [] in .pre-commit-config.yaml is handled, and shapes that can't be safely edited produce a warning instead of writing the file back unchanged.
d018b7f: Fix the Husky integration overwriting an existing .husky/pre-commit hook: ultracite init ran husky init, which unconditionally replaces the hook with npm test. It now runs plain husky to set up the hooks infrastructure without touching the hook file.
d018b7f: Fix ultracite init destroying user files it couldn't parse or merge: unparseable tsconfig.json files are no longer replaced with a minimal config, unparseable .vscode/.zed settings are no longer overwritten wholesale, and lint-staged configs with function-valued entries are left untouched — all now warn and skip instead.
d018b7f: Fix re-running ultracite init on an oxlint setup silently enabling the full js-plugins preset — the previously selected JS plugins are now preserved when no new selection is made. Init also no longer flips an explicit "type": "commonjs" in package.json to "module"; it warns instead.
d018b7f: Fix switching linters removing storybook from the project's dependencies. It was swept into the removal set as a peer of eslint-plugin-storybook, but it's a user-facing tool a project may use independently of linting.
d018b7f: Fix Stylelint target generation dropping directories with a dot in their name (e.g. app.web) and producing non-matching globs from Windows-style backslash paths. Framework detection also handles negated workspace patterns (!packages/legacy) again.
d018b7f: Fix the CLI becoming a silent no-op (exiting 0 without linting anything) when a generic TEST environment variable is set, as is common in CI matrices. The internal test guard now uses ULTRACITE_TEST.
36c7b80: Move @typescript-eslint/utils from dependencies to devDependencies. It was accidentally shipped as a runtime dependency in 7.9.0, pulling eslint and the typescript-eslint packages into every consumer's install (including oxlint-only setups) via npm's automatic peer dependency installation. Nothing in the published package imports it — it only exists to support the workspace-internal rule-parity script.
cd0a36c: Add --claude and --codex flags to ultracite fix. After the normal autofix pass, remaining diagnostics are handed to the Claude Code or Codex CLI non-interactively, one agent run per affected file, with a live per-issue spinner that flips to ✓/✗ once the fix is verified by a re-lint. Fixes that don't survive verification are retried (up to 3 attempts per file) with the fresh diagnostics and feedback that the previous approach failed. Works with all three linter modes (Oxlint, Biome, ESLint); exits non-zero if any issues remain, matching the plain fix contract.
The ultracite/oxlint/js-plugins preset no longer enables React Doctor's nextjs-* and TanStack (query-*, tanstack-start-*) rules for every consumer. Rules like nextjs-no-img-element and tanstack-start-no-anchor-element fire on plain <img>/<a> JSX and recommend framework replacements, which falsely errored in Vite + React and other non-Next/non-TanStack projects.
Those rules now live in two new add-on presets:
ultracite/oxlint/next/js-plugins
ultracite/oxlint/tanstack/js-plugins
ultracite init wires the matching add-on automatically when you select the framework together with oxlint-plugin-react-doctor. If you manage oxlint.config.ts by hand and use Next.js or TanStack, add the matching add-on preset to extends alongside js-plugins to keep those rules — or re-run npx ultracite init.
Also fixes re-running init on a config that already extends js-plugins producing a duplicate import jsPlugins declaration.
Patch Changes
3320cd8: Update Biome to 2.5.6. No stable (non-nursery) rules were added, removed, or promoted between 2.5.3 and 2.5.6, so the preset configs are unchanged.
477cd6e: Update ESLint to 10.8.0 and all ESLint plugins to their latest versions. Highlights:
eslint-plugin-react-doctor 0.9.3: the react preset expands from 149 to 417 rules, adopting the upstream recommended set (react-router, three.js/r3f, ink, motion, remotion, zustand/valtio/mobx, and more) while excluding rules that duplicate already-enabled react, react-hooks, and jsx-a11y rules. The next preset gains nextjs-async-dynamic-api-not-awaited and nextjs-metadata-url-consistency; the tanstack preset gains tanstack-start-missing-scripts, query-floating-mutate-async, and query-no-mutation-in-effect-as-read.
eslint-plugin-unicorn 72: adds no-missing-local-resource, no-multiple-promise-resolver-calls, no-shorthand-property-overrides, no-transition-all, no-unnecessary-string-trim, no-useless-re-export, prefer-then-catch, and require-frontmatter-fields. CSS-only rules are excluded from the preset since they fail config validation for JS files.
eslint-plugin-sonarjs 4.2: adds 11 rules including no-fixed-wait-in-tests, parameterized-tests, assertions-in-test-cases, prefer-native-lodash-alternative, and explicit-test-skip.
typescript-eslint 8.65: @typescript-eslint/no-loop-func and @typescript-eslint/no-restricted-imports were deprecated upstream in favor of the base rules, which now apply to TypeScript files.
eslint-plugin-astro 3: removes astro/no-omitted-end-tags and astro/valid-compile.
eslint-plugin-svelte 3.22: adds no-bind-value-on-checkable-inputs and no-conflicting-module-names; no-restricted-html-elements is now off because its schema requires a user-supplied element list.
@angular-eslint/eslint-plugin 22.1: adds inject-at-top and prefer-service-decorator.
b81578b: Fix the useSortedPackageJson action not being executed by turning on assist actions for package.json-like files.
9ec454a: Update oxlint to 1.76.0 and oxfmt to 0.61.0. New stable rules added to the presets: oxc/bad-match-all-arg, id-denylist, node/exports-style (core), react/function-component-definition with arrow-function components (react), and vitest/padding-around-test-blocks (vitest). node/no-top-level-await is off — top-level await is idiomatic in ESM, Astro frontmatter, and build scripts — and the ESLint preset's n/no-top-level-await is now off to match. No rules were removed or promoted out of nursery.
ba61c02: Fix generated oxlint.config.ts accessing plugin.name on ExternalPluginEntry without narrowing the string form, which caused a TypeScript error in projects that type-check the config (#753)
f480c12: Upgrade Biome to 2.5.3 to fix the LSP scanner deadlock (#10845) where editors get stuck on "Biome is scanning the project".
1d70c40: Fail fast with an actionable message when Biome can't resolve ultracite/biome/core. Biome resolves that config out of the project's node_modules, so it errors with an opaque "module not found" whenever Ultracite isn't installed there — a state npx ultracite check / bunx ultracite check hide, because they run the CLI from a temp cache regardless. check and fix now detect it before invoking Biome and say what's actually wrong, and doctor verifies that Ultracite resolves rather than just appearing in package.json (#750).
38d10bc: Move Biome's noUnknownAttribute rule out of the core preset and into the react preset. The rule only recognises React's JSX attribute names, so projects using Solid, Svelte, Vue, or Qwik were incorrectly flagged for framework-standard attributes such as class.
cf723bd: Add interactive and non-interactive selection for optional Oxlint JS plugins during init.
dc6d760: Disable the n/no-unpublished-import rule in the ESLint core config. This rule flags imports of packages that aren't listed as published dependencies, but it produces a lot of false positives in practice, so it's now turned off.
15ecfba: Fix ultracite init --linter eslint installing an unusable toolchain. The generated ESLint config imports eslint-plugin-storybook unconditionally (which requires the storybook peer) and the generated Stylelint config extends stylelint-config-standard / stylelint-config-idiomatic-order / stylelint-prettier, but none of those packages were installed — so a fresh ESLint setup failed to load with "Cannot find package 'storybook'" or "Could not find stylelint-config-standard". These four packages are now installed with the ESLint linter.
2f73a41: Upgrade to oxlint 1.73.0 and oxfmt 0.58.0, and enable the new lint rules they introduce: no-unreachable-loop, unicorn/explicit-timer-delay, and unicorn/no-confusing-array-with.
83b2783: Add an [astro] formatter mapping (astro-build.astro-vscode) to the oxlint VS Code editor settings generated by ultracite init, since oxfmt doesn't format .astro files.
d186c53: Move every JS-plugin-based rule set out of the Oxlint core and framework presets and into a single opt-in ultracite/oxlint/js-plugins preset. This covers eslint-plugin-github and eslint-plugin-sonarjs (previously in core) as well as oxlint-plugin-react-doctor (previously bundled into the react, next, and tanstack presets). The core, react, next, and tanstack presets now run entirely on Oxlint's native Rust rules, so new setups no longer install those dependencies and no longer pay the slower JS-plugin lint pass. To keep the extra ESLint-parity and React Doctor rules, install eslint-plugin-github, eslint-plugin-sonarjs, and oxlint-plugin-react-doctor and extend ultracite/oxlint/js-plugins alongside core (and your framework preset).
057753e: Add performance benchmarks for ultracite check / ultracite fix across all three providers (oxlint, biome, eslint). A new CI job builds the PR and its base branch on the same runner, benchmarks them interleaved, and fails on a statistically significant regression (median ratio > 1.25x with Mann-Whitney U p < 0.05) so config changes can't silently slow the linters down again.
c335a1f: Add **/node_modules and **/.git to the shared ignore patterns. oxlint only skips node_modules when a .gitignore lists it, so in projects without one, ultracite fix would lint and autofix files inside node_modules — corrupting installed packages (e.g. rewriting var enum wrappers in typescript/lib/typescript.js to self-referencing const, causing "Cannot access 'Comparison' before initialization" when oxlint loads eslint-plugin-sonarjs). Fixes #737.
ecd10cc: Disable sonarjs/no-implicit-dependencies and github/no-implicit-buggy-globals in the oxlint and ESLint presets. Both produce false positives through oxlint's JS plugin bridge: no-implicit-dependencies has no dependency-manifest resolution so it flags builtin (bun:test) and workspace imports as missing dependencies, and no-implicit-buggy-globals misreads module-scoped declarations such as Astro frontmatter as implicit globals.
c76f59d: Disable sonarjs/file-name-differ-from-class in the oxlint and ESLint presets. It fires on any file whose name differs from an exported class, which is noise for the many config and module files that export objects rather than classes.
29e30ce: Fold the github and sonarjs rules into the oxlint core preset. The standalone ultracite/oxlint/github and ultracite/oxlint/sonarjs presets are removed — ultracite ships framework presets, not individual plugins. Their rules now live in ultracite/oxlint/core, so every oxlint setup gets eslint-plugin-github and eslint-plugin-sonarjs through oxlint's JS plugin bridge, matching how the ESLint preset already bundles them into core.
This is a breaking change to generated configs: ultracite/oxlint/github and ultracite/oxlint/sonarjs no longer exist. Re-run ultracite init to regenerate oxlint.config.ts.
Also fixed: nine sonarjs rules (async-test-assertions, hooks-before-test-cases, no-duplicate-test-title, no-empty-test-title, no-floating-point-equality, no-forced-browser-interaction, no-trivial-assertions, prefer-specific-assertions, super-linear-regex) that exist in eslint-plugin-sonarjs but that oxlint's JS plugin bridge does not register. Naming them made oxlint hard-fail config parsing, which broke ultracite fix/check for oxlint projects using the sonarjs preset. They are omitted from oxlint core (still enabled in the ESLint preset), and a test now runs oxlint against core to catch this class of regression.
The React Doctor, github, and sonarjs plugins are installed into your project's devDependencies at init (as the ESLint plugins already were), rather than bundled as dependencies of ultracite — oxlint resolves JS plugin specifiers from the project root, so they must be installed there directly.
8a4291f: Upgrade to Oxlint 1.72.0 and Oxfmt 0.57.0. Oxfmt 0.57 adds native CSS and GraphQL formatters. The five new non-nursery Oxlint rules from the 1.71/1.72 releases are already covered by the presets (node/no-sync, node/no-mixed-requires, unicorn/prefer-number-coercion, unicorn/max-nested-calls, vue/no-async-in-computed-properties). The markdown ::: container-directive fence patch (which keeps proseWrap: "never" from folding fences into prose) was regenerated against the 0.57 bundle.
Configuration
📅Schedule: (in timezone Europe/Warsaw)
Branch creation
At any time (no schedule defined)
Automerge
At any time (no schedule defined)
🚦Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
👻Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
If you want to rebase/retry this PR, check this box
This PR contains the following updates:
| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@biomejs/biome](https://biomejs.dev) ([source](https://github.com/biomejs/biome/tree/HEAD/packages/@biomejs/biome)) | [`2.5.2` → `2.5.13`](https://renovatebot.com/diffs/npm/@biomejs%2fbiome/2.5.2/2.5.13) |  |  |
| [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) | `v1.59.0` → `v1.60.1` |  |  |
| [ultracite](https://www.ultracite.ai/) ([source](https://github.com/haydenbleasel/ultracite)) | [`7.9.0` → `7.12.2`](https://renovatebot.com/diffs/npm/ultracite/7.9.0/7.12.2) |  |  |
---
### Release Notes
<details>
<summary>biomejs/biome (@​biomejs/biome)</summary>
### [`v2.5.13`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#2513)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.12...@biomejs/biome@2.5.13)
##### Patch Changes
- [#​11379](https://github.com/biomejs/biome/pull/11379) [`07a0073`](https://github.com/biomejs/biome/commit/07a0073c1e1b1de323292968b8988cc6f79ec5c7) Thanks [@​Netail](https://github.com/Netail)! - Added the nursery rule [`useLayeredStyles`](https://biomejs.dev/linter/rules/use-layered-styles/), which enforces that style rules are defined within a cascade layer and import rules to import its styles into a cascade layer.
```css
/* Invalid */
@import 'foo.css';
.my-style {
color: red;
}
/* Valid */
@import 'foo.css' layer(base);
@layer base {
.my-style {
color: red;
}
}
```
- [#​11667](https://github.com/biomejs/biome/pull/11667) [`e997900`](https://github.com/biomejs/biome/commit/e997900c3eaf9d5efaf1ca12a067a962e1a04d0b) Thanks [@​devtechedge](https://github.com/devtechedge)! - Added the nursery rule [`useBetterDomTraversing`](https://biomejs.dev/linter/rules/use-better-dom-traversing), which prefers `.firstChild`, `.firstElementChild`, `.closest()`, and merged `.querySelector()` calls over positional DOM traversal.
```js
element.childNodes[0];
element.children[0];
element.parentElement.parentElement;
element.querySelector("a").querySelector("b");
```
- [#​11620](https://github.com/biomejs/biome/pull/11620) [`20e513a`](https://github.com/biomejs/biome/commit/20e513af6fb0127854f4eb5cb9edee0f20d1b543) Thanks [@​jakeleventhal](https://github.com/jakeleventhal)! - Fixed [#​11610](https://github.com/biomejs/biome/issues/11610), [#​11611](https://github.com/biomejs/biome/issues/11611), [#​11612](https://github.com/biomejs/biome/issues/11612), [#​11615](https://github.com/biomejs/biome/issues/11615), and [#​11616](https://github.com/biomejs/biome/issues/11616): Biome no longer fully infers an imported generic declaration just to apply its type arguments, restoring type-aware lint performance for large libraries such as Zod. This improves [`useRegexpExec`](https://biomejs.dev/linter/rules/use-regexp-exec), [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises), [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises), [`useNullishCoalescing`](https://biomejs.dev/linter/rules/use-nullish-coalescing), and [`noUnsafePlusOperands`](https://biomejs.dev/linter/rules/no-unsafe-plus-operands).
- [#​11657](https://github.com/biomejs/biome/pull/11657) [`e322040`](https://github.com/biomejs/biome/commit/e32204014687469e6c6f3684d32caf56311cc118) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7495](https://github.com/biomejs/biome/issues/7495): [`noUselessConstructor`](https://biomejs.dev/linter/rules/no-useless-constructor/) now ignores TypeScript constructors that forward at least one argument to `super`, preserving constructors that narrow the subclass's accepted parameter types. The exemption also applies when the parent and child signatures are identical; JavaScript and zero-argument forwarding behavior are unchanged.
- [#​11670](https://github.com/biomejs/biome/pull/11670) [`4969ee1`](https://github.com/biomejs/biome/commit/4969ee1d73b48615f46f024539cfdb847f07064d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7076](https://github.com/biomejs/biome/issues/7076): [`useAriaPropsForRole`](https://biomejs.dev/linter/rules/use-aria-props-for-role) and [`useFocusableInteractive`](https://biomejs.dev/linter/rules/use-focusable-interactive) no longer report non-focusable elements with `role="separator"`. A separator with an explicit `tabIndex` or `tabindex` still requires `aria-valuenow`.
- [#​11627](https://github.com/biomejs/biome/pull/11627) [`23aad6d`](https://github.com/biomejs/biome/commit/23aad6df8f71bd9b134633c43d2334771687d10d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6571](https://github.com/biomejs/biome/issues/6571) so Grit plugins can capture and inspect multiple named import specifiers.
- [#​11631](https://github.com/biomejs/biome/pull/11631) [`00dbd3a`](https://github.com/biomejs/biome/commit/00dbd3a30489617d5bb55466006ac9977612a23b) Thanks [@​ematipico](https://github.com/ematipico)! - Reduced unnecessary type inference when type-aware lint rules inspect members of namespace imports from libraries such as Zod. Fixed type inference so blanket re-exports do not expose default exports.
- [#​11628](https://github.com/biomejs/biome/pull/11628) [`a2f8ff7`](https://github.com/biomejs/biome/commit/a2f8ff7a1eb01f5c91e7154937e17f761863e3bc) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noXorAsExponentiation`](https://biomejs.dev/linter/rules/no-xor-as-exponentiation/), which reports the bitwise XOR operator `^` between two decimal integer literals, where the exponentiation operator `**` was likely intended.
```js
const kibibyte = 2 ^ 10; // 8, not 1024
```
- [#​11670](https://github.com/biomejs/biome/pull/11670) [`4969ee1`](https://github.com/biomejs/biome/commit/4969ee1d73b48615f46f024539cfdb847f07064d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7192](https://github.com/biomejs/biome/issues/7192): [`noUnusedPrivateClassMembers`](https://biomejs.dev/linter/rules/no-unused-private-class-members/) now considers compound assignments such as `??=` to read and use private class members.
- [#​11676](https://github.com/biomejs/biome/pull/11676) [`840a52a`](https://github.com/biomejs/biome/commit/840a52ad14b027e21661a9121b316ccac139d736) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11672](https://github.com/biomejs/biome/issues/11672) and [#​11671](https://github.com/biomejs/biome/issues/11671) by disabling the experimental capitalized-call and effect-dependency checks in [`useReactCompiler`](https://biomejs.dev/linter/rules/use-react-compiler/), matching their exclusion from upstream's recommended lint preset. Valid calls such as `Intl.NumberFormat()` and captures of variables declared inside effects no longer produce these diagnostics.
- [#​11660](https://github.com/biomejs/biome/pull/11660) [`49485ed`](https://github.com/biomejs/biome/commit/49485eda65813e5b7e2be68cdd56ffd90674f713) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11653](https://github.com/biomejs/biome/issues/11653): Astro template suppression comments (`{/* biome-ignore lint: reason */}`) now suppress matching HTML lint diagnostics on the following line when full HTML support is enabled.
- [#​11664](https://github.com/biomejs/biome/pull/11664) [`9a73b9c`](https://github.com/biomejs/biome/commit/9a73b9cc4bb1000336dc25704546f4d63a9bb326) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of [`useRegexpExec`](https://biomejs.dev/linter/rules/use-regexp-exec/).
- [#​11661](https://github.com/biomejs/biome/pull/11661) [`5341b3f`](https://github.com/biomejs/biome/commit/5341b3fb9c7bae8ce2426d32e6e72aed9637a82f) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7479](https://github.com/biomejs/biome/issues/7479). [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) now treats Unicode escapes in identifiers as the same binding as their decoded spelling.
- [#​11630](https://github.com/biomejs/biome/pull/11630) [`62e1fc5`](https://github.com/biomejs/biome/commit/62e1fc59621b6ab5c3988ae36b6591180e07d08f) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed the HTML formatter inserting whitespace between adjacent Svelte expressions when their combined length exceeds the line width.
```diff
<span>
- {head.median - base.median >= 0 ? "+" : "−"}
- {formatMs(Math.abs(head.median - base.median))}
+ {head.median - base.median >= 0 ? "+" : "−"}{formatMs(Math.abs(head.median - base.median))}
</span>
```
- [#​11658](https://github.com/biomejs/biome/pull/11658) [`ed4bfa4`](https://github.com/biomejs/biome/commit/ed4bfa4a1de63241a7c3586ff12d7f990cf84303) Thanks [@​fredrikblau](https://github.com/fredrikblau)! - Fixed [#​11644](https://github.com/biomejs/biome/issues/11644): [`useHeadingContent`](https://biomejs.dev/linter/rules/use-heading-content/) no longer reports headings that render their text with a directive: `set:html` and `set:text` in Astro files, `v-html` and `v-text` in Vue files.
```astro
<h1 set:html={heading} />
<h2 set:text={heading}></h2>
```
```vue
<template>
<h1 v-html="heading"></h1>
<h2 v-text="heading"></h2>
</template>
```
- [#​11613](https://github.com/biomejs/biome/pull/11613) [`47d7383`](https://github.com/biomejs/biome/commit/47d7383d049b36ff8eaba50912176928143dd9e9) Thanks [@​ematipico](https://github.com/ematipico)! - Improved the performance of Biome Formatter up to \~50% in some cases.
- [#​11655](https://github.com/biomejs/biome/pull/11655) [`fd8fc74`](https://github.com/biomejs/biome/commit/fd8fc741ee2b77b3b127859fabc7a29b19e1fb0b) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6974](https://github.com/biomejs/biome/issues/6974), where [`noUnusedPrivateClassMembers`](https://biomejs.dev/linter/rules/no-unused-private-class-members/) incorrectly reported TypeScript private constructor properties read through object destructuring from `this` as unused.
- [#​11618](https://github.com/biomejs/biome/pull/11618) [`21a10cf`](https://github.com/biomejs/biome/commit/21a10cfcac4db8044e8bff6b24d9b1e197c2b343) Thanks [@​siketyan](https://github.com/siketyan)! - Fixed [#​11605](https://github.com/biomejs/biome/issues/11605): Type inference now infers the type of an unannotated callback parameter from the signature of the function the callback is passed to, and honours explicit type arguments on call expressions. This improves type-aware analysis for [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/), [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/), [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/), [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/), [`noUnnecessaryConditions`](https://biomejs.dev/linter/rules/no-unnecessary-conditions/), [`noUnsafePlusOperands`](https://biomejs.dev/linter/rules/no-unsafe-plus-operands/), [`noUselessTypeConversion`](https://biomejs.dev/linter/rules/no-useless-type-conversion/), [`useArrayFind`](https://biomejs.dev/linter/rules/use-array-find/), [`useArraySortCompare`](https://biomejs.dev/linter/rules/use-array-sort-compare/), [`useAwaitThenable`](https://biomejs.dev/linter/rules/use-await-thenable/), [`useDisposables`](https://biomejs.dev/linter/rules/use-disposables/), [`useExhaustiveSwitchCases`](https://biomejs.dev/linter/rules/use-exhaustive-switch-cases/), [`useIncludes`](https://biomejs.dev/linter/rules/use-includes/), [`useNullishCoalescing`](https://biomejs.dev/linter/rules/use-nullish-coalescing/), [`useRegexpExec`](https://biomejs.dev/linter/rules/use-regexp-exec/), and [`useStringStartsEndsWith`](https://biomejs.dev/linter/rules/use-string-starts-ends-with/). For example, `noFloatingPromises` can now detect Promises reached through such parameters:
```ts
interface Context {
doSomething(): Promise<void>;
}
declare function test(callback: (ctx: Context) => Promise<void>): void;
test(async (ctx) => {
ctx.doSomething(); // now reported as a floating promise
});
```
- [#​11698](https://github.com/biomejs/biome/pull/11698) [`b019982`](https://github.com/biomejs/biome/commit/b019982bfbf8a9db58bd16b1e5e1a241128540d4) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed parsing of unquoted CSS URLs beginning with `@` or `!`, such as `url(@/assets/icon.svg)` and `url(!font.woff2)`. Preserved escaped and non-ASCII whitespace in raw URLs during formatting.
```diff
-background-image: url(image\);
+background-image: url(image\ );
```
- [#​11622](https://github.com/biomejs/biome/pull/11622) [`c23e4c7`](https://github.com/biomejs/biome/commit/c23e4c7e7a33d3f00863d80be663678af8c2a073) Thanks [@​Netail](https://github.com/Netail)! - Added the nursery rule [`noUnsafeIframeSandbox`](https://biomejs.dev/linter/rules/no-unsafe-iframe-sandbox/), which reports `iframe` elements whose `sandbox` attribute combines `allow-scripts` and `allow-same-origin`, since that combination lets the embedded document remove its own sandboxing.
```jsx
<iframe src="https://example.com" sandbox="allow-scripts allow-same-origin" />
```
- [#​11606](https://github.com/biomejs/biome/pull/11606) [`de0528f`](https://github.com/biomejs/biome/commit/de0528f940ea41b798816e1923b2b51ffcd0b473) Thanks [@​dyc3](https://github.com/dyc3)! - Added the recommended [`noSvelteAtHtmlTags`](https://biomejs.dev/linter/rules/no-svelte-at-html-tags) nursery rule, which reports Svelte `{@html}` tags that render unescaped HTML.
- [#​11670](https://github.com/biomejs/biome/pull/11670) [`4969ee1`](https://github.com/biomejs/biome/commit/4969ee1d73b48615f46f024539cfdb847f07064d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6782](https://github.com/biomejs/biome/issues/6782): GritQL plugins now match captured JSX component names against code snippets such as `React.Fragment`.
- [#​11687](https://github.com/biomejs/biome/pull/11687) [`09d97d9`](https://github.com/biomejs/biome/commit/09d97d973b522c6bd95e6d26006cb4b3a221396d) Thanks [@​hori-design](https://github.com/hori-design)! - Fixed [#​11678](https://github.com/biomejs/biome/issues/11678): [`useReactCompiler`](https://biomejs.dev/linter/rules/use-react-compiler/) no longer panics on files that contain non-ASCII characters. This bumps the React Compiler version.
- [#​11595](https://github.com/biomejs/biome/pull/11595) [`a64d757`](https://github.com/biomejs/biome/commit/a64d7572d5684f3555be5dc02eb1d29a16f290dc) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery Vue-domain rule [`useVueBaseImport`](https://biomejs.dev/linter/rules/use-vue-base-import/) rule, which enforces importing Vue APIs from `vue` instead of internal `@vue/*` packages.
- [#​11675](https://github.com/biomejs/biome/pull/11675) [`353cbae`](https://github.com/biomejs/biome/commit/353cbae47056f8e03b27d1d9b69e165b5b72f122) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [`useReactCompiler`](https://biomejs.dev/linter/rules/use-react-compiler/) silently producing no diagnostics in WebAssembly builds, including the playground.
- [#​11625](https://github.com/biomejs/biome/pull/11625) [`ea20e5a`](https://github.com/biomejs/biome/commit/ea20e5a4640b4455cd12e0eb6ffaa590803c67a2) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Improved linting performance for large CSS and JSON files.
- [#​11670](https://github.com/biomejs/biome/pull/11670) [`4969ee1`](https://github.com/biomejs/biome/commit/4969ee1d73b48615f46f024539cfdb847f07064d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7527](https://github.com/biomejs/biome/issues/7527): suppression actions for diagnostics emitted on comments are now inserted before the diagnostic comment. In particular, suppressing [`noTsIgnore`](https://biomejs.dev/linter/rules/no-ts-ignore/) now places the `biome-ignore` comment before `@ts-ignore`.
- [#​11655](https://github.com/biomejs/biome/pull/11655) [`fd8fc74`](https://github.com/biomejs/biome/commit/fd8fc741ee2b77b3b127859fabc7a29b19e1fb0b) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​8629](https://github.com/biomejs/biome/issues/8629), where [`noUnusedPrivateClassMembers`](https://biomejs.dev/linter/rules/no-unused-private-class-members/) incorrectly reported used private TypeScript method overload signatures as unused.
- [#​11669](https://github.com/biomejs/biome/pull/11669) [`579f401`](https://github.com/biomejs/biome/commit/579f401b0a97760db99d64b97b2bc29ab20e3f8a) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Improved the performance of [`noExcessiveLinesPerFile`](https://biomejs.dev/linter/rules/no-excessive-lines-per-file/) when `skipBlankLines` is `false`.
### [`v2.5.12`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#2512)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.11...@biomejs/biome@2.5.12)
##### Patch Changes
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro attribute expressions rejecting TypeScript and JSX syntax that is accepted in text expressions.
```astro
<Component icon={<Icon />} count={total as number} onSelect={(e: Event) => e} />
```
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed a bare `>` in the children of an Astro expression being treated as markup, such as `{x && <div>a > b</div>}`.
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed HTML comments inside an Astro expression failing to parse. They are now read as trivia, wherever they appear among the children.
```astro
{x && <div><!-- first -->text<!-- last --></div>}
{cond && <a></a><!-- c --><b></b>}
```
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed `is:raw` children inside an Astro expression being read as JSX, such as `{x && <div is:raw>{not js} < & text</div>}`.
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/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.
```astro
{cond && <style>a { color: red }</style>}
{cond && <script>let x = {a: 1};</script>}
```
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Added support for template literal attribute values inside an Astro expression, such as ``{x && <C data-x=`t${x}` />}``.
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed unquoted attribute values being rejected inside an Astro expression, such as `{x && <a class=foo maxlength=255 href=/about>go</a>}`.
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed a template literal nested inside `${}` breaking the rest of an Astro file, such as ``const href = `/blog${page === 0 ? '' : `/${page + 1}`}`;``.
- [#​11440](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed a quote inside a regex character class breaking the rest of an Astro file, such as `const unsafe = /[/"]/;`.
- [#​11508](https://github.com/biomejs/biome/pull/11508) [`54f3a2e`](https://github.com/biomejs/biome/commit/54f3a2ea2765c80df11d04e47cd8da0354f5f7ca) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`useFlatMathMinMax`](https://biomejs.dev/linter/rules/use-flat-math-min-max/). Because `Math.min()` and `Math.max()` accept any number of arguments, the rule reports unnecessary nested calls to the same method:
```js
Math.max(Math.max(a, b), c);
```
The fix flattens this expression to `Math.max(a, b, c)`.
- [#​11585](https://github.com/biomejs/biome/pull/11585) [`c5c8315`](https://github.com/biomejs/biome/commit/c5c8315cc2174b52834513d4c01edd09341e46cd) Thanks [@​Netail](https://github.com/Netail)! - Fixed [#​11475](https://github.com/biomejs/biome/issues/11475): `noUnresolvedImports` no longer reports Bun runtime built-in modules (`bun`, `bun:bundle`, `bun:ffi`, `bun:jsc`, `bun:sqlite`, `bun:test`).
- [#​11368](https://github.com/biomejs/biome/pull/11368) [`52a57b3`](https://github.com/biomejs/biome/commit/52a57b374af947ffcba11f0dd97b3cb9676ce044) Thanks [@​Austin1serb](https://github.com/Austin1serb)! - Fixed [#​6830](https://github.com/biomejs/biome/issues/6830): Biome now reports a diagnostic for excessively deep syntax instead of overflowing the native stack while releasing the parsed tree.
- [#​11596](https://github.com/biomejs/biome/pull/11596) [`1fc42ed`](https://github.com/biomejs/biome/commit/1fc42ed80a00e3ca86d44de8e1f66707061eb862) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noThisOutsideOfClass`](https://biomejs.dev/linter/rules/no-this-outside-of-class/). The rule reports `this` outside class members and TypeScript functions with an explicit `this` parameter.
```js
function Person(name) {
this.name = name;
}
```
- [#​11555](https://github.com/biomejs/biome/pull/11555) [`2516335`](https://github.com/biomejs/biome/commit/25163350c44aee821d3aeac5b20569cd37d1ae31) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11529](https://github.com/biomejs/biome/issues/11529), where [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) 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](https://github.com/biomejs/biome/pull/11518) [`0fee70c`](https://github.com/biomejs/biome/commit/0fee70c6420140d62e9e9507ff3eda735077691b) Thanks [@​HarperZ9](https://github.com/HarperZ9)! - Fixed [#​11500](https://github.com/biomejs/biome/issues/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](https://github.com/biomejs/biome/pull/11580) [`1277af2`](https://github.com/biomejs/biome/commit/1277af285ff5ab2e7a4deb07b32855c9caa7b770) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​5091](https://github.com/biomejs/biome/issues/5091): Biome no longer moves comments next to the `<` of a generic, which causes invalid TypeScript syntax:
```diff
- Generic<// a comment
+ Generic<
+ // a comment
```
- [#​11577](https://github.com/biomejs/biome/pull/11577) [`42995d2`](https://github.com/biomejs/biome/commit/42995d2d19a937c44e9abbd400901bc6ec0dcaf5) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​4592](https://github.com/biomejs/biome/issues/4592). Biome no longer crashes while parsing malformed `delete` expressions.
- [#​11590](https://github.com/biomejs/biome/pull/11590) [`67963b4`](https://github.com/biomejs/biome/commit/67963b4085558e414bc48bddb82618b7cc6d8fdf) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6427](https://github.com/biomejs/biome/issues/6427) so Grit plugins can use `function = ...` as a node argument.
- [#​11600](https://github.com/biomejs/biome/pull/11600) [`a689cb5`](https://github.com/biomejs/biome/commit/a689cb5405885db94693e8ce39a22a53950cf2bf) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6644](https://github.com/biomejs/biome/issues/6644): [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) now recognizes all interface declarations in a TypeScript declaration-merging group when the interface is referenced.
The following snippet no longer triggers the rule.
```ts
interface Things {
foo: string;
}
interface Things {
bar: string;
}
export type Key = keyof Things;
interface Things {
baz: string;
}
```
- [#​11591](https://github.com/biomejs/biome/pull/11591) [`d4a0716`](https://github.com/biomejs/biome/commit/d4a07167df43309f424dc906fc5d0dc80b5e208d) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6615](https://github.com/biomejs/biome/issues/6615). [`noDuplicateProperties`](https://biomejs.dev/linter/rules/no-duplicate-properties/) no longer reports declarations nested in block at-rules as duplicates of declarations in their parent block.
- [#​11492](https://github.com/biomejs/biome/pull/11492) [`f2a07aa`](https://github.com/biomejs/biome/commit/f2a07aadedd47034dccffd941935c71481b4c354) Thanks [@​santichausis](https://github.com/santichausis)! - Fixed [#​11454](https://github.com/biomejs/biome/issues/11454): [`noMisplacedAssertion`](https://biomejs.dev/linter/rules/no-misplaced-assertion/) 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:
```js
import { fc, test } from "@fast-check/vitest";
test.prop([fc.string()])("round-trips", (s) => {
expect(s).toBe(s);
});
```
- [#​11589](https://github.com/biomejs/biome/pull/11589) [`65742b3`](https://github.com/biomejs/biome/commit/65742b3658ab08aed31be6afdfbb4d8d5a737c47) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​4928](https://github.com/biomejs/biome/issues/4928): [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports a value declaration as unused when its merged namespace is referenced.
- [#​11559](https://github.com/biomejs/biome/pull/11559) [`472dbc2`](https://github.com/biomejs/biome/commit/472dbc2c38ed1dbed6066fe26eac7ef97c79b05d) Thanks [@​levrik](https://github.com/levrik)! - Fixed a false positive in [`noVueDuplicateKeys`](https://biomejs.dev/linter/rules/no-vue-duplicate-keys/) 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:
```vue
<script setup>
import { toRef } from 'vue';
const props = defineProps(['foo']);
const foo = toRef(props, 'foo');
</script>
```
- [#​11594](https://github.com/biomejs/biome/pull/11594) [`6586ceb`](https://github.com/biomejs/biome/commit/6586cebec6182d37361ce42ca906ab190fd9a15b) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6640](https://github.com/biomejs/biome/issues/6640). Biome no longer crashes when linting malformed `for...of` statements.
- [#​11571](https://github.com/biomejs/biome/pull/11571) [`85b197d`](https://github.com/biomejs/biome/commit/85b197da722fc8d07cd10b8fa7e1271652ed5e26) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​10838](https://github.com/biomejs/biome/issues/10838): [`useSortedAttributes`](https://biomejs.dev/assist/actions/use-sorted-attributes/) no longer corrupts JSX attributes when nested JSX elements also require sorting.
- [#​11533](https://github.com/biomejs/biome/pull/11533) [`97e76c0`](https://github.com/biomejs/biome/commit/97e76c04cf19de45e63a22a9954c5077f45d4e6c) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11520](https://github.com/biomejs/biome/issues/11520), where the Biome scanner would start analysing dependencies multiple times, leading to long and unresponsive sessions.
- [#​11564](https://github.com/biomejs/biome/pull/11564) [`18a0e1f`](https://github.com/biomejs/biome/commit/18a0e1f058df3c8434d98d700eef5949695cb8f6) Thanks [@​Netail](https://github.com/Netail)! - Fixed the diagnostic range of [`noInferrableTypes`](https://biomejs.dev/linter/rules/no-inferrable-types/) so it now highlights only the type instead of including the leading `:` colon, spaces and comments.
- [#​11540](https://github.com/biomejs/biome/pull/11540) [`124fdaa`](https://github.com/biomejs/biome/commit/124fdaa1dad40a1e5d485da13ca51064b0a40adb) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`#11537`](https://github.com/biomejs/biome/issues/11537): [`noShorthandPropertyOverrides`](https://biomejs.dev/linter/rules/no-shorthand-property-overrides/) 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](https://github.com/biomejs/biome/pull/11532) [`7ceb0ee`](https://github.com/biomejs/biome/commit/7ceb0ee925461abd9afe17b12a09abc9c0b43c28) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11528](https://github.com/biomejs/biome/issues/11528): [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) 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](https://github.com/biomejs/biome/pull/11474) [`3c6412e`](https://github.com/biomejs/biome/commit/3c6412e7f44b9aaac750844f9a5f7ff7c3aa0feb) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10241](https://github.com/biomejs/biome/issues/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](https://github.com/biomejs/biome/pull/11593) [`6c7fd27`](https://github.com/biomejs/biome/commit/6c7fd273848765547dba547e550fc319df7455a0) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noVueDeprecatedScopedSlots`](https://biomejs.dev/linter/rules/no-vue-deprecated-scoped-slots/). 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](https://github.com/biomejs/biome/pull/11440) [`b88f1ea`](https://github.com/biomejs/biome/commit/b88f1ea368c727ff8ebeddcaf9448f6ddc7c4878) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11581) [`f4e5ebb`](https://github.com/biomejs/biome/commit/f4e5ebb40e64d4488166bf0f2b7d8d96a6118016) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`useModernMathApis`](https://biomejs.dev/linter/rules/use-modern-math-apis/). The rule reports legacy mathematical patterns that have direct modern `Math` equivalents.
```js
Math.sqrt(a * a + b * b);
```
- [#​11597](https://github.com/biomejs/biome/pull/11597) [`a20f44a`](https://github.com/biomejs/biome/commit/a20f44a3eb2086e65a6d8077eb89ab81f4d73130) Thanks [@​Netail](https://github.com/Netail)! - Added the nursery rule [`noBunModules`](https://biomejs.dev/linter/rules/no-bun-modules/), which forbids the use of Bun builtin modules (e.g. `bun:sqlite`, `bun:ffi`).
- [#​11545](https://github.com/biomejs/biome/pull/11545) [`7d54688`](https://github.com/biomejs/biome/commit/7d54688ee89244af3b287402295340d1d35ca96e) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11542](https://github.com/biomejs/biome/issues/11542): Biome now reports HTML comments between Svelte tag attributes as parse errors.
- [#​11582](https://github.com/biomejs/biome/pull/11582) [`b6611dd`](https://github.com/biomejs/biome/commit/b6611dd5c1a54b80f181f4c0b5147a2c1c311dc9) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​3862](https://github.com/biomejs/biome/issues/3862). Biome now parses legacy Internet Explorer `filter` and `-ms-filter` values such as `progid:DXImageTransform...` and `alpha(opacity=40)`.
- [#​11575](https://github.com/biomejs/biome/pull/11575) [`65da251`](https://github.com/biomejs/biome/commit/65da251b0f4da0680f7ccb8820ba428344018848) Thanks [@​dyc3](https://github.com/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](https://github.com/biomejs/biome/pull/11576) [`0f78499`](https://github.com/biomejs/biome/commit/0f784998407970338481a1017279b9f17c43c61f) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​3515](https://github.com/biomejs/biome/issues/3515) and [#​10395](https://github.com/biomejs/biome/issues/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](https://github.com/biomejs/biome/pull/11539) [`0fca643`](https://github.com/biomejs/biome/commit/0fca643d22845c28e0e0b7375013155e3f80717f) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11512](https://github.com/biomejs/biome/issues/11512), where [`style/noDescendingSpecificity`](https://biomejs.dev/linter/rules/no-descending-specificity/) missed lower-specificity selectors after a later higher-specificity selector with the same tail selector.
- [#​11544](https://github.com/biomejs/biome/pull/11544) [`040f867`](https://github.com/biomejs/biome/commit/040f86770cb830cd9ddaa827fd5b78fa16e7484c) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11541](https://github.com/biomejs/biome/issues/11541): formatting a Svelte render tag followed by an HTML comment no longer duplicates the comment.
```diff
<div>
{@render children?.()}
<!-- comment -->
- <!-- comment -->
</div>
```
- [#​11565](https://github.com/biomejs/biome/pull/11565) [`ee69e0e`](https://github.com/biomejs/biome/commit/ee69e0e8a90e6231a3b208cda4b3e65ad8b2006a) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11525](https://github.com/biomejs/biome/issues/11525). Now the configuration schema correctly provides auto-completion for linter domains.
- [#​11583](https://github.com/biomejs/biome/pull/11583) [`b19390c`](https://github.com/biomejs/biome/commit/b19390cb6ec276651365542387469c8b4634e0af) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11352](https://github.com/biomejs/biome/issues/11352): [`useExplicitLengthCheck`](https://biomejs.dev/linter/rules/use-explicit-length-check/) 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](https://github.com/biomejs/biome/pull/11562) [`753e955`](https://github.com/biomejs/biome/commit/753e955afe7bcb360506795fed25697828a59772) Thanks [@​ematipico](https://github.com/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](https://github.com/biomejs/biome/pull/11217) [`7d3ee9c`](https://github.com/biomejs/biome/commit/7d3ee9ccd5cf5df585ab3a17517a9514608a645a) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed handling of `biome-ignore format` suppression comments on TypeScript declared class properties with string literal names.
```ts
class A {
declare /* biome-ignore format: exercise suppression checking */ 'a-b': 0;
}
```
- [#​11497](https://github.com/biomejs/biome/pull/11497) [`f5d7896`](https://github.com/biomejs/biome/commit/f5d7896f7115a2d57962471f6d7abfafddb07918) Thanks [@​dyc3](https://github.com/dyc3)! - Added the [`noInvalidFileInputAccept`](https://biomejs.dev/linter/rules/no-invalid-file-input-accept/) nursery rule. The rule reports invalid literal `accept` values on file inputs in JSX and HTML, and normalizes common mistakes.
```jsx
<input type="file" accept="image/jpg" />
```
- [#​11345](https://github.com/biomejs/biome/pull/11345) [`ac58958`](https://github.com/biomejs/biome/commit/ac58958135d79e790f0edee8ec18aaa16f5d9849) Thanks [@​jakeleventhal](https://github.com/jakeleventhal)! - Improved type inference performance by avoiding resolution of unused members in object arguments.
- [#​11554](https://github.com/biomejs/biome/pull/11554) [`2d55931`](https://github.com/biomejs/biome/commit/2d559316e00764b0843d3d483d4a093fb566de57) Thanks [@​Netail](https://github.com/Netail)! - Added the new nursery rule [`useReactNamingConvention`](https://biomejs.dev/linter/rules/use-react-naming-convention/), 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](https://github.com/biomejs/biome/pull/11491) [`1d6210b`](https://github.com/biomejs/biome/commit/1d6210b8ef6a2d2d83ea16e6fb95c124fd370690) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noUnmodifiedLoopCondition`](https://biomejs.dev/linter/rules/no-unmodified-loop-condition/), which reports variables in loop conditions that are never modified in the loop.
```js
let node = getNode();
while (node) {
process(node);
}
```
### [`v2.5.11`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#2511)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.10...@biomejs/biome@2.5.11)
##### Patch Changes
- [#​11499](https://github.com/biomejs/biome/pull/11499) [`9743d0c`](https://github.com/biomejs/biome/commit/9743d0ceae807757300e5b0ec66266125bc664c1) Thanks [@​scs0209](https://github.com/scs0209)! - Fixed [#​11496](https://github.com/biomejs/biome/issues/11496): [`useValidAnchor`](https://biomejs.dev/linter/rules/use-valid-anchor/) now treats Astro JSX shorthand attributes like `<a {href}>` as a valid `href`.
- [#​11437](https://github.com/biomejs/biome/pull/11437) [`88f805e`](https://github.com/biomejs/biome/commit/88f805e19b67ab4c876e4fc4a8b4018bd03df20b) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed [#​9944](https://github.com/biomejs/biome/issues/9944): adjacent elements inside an Astro expression now parse as an implicit fragment instead of raising an error.
```astro
{options.map(() =>
<div />
<div />
)}
```
- [#​11437](https://github.com/biomejs/biome/pull/11437) [`88f805e`](https://github.com/biomejs/biome/commit/88f805e19b67ab4c876e4fc4a8b4018bd03df20b) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro templates rejecting unclosed HTML void elements, such as `{cond && <br>}`.
- [#​11507](https://github.com/biomejs/biome/pull/11507) [`e2fc036`](https://github.com/biomejs/biome/commit/e2fc03669886732ac9c768aa00f3ffae46241848) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11157](https://github.com/biomejs/biome/issues/11157): [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports Vue `<script setup>` bindings used by CSS `v-bind()` as unused.
- [#​11398](https://github.com/biomejs/biome/pull/11398) [`afc4615`](https://github.com/biomejs/biome/commit/afc4615079a72e0810f06186f78e5cb177a5638e) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11389](https://github.com/biomejs/biome/issues/11389): Files passed through `--stdin-file-path` now use full HTML support for Astro, Svelte, and Vue when it is enabled.
- [#​11526](https://github.com/biomejs/biome/pull/11526) [`372cd68`](https://github.com/biomejs/biome/commit/372cd68ba156efdc497017598a49e21484a3ada9) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [`noVueRefAsOperand`](https://biomejs.dev/linter/rules/no-vue-ref-as-operand/) 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](https://github.com/biomejs/biome/pull/11458) [`a7cd286`](https://github.com/biomejs/biome/commit/a7cd2868c3d98fbc75174c5bf69fdcfacbe87304) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11436](https://github.com/biomejs/biome/issues/11436): GritQL snippets such as `export { $specifiers } from $source` now match named re-exports with aliases, inline `type` modifiers, and multiple specifiers.
- [#​11515](https://github.com/biomejs/biome/pull/11515) [`382b15d`](https://github.com/biomejs/biome/commit/382b15d5e0f9e2a75604fec1f15ad342628c99c7) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11390](https://github.com/biomejs/biome/issues/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](https://github.com/biomejs/biome/pull/11516) [`6f40e82`](https://github.com/biomejs/biome/commit/6f40e8224afc58737626d997c0ea779e2b37baca) Thanks [@​levrik](https://github.com/levrik)! - Fixed [`noVueRefAsOperand`](https://biomejs.dev/linter/rules/no-vue-ref-as-operand/) 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.
```js
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](https://github.com/biomejs/biome/pull/11495) [`496268d`](https://github.com/biomejs/biome/commit/496268d1470b6a4cd52bff03d37899c4bb7f77ab) Thanks [@​Netail](https://github.com/Netail)! - Fixed [`useGraphqlNamingConvention`](https://biomejs.dev/linter/rules/use-graphql-naming-convention/) so it no longer reports GraphQL enum value definitions with comments & descriptions and now displays a more accurate diagnostic range.
- [#​11407](https://github.com/biomejs/biome/pull/11407) [`6ef52b0`](https://github.com/biomejs/biome/commit/6ef52b02fc07e23de7ad498d853aaf2c825717fd) Thanks [@​1678092075](https://github.com/1678092075)! - Fixed [#​11214](https://github.com/biomejs/biome/issues/11214): [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports type parameters declared by non-default function overload signatures that have an implementation.
- [#​11322](https://github.com/biomejs/biome/pull/11322) [`5c353e6`](https://github.com/biomejs/biome/commit/5c353e6c85a40807e2710684da807f8757ee650c) Thanks [@​jp-knj](https://github.com/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:
```astro
<div set:html={content} />
```
- [#​11462](https://github.com/biomejs/biome/pull/11462) [`18883b7`](https://github.com/biomejs/biome/commit/18883b7dafb8f9581534508fdd5b1e34632f2724) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10776](https://github.com/biomejs/biome/issues/10776): [`useVueHyphenatedAttributes`](https://biomejs.dev/linter/rules/use-vue-hyphenated-attributes/) no longer reports lowercase attribute names containing punctuation, such as `pt:header:data-test-id` and `some_attr`.
- [#​11476](https://github.com/biomejs/biome/pull/11476) [`3270ca4`](https://github.com/biomejs/biome/commit/3270ca4560a28eee92564b9ef64f778972cbf3ab) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10330](https://github.com/biomejs/biome/issues/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.
```diff
-<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](https://github.com/biomejs/biome/pull/11191) [`3e5367f`](https://github.com/biomejs/biome/commit/3e5367f13de6c8cb80070544774c9935cac7d649) Thanks [@​ematipico](https://github.com/ematipico)! - Added the nursery rule [`noUndeclaredCustomProperties`](https://biomejs.dev/linter/rules/no-undeclared-custom-properties/), 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:
```css
a { color: var(--undefined-color); }
```
- [#​11435](https://github.com/biomejs/biome/pull/11435) [`7754894`](https://github.com/biomejs/biome/commit/7754894ffaaaa2ab0b281789d5ceae8f333d61a4) Thanks [@​levrik](https://github.com/levrik)! - Fixed: Variables and imports used as custom Vue directives are no longer reported as unused.
For example:
```vue
<script setup>
const vHighlight = {
mounted: (element) => {
element.style.color = "red";
},
};
</script>
<template>
<p v-highlight>Hello</p>
</template>
```
- [#​11501](https://github.com/biomejs/biome/pull/11501) [`e6acded`](https://github.com/biomejs/biome/commit/e6acdedf09c452371670aaaffd0b55206019d1e6) Thanks [@​aminya](https://github.com/aminya)! - Improved the performance of [`useArraySortCompare`](https://biomejs.dev/linter/rules/use-array-sort-compare/) by skipping type inference for calls to unrelated methods.
- [#​11467](https://github.com/biomejs/biome/pull/11467) [`66b282c`](https://github.com/biomejs/biome/commit/66b282cb70cd61b7606ce1658482b9fe082e37e6) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11464](https://github.com/biomejs/biome/issues/11464): Biome now parses parenthesized object literals returned from arrow functions when they contain a conditional expression and a nested arrow function.
- [#​11456](https://github.com/biomejs/biome/pull/11456) [`db9aa2a`](https://github.com/biomejs/biome/commit/db9aa2a3d936372cbe3ee87687ad25816c9d75ae) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10278](https://github.com/biomejs/biome/issues/10278): Marked the fix for [`noThisInStatic`](https://biomejs.dev/linter/rules/no-this-in-static/) as unsafe by default.
- [#​11502](https://github.com/biomejs/biome/pull/11502) [`652aedb`](https://github.com/biomejs/biome/commit/652aedbdcba8ccb45ccecbce506c90a337f2d64c) Thanks [@​levrik](https://github.com/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:
```vue
<script setup>
const open = defineModel();
</script>
<template>
<button @click="open = !open">Toggle</button>
</template>
```
### [`v2.5.10`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#2510)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.9...@biomejs/biome@2.5.10)
##### Patch Changes
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro rejecting JavaScript comments between attributes.
```astro
<div /* block comment */ class="something"></div>
<Component /* c */ client:load />
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11438) [`3133ffa`](https://github.com/biomejs/biome/commit/3133ffa9ae6beaa3c4464edac60de47d5c6f6426) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed [#​8294](https://github.com/biomejs/biome/issues/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.
```astro
<div>{/* a note */}</div>
<div class={/* a note */}>x</div>
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed [#​9165](https://github.com/biomejs/biome/issues/9165): an empty Astro expression such as `<div>{}</div>` no longer fails to parse. Astro renders `{}` as nothing.
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro expressions containing a comment failing to parse.
```astro
<div>{/* block comment */ x}</div>
<div>{/* only a comment */}</div>
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Added support for Astro's fragment shorthand.
```astro
<>
<p>a</p>
</>
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed an Astro frontmatter block being cut short by a closing tag inside a string or comment.
```astro
---
const a = "</script>";
// </script> in a comment
---
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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.
```astro
<!-- c -->
---
this is text, not frontmatter
---
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed an Astro frontmatter block ending early on a line that merely starts with a dash.
```astro
---
--count;
---
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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.
```astro
<article is:raw><% awesome %></article>
<script is:raw>{{ mustache }}</script>
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro rejecting attribute names that start with a colon, such as `:href`.
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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.
```astro
<pre>{value}</pre>
<textarea><div>{value}</div></textarea>
```
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Added support for template literal attribute values in Astro, such as ``<div class=`a ${b} c`>``.
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/Princesseuh)! - Fixed Astro rejecting HTML5 unquoted attribute values that contain `` ` ``, `=`, `'` or `"`, such as `<a href=a=b>` and `<a href=a'b>`.
- [#​11393](https://github.com/biomejs/biome/pull/11393) [`dec5a8f`](https://github.com/biomejs/biome/commit/dec5a8fadcb565867526e51da0895c2be27400bb) Thanks [@​1678092075](https://github.com/1678092075)! - Fixed [#​11207](https://github.com/biomejs/biome/issues/11207): [`useStrictMode`](https://biomejs.dev/linter/rules/use-strict-mode/) no longer reports Vue event handlers such as `@click="count++"`.
- [#​11431](https://github.com/biomejs/biome/pull/11431) [`c065f99`](https://github.com/biomejs/biome/commit/c065f995909d16cbf98c247a280a48c156aeb61f) Thanks [@​levrik](https://github.com/levrik)! - Fixed [#​11429](https://github.com/biomejs/biome/issues/11429): Variables and imports used by Vue same-name bindings such as `:disabled` or `v-bind:disabled` are no longer reported as unused.
- [#​11409](https://github.com/biomejs/biome/pull/11409) [`405dedb`](https://github.com/biomejs/biome/commit/405dedb0ff65dd29927faf587f6542e3c29db248) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed a memory leak in the LSP server where memory usage kept growing over long editor sessions.
- [#​11422](https://github.com/biomejs/biome/pull/11422) [`a51eff7`](https://github.com/biomejs/biome/commit/a51eff7f7ca71e8786a2f0db1d560455b83ce21c) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11416](https://github.com/biomejs/biome/issues/11416): Biome no longer crashes when parsing incomplete `{let}` or `{const}` declarations in Svelte files.
- [#​11378](https://github.com/biomejs/biome/pull/11378) [`34b715c`](https://github.com/biomejs/biome/commit/34b715cb417d482bfb4630c05a576b748c8faa8b) Thanks [@​Netail](https://github.com/Netail)! - Added extra rule sources from `@eslint/css`. `biome migrate eslint` detects rules in your eslint configurations more reliably.
- [#​11403](https://github.com/biomejs/biome/pull/11403) [`8f7786f`](https://github.com/biomejs/biome/commit/8f7786ff62aef61cf75a91e1604cd380456a60f4) Thanks [@​Princesseuh](https://github.com/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](https://github.com/biomejs/biome/pull/11443) [`8d45229`](https://github.com/biomejs/biome/commit/8d452293d04272c876b0414292540645cfea87e4) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11390](https://github.com/biomejs/biome/issues/11390): [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) no longer performs unnecessary type inference on call arguments when checking methods of non-generic class instances created with `new`.
- [#​11425](https://github.com/biomejs/biome/pull/11425) [`9c2667b`](https://github.com/biomejs/biome/commit/9c2667ba2010441054bc0b54a1a810e851207560) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​6426](https://github.com/biomejs/biome/issues/6426): GritQL plugins now match and rewrite metavariables embedded in quoted strings.
- [#​11441](https://github.com/biomejs/biome/pull/11441) [`00317c3`](https://github.com/biomejs/biome/commit/00317c353a65214d8f4cb0f2d930d7c4e1f305dd) Thanks [@​dyc3](https://github.com/dyc3)! - Improved performance of [`useNamedCaptureGroup`](https://biomejs.dev/linter/rules/use-named-capture-group/), [`noMisplacedAssertion`](https://biomejs.dev/linter/rules/no-misplaced-assertion/), [`noSkippedTests`](https://biomejs.dev/linter/rules/no-skipped-tests/), [`noExportsInTest`](https://biomejs.dev/linter/rules/no-exports-in-test/), [`noDuplicateTestHooks`](https://biomejs.dev/linter/rules/no-duplicate-test-hooks/), [`noIdenticalTestTitle`](https://biomejs.dev/linter/rules/no-identical-test-title/), [`useTestHooksInOrder`](https://biomejs.dev/linter/rules/use-test-hooks-in-order/), and [`useTestHooksOnTop`](https://biomejs.dev/linter/rules/use-test-hooks-on-top/).
### [`v2.5.9`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#259)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.8...@biomejs/biome@2.5.9)
##### Patch Changes
- [#​11321](https://github.com/biomejs/biome/pull/11321) [`41386f3`](https://github.com/biomejs/biome/commit/41386f38935b299f1a1f759a2c7fdd5c2050bded) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11315](https://github.com/biomejs/biome/issues/11315): The CSS parser now recovers at declaration boundaries after bogus declarations, allowing subsequent valid declarations to be parsed.
- [#​11248](https://github.com/biomejs/biome/pull/11248) [`57b197e`](https://github.com/biomejs/biome/commit/57b197e6d5a2432ff6904afff24125e4d8ec01cd) Thanks [@​yanthomasdev](https://github.com/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](https://github.com/biomejs/biome/pull/11377) [`a8798ea`](https://github.com/biomejs/biome/commit/a8798ea9f2569fab4524704c99a8e2fe45c2ca32) Thanks [@​Netail](https://github.com/Netail)! - Added a new nursery rule [`useNamedLayer`](https://biomejs.dev/linter/rules/use-named-layer) which disallows anonymous cascade layers.
```css
@layer {
a {
color: red;
}
}
```
- [#​11327](https://github.com/biomejs/biome/pull/11327) [`6771cf5`](https://github.com/biomejs/biome/commit/6771cf5bd2e64f256468a33b811adec8e793d933) Thanks [@​dyc3](https://github.com/dyc3)! - The HTML formatter now preserves meaningful blank lines in HTML, including spacing after elements with trailing spaces and blank lines between comment groups.
```diff
<div>
<!-- first group -->
+
<!-- second group -->
</div>
```
- [#​10312](https://github.com/biomejs/biome/pull/10312) [`ba8aa18`](https://github.com/biomejs/biome/commit/ba8aa183371b3080691ed80e5a9f6f0239cf451b) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`useTailwindShorthandClasses`](https://biomejs.dev/linter/rules/use-tailwind-shorthand-classes/), which suggests shorter Tailwind utility classes. For example, the rule suggests replacing `w-4 h-4` with `size-4`.
- [#​11333](https://github.com/biomejs/biome/pull/11333) [`715e0cd`](https://github.com/biomejs/biome/commit/715e0cd1b3e6c88d153554901a659378c728e408) Thanks [@​kkkhs](https://github.com/kkkhs)! - Fixed [#​11328](https://github.com/biomejs/biome/issues/11328): `lint/nursery/useExpect` now recognizes Vitest Browser Mode `expect.element()` calls as assertions.
- [#​11343](https://github.com/biomejs/biome/pull/11343) [`9b98211`](https://github.com/biomejs/biome/commit/9b982113dba8b37c163662d59611ecba0fc02f5a) Thanks [@​johncarmack1984](https://github.com/johncarmack1984)! - Fixed [#​11311](https://github.com/biomejs/biome/issues/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`](https://biomejs.dev/linter/rules/no-unknown-at-rules/) diagnostic.
```css
@variant @xl {
div {
background: red;
}
}
```
- [#​11220](https://github.com/biomejs/biome/pull/11220) [`3e8c488`](https://github.com/biomejs/biome/commit/3e8c4887c4ef87df45f56aafa4fbc5f497dae42f) Thanks [@​santichausis](https://github.com/santichausis)! - Fixed [#​9541](https://github.com/biomejs/biome/issues/9541): [`noUndeclaredVariables`](https://biomejs.dev/linter/rules/no-undeclared-variables/), [`noUnusedImports`](https://biomejs.dev/linter/rules/no-unused-imports/), and [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) 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:
```svelte
<script module>
export function greet() {
console.log("Hello!");
}
</script>
<script>
greet();
</script>
```
- [#​11300](https://github.com/biomejs/biome/pull/11300) [`36430eb`](https://github.com/biomejs/biome/commit/36430ebb9679d1ca5d9c231a36b709a4b5eab9b3) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed the HTML formatter's whitespace handling for `marquee`, `noscript`, `video`, `audio`, and `object` elements.
```diff
- <marquee behavior="alternate"> This text will bounce </marquee>
+ <marquee behavior="alternate">This text will bounce</marquee>
```
- [#​11299](https://github.com/biomejs/biome/pull/11299) [`6559e6c`](https://github.com/biomejs/biome/commit/6559e6ced6d4ebe8c26e818931df15d02829130d) Thanks [@​jp-knj](https://github.com/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](https://github.com/biomejs/biome/pull/11365) [`7529811`](https://github.com/biomejs/biome/commit/7529811358079edb5a2a9d4a5f67a9f639a63f3a) Thanks [@​MHJahanbakhsh](https://github.com/MHJahanbakhsh)! - Fixed [#​11229](https://github.com/biomejs/biome/issues/11229): The [`useGenericFontNames`](https://biomejs.dev/linter/rules/use-generic-font-names/) rule now treats `math` as a valid generic font family.
- [#​11346](https://github.com/biomejs/biome/pull/11346) [`674f5f4`](https://github.com/biomejs/biome/commit/674f5f4b4e6923b6c7fd12d76e366f82ffe6405b) Thanks [@​Jayllyz](https://github.com/Jayllyz)! - Fixed [#​11335](https://github.com/biomejs/biome/issues/11335): [`noComponentHookFactories`](https://biomejs.dev/linter/rules/no-component-hook-factories/) now reports a `use`-prefixed variable only when a function is assigned to it directly.
```js
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](https://github.com/biomejs/biome/pull/11334) [`c87c46a`](https://github.com/biomejs/biome/commit/c87c46ab350a40fd38b2288887e2d6e930a2d62d) Thanks [@​zkasuran](https://github.com/zkasuran)! - Fixed [#​11317](https://github.com/biomejs/biome/issues/11317): [`noSvgWithoutTitle`](https://biomejs.dev/linter/rules/no-svg-without-title/) no longer reports an `svg` that uses the boolean shorthand `aria-hidden` (equivalent to `aria-hidden={true}` in React).
- [#​11364](https://github.com/biomejs/biome/pull/11364) [`13853b1`](https://github.com/biomejs/biome/commit/13853b16685d96bfb9158de7d5f4c101d114ba32) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed a bug where [`useJsxKeyInIterable`](https://biomejs.dev/linter/rules/use-jsx-key-in-iterable/) incorrectly flagged Astro files.
- [#​11321](https://github.com/biomejs/biome/pull/11321) [`41386f3`](https://github.com/biomejs/biome/commit/41386f38935b299f1a1f759a2c7fdd5c2050bded) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11315](https://github.com/biomejs/biome/issues/11315): Invalid CSS declarations in HTML `style` attributes now produce parser diagnostics instead of causing a panic.
- [#​11325](https://github.com/biomejs/biome/pull/11325) [`67c3bf0`](https://github.com/biomejs/biome/commit/67c3bf01131c0c079fa058eef96dc28d52c2c59f) Thanks [@​dyc3](https://github.com/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.
```diff
<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](https://github.com/biomejs/biome/pull/11367) [`fe5b5d4`](https://github.com/biomejs/biome/commit/fe5b5d41c863d5e2f3b58570e15f483c189d2825) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed TypeScript `compilerOptions.paths` resolution when mapping targets omit `./`. Biome now resolves these targets relative to their configured path base.
- [#​11316](https://github.com/biomejs/biome/pull/11316) [`17e48d6`](https://github.com/biomejs/biome/commit/17e48d6088f23ec30a1e17e315b52369f1e4836c) Thanks [@​wanxiankai](https://github.com/wanxiankai)! - Fixed [#​11289](https://github.com/biomejs/biome/issues/11289): the safe fix for [`noExtraBooleanCast`](https://biomejs.dev/linter/rules/no-extra-boolean-cast/) now preserves parentheses around nested conditional expressions.
- [#​11254](https://github.com/biomejs/biome/pull/11254) [`d25d113`](https://github.com/biomejs/biome/commit/d25d113aed23ca9b5ee9b7cdedb208d8a0b2cd00) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​11242](https://github.com/biomejs/biome/issues/11242): Biome no longer crashes with an access violation when analysing files on Windows ARM64.
- [#​11221](https://github.com/biomejs/biome/pull/11221) [`85aac73`](https://github.com/biomejs/biome/commit/85aac73d32a5f4a0db7bab3ebd195e35b32d513d) Thanks [@​freeatnet](https://github.com/freeatnet)! - Added the nursery rule [`noUnsafeTypeAssertion`](https://biomejs.dev/linter/rules/no-unsafe-type-assertion/), which disallows TypeScript type assertions while allowing const assertions.
```ts
const value = input as SomeType;
```
- [#​11314](https://github.com/biomejs/biome/pull/11314) [`7ffb677`](https://github.com/biomejs/biome/commit/7ffb677159c96f072a508e58bfad26ca9c301ac4) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11310](https://github.com/biomejs/biome/issues/11310): Restored the performance of [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/) and [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) when analyzed expressions share deep imported type paths.
- [#​11356](https://github.com/biomejs/biome/pull/11356) [`6cd3263`](https://github.com/biomejs/biome/commit/6cd32636f463551b8dfe46aeefb1191d437b4694) Thanks [@​johncarmack1984](https://github.com/johncarmack1984)! - The Tailwind parser now understands modifiers on bare utilities (`@container/sidebar`, `shadow/50`).
- [#​11318](https://github.com/biomejs/biome/pull/11318) [`76059e9`](https://github.com/biomejs/biome/commit/76059e9f03f5962a18f3569714f8f207ef7c1d91) Thanks [@​johncarmack1984](https://github.com/johncarmack1984)! - The Tailwind parser now understands container-query variants (`@sm:`, `@max-lg:`, `@min-[400px]:`) and child and descendant variants (`*:`, `**:`).
- [#​11357](https://github.com/biomejs/biome/pull/11357) [`faa2074`](https://github.com/biomejs/biome/commit/faa2074472bc0f4a11f4e80cb4d2b0e55883226a) Thanks [@​johncarmack1984](https://github.com/johncarmack1984)! - The Tailwind parser now accepts the legacy leading `!` important marker (`!flex`, `hover:!p-4`).
- [#​11344](https://github.com/biomejs/biome/pull/11344) [`f34e15c`](https://github.com/biomejs/biome/commit/f34e15c946511d998c01b6c6e410f0c220c2943e) Thanks [@​johncarmack1984](https://github.com/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](https://github.com/biomejs/biome/pull/11324) [`2f5d452`](https://github.com/biomejs/biome/commit/2f5d452015ee80a02d4e50c4bb2ba930092048a2) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed HTML formatting that inserted rendered whitespace between an element and touching text when the line wrapped.
```diff
<div>
- before<meter value=".5"></meter>
- after
+ before<meter value=".5"></meter
+ >after
</div>
```
- [#​11312](https://github.com/biomejs/biome/pull/11312) [`e65f07e`](https://github.com/biomejs/biome/commit/e65f07e06342c7d8d74b98ff219d72c7e081b4f2) Thanks [@​xosnos](https://github.com/xosnos)! - Added a new nursery rule [`useControlLabel`](https://biomejs.dev/linter/rules/use-control-label/) for both HTML and JSX, which reports interactive control elements (`button`, `menuitem`) without an accessible label.
```jsx
<button />
```
- [#​11364](https://github.com/biomejs/biome/pull/11364) [`13853b1`](https://github.com/biomejs/biome/commit/13853b16685d96bfb9158de7d5f4c101d114ba32) Thanks [@​ematipico](https://github.com/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](https://github.com/biomejs/biome/pull/11301) [`610ee28`](https://github.com/biomejs/biome/commit/610ee282989ac73908e0f8bd6e49bc40cccfef77) Thanks [@​dyc3](https://github.com/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`.
```diff
- <video src="brave.webm"><track kind="subtitles" src="brave.en.vtt"></video>
+ <video src="brave.webm">
+ <track kind="subtitles" src="brave.en.vtt">
+ </video>
```
### [`v2.5.8`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#258)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.7...@biomejs/biome@2.5.8)
##### Patch Changes
- [#​10710](https://github.com/biomejs/biome/pull/10710) [`0a0fbc1`](https://github.com/biomejs/biome/commit/0a0fbc15d67c410c80dfae398903f845544fcd65) Thanks [@​dyc3](https://github.com/dyc3)! - Added a new nursery rule [`useReactCompiler`](https://biomejs.dev/linter/rules/use-react-compiler/), which reports diagnostics from React Compiler lint mode.
- [#​11251](https://github.com/biomejs/biome/pull/11251) [`ea9dd8a`](https://github.com/biomejs/biome/commit/ea9dd8a93e65f849840415e8e26cd668aa1af913) Thanks [@​dyc3](https://github.com/dyc3)! - Improved performance of [`noImportCycles`](https://biomejs.dev/linter/rules/no-import-cycles/).
- [#​11247](https://github.com/biomejs/biome/pull/11247) [`52b44d6`](https://github.com/biomejs/biome/commit/52b44d6795741d051bf703bd69c6cb447af8fd1d) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noSvelteLegacyConst`](https://biomejs.dev/linter/rules/no-svelte-legacy-const/), which disallows legacy Svelte `{@const}` tags and recommends declaration tags with `$derived()`.
Invalid:
```svelte
{#each boxes as box}
{@const area = box.width * box.height}
<p>{area}</p>
{/each}
```
Valid:
```svelte
{#each boxes as box}
{const area = $derived(box.width * box.height)}
<p>{area}</p>
{/each}
```
- [#​11252](https://github.com/biomejs/biome/pull/11252) [`d5f5704`](https://github.com/biomejs/biome/commit/d5f570414fdcdddf62372e35c05f6dababad9287) Thanks [@​Turtle-Hwan](https://github.com/Turtle-Hwan)! - Fixed [#​11250](https://github.com/biomejs/biome/issues/11250): [`useAwait`](https://biomejs.dev/linter/rules/use-await/) no longer reports async functions that contain an `await using` declaration.
- [#​11143](https://github.com/biomejs/biome/pull/11143) [`6be7be1`](https://github.com/biomejs/biome/commit/6be7be1b147d7b4352ff5625a78bd54a48958950) Thanks [@​vznh](https://github.com/vznh)! - Fixed [#​11017](https://github.com/biomejs/biome/issues/11017): [`noUselessUndefined`](https://biomejs.dev/linter/rules/no-useless-undefined/) no longer reports `return undefined` when the enclosing function has a return type annotation other than `undefined` or `void`.
- [#​11234](https://github.com/biomejs/biome/pull/11234) [`caefe39`](https://github.com/biomejs/biome/commit/caefe393c66340914c481f7ccfc82979cf76b61b) Thanks [@​subotac](https://github.com/subotac)! - Fixed [#​11228](https://github.com/biomejs/biome/issues/11228): CSS block comments between a declaration colon and value now preserve their source indentation.
```diff
:root {
--font-stack:
-/* comment */
+ /* comment */
system-ui;
}
```
- [#​11285](https://github.com/biomejs/biome/pull/11285) [`bca1f73`](https://github.com/biomejs/biome/commit/bca1f73d939423056337bfd0a42cbdcb66bb1e3f) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed [#​11280](https://github.com/biomejs/biome/issues/11280): CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.
```diff
-:/* comment */ where(div) {}
+:where(/* comment */ div) {}
```
- [#​11080](https://github.com/biomejs/biome/pull/11080) [`af16a0b`](https://github.com/biomejs/biome/commit/af16a0bf884c48bad2caab70e7930096e81e1c99) Thanks [@​dyc3](https://github.com/dyc3)! - HTML `style` attribute values are now parsed as CSS. All Biome CSS lint rules are applied to the `style` attributes.
- [#​11195](https://github.com/biomejs/biome/pull/11195) [`6a85588`](https://github.com/biomejs/biome/commit/6a85588578725195625281984a47c9a8563bf103) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed Svelte files failing to parse when an expression begins with an object literal.
Now the following snippet is correctly parsed:
```svelte
<p>{{ a: true }}</p>
<div class={{ active: isActive }}></div>
```
- [#​11173](https://github.com/biomejs/biome/pull/11173) [`481d008`](https://github.com/biomejs/biome/commit/481d008f6e8872a78749496fbbf1f9c761d9a770) Thanks [@​Austin1serb](https://github.com/Austin1serb)! - Fixed [#​10242](https://github.com/biomejs/biome/issues/10242): JavaScript GritQL patterns with multiple metavariables now match snippets consistently in WebAssembly.
- [#​11187](https://github.com/biomejs/biome/pull/11187) [`23c0369`](https://github.com/biomejs/biome/commit/23c0369c43b59284ca68c65883d6ede4228b6fb8) Thanks [@​ematipico](https://github.com/ematipico)! - Added the nursery rule [`noInvalidPropertyInitValue`](https://biomejs.dev/linter/rules/no-invalid-property-init-value/), 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>`:
```css
@property --size {
syntax: "<length>";
inherits: false;
initial-value: red;
}
```
- [#​11272](https://github.com/biomejs/biome/pull/11272) [`73896e6`](https://github.com/biomejs/biome/commit/73896e6712ba4c398ba21141829f8361ace45eb2) Thanks [@​ematipico](https://github.com/ematipico)! - Improved the diagnostic emitted by [`noRootType`](https://biomejs.dev/linter/rules/no-root-type).
- [#​11240](https://github.com/biomejs/biome/pull/11240) [`bd0b68d`](https://github.com/biomejs/biome/commit/bd0b68d418890059930074b46273aa616fbb735a) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11223](https://github.com/biomejs/biome/issues/11223): Improved the
performance of [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/)
when analyzing async class methods that call other methods through `this`.
- [#​11172](https://github.com/biomejs/biome/pull/11172) [`4a0bc5c`](https://github.com/biomejs/biome/commit/4a0bc5c46e6dbbefd17fa133ea426aa41f0a23f8) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Fixed [#​10806](https://github.com/biomejs/biome/issues/10806): [`noUselessFragments`](https://biomejs.dev/linter/rules/no-useless-fragments/) no longer causes Biome to panic when its unsafe fix removes a fragment used as a JSX attribute value.
- [#​11227](https://github.com/biomejs/biome/pull/11227) [`4d603b0`](https://github.com/biomejs/biome/commit/4d603b072fae45a69bd291ab92f3379232cd34df) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Fixed [#​11178](https://github.com/biomejs/biome/issues/11178): [`noUndeclaredVariables`](https://biomejs.dev/linter/rules/no-undeclared-variables/) 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](https://github.com/biomejs/biome/pull/11187) [`23c0369`](https://github.com/biomejs/biome/commit/23c0369c43b59284ca68c65883d6ede4228b6fb8) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed CSS parsing of registered custom properties: Biome now correctly validates the `syntax` descriptor of `@property` rules.
### [`v2.5.7`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#257)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.6...@biomejs/biome@2.5.7)
##### Patch Changes
- [#​10822](https://github.com/biomejs/biome/pull/10822) [`c171b3b`](https://github.com/biomejs/biome/commit/c171b3bd513d49c35b66cdc74a5de0ca0766b6ef) Thanks [@​pkallos](https://github.com/pkallos)! - Added the option `ignoreIfStatements` to [useNullishCoalescing](https://biomejs.dev/linter/rules/use-nullish-coalescing/). 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](https://github.com/biomejs/biome/pull/11136) [`e63354c`](https://github.com/biomejs/biome/commit/e63354cf280783fa6380951342bdacc3fd55e681) Thanks [@​AkashNaickar](https://github.com/AkashNaickar)! - Added a new nursery rule [`noExtendNative`](https://biomejs.dev/linter/rules/no-extend-native/), which reports extending the prototype of a built-in object.
- [#​10094](https://github.com/biomejs/biome/pull/10094) [`e007143`](https://github.com/biomejs/biome/commit/e00714360807115210e549caca0f235431ca9a8a) Thanks [@​THEjacob1000](https://github.com/THEjacob1000)! - Added the nursery rule [`noTailwindArbitraryValue`](https://biomejs.dev/linter/rules/no-tailwind-arbitrary-value/). Biome now reports Tailwind CSS arbitrary values such as `w-[400px]`, including in HTML/JSX class attributes, configured utility functions, and tagged templates.
- [#​11184](https://github.com/biomejs/biome/pull/11184) [`135f476`](https://github.com/biomejs/biome/commit/135f476de8b853f9dc2ff2679ea3525432e66e8e) Thanks [@​subotac](https://github.com/subotac)! - Fixed [#​11176](https://github.com/biomejs/biome/issues/11176): `noUnknownPseudoClass` now recognizes Vue's `:deep()` pseudo-class inside `.vue` style blocks.
- [#​8239](https://github.com/biomejs/biome/pull/8239) [`a519f9d`](https://github.com/biomejs/biome/commit/a519f9d19e56015ec906efac4629fbb15708f5e2) Thanks [@​cormacrelf](https://github.com/cormacrelf)! - Fixed [#​8233](https://github.com/biomejs/biome/issues/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
```
```shell
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](https://github.com/biomejs/biome/pull/11138) [`8c2c6bd`](https://github.com/biomejs/biome/commit/8c2c6bd96c035e506bc60b75448afb039e331fb1) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`noUnnecessaryConditions`](https://biomejs.dev/linter/rules/no-unnecessary-conditions/): 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`:
```ts
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`:
```ts
type Mapper<T> = () => T;
declare function map<T>(mapper: Mapper<T>): T;
map(() => 42) || flag;
```
- [#​11138](https://github.com/biomejs/biome/pull/11138) [`8c2c6bd`](https://github.com/biomejs/biome/commit/8c2c6bd96c035e506bc60b75448afb039e331fb1) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11087](https://github.com/biomejs/biome/issues/11087): [`noUnnecessaryConditions`](https://biomejs.dev/linter/rules/no-unnecessary-conditions/) 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:
```ts
declare const usage: { range: { startDate: string } } | null;
const startDate = usage?.range.startDate ?? "N/A";
```
- [#​11118](https://github.com/biomejs/biome/pull/11118) [`9c16840`](https://github.com/biomejs/biome/commit/9c16840801a806ba34fbba7da28b9724d250207a) Thanks [@​subotac](https://github.com/subotac)! - Fixed [#​11098](https://github.com/biomejs/biome/issues/11098): The HTML formatter now preserves the configured trailing newline when a file ends with a comment.
```diff
-<!-- trailing comment -->
\ No newline at end of file
+<!-- trailing comment -->
```
- [#​11201](https://github.com/biomejs/biome/pull/11201) [`0e80610`](https://github.com/biomejs/biome/commit/0e8061069915651a6f7cbba918a0bde0dbc1491f) Thanks [@​Bishwas-py](https://github.com/Bishwas-py)! - Fixed [#​11182](https://github.com/biomejs/biome/issues/11182): suppression comments for [`noPositiveTabindex`](https://biomejs.dev/linter/rules/no-positive-tabindex/) now suppress the rule in HTML files when the attributes of the element span multiple lines.
- [#​11079](https://github.com/biomejs/biome/pull/11079) [`607afd2`](https://github.com/biomejs/biome/commit/607afd2488efb30cff5ea4b27de4d98eca728e0f) Thanks [@​dyc3](https://github.com/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:
```diff
- <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](https://github.com/biomejs/biome/pull/11156) [`fed72c7`](https://github.com/biomejs/biome/commit/fed72c7e8a2bff2942d5970f182262d18e049914) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Fixed [#​11129](https://github.com/biomejs/biome/issues/11129): [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) no longer reports Vue bindings as unused when they are assigned through automatically unwrapped template refs.
- [#​11124](https://github.com/biomejs/biome/pull/11124) [`d890b39`](https://github.com/biomejs/biome/commit/d890b39c3ef21040bded453d9af91e1b301a0d67) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed CSS formatting of line comments between a declaration colon and value to preserve their source indentation.
```diff
.test {
background:
- /////// foo
- // bar
+ /////// foo
+ // bar
radial-gradient(circle, #​000, transparent);
}
```
- [#​11113](https://github.com/biomejs/biome/pull/11113) [`3d8ab73`](https://github.com/biomejs/biome/commit/3d8ab73619f1c62a2d544d41ffee16eab9307d51) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed CSS formatting of long block comments between comma-separated property values:
```diff
.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](https://github.com/biomejs/biome/pull/11127) [`da5c1a5`](https://github.com/biomejs/biome/commit/da5c1a539bac009eb1bc275b9bd8194f9ca7d5fa) Thanks [@​dyc3](https://github.com/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. `'` and `"` count as the characters they stand for, and only the character that ends up as the delimiter stays escaped:
```diff
- <div title='123 '" 456'></div>
+ <div title="123 '" 456"></div>
```
Entities that are not quotes, such as `&` or `&[#​39](https://github.com/biomejs/biome/issues/39);`, are left exactly as written.
- [#​11193](https://github.com/biomejs/biome/pull/11193) [`77035bb`](https://github.com/biomejs/biome/commit/77035bb3777805c682eefa4cbed5464c94baccba) Thanks [@​dyc3](https://github.com/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:
```diff
<div>foo</div>
-
text
```
- [#​11106](https://github.com/biomejs/biome/pull/11106) [`ad80f57`](https://github.com/biomejs/biome/commit/ad80f572c922fc2a80c90b8e26a66300dfb82d24) Thanks [@​dyc3](https://github.com/dyc3)! - The HTML formatter now writes the HTML5 doctype in lowercase, matching Prettier:
```diff
- <!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:
```html
<!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](https://github.com/biomejs/biome/pull/11188) [`60679db`](https://github.com/biomejs/biome/commit/60679dbe6d53c9b78571042b0b8c906bd345e52d) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed the HTML formatter printing a comment twice when it ended the line of the last element in a document:
```diff
- text<!-- a --><!-- a -->
+ text<!-- a -->
```
- [#​11077](https://github.com/biomejs/biome/pull/11077) [`4dcd0d9`](https://github.com/biomejs/biome/commit/4dcd0d99579650191696dc5fb8b16dd69d65c4ee) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed a bug where the HTML formatter collapsed the whitespace inside `<textarea>`, `<xmp>` and `<plaintext>`, changing what the page renders.
```diff
- <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](https://github.com/biomejs/biome/pull/11194) [`abfbb11`](https://github.com/biomejs/biome/commit/abfbb11da260852f0b4aa15f36207314ccfae436) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed the HTML formatter refusing to format a Svelte file containing an array pattern that skips a position:
```svelte
{#each animals as [, value]}
<p>{value}</p>
{/each}
```
- [#​10094](https://github.com/biomejs/biome/pull/10094) [`e007143`](https://github.com/biomejs/biome/commit/e00714360807115210e549caca0f235431ca9a8a) Thanks [@​THEjacob1000](https://github.com/THEjacob1000)! - Fixed [`useSortedClasses`](https://biomejs.dev/linter/rules/use-sorted-classes/) 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](https://github.com/biomejs/biome/pull/11078) [`10da30e`](https://github.com/biomejs/biome/commit/10da30e0013c35fe2f4d3e335b88a539ae6eb87a) Thanks [@​dyc3](https://github.com/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:
```vue
<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](https://github.com/biomejs/biome/pull/11231) [`4afd901`](https://github.com/biomejs/biome/commit/4afd901edd5d921d63a259a049c6a774269c7b82) Thanks [@​ematipico](https://github.com/ematipico)! - Improved the performance of the following lint rules:
- [`noArguments`](https://biomejs.dev/linter/rules/no-arguments/).
- [`noGlobalAssign`](https://biomejs.dev/linter/rules/no-global-assign/).
- [`noUndeclaredVariables`](https://biomejs.dev/linter/rules/no-undeclared-variables/).
- [`noRestrictedGlobals`](https://biomejs.dev/linter/rules/no-restricted-globals/).
- [`noInvalidUseBeforeDeclaration`](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration/).
- [`noShadow`](https://biomejs.dev/linter/rules/no-shadow/).
- [`noRedeclare`](https://biomejs.dev/linter/rules/no-redeclare/).
- [#​11134](https://github.com/biomejs/biome/pull/11134) [`2fa0a62`](https://github.com/biomejs/biome/commit/2fa0a62224af7a13869a28ef80aefa5ea75ebfd2) Thanks [@​yanthomasdev](https://github.com/yanthomasdev)! - Clarified the warning emitted when using the experimental `json` and `json-pretty` reporters.
- [#​11198](https://github.com/biomejs/biome/pull/11198) [`ed88b13`](https://github.com/biomejs/biome/commit/ed88b13a6d95fe564aaee6c731651e7dec8266db) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Fixed [#​11171](https://github.com/biomejs/biome/issues/11171): variables referenced only inside a Svelte attachment (`{@attach ...}`) are no longer reported as unused by [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) and [`noUnusedImports`](https://biomejs.dev/linter/rules/no-unused-imports/).
- [#​11155](https://github.com/biomejs/biome/pull/11155) [`6ee17ea`](https://github.com/biomejs/biome/commit/6ee17ea2df979a67df5d1263734f28f77c6512a6) Thanks [@​dyc3](https://github.com/dyc3)! - Improved performance when printing diagnostics to the console.
- [#​11160](https://github.com/biomejs/biome/pull/11160) [`217f8ad`](https://github.com/biomejs/biome/commit/217f8adbc2f700ab4c92cb227434c2afba9f9611) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) by skipping type inference for assignment statements, which are always considered handled.
- [#​11159](https://github.com/biomejs/biome/pull/11159) [`26c23d9`](https://github.com/biomejs/biome/commit/26c23d96ced1a6eca496955fbe93279da2747346) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Fixed [#​11144](https://github.com/biomejs/biome/issues/11144): [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) no longer reports already-awaited optional Promise values.
- [#​11138](https://github.com/biomejs/biome/pull/11138) [`8c2c6bd`](https://github.com/biomejs/biome/commit/8c2c6bd96c035e506bc60b75448afb039e331fb1) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​11121](https://github.com/biomejs/biome/issues/11121): [`noUnnecessaryConditions`](https://biomejs.dev/linter/rules/no-unnecessary-conditions/) 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`:
```ts
declare function query(options: { initial: string }): { isPending: false };
declare function query(options: { initial?: string }): { isPending: boolean };
const { isPending } = query({});
isPending || fallback;
```
- [#​11152](https://github.com/biomejs/biome/pull/11152) [`c4fc6a9`](https://github.com/biomejs/biome/commit/c4fc6a90777358905cf99b3261a479de73d7d383) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of collecting rule timings with `--profile-rules` in heavily multithreaded environments.
- [#​11128](https://github.com/biomejs/biome/pull/11128) [`4d3ff76`](https://github.com/biomejs/biome/commit/4d3ff7689c36af5149142d9a4a23165154359655) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​7635](https://github.com/biomejs/biome/issues/7635): [`noDeprecatedImports`](https://biomejs.dev/linter/rules/no-deprecated-imports/) now detects deprecated ambient declarations that are exported separately.
- [#​11117](https://github.com/biomejs/biome/pull/11117) [`01f7ef5`](https://github.com/biomejs/biome/commit/01f7ef58d4c19a587b01754c96b577a2a5a47418) Thanks [@​subotac](https://github.com/subotac)! - Fixed [#​11014](https://github.com/biomejs/biome/issues/11014): [`noDelete`](https://biomejs.dev/linter/rules/no-delete/) no longer reports `process.env["FOO"]` style property deletions.
- [#​11168](https://github.com/biomejs/biome/pull/11168) [`9847e68`](https://github.com/biomejs/biome/commit/9847e680ff8bb891a6c910e881af98a4fffa33c2) Thanks [@​saberoueslati](https://github.com/saberoueslati)! - Added the nursery rule [`noNonScalableViewport`](https://biomejs.dev/linter/rules/no-non-scalable-viewport), which reports viewport metadata that disables user scaling with `user-scalable=no`.
For example:
```html
<meta name="viewport" content="width=device-width, user-scalable=no" />
```
- [#​11154](https://github.com/biomejs/biome/pull/11154) [`a1d6b1f`](https://github.com/biomejs/biome/commit/a1d6b1fc544c6c6cc1dd38f87898d34738fbe97b) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of [`noImportCycles`](https://biomejs.dev/linter/rules/no-import-cycles/) by skipping graph traversals for imports that cannot be part of a cycle.
- [#​11175](https://github.com/biomejs/biome/pull/11175) [`d96d6dd`](https://github.com/biomejs/biome/commit/d96d6dd07702828f7e7a5ce59f8fc00006f6ee46) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed CSS parsing of registered custom properties: Biome now correctly validates the `syntax` descriptor of `@property` rules.
### [`v2.5.6`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#256)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.5...@biomejs/biome@2.5.6)
##### Patch Changes
- [#​11035](https://github.com/biomejs/biome/pull/11035) [`0e4b03b`](https://github.com/biomejs/biome/commit/0e4b03be71b32120c6326e58e046260bcf5d3231) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed a performance regression in [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/) that caused type inference to run repeatedly while linting a file.
- [#​11043](https://github.com/biomejs/biome/pull/11043) [`22ec076`](https://github.com/biomejs/biome/commit/22ec076462d4590ea7c95f7693b69040c32a38aa) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed CSS formatting for multiline function arguments preceded by comments:
```diff
.example {
value: outer(
1,
/* comment */
nested(
- first,
- second
- )
+ first,
+ second
+ )
);
}
```
- [#​11007](https://github.com/biomejs/biome/pull/11007) [`c9acb25`](https://github.com/biomejs/biome/commit/c9acb25acbce7a6f632b5041ec546a604aea4343) Thanks [@​BTF-Kabir-2020](https://github.com/BTF-Kabir-2020)! - Fixed [#​9195](https://github.com/biomejs/biome/issues/9195): [`useHookAtTopLevel`](https://biomejs.dev/linter/rules/use-hook-at-top-level/) no longer reports hooks in named `forwardRef` components that receive a `ref` parameter.
- [#​10152](https://github.com/biomejs/biome/pull/10152) [`50a9bd8`](https://github.com/biomejs/biome/commit/50a9bd84df852de305e94a91fb57f6b3b1378187) Thanks [@​Zelys-DFKH](https://github.com/Zelys-DFKH)! - Fixed [#​10131](https://github.com/biomejs/biome/issues/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`.
- [#​11105](https://github.com/biomejs/biome/pull/11105) [`8ffe2b9`](https://github.com/biomejs/biome/commit/8ffe2b9f2f9224921a0bad4a772e6611006351e5) Thanks [@​dadavidtseng](https://github.com/dadavidtseng)! - Fixed [#​11092](https://github.com/biomejs/biome/issues/11092): The [`noUselessTernary`](https://biomejs.dev/linter/rules/no-useless-ternary/) quick fix now preserves operator spacing when simplifying or inverting boolean ternary expressions.
- [#​10533](https://github.com/biomejs/biome/pull/10533) [`5809875`](https://github.com/biomejs/biome/commit/580987506a32cdde2f5498e0f712e3493ef9adde) Thanks [@​Mokto](https://github.com/Mokto)! - Fixed [#​10515](https://github.com/biomejs/biome/issues/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.
- [#​11040](https://github.com/biomejs/biome/pull/11040) [`0abb620`](https://github.com/biomejs/biome/commit/0abb620b2f0bd748bf272a2bc56aa02f5a6d0fee) Thanks [@​Mokto](https://github.com/Mokto)! - Fixed an issue where the HTML formatter would duplicate a comment placed directly before a Svelte `{@const ...}` or `{@debug ...}` block. The duplication compounded on every subsequent `--write`, causing the file to grow exponentially.
- [#​10858](https://github.com/biomejs/biome/pull/10858) [`6d18204`](https://github.com/biomejs/biome/commit/6d182043b1de76f8668464bfa5c295161135ee0e) Thanks [@​ruidosujeira](https://github.com/ruidosujeira)! - Fixed [#​10839](https://github.com/biomejs/biome/issues/10839): Svelte `{#each}` array destructuring no longer includes spaces inside square brackets, and multiline bind function expressions now indent their getter, setter, and function body correctly.
- [#​11009](https://github.com/biomejs/biome/pull/11009) [`2c36626`](https://github.com/biomejs/biome/commit/2c36626bed58e63e49ecca8a40c717adae4c91ac) Thanks [@​ematipico](https://github.com/ematipico)! - Improved the accuracy of type-aware lint rules by resolving more inferred types. For example, [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) now detects floating Promises returned by aliased callbacks and arrays of Promises created by async mapping callbacks.
The following statements are now reported:
```ts
type AsyncCallback = () => Promise<void>;
declare const callback: AsyncCallback;
callback();
[1, 2, 3].map(async (value) => value);
```
- [#​10973](https://github.com/biomejs/biome/pull/10973) [`9cb044c`](https://github.com/biomejs/biome/commit/9cb044cf2c8971642942c302a6d2b0000964c2f0) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed false positives in [`noMisleadingReturnType`](https://biomejs.dev/linter/rules/no-misleading-return-type/) when generic-constraint, normalization, substitution, or structural return-type comparison cannot complete. The rule now suppresses diagnostics rather than suggesting a return type derived from partial information. For example, this unresolved return type is no longer reported:
```ts
function unresolvedReturnType(): MissingType {
return "value" as const;
}
```
- [#​11071](https://github.com/biomejs/biome/pull/11071) [`15047a2`](https://github.com/biomejs/biome/commit/15047a2fff2aa2a5c0c096257b4718b0141431be) Thanks [@​dyc3](https://github.com/dyc3)! - The HTML parser now accepts mixed-case `doctype` declarations.
- [#​11030](https://github.com/biomejs/biome/pull/11030) [`cc90e65`](https://github.com/biomejs/biome/commit/cc90e65a6b14388a5fab1d893f782055fac34f91) Thanks [@​marschattha](https://github.com/marschattha)! - The `rdjson` reporter now populates the [severity](https://github.com/reviewdog/reviewdog/blob/master/proto/rdf/reviewdog.proto) field of each diagnostic (`ERROR`, `WARNING`, or `INFO`), so tools consuming Reviewdog Diagnostic Format output no longer need to assume a default severity.
- [#​11009](https://github.com/biomejs/biome/pull/11009) [`2c36626`](https://github.com/biomejs/biome/commit/2c36626bed58e63e49ecca8a40c717adae4c91ac) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed a performance regression in type-aware JavaScript lint rules by inferring only requested types and memoizing export resolution.
- [#​11056](https://github.com/biomejs/biome/pull/11056) [`903b177`](https://github.com/biomejs/biome/commit/903b1770528040484b5f8b71f652130a8e6bd960) Thanks [@​dyc3](https://github.com/dyc3)! - Added support for Svelte declaration tags using `let` and `const`. Biome can now parse, format, and lint bindings declared in these tags.
- [#​11045](https://github.com/biomejs/biome/pull/11045) [`89c27c6`](https://github.com/biomejs/biome/commit/89c27c62541d2b628247563441e48f2303e53f4b) Thanks [@​ematipico](https://github.com/ematipico)! - Improved the performance of Biome formatter up to \~7% across the board.
- [#​9806](https://github.com/biomejs/biome/pull/9806) [`781d68d`](https://github.com/biomejs/biome/commit/781d68dae5df6ad188efc8afb0972112654614fe) Thanks [@​dyc3](https://github.com/dyc3)! - Added the nursery rule [`noJsRestrictedProperties`](https://biomejs.dev/linter/rules/no-js-restricted-properties/), 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.
### [`v2.5.5`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#255)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.4...@biomejs/biome@2.5.5)
##### Patch Changes
- [#​10972](https://github.com/biomejs/biome/pull/10972) [`ab8c21b`](https://github.com/biomejs/biome/commit/ab8c21b35e81708276e4283a4a0ff86ea815e345) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`useExhaustiveSwitchCases`](https://biomejs.dev/linter/rules/use-exhaustive-switch-cases/) for unions of bigint literals. The rule now reports missing bigint cases and compares bigint literals by value, including binary, octal, hexadecimal, and separator-containing spellings. For example, this switch now reports the missing `2n` case:
```ts
declare const value: 1n | 2n;
switch (value) {
case 1n:
break;
}
```
- [#​10972](https://github.com/biomejs/biome/pull/10972) [`ab8c21b`](https://github.com/biomejs/biome/commit/ab8c21b35e81708276e4283a4a0ff86ea815e345) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed false positives in [`noBaseToString`](https://biomejs.dev/linter/rules/no-base-to-string/) and [`useNullishCoalescing`](https://biomejs.dev/linter/rules/use-nullish-coalescing/) when member, stringification, or nullish inference cannot complete. These rules now suppress diagnostics instead of reporting from partial type information. For example, neither expression is reported when a recursive type cannot be fully resolved:
```ts
type Recursive = Recursive;
declare const value: Recursive;
String(value);
value || "fallback";
```
- [#​10977](https://github.com/biomejs/biome/pull/10977) [`0bf7486`](https://github.com/biomejs/biome/commit/0bf748653e488d0b959d39847641438cdb28188b) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​10922](https://github.com/biomejs/biome/issues/10922): the action [`useSortedAttributes`](https://biomejs.dev/assist/actions/use-sorted-attributes/) no longer triggers for HTML instructions.
- [#​10957](https://github.com/biomejs/biome/pull/10957) [`cf263c4`](https://github.com/biomejs/biome/commit/cf263c4700e9f24115e541d1f142934a9b2d878f) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [`noThenProperty`](https://biomejs.dev/linter/rules/no-then-property/) failing to detect `Object.fromEntries`, `Object.defineProperty`, and `Reflect.defineProperty` calls with comments between their tokens.
- [#​10983](https://github.com/biomejs/biome/pull/10983) [`edc0ed7`](https://github.com/biomejs/biome/commit/edc0ed738ab8da3d512d527196684bd668090854) Thanks [@​ayaangazali](https://github.com/ayaangazali)! - Fixed [#​10980](https://github.com/biomejs/biome/issues/10980): [`useAriaPropsSupportedByRole`](https://biomejs.dev/linter/rules/use-aria-props-supported-by-role/) no longer reports false positives when the attribute that determines an element's implicit ARIA role is written as a shorthand attribute, such as `<a {href} aria-label="...">` in Astro and Svelte files.
Shorthand attributes are now taken into account when computing the implicit role, so the anchor above correctly resolves to the `link` role instead of `generic`.
- [#​10889](https://github.com/biomejs/biome/pull/10889) [`89526e3`](https://github.com/biomejs/biome/commit/89526e3858c437408ec9ff192c35a866ad991d1b) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed CSS formatter casing for syntax-owned names while preserving author-defined names, including scoped keyframes and container scroll-state queries.
```diff
- A:HOVER { COLOR: INITIAL; }
+ A:hover { color: initial; }
- @KEYFRAMES :GLOBAL KeepFrames { FROM { COLOR: RED; } }
+ @keyframes :GLOBAL KeepFrames { from { color: RED; } }
- @CONTAINER scroll-state((SCROLLED: TOP) AND (STUCK)) { A:HOVER { COLOR: RED; } }
+ @container scroll-state((SCROLLED: TOP) AND (STUCK)) { A:hover { color: RED; } }
```
- [#​10964](https://github.com/biomejs/biome/pull/10964) [`794ccd0`](https://github.com/biomejs/biome/commit/794ccd0528345c4eaa87af1d86f02475277a0a22) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed CSS formatting for comments between declaration values and `!important`.
```diff
-a { color: /* before */ /* after */ red !important; }
+a { color: /* before */ red /* after */ !important; }
```
- [#​10993](https://github.com/biomejs/biome/pull/10993) [`b7a9694`](https://github.com/biomejs/biome/commit/b7a969425d4292fc7c50440da7aea41ce5c9a9c2) Thanks [@​denbezrukov](https://github.com/denbezrukov)! - Fixed the CSS formatter to preserve comments on the correct side of selector combinators and before declaration blocks.
```diff
-.before > /* comment */ .after {}
+.before /* comment */ > .after {}
```
It now also keeps selectors with escaped newlines in attribute values inline when they fit.
```diff
-div
- span[foo="bar\
+div span[foo="bar\
value"] {}
```
- [#​10978](https://github.com/biomejs/biome/pull/10978) [`8ebafe1`](https://github.com/biomejs/biome/commit/8ebafe1c7489f1f7af379b8e52b8ad063c82d28a) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​10870](https://github.com/biomejs/biome/issues/10870): [`noUnresolvedImports`](https://biomejs.dev/linter/rules/no-unresolved-imports/) no longer reports false positives such as `import type { NextRequest } from "next/server"`.
- [#​10901](https://github.com/biomejs/biome/pull/10901) [`68c10e6`](https://github.com/biomejs/biome/commit/68c10e672fc886b31423b18be01e873d3bf77f43) Thanks [@​Socialpranker](https://github.com/Socialpranker)! - Fixed [#​10622](https://github.com/biomejs/biome/issues/10622): the HTML/Vue parser no longer panics on the argument-less `v-bind` shorthand (`:="props"`).
This syntax is valid Vue and equivalent to `v-bind="props"`, so the parser now accepts it (along with the longhand `v-bind:="props"`) instead of crashing while building a diagnostic for a missing argument.
- [#​10936](https://github.com/biomejs/biome/pull/10936) [`7df46f5`](https://github.com/biomejs/biome/commit/7df46f5be0880a02cb37453f01b83c1ba59b1e44) Thanks [@​ematipico](https://github.com/ematipico)! - Improved generic tuple inference for [`useIncludes`](https://biomejs.dev/linter/rules/use-includes/). The rule now recognizes specialised tuple element types returned through generic aliases.
- [#​10941](https://github.com/biomejs/biome/pull/10941) [`f787725`](https://github.com/biomejs/biome/commit/f7877258271e20523d5c673e62912fce1d85cd56) Thanks [@​siketyan](https://github.com/siketyan)! - Fixed [`#10855`](https://github.com/biomejs/biome/issues/10855): Biome now supports parsing and formatting CSS custom media queries declared with [`@custom-media`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@custom-media).
- [#​10969](https://github.com/biomejs/biome/pull/10969) [`72d309b`](https://github.com/biomejs/biome/commit/72d309b655cee70473e20b061f5a45112139688c) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed an issue where Biome logs became too verbose, dumping information not relevant to user's operations.
- [`e62f6b6`](https://github.com/biomejs/biome/commit/e62f6b61461227bbfd57fdf1b50b2dc8c01ea5a0) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​10963](https://github.com/biomejs/biome/issues/10963): Biome no longer panics when a type-aware rule such as [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) checks a call to a function with multiple call signatures imported from another module.
- [#​10931](https://github.com/biomejs/biome/pull/10931) [`899c60d`](https://github.com/biomejs/biome/commit/899c60d506115b3f62030236cbd0901bf294e6ab) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed `check --write` command. Now the command reports code frame of the formatted code, if the formatter is enabled.
- [#​10904](https://github.com/biomejs/biome/pull/10904) [`ceee4f4`](https://github.com/biomejs/biome/commit/ceee4f43dabf88d86b87c9a4ce6051b6738869c2) Thanks [@​qzwxsaedc](https://github.com/qzwxsaedc)! - Fixed [#​10892](https://github.com/biomejs/biome/issues/10892): [`noUnnecessaryConditions`](https://biomejs.dev/linter/rules/no-unnecessary-conditions/) no longer reports a false positive when checking a member of a discriminated union that is accessed through a default type-only namespace import. The following code is no longer flagged:
```ts
import type Types from "./types";
declare function parse(): Types.Result<string>;
const result = parse();
if (!result.success) {
}
```
- [#​10962](https://github.com/biomejs/biome/pull/10962) [`f0a67f2`](https://github.com/biomejs/biome/commit/f0a67f2e56c0785595c5cf14a93dba5bb32acf2d) Thanks [@​ematipico](https://github.com/ematipico)! - Biome no longer removes embedded styles and scripts in HTML files.
- [#​11000](https://github.com/biomejs/biome/pull/11000) [`5039a1e`](https://github.com/biomejs/biome/commit/5039a1ee35771d0193de65a6326781313ab77afb) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed a bug where closing one editor stopped a shared Biome daemon used by other editors. LSP proxy processes now exit when either the editor or daemon disconnects.
- [#​10957](https://github.com/biomejs/biome/pull/10957) [`cf263c4`](https://github.com/biomejs/biome/commit/cf263c4700e9f24115e541d1f142934a9b2d878f) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of the [`noThenProperty`](https://biomejs.dev/linter/rules/no-then-property/) lint rule by about 50%.
- [#​10992](https://github.com/biomejs/biome/pull/10992) [`4bf9b21`](https://github.com/biomejs/biome/commit/4bf9b21319df240e2c5ef2e5a9cb2e9582a0e1d1) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/): The rule now reports Promise-returning callbacks where a synchronous callback is expected when calls use tuple spreads or tuple rest parameters, including generic and deeply nested tuples, and when constructor signatures come from interface or object types. Recursive or excessively nested tuple spreads use a conservative fallback so analysis terminates.
For example, the following callback is now reported.
```ts
declare function consume(...args: [number, () => void]): void;
const prefix: [number] = [1];
consume(...prefix, async () => {});
```
- [#​10915](https://github.com/biomejs/biome/pull/10915) [`b3b12b3`](https://github.com/biomejs/biome/commit/b3b12b3fe390feabbd9ba097922d6c7e56823406) Thanks [@​Functionhx](https://github.com/Functionhx)! - Added the rule [`noNegationInEqualityCheck`](https://biomejs.dev/linter/rules/no-negation-in-equality-check/). The rule flags negated expressions on the left side of strict equality checks like `!foo === bar` — due to operator precedence this evaluates as `(!foo) === bar` which is almost always a mistake for `foo !== bar`.
The rule provides an unsafe fix that flips the operator.
```js
// Invalid
!foo === bar;
!foo !== bar;
// Valid
foo !== bar;
foo === bar;
```
- [#​10970](https://github.com/biomejs/biome/pull/10970) [`bd1038b`](https://github.com/biomejs/biome/commit/bd1038be1ad110aae60bcdbe9a154c6a2fc85c14) Thanks [@​ematipico](https://github.com/ematipico)! - Improved overload selection for [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/). Biome now handles overloaded calls, overloaded constructors, rest parameters, union arguments, and generic constraints without selecting an incompatible signature. For example, `noMisusedPromises` now reports the async callback passed to the synchronous overload:
```ts
declare function consume(kind: "async", callback: () => Promise<void>): void;
declare function consume(kind: "sync", callback: () => void): void;
consume("sync", async () => {});
```
- [#​10933](https://github.com/biomejs/biome/pull/10933) [`48a4abb`](https://github.com/biomejs/biome/commit/48a4abb99d41b1241b2a5812a12247b671d0dfed) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`useArrayFind`](https://biomejs.dev/linter/rules/use-array-find/) to recognize bigint zero indexes.
- [#​10931](https://github.com/biomejs/biome/pull/10931) [`899c60d`](https://github.com/biomejs/biome/commit/899c60d506115b3f62030236cbd0901bf294e6ab) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed an orchestration issue that could lead to deadlocks when type-aware rules are enabled.
- [#​10969](https://github.com/biomejs/biome/pull/10969) [`72d309b`](https://github.com/biomejs/biome/commit/72d309b655cee70473e20b061f5a45112139688c) Thanks [@​ematipico](https://github.com/ematipico)! - Hardened the Biome Language Server by improving its synchronisation logic.
- [#​10972](https://github.com/biomejs/biome/pull/10972) [`ab8c21b`](https://github.com/biomejs/biome/commit/ab8c21b35e81708276e4283a4a0ff86ea815e345) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed false positives in [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/) and [`useAwaitThenable`](https://biomejs.dev/linter/rules/use-await-thenable/) when Promise or thenable inference cannot complete. These rules now suppress diagnostics instead of treating incomplete type information as a definite result. For example, `useAwaitThenable` no longer reports `await value` when the value's thenability is unknown:
```ts
declare const value: unknown;
async function consume() {
await value;
}
```
### [`v2.5.4`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#254)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.3...@biomejs/biome@2.5.4)
##### Patch Changes
- [#​10665](https://github.com/biomejs/biome/pull/10665) [`55ff995`](https://github.com/biomejs/biome/commit/55ff995098148446b7e7fdfc19053902bb987122) Thanks [@​dyc3](https://github.com/dyc3)! - Improved the performance of the HTML parser slightly in our synthetic benchmarks.
- [#​10894](https://github.com/biomejs/biome/pull/10894) [`f4fb10e`](https://github.com/biomejs/biome/commit/f4fb10e176e537e8ce2cac0c3fd4c38a77f91886) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​6392](https://github.com/biomejs/biome/issues/6392): On-type formatting no longer moves comments before an `if` statement into its body.
- [#​10939](https://github.com/biomejs/biome/pull/10939) [`f2799db`](https://github.com/biomejs/biome/commit/f2799db38e3d8a644207d9b8f957abea6cb3d9fa) Thanks [@​Netail](https://github.com/Netail)! - Fixed [#​10930](https://github.com/biomejs/biome/issues/10930): [`noLabelWithoutControl`](https://biomejs.dev/linter/rules/no-label-without-control/) now correctly detects text interpolation in Astro, Svelte & Vue as valid accessible content.
- [#​10945](https://github.com/biomejs/biome/pull/10945) [`ae15d98`](https://github.com/biomejs/biome/commit/ae15d98bbf2222fbb34e3e31832cba9676a6d01c) Thanks [@​Netail](https://github.com/Netail)! - Fixed [#​10942](https://github.com/biomejs/biome/issues/10942): Svelte directives don't throw an accidental debug log anymore.
- [#​10842](https://github.com/biomejs/biome/pull/10842) [`5e1abfe`](https://github.com/biomejs/biome/commit/5e1abfee59155b5fdca8813314371ed54c06acfb) Thanks [@​JamBalaya56562](https://github.com/JamBalaya56562)! - Fixed [#​9196](https://github.com/biomejs/biome/issues/9196): `biome check --write --unsafe` no longer hangs forever when applying the [`noCommentText`](https://biomejs.dev/linter/rules/no-comment-text/) code fix.
The rule's fix now wraps the comment in a real JSX expression container (`{/* comment */}`) instead of re-inserting the braces as plain JSX text, so the fixed code is no longer reported again by the same rule.
- [#​10891](https://github.com/biomejs/biome/pull/10891) [`ecca79e`](https://github.com/biomejs/biome/commit/ecca79e8ff10f40aa676212c0db0a970c6091615) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [`#10885`](https://github.com/biomejs/biome/issues/10885): prevented a module-inference regression introduced by a housekeeping change.
- [#​10886](https://github.com/biomejs/biome/pull/10886) [`60c8043`](https://github.com/biomejs/biome/commit/60c8043527f7ccc7b505471e1042f2a4324e4d31) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10727](https://github.com/biomejs/biome/issues/10727): Biome now breaks the arguments of curried `test.each`, `it.each`, `describe.each`, and `test.for` calls when they exceed the configured line width.
```diff
- test.each([[1, 2]])("a description that is long enough to push the hugged opening line beyond the print width", (a, b) => {
- expect(a).toBe(b);
- });
+ test.each([[1, 2]])(
+ "a description that is long enough to push the hugged opening line beyond the print width",
+ (a, b) => {
+ expect(a).toBe(b);
+ },
+ );
```
- [#​10895](https://github.com/biomejs/biome/pull/10895) [`01a85f0`](https://github.com/biomejs/biome/commit/01a85f04b09f0af05b16a15c137421e312ceada5) Thanks [@​ematipico](https://github.com/ematipico)! - Biome will now remove stale Unix daemon sockets from older Biome versions when starting a newer daemon.
### [`v2.5.3`](https://github.com/biomejs/biome/blob/HEAD/packages/@biomejs/biome/CHANGELOG.md#253)
[Compare Source](https://github.com/biomejs/biome/compare/@biomejs/biome@2.5.2...@biomejs/biome@2.5.3)
##### Patch Changes
- [#​10815](https://github.com/biomejs/biome/pull/10815) [`86613d5`](https://github.com/biomejs/biome/commit/86613d5b01eb965b460ccefbf27f168d87774aaf) Thanks [@​WaterWhisperer](https://github.com/WaterWhisperer)! - Fixed a parser panic reported in [#​10708](https://github.com/biomejs/biome/issues/10708): Biome now recovers when unsupported CSS Modules `@value` rules or scoped `@keyframes` names end at EOF.
- [#​10534](https://github.com/biomejs/biome/pull/10534) [`da9b403`](https://github.com/biomejs/biome/commit/da9b403b6bbacc8d75d56e327a46f4ed0285913e) Thanks [@​Mokto](https://github.com/Mokto)! - Fixed [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) false positives in Svelte files: Svelte store subscriptions (`$store` references in templates now keep the underlying `store` binding from being flagged), and `$bindable()` props that are only written to in the script block (write-only is intentional for bindable props) are no longer reported as unused.
- [#​10827](https://github.com/biomejs/biome/pull/10827) [`098ba41`](https://github.com/biomejs/biome/commit/098ba41c99e6efaac8eb182eec258a567bb00123) Thanks [@​Aqu1bp](https://github.com/Aqu1bp)! - Fixed [#​10698](https://github.com/biomejs/biome/issues/10698): The [`noUnsafeOptionalChaining`](https://biomejs.dev/linter/rules/no-unsafe-optional-chaining/) rule now reports unsafe optional chains wrapped in TypeScript `as`, `satisfies`, type assertion, and instantiation expressions, such as `new (value?.constructor as Constructor)()`.
- [#​10773](https://github.com/biomejs/biome/pull/10773) [`3c6513d`](https://github.com/biomejs/biome/commit/3c6513d4e9a82a195785144caa9d96093c3861ff) Thanks [@​otkrickey](https://github.com/otkrickey)! - Fixed [#​10772](https://github.com/biomejs/biome/issues/10772): [`useVueValidVOn`](https://biomejs.dev/linter/rules/use-vue-valid-v-on/) no longer reports a missing handler for v-on directives using a verb modifier (`.stop` / `.prevent`) without an expression, e.g. `<div @click.stop></div>`. The rule also accepts the arg-less object syntax `<div v-on="$listeners"></div>` instead of reporting a missing event name.
- [#​10721](https://github.com/biomejs/biome/pull/10721) [`d83c66b`](https://github.com/biomejs/biome/commit/d83c66b39a820703d94100f8a6502cc6dbad26a1) Thanks [@​minseong0324](https://github.com/minseong0324)! - Improved type-aware lint rule inference for built-in globals and indexed function calls. Biome now resolves `Error(...)`, `new Error(...)`, optional `Error#stack`, and calls through indexed function values such as `handlers[0]()` more accurately.
- [#​10865](https://github.com/biomejs/biome/pull/10865) [`6450276`](https://github.com/biomejs/biome/commit/6450276764ee4794a0fcb46c139f95b68d892427) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [#​10845](https://github.com/biomejs/biome/issues/10845). Biome Language Server no longer goes in deadlock when the scanner is enabled.
- [#​10853](https://github.com/biomejs/biome/pull/10853) [`93d8e53`](https://github.com/biomejs/biome/commit/93d8e5352454bccfbd179db03b3155776599c52c) Thanks [@​Netail](https://github.com/Netail)! - Fixed [#​10840](https://github.com/biomejs/biome/issues/10840): Astro shorthand attribute syntax is now correctly being parsed from embedded nodes.
- [#​10820](https://github.com/biomejs/biome/pull/10820) [`bba3092`](https://github.com/biomejs/biome/commit/bba30920715920142e933939f6270feedca933a5) Thanks [@​JamBalaya56562](https://github.com/JamBalaya56562)! - Fixed [#​10619](https://github.com/biomejs/biome/issues/10619): [`noProcessEnv`](https://biomejs.dev/linter/rules/no-process-env/) now also reports computed (bracket) member access. Previously only dot access was checked, so `process["env"]` and `env["NODE_ENV"]` (where `env` is imported from `node:process`) were missed. Both static and computed accesses are now reported.
- [#​10835](https://github.com/biomejs/biome/pull/10835) [`3447b2f`](https://github.com/biomejs/biome/commit/3447b2f5a3c430efc8e917514260af5341c5509d) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10824](https://github.com/biomejs/biome/issues/10824): [`useDomQuerySelector`](https://biomejs.dev/linter/rules/use-dom-query-selector/) now supports an `ignore` option for receiver identifiers that should not be reported.
- [#​10875](https://github.com/biomejs/biome/pull/10875) [`b12e486`](https://github.com/biomejs/biome/commit/b12e486d0f0b80d02d2208e239190f8756d39d48) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10795](https://github.com/biomejs/biome/issues/10795): `--profile-rules` now reports timings for each plugin separately as `plugin/<pluginName>`, matching the naming used by plugin suppressions, instead of aggregating all plugins under a single `plugin/plugin` entry.
- [#​10877](https://github.com/biomejs/biome/pull/10877) [`d6bc447`](https://github.com/biomejs/biome/commit/d6bc4473a210758ee49f6cad41bc69587a7cf125) Thanks [@​ematipico](https://github.com/ematipico)! - Fixed [biome-zed#164](https://github.com/biomejs/biome-zed/issues/164): Biome no longer inserts stray whitespace when format-on-type runs after closing delimiters such as `)`, `]`, and `}`.
- [#​10867](https://github.com/biomejs/biome/pull/10867) [`a21463e`](https://github.com/biomejs/biome/commit/a21463e5f616a2db5035b470cd206ac9da4d9423) Thanks [@​dyc3](https://github.com/dyc3)! - Fixed [#​10864](https://github.com/biomejs/biome/issues/10864): Biome no longer crashes when checking or linting HTML files with unquoted attribute values such as `<textarea rows=4></textarea>`.
</details>
<details>
<summary>cznic/sqlite (modernc.org/sqlite)</summary>
### [`v1.60.1`](https://gitlab.com/cznic/sqlite/compare/v1.60.0...v1.60.1)
[Compare Source](https://gitlab.com/cznic/sqlite/compare/v1.60.0...v1.60.1)
### [`v1.60.0`](https://gitlab.com/cznic/sqlite/compare/v1.59.0...v1.60.0)
[Compare Source](https://gitlab.com/cznic/sqlite/compare/v1.59.0...v1.60.0)
</details>
<details>
<summary>haydenbleasel/ultracite (ultracite)</summary>
### [`v7.12.2`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.12.2)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.12.1...ultracite@7.12.2)
##### Patch Changes
- [`0f192a7`](https://github.com/haydenbleasel/ultracite/commit/0f192a7): `ultracite init` no longer wipes an existing Biome config it can't read. Previously a `biome.json` or `biome.jsonc` with a syntax error, or a nested monorepo config with `"extends": "//"`, was treated as empty and replaced with just the Ultracite `extends`, losing every other setting. Now:
- A config with a syntax error is left unchanged, with a warning asking you to fix it and re-run `init`.
- A nested config that extends the root config (`"extends": "//"`) is left unchanged, since the Ultracite presets belong in the root config.
- A string `extends` is turned into a list that also includes the Ultracite presets.
- Updates edit the file in place, so comments and formatting in `biome.jsonc` are preserved.
`ultracite doctor` and the `check`/`fix` resolution check now follow a nested config that extends `"//"` to the root config, instead of warning that the nested config doesn't extend `ultracite/biome/core`.
- [`c68ba48`](https://github.com/haydenbleasel/ultracite/commit/c68ba48): `ultracite check` and `ultracite fix` handle their arguments and missing tools more reliably:
- `check` now treats explicit files the way `fix` does. Oxlint only gets files it can lint, Prettier runs with `--ignore-unknown`, and oxfmt with `--no-error-on-unmatched-pattern`. Before, `ultracite check README.md` or `ultracite check Dockerfile src/index.ts` failed even though nothing was wrong.
- Linter flags that take a value keep it, even when the value looks like a file: `--tsconfig tsconfig.json`, `-c .oxlintrc.json`, `--config-path biome.json`, `--only lint/suspicious/noDebugger`, `--since origin/main` and similar. Before, the value was treated as a lint target, so `ultracite fix --tsconfig tsconfig.json` skipped Oxlint entirely and only formatted `tsconfig.json`. Ultracite's own `--claude`, `--codex`, `--hook` and `--unsafe` never take a value, so the next argument is always a target.
- `ultracite fix --unsafe` with the ESLint toolchain no longer fails with ESLint's "Invalid option '--unsafe'". ESLint has no unsafe fixes, so the flag is dropped with a warning.
- A linter that isn't installed is reported with a plain message instead of a stack trace. The other tools still run first. Stylelint is optional in the ESLint toolchain, as `ultracite doctor` already said, so a project without it now skips CSS linting with a warning instead of failing. "No linter configuration found" is also printed without a stack trace.
- Linters installed in the project's `node_modules/.bin` are found even when Ultracite isn't run through a package manager script, `npx` or `bunx`, for example `./node_modules/.bin/ultracite check`.
- [`f61f393`](https://github.com/haydenbleasel/ultracite/commit/f61f393): `ultracite init` now looks for existing ESLint, Prettier and Stylelint configs in the same order the tools do, so when a project has more than one, init updates the one the tool actually loads. For example, Prettier reads `.prettierrc.json` before `prettier.config.mjs`, and ESLint reads `eslint.config.js` before `eslint.config.mjs`. Before, init could update a config the tool ignored and leave the active one in place. Stylelint's `.stylelintrc.ts` and `stylelint.config.ts` are now recognised too.
- [`242cd2a`](https://github.com/haydenbleasel/ultracite/commit/242cd2a): `ultracite init` now updates an existing `.vscode/settings.json` or `.zed/settings.json` in place, so your comments and formatting are kept. Before, the file was re-serialised as plain JSON, which stripped every comment. A settings file with a syntax error is now left unchanged with a warning. Before, it was rewritten with whatever part the parser could recover, which dropped the rest.
For the ESLint toolchain, init now also installs the Prettier VS Code extension (`esbenp.prettier-vscode`), since the settings it writes make Prettier the default formatter. Before, only the ESLint extension was installed, so format-on-save did nothing until you added Prettier yourself.
- [`c273393`](https://github.com/haydenbleasel/ultracite/commit/c273393): `ultracite init` now checks every flag value before it changes anything in the project. An unknown value for `--linter`, `--pm`, `--frameworks`, `--editors`, `--agents`, `--hooks`, `--integrations` or `--js-plugins` stops init with a message listing the valid values. Previously a misspelled `--linter` (for example `--linter Biome`) deleted every existing Biome, ESLint, Prettier, Stylelint, Oxlint and oxfmt config file and then crashed.
When `--linter` is not passed and init runs without prompts (because of `--quiet`, `CI`, or flags such as `--agents` or `--pm`), it now keeps the linter the project is already set up with and only falls back to Oxlint when there is none. Running `ultracite init --agents universal` on a Biome project no longer migrates it to Oxlint. The interactive linter prompt also preselects the detected linter.
- [`3cb2e71`](https://github.com/haydenbleasel/ultracite/commit/3cb2e71): Clearer wording in the CLI:
- `ultracite init --help` now lists the valid values for `--pm`, `--linter`, `--frameworks`, `--hooks` and `--integrations`, and explains what `--type-aware` does for Biome and for Oxlint.
- The agent rules file says "Oxlint + Oxfmt will catch most mechanical issues automatically" instead of "Oxlint + Oxfmt's linter will catch…".
- `init` and `upgrade` say "Using pnpm (detected from the project)" instead of "Detected lockfile", since the package manager can also come from `packageManager`.
- `ultracite doctor` spells "unrecognized" consistently, formats commands as code, and describes warnings as "Some checks have warnings" instead of "optional improvements".
- [`6ae8be8`](https://github.com/haydenbleasel/ultracite/commit/6ae8be8): The ESLint `nestjs` preset imports `@darraghor/eslint-plugin-nestjs-typed`, but `ultracite init --linter eslint --frameworks nestjs` never installed it, so ESLint failed to load the config with "Cannot find package". `init` now installs the plugin with the preset, and `ultracite upgrade` installs the plugins of every framework preset your `eslint.config.*` imports, so existing NestJS projects pick it up on their next upgrade.
- [`a8e83bb`](https://github.com/haydenbleasel/ultracite/commit/a8e83bb): `ultracite init` now migrates an existing `.oxlintrc.json`, `.oxfmtrc.json` or `.oxfmtrc.jsonc` when it sets up Oxlint. Oxlint and oxfmt refuse to load any config when a JSON config sits next to `oxlint.config.ts` or `oxfmt.config.ts`, and init used to write the TS configs beside the JSON ones, so `ultracite check` and `ultracite fix` stopped working. Init now moves the JSON config's settings into the TS config and deletes the JSON file:
- `rules`, `overrides`, `env`, `plugins` and other options become properties of the generated config.
- `ignorePatterns`, `settings` and `jsPlugins` are added to the ones Ultracite generates instead of replacing them.
- Ultracite `extends` entries become presets. Other `extends` paths can't be referenced from a TS config, so init names them in a warning.
A JSON config that doesn't parse is left in place, and neither file is written.
Re-running `ultracite init` also keeps what you added to `oxlint.config.ts` and `oxfmt.config.ts`: custom `rules`, `overrides`, `ignorePatterns`, `settings` and other properties, extra `extends` entries, your own imports and statements, and comments are carried over while the Ultracite parts are regenerated. Before, both files were regenerated from scratch. A config that doesn't parse is now left unchanged with a warning instead of being overwritten.
`ultracite doctor` now fails when a JSON config and a TS config for Oxlint or oxfmt sit side by side, and suggests running `init` to migrate a lone `.oxfmtrc.json`.
- [`981ef2f`](https://github.com/haydenbleasel/ultracite/commit/981ef2f): `ultracite init --linter oxlint` no longer adds `"type": "module"` to `package.json`. That field changes how Node loads every `.js` file in the package, so CommonJS files such as a `next.config.js`, `postcss.config.js` or `jest.config.js` using `module.exports` stopped working after init.
Instead, init writes the Oxlint and oxfmt configs as `oxlint.config.mts` and `oxfmt.config.mts` when the package isn't an ES module package (no `"type"` or `"type": "commonjs"`). A `.mts` file always loads as an ES module, with no `MODULE_TYPELESS_PACKAGE_JSON` warning on every run, and it works under `"type": "commonjs"`. ES module packages (`"type": "module"`) still get `oxlint.config.ts` and `oxfmt.config.ts`.
Re-running init updates an existing config under the name it already has. The one exception is a `.ts` config in a `"type": "commonjs"` package, which Node can't load: init renames it to `.mts` and says so. `ultracite doctor`, linter detection and the stale-config cleanup all recognise the `.mts` names. `doctor` fails a `.ts` config in a CommonJS package, and fails when a `.ts` and an `.mts` config sit side by side.
Requires oxfmt >= 0.59.0, the first release that finds `oxfmt.config.mts` on its own.
- [`9a9beb0`](https://github.com/haydenbleasel/ultracite/commit/9a9beb0): `ultracite init` is gentler with `package.json`:
- It keeps the file's key order, indentation and line endings. Before, every write moved `devDependencies` to the top and pushed keys like `private`, `main` and `exports` to the end.
- It no longer overwrites a `check` or `fix` script the project already has (for example `"check": "tsc --noEmit"`). It prints a warning instead, and it only adds the scripts that are missing. Existing scripts that already run Ultracite with extra flags are left as they are.
- When switching linters, it only removes the previous linter's packages from `devDependencies`. Packages in `dependencies` and `peerDependencies` stay, such as `prettier` used at runtime or `eslint` as the peer of a published plugin.
- With `--skip-install`, the final message now says the dependencies were added to `package.json` instead of claiming they were installed.
- [`c3027a5`](https://github.com/haydenbleasel/ultracite/commit/c3027a5): Re-running `ultracite init` no longer silently discards changes to an ESLint, Prettier or Stylelint config that builds on Ultracite's:
- `eslint.config.*`: the framework presets it already spreads are kept alongside any newly selected ones, together with your own config objects, imports and a `defineConfig(...)` wrapper. Before, the file was regenerated from the selected frameworks only, dropping earlier presets and every custom rule.
- `prettier.config.*`: your options, imports and extra plugins are kept. Framework plugins are added, and `prettier-plugin-tailwindcss` stays last.
- Stylelint: a config that already uses `ultracite/stylelint`, including one in `package.json`, is left as it is.
A config that doesn't use Ultracite's presets is still replaced, but init now prints a warning naming the file (or the `package.json` key) it replaced. A config that doesn't parse is left unchanged with a warning. With the ESLint toolchain, a `"prettier"` or `"stylelint"` key in `package.json` is now handled by these updates (and reported when replaced) instead of being deleted silently during migration.
- [`44d6997`](https://github.com/haydenbleasel/ultracite/commit/44d6997): `ultracite init` no longer creates directories for agent rule files or agent hook configs (such as `.claude/` or `.cursor/`) before checking that the path stays inside the project. When one of those directories was a symlink to somewhere outside the project, init created the missing directories there before it refused to write the file.
- [`33a1ccd`](https://github.com/haydenbleasel/ultracite/commit/33a1ccd): `ultracite init --integrations husky --skip-install` no longer replaces an existing `prepare` script in `package.json`. It adds `husky` to the script, as the installing path already did (for example `"prepare": "svelte-kit sync && husky"`). The Lefthook setup messages now name the config file init actually updates or creates, such as `.lefthook.yaml`, instead of always saying `lefthook.yml`.
- [`923667b`](https://github.com/haydenbleasel/ultracite/commit/923667b): `ultracite init` no longer overrides a tsconfig that turns `strictNullChecks` off. It leaves an explicit `"strictNullChecks": false`, whether set in the file or in a config it extends, as it is and prints a warning. It also follows `extends`, whether a relative path or a package such as `@tsconfig/strictest`, so a tsconfig that already inherits `strict` or `strictNullChecks` is no longer given a redundant `"strictNullChecks": true`.
- [`199974c`](https://github.com/haydenbleasel/ultracite/commit/199974c): `ultracite init` and `ultracite upgrade` now work in Yarn 2+ monorepos. The root install used to pass Yarn 1's `-W` flag, which Yarn 2+ rejects with "Unsupported option name", whenever the Yarn version wasn't named in `package.json`'s `packageManager` field. That happened with `--pm yarn`, with lockfile-only detection, and always in the second half of `upgrade`, which re-ran the new CLI with a bare `--pm yarn`. Ultracite now tells Yarn 2+ apart from Yarn 1 by `.yarnrc.yml` or the lockfile format. `upgrade` only forwards `--pm` to the newly installed CLI when you passed it.
`ultracite doctor` (and the check at the end of `upgrade`) no longer fails in Yarn Plug'n'Play projects just because it can't find Ultracite or the tools in `node_modules`. It warns that it can't verify them there instead.
- [`c33158d`](https://github.com/haydenbleasel/ultracite/commit/c33158d): Agent hooks set up by `ultracite init --hooks` work in more places and no longer double up.
- **GitHub Copilot:** `.github/hooks/ultracite.json` is now written in the format the Copilot CLI and Copilot cloud agent read: `"version": 1` and a camelCase `postToolUse` event. VS Code reads this format too. The previous file, with a PascalCase `PostToolUse` and no `version`, was only picked up by VS Code. Re-running init moves an existing Ultracite hook to the new format instead of keeping both.
- **`fix --hook` with Copilot:** the file path is now read from Copilot payloads: `toolArgs.path` from the Copilot CLI (including when `toolArgs` is a JSON string), `tool_input.filePath` from VS Code's edit tools, every file of a `multi_replace_string_in_file` edit, and the file headers of an `apply_patch`. So only the edited files are fixed, not the whole project. These hosts run the hook after every tool, so tools that edit nothing (such as `read_file`, `view` or `bash`) now skip the fix instead of fixing the whole project.
- **Re-running init no longer adds a second hook** next to one generated by Ultracite 7.8.2 or earlier: `pnpm fix …`, `bun fix …`, `yarn fix …`, or `npm run fix --skip=…`. That older hook is updated in place, so it stops running a whole-project fix after every edit.
- **A hook is now added** even when the settings file mentions `ultracite` somewhere else, such as a Claude Code permission rule like `Bash(npx ultracite check)`. Only existing hook commands are checked.
- `fix --hook` now fixes a file at the project root whose name starts with `..`, instead of treating it as outside the project.
- **Zed:** the `includePackageJsonAutoImports` preference is now set for vtsls, Zed's default TypeScript and JavaScript language server (`lsp.vtsls.settings`), for both TypeScript and JavaScript. It was previously set under `typescript-language-server`, which Zed doesn't use by default.
- [`dc5e18e`](https://github.com/haydenbleasel/ultracite/commit/dc5e18e): Re-running `ultracite init` now updates the Ultracite rules in `AGENTS.md`, `.claude/CLAUDE.md`, `GEMINI.md` and the other shared rule files in place, instead of appending another full copy.
- Previously a copy was appended whenever the linter, the package manager or the rules text changed. For example, switching from Biome to Oxlint left instructions for both engines side by side. Duplicate copies written by earlier versions are now removed, and your own content before and after the block is kept.
- Files with CRLF line endings keep them, and are no longer duplicated on every run.
- The commands in the rules now run the project's installed CLI (`npx ultracite fix`, `yarn ultracite fix`, `pnpm exec ultracite fix`, `bunx ultracite fix`) instead of a dlx runner. For Deno they read `deno run -A npm:ultracite fix`; previously the broken `deno run -A npm: ultracite fix` was written.
- **Firebender:** rules are now written to `.firebender/rules/ultracite.mdc` with `alwaysApply: true`, where Firebender reads project rules. They were previously written as Markdown into `firebender.json`, which made that file invalid JSON and overwrote an existing Firebender config. A `firebender.json` that still holds only those Markdown rules is reset to `{}`.
- [`2ad7704`](https://github.com/haydenbleasel/ultracite/commit/2ad7704): Fixes for `ultracite fix --claude` and `ultracite fix --codex`:
- On a terminal, the ✓/✗ result for every issue is now kept once a file is done. Previously a file with more issues than the terminal had rows kept only the last screenful of results.
- In CI logs and other piped output, lint messages are no longer cut off at 80 columns.
- The prompt is now sent to the agent CLI on stdin (`claude -p`, `codex exec -`) instead of as a command-line argument. On Windows, an npm-installed CLI runs through a `.cmd` shim, which can cut an argument off at its first newline, so the agent received only the prompt's first line and never saw the file or its issues.
- `--codex` now passes `--skip-git-repo-check`, so it also works in a project that isn't a git repository. Previously `codex exec` refused to run there and every file failed.
- [`0b0775e`](https://github.com/haydenbleasel/ultracite/commit/0b0775e): The Husky pre-commit hook written by `ultracite init --integrations husky` is more robust.
- The hook now runs the project's installed tools (`npx`, `yarn`, `pnpm exec`, `bunx`) instead of `yarn dlx` / `pnpm dlx`. `yarn dlx` does not exist in Yarn 1, so every commit failed with "Ultracite found issues that could not be auto-fixed", and `pnpm dlx` downloaded the latest release instead of the version the project pins. Husky itself is also initialised with the installed binary.
- Installing Husky now chains `husky` onto an existing `prepare` script (for example SvelteKit's `svelte-kit sync || echo ''`) instead of replacing it. Choosing Husky and lefthook together no longer leaves only one of them in `prepare`.
- When nothing is staged, the hook no longer exits early, so commands you keep after the Ultracite section still run.
- Re-running init replaces the Ultracite section of a hook saved with CRLF line endings instead of appending a second one. It also no longer drops the last line of a hook that only mentions "# ultracite" in a comment. Commands after a lint-staged section written by an older version are kept.
- [`3862221`](https://github.com/haydenbleasel/ultracite/commit/3862221): `ultracite init --integrations lint-staged` no longer damages or shadows an existing lint-staged config.
- An extension-less `.lintstagedrc` written in YAML (which is how lint-staged reads it) is now edited as YAML. Previously it was overwritten with a JSON file holding only the Ultracite task, deleting the user's own tasks, and single-quoted YAML was silently skipped.
- YAML configs are edited through the `yaml` Document API, so comments and the rest of the file are kept.
- TypeScript configs (`.lintstagedrc.ts`, `.mts`, `.cts` and `lint-staged.config.ts`, `.mts`, `.cts`) and the `lint-staged` key of `package.yaml` are now recognised. Previously init created a `.lintstagedrc.json` next to them, and because lint-staged uses only the first config in a directory, the user's config silently stopped running.
- When a config can't be edited automatically (a function-based entry, a CommonJS module that can't be loaded, a syntax error), init now leaves it untouched and prints the command to add, instead of writing a second config file that would replace it.
- When both exist, the dedicated config file is updated rather than the `package.json` key, matching the file lint-staged actually uses.
- A single command already mapped to the same glob is kept alongside `ultracite fix` instead of being replaced.
- The task now runs the project's installed Ultracite (`npx`, `yarn`, `pnpm exec`, `bunx`) instead of `yarn dlx` / `pnpm dlx`, which don't exist in Yarn 1 or download the latest release instead of the pinned one. Re-running init upgrades a `dlx` command written by an earlier version.
- [`bffb8dd`](https://github.com/haydenbleasel/ultracite/commit/bffb8dd): Corrected the bundled Ultracite agent skill (`skills/ultracite/SKILL.md`):
- It said Oxlint has `github` and `sonarjs` presets that init includes by default. Neither exists. The GitHub, SonarJS and React Doctor rules live in the `ultracite/oxlint/js-plugins` preset (plus `next/js-plugins` and `tanstack/js-plugins`), next to the `anti-slop` and `shadcn` presets, and all of them are opt-in.
- The `--js-plugins` init flag is now documented.
- The skill now tells agents to run the project's installed CLI (`npx`, `pnpm exec`, `yarn`, `bunx`) rather than `pnpx` or `yarn dlx`.
- [`da2934a`](https://github.com/haydenbleasel/ultracite/commit/da2934a): Installing the Ultracite skill from `ultracite init` now works when init runs in a normal terminal.
Init runs `skills add haydenbleasel/ultracite` without a terminal attached. When the `skills` CLI needed to ask which agents to install to, or whether to install into the project or globally, it could not show the prompt:
- In some cases it cancelled the prompt and exited successfully with nothing installed, so init said "Ultracite skill installed." when the skill wasn't there.
- In others it failed, so the install never happened.
Init now passes `--yes` and names the agents to install for, so `skills` installs without asking. The skill always goes to the shared `.agents/skills` directory, which Codex, Cursor, GitHub Copilot, Gemini CLI, Amp, Cline, OpenCode and other agents read. It also goes to the skills directory of any agent whose project folder exists (for example `.claude`, `.windsurf`, `.codebuddy` or `.roo`, which init creates when you choose those agents).
Init names the agents because otherwise, in a project where `skills` detects no agent, `--yes` would install for every agent it knows. That also created a `.claude/skills` link and a second copy in a top-level `agent/` directory (for the Eve framework). The "install it later" hint still shows the interactive `skills add haydenbleasel/ultracite` command.
- [`4941883`](https://github.com/haydenbleasel/ultracite/commit/4941883): `ultracite init` now edits `.pre-commit-config.yaml` and lefthook configs through the `yaml` Document API instead of splicing text, so the result is always valid YAML and comments are kept.
- **pre-commit:** a `repos:` list written flush with its key (`- repo:`, the style `pre-commit sample-config` generates) or with four-space indentation no longer turns into invalid YAML that breaks every commit. The file's sequence style is kept.
- **lefthook:** a four-space indented `pre-commit:` block, a compact `jobs:` list, or a column-0 comment inside the block no longer produce invalid YAML or a duplicate `jobs:` key. A hook that uses `commands:` gets a `jobs:` list next to it.
- **lefthook:** init now finds an existing `.lefthook.yml`, `lefthook.yaml`, `.config/lefthook.yml` (and the other names lefthook reads) and edits it. Previously it created `lefthook.yml`, which lefthook picks first, so the user's own config silently stopped running. A JSON or TOML config is left alone with instructions.
- **lefthook:** the job's globs are now `*.js`, `*.ts`, and so on. With lefthook's default matcher, the previous `**/*.js` form skipped root-level files such as `package.json` or `index.ts`. Projects that set `glob_matcher: doublestar` get `**/*.js`.
- **lefthook:** re-running init with a different package manager updates the existing job instead of adding a second one.
- Both now run the project's installed Ultracite (`npx`, `yarn`, `pnpm exec`, `bunx`) instead of `yarn dlx` / `pnpm dlx`. Re-running init upgrades a hook or job written by an earlier version.
- `lefthook install` runs the project's installed lefthook, and the `prepare` script is chained onto an existing one (e.g. `svelte-kit sync || echo '' && lefthook install`) instead of replacing it.
- [`9c30c15`](https://github.com/haydenbleasel/ultracite/commit/9c30c15): Raise the `oxlint` peer dependency to `^1.82.0`. The core Oxlint preset configures `no-unmodified-loop-condition` with `checkConditionalExpressions`, an option Oxlint 1.79 to 1.81 reject, so those releases failed with "Failed to parse oxlint configuration file" even though the old `^1.79.0` range allowed them. `ultracite doctor` and your package manager now flag the too-old version instead.
Requires oxlint >= 1.82.0
- [`47303f4`](https://github.com/haydenbleasel/ultracite/commit/47303f4): The Biome `jest` preset now declares Jest's globals (`describe`, `it`, `test`, `expect`, `jest`, `beforeEach` and the rest of `globals.jest`) for test files. Jest injects them without imports, so `noUndeclaredVariables` from the core preset reported every one of them in idiomatic Jest tests. The ESLint `jest` preset already declared them.
The route-file exemptions in the Biome `remix` and `tanstack` presets (`useFilenamingConvention` off in `routes/`) now cover `.js` and `.jsx` route files as well as `.ts` and `.tsx`.
- [`8f236ee`](https://github.com/haydenbleasel/ultracite/commit/8f236ee): The ESLint core preset now lints `.jsx`, `.tsx`, `.mts` and `.cts` files. Its main block previously matched only `.js`, `.mjs`, `.cjs`, `.ts`, `.json` and `.html`, so ESLint skipped `.mts` and `.cts` files entirely ("File ignored because no matching configuration was supplied"), and `.jsx` and `.tsx` files received only the framework preset's rules. `.tsx` files were also parsed by espree, so any TypeScript syntax in them was a parse error. Every TypeScript extension now uses `@typescript-eslint/parser` with type information, and the core rules apply to all of them.
Type information now comes from the TypeScript project service (`parserOptions.projectService: true`) instead of a fixed `project: "./tsconfig.json"`. Solution-style tsconfigs (`"files": []` plus `references`, as in the Vite templates) and monorepo packages now resolve their own tsconfig, where previously every file failed to parse. A file that no tsconfig includes still has to be added to one; typescript-eslint reports it as "not found by the project service" (see <https://typescript-eslint.io/troubleshooting/typed-linting/#i-get-errors-telling-me-was-not-found-by-the-project-service-consider-either-including-it-in-the-tsconfigjson-or-including-it-in-allowdefaultproject>).
The JavaScript block no longer matches `**/*.json`. espree cannot parse JSON, so every `package.json` and `tsconfig.json` failed with `Parsing error: Unexpected token :`. The ESLint preset does not lint JSON, matching the Oxlint preset.
- [`94722fa`](https://github.com/haydenbleasel/ultracite/commit/94722fa): The `eslint.config.mjs` that `ultracite init` writes no longer fails its own lint. Every ESLint preset exported a default named `config`, so `import core from "ultracite/eslint/core"` (and each framework import) tripped `import-x/no-rename-default` twice per preset. Each preset's default export is now named after the identifier the generated config imports it as (`core`, `react`, `tanstack`, …).
- [`d9803a6`](https://github.com/haydenbleasel/ultracite/commit/d9803a6): Fix ESLint presets that crashed the whole run or never reached the files they target.
- **TanStack:** `@tanstack/start/no-async-client-component` and `@tanstack/start/no-client-code-in-server-component` need type information and aborted ESLint ("You have used a rule which requires type information") on the first `.jsx` or `.tsx` file. They now run only on TypeScript files. The Query, Router and Start rules also apply to plain `.ts`/`.js` modules, where query and mutation options are usually declared, instead of only to `.jsx` and `.tsx`. `sort-keys` is off, and `no-use-before-define` and `unicorn/filename-case` are off in `routes/` directories, matching the Oxlint and Biome TanStack presets (alphabetical option keys break TanStack's type inference, and file routes are mutually recursive and encode the URL in the filename).
- **Qwik:** `qwik/valid-lexical-scope` needs type information and crashed ESLint on `.jsx` files. It now runs only on `.tsx` files.
- **Jest:** `jest/no-deprecated-functions` threw "Unable to detect Jest version" and crashed ESLint when the `jest` package was not resolvable from the plugin, for example in bun:test projects. The preset now reads the Jest version from the project and falls back to the latest major.
- **Cypress:** the block used `globals.cypress`, which does not exist in the `globals` package, so `cy`, `Cypress`, `describe` and `it` were reported by `no-undef`. It now uses `eslint-plugin-cypress`'s own globals config. It also matched only `*.cy.js`, and now covers `*.cy.*` for every JS and TS extension plus files under `cypress/`.
- **Storybook:** the block matched only `*.stories.js` and `*.stories.ts`. It now covers `*.stories.*` and `*.story.*` for every JS and TS extension, including `.tsx` and `.jsx`.
- [`d6ee272`](https://github.com/haydenbleasel/ultracite/commit/d6ee272): The ESLint `vue`, `svelte` and `astro` presets now parse their files. They registered only the plugin and its rules, never a parser, so ESLint read every `.vue`, `.svelte` and `.astro` file with espree and reported a parse error (`Unexpected token <`) instead of linting it.
Each preset now starts from its plugin's own base config (`flat/base` in `eslint-plugin-vue`, `eslint-plugin-svelte` and `eslint-plugin-astro`), which registers `vue-eslint-parser`, `svelte-eslint-parser` or `astro-eslint-parser` from the plugin's dependencies, and parses `<script lang="ts">` blocks with `@typescript-eslint/parser`. The Svelte preset also covers `.svelte.js` and `.svelte.ts` rune modules. In Astro files, client-side `<script>` tags are linted without type information, because those in-memory virtual files cannot be type-checked.
- [`f7599ef`](https://github.com/haydenbleasel/ultracite/commit/f7599ef): The ESLint core preset no longer re-enables formatting rules that `eslint-config-prettier` turns off. The unicorn rule set was spread after `eslint-config-prettier`, which switched `unicorn/number-literal-case`, `unicorn/template-indent` and `unicorn/empty-brace-spaces` back on. `unicorn/number-literal-case` demands `0xABCD` while Prettier prints `0xabcd`, so `prettier/prettier` and the unicorn rule reported every hex literal and `eslint --fix` could not satisfy both. `eslint-config-prettier` now applies after every plugin, so `unicorn/number-literal-case` and `unicorn/template-indent` are off. `unicorn/empty-brace-spaces` stays on (Prettier prints empty braces as `{}` too), matching the Oxlint preset.
- [`efa80e8`](https://github.com/haydenbleasel/ultracite/commit/efa80e8): The ESLint core preset no longer reports the same problem two or three times on TypeScript files, and no longer flags ambient type-only globals.
- `no-undef` and `sonarjs/no-reference-error` are off for `.ts`, `.tsx`, `.mts` and `.cts` files. TypeScript already reports unresolved identifiers, and both rules flagged valid ambient types such as `NodeJS.Timeout` (see the [typescript-eslint FAQ](https://typescript-eslint.io/troubleshooting/faqs/eslint#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors)).
- The base `class-methods-use-this`, `consistent-return`, `no-unused-private-class-members`, `prefer-destructuring` and `prefer-promise-reject-errors` rules are off for TypeScript files, because their `@typescript-eslint` extension rules are enabled there. The base rules duplicated every report and ignored the extensions' TypeScript-aware options.
- `unused-imports/no-unused-vars` is off. It is a copy of `no-unused-vars` / `@typescript-eslint/no-unused-vars`, which stay on as in the Oxlint preset, so every unused variable was reported twice. `unused-imports/no-unused-imports` stays on for its autofix.
- `@typescript-eslint/max-params` is off, matching `max-params` being off in the core presets. It was on with its default limit of 3 parameters for TypeScript files only.
- [`9ae95d1`](https://github.com/haydenbleasel/ultracite/commit/9ae95d1): Fix false positives in the ESLint `vue`, `svelte` and `astro` presets that appeared once their files were actually parsed.
- `vue/block-lang` and `svelte/block-lang` rejected every `<script lang="ts">`, because with no options they only allow blocks without a `lang` attribute. They now allow TypeScript scripts, and `less` and `scss` styles, as well as blocks without `lang`.
- Astro's client-side `<script>` tags are linted as virtual files named `1_1.ts`, `2_1.js` and so on, which `github/filenames-match-regex` reported on every page with a script tag. Filename rules are now off for those virtual files.
- The Astro preset passes `@typescript-eslint/parser` for the frontmatter explicitly. ESLint's config merge dropped the copy in `eslint-plugin-astro`'s base config, so frontmatter TypeScript parsed only when `@typescript-eslint/parser` happened to resolve from the working directory, and failed with `The keyword 'interface' is reserved` otherwise.
- `ultracite init` now installs `eslint-plugin-jsx-a11y` for Astro projects using ESLint. The preset enables `eslint-plugin-astro`'s `astro/jsx-a11y/*` rules, which load that package and reported "you need to install eslint-plugin-jsx-a11y" on every `.astro` file when it was missing.
- [`e88e76d`](https://github.com/haydenbleasel/ultracite/commit/e88e76d): The Prettier preset now sets `tailwindFunctions` to `clsx`, `cva`, `tw`, `twMerge`, `cn`, `twJoin` and `tv`, so `prettier-plugin-tailwindcss` sorts Tailwind classes passed to those helpers anywhere in a file, for example `const button = cva("p-4 flex")`. Previously only class strings inside `class`/`className` attributes were sorted with Prettier, while the Oxfmt (`sortTailwindcss.functions`) and Biome (`useSortedClasses`) presets already sorted these helpers. See <https://github.com/tailwindlabs/prettier-plugin-tailwindcss#sorting-classes-in-function-calls>.
- [`3a5885e`](https://github.com/haydenbleasel/ultracite/commit/3a5885e): `github/filenames-match-regex` now accepts file-based routing filenames in the ESLint preset too, and Next.js's special pages in both presets.
- The page-route regex (Oxlint `js-plugins` and now ESLint `core`) allows a leading underscore, so the Next.js pages-router files `_app`, `_document` and `_error`, which cannot be renamed, are no longer reported. Other names in `pages/` (`BadPage.ts`) and camelCase route params (`[postId]`, which `unicorn/filename-case` rejects too) are still reported.
- The ESLint `core` preset now carries the same route exemptions as the Oxlint `js-plugins` preset ([#​799](https://github.com/haydenbleasel/ultracite/issues/799), [#​804](https://github.com/haydenbleasel/ultracite/issues/804)). Previously it applied the plain kebab-case regex everywhere and reported SvelteKit's `+page.ts` and `+server.ts`, TanStack Router and React Router files such as `posts.$postId.ts`, and page routes such as `[slug].json.ts` and `[...auth].ts`.
- The route-directory exemptions (`github/filenames-match-regex` in Oxlint `js-plugins` and ESLint `core`, plus `unicorn/filename-case` and `no-use-before-define` in the Oxlint and ESLint `tanstack` presets) now cover `.js` and `.jsx` route files as well as `.ts` and `.tsx`.
- [`ad2adb0`](https://github.com/haydenbleasel/ultracite/commit/ad2adb0): The Stylelint preset now handles Tailwind CSS v4, SCSS and Less, and skips build output.
- **Tailwind CSS v4:** `@theme`, `@utility`, `@variant`, `@custom-variant`, `@slot`, `@plugin` and `@config` are allowed by `at-rule-no-unknown`, alongside the Tailwind v3 at-rules and `@source` and `@reference`. `at-rule-prelude-no-invalid` no longer rejects every `@apply` (or the other Tailwind at-rules' preludes). `custom-property-pattern` accepts theme namespace resets (`--color-*: initial`) and sub-properties (`--text-xl--line-height`). `declaration-property-value-no-unknown` and `function-no-unknown` accept `--spacing()`, `--alpha()`, `--value()`, `--modifier()` and `theme()`. `media-query-no-invalid` accepts `theme()` breakpoints, and `nesting-selector-no-missing-scoping-root` allows `&` inside `@custom-variant`, `@variant` and `@utility`. See <https://tailwindcss.com/docs/functions-and-directives>.
- **`import-notation`** is now `"string"`. `stylelint-config-standard`'s `"url"` autofix rewrote `@import "tailwindcss"` to `@import url("tailwindcss")`.
- **SCSS and Less:** `.scss` files are parsed with `postcss-scss` and `.less` files with `postcss-less`. Previously every SCSS and Less file failed with `CssSyntaxError` (for example on `//` comments or mixin calls). The CSS-only rules that cannot understand preprocessor syntax (unknown at-rules such as `@use`, `@include` and `@mixin`, `$variable` and `@variable` values, Sass and Less built-in functions, `//` comments) are relaxed for those files. `ultracite init` now installs `postcss-scss` and `postcss-less` with the ESLint toolchain.
- **`.sass`:** the indented Sass syntax has no maintained PostCSS parser, so `ultracite check` and `ultracite fix` no longer pass `.sass` files to Stylelint, where every one failed to parse.
- **Ignores:** `ignoreFiles` now uses the shared ignore list (`dist`, `build`, `.next`, `coverage`, `storybook-static` and so on). Stylelint does not read `.gitignore`, so it previously linted generated CSS.
- **Prettier:** `prettier/prettier` is now enabled. The preset loaded `stylelint-prettier` but never turned its rule on.
- `ultracite check` and `ultracite fix` now escape glob characters in directory arguments before passing them to Stylelint. A directory such as `app/(marketing)` previously matched no files.
- [`89989df`](https://github.com/haydenbleasel/ultracite/commit/89989df): Test-file overrides in every preset now match `.mts`, `.cts`, `.mjs` and `.cjs` test files as well as `.ts`, `.tsx`, `.js` and `.jsx`. The globs were `**/*.{test,spec,test-d,spec-d}.{ts,tsx,js,jsx}` and `**/__tests__/**/*.{ts,tsx,js,jsx}`, so a `sum.test.mts` file got none of the test-file relaxations and none of the Jest or Vitest rules. For example, `test.only` in a `.test.mts` file was not reported by `vitest/no-focused-tests` or `jest/no-focused-tests`. This covers the Oxlint `core`, `js-plugins`, `jest` and `vitest` presets, the Biome `core` preset, and the ESLint `core`, `jest` and `vitest` presets.
- [`4d28cf9`](https://github.com/haydenbleasel/ultracite/commit/4d28cf9): The Oxlint `vue` preset now lists every non-nursery Vue rule Oxlint implements, like the other framework presets. It previously enabled 18 of them. Among the 27 newly enabled rules are `vue/valid-define-props`, `vue/valid-define-emits`, `vue/no-export-in-script-setup`, `vue/no-lifecycle-after-await`, `vue/no-arrow-functions-in-watch`, `vue/no-side-effects-in-computed-properties`, `vue/require-typed-ref`, `vue/define-props-destructuring` and the `vue/no-deprecated-*` rules. `vue/max-props` is off in both the Oxlint and ESLint presets, because its default allows a single prop per component.
The Oxlint `vitest` preset now enables `vitest/consistent-test-it`, the one Vitest rule it was missing (the ESLint preset already had it).
Vue files whose names Nuxt and file-based Vue Router require (`pages/**`, `layouts/**`, `error.vue` and `app.vue`) are exempt from `useVueMultiWordComponentNames` in the Biome preset and from `vue/multi-word-component-names` in the ESLint preset. Previously `pages/index.vue`, `layouts/default.vue` and `error.vue` were always reported.
### [`v7.12.1`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.12.1)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.12.0...ultracite@7.12.1)
##### Patch Changes
- [`00dd547`](https://github.com/haydenbleasel/ultracite/commit/00dd547): Agent hooks now fix only the file the agent edited. The generated hook command passes a new `--hook` flag, and `ultracite fix --hook` reads the agent's payload from stdin: Claude Code and CodeBuddy (`tool_input.file_path`), Cursor (`file_path`) and Windsurf (`tool_info.file_path`). A file outside the project, one that no longer exists, or one the project's linters do not handle is skipped. A payload with no readable file path keeps the previous whole-project run. Re-running `ultracite init` rewrites a hook from an earlier init to the new command, including one generated before this change or for a different package manager or linter.
- [`300a814`](https://github.com/haydenbleasel/ultracite/commit/300a814): Update `@biomejs/biome` to 2.5.14. Releases 2.5.13 and 2.5.14 add only nursery rules (14 of them, including `noReturnInFinally`, `noSvelteAtHtmlTags` and `useValidTestTitle`), with no stable-group additions, removals or promotions. The Biome presets deliberately leave nursery rules off, so they are unchanged.
- [`b8b183b`](https://github.com/haydenbleasel/ultracite/commit/b8b183b): Update dependencies (`@clack/prompts` 1.8, `empathic` 2.1, `magicast`, `nypm`, `string-width` 8.3, `yaml` and `zod` 4.6).
- [`b59b8aa`](https://github.com/haydenbleasel/ultracite/commit/b59b8aa): Update oxfmt to 0.70.0 and re-target the markdown `:::` fence patch. Releases 0.68 through 0.70 change no options; they fix suppression-comment handling, comment placement around operators and `as`/`satisfies`, and several YAML and CSS edge cases. oxfmt 0.69 also ships a native markdown formatter, but `.md` files still go through the Prettier-based path, so the fence patch still applies.
- [`548c8b0`](https://github.com/haydenbleasel/ultracite/commit/548c8b0): Update oxlint to 1.85.0. Releases 1.83 through 1.85 add and remove no rules and change no options, so the presets are unchanged. Note that `jest/prefer-strict-equal`, `vitest/prefer-strict-equal`, `vitest/prefer-to-be-truthy` and `vitest/prefer-to-be-falsy` now offer suggestions instead of fixes, so `ultracite fix` reports them rather than rewriting them. `unicorn/consistent-function-scoping` also now catches functions that only capture variables from an outer ancestor scope.
- [`0bc6cd6`](https://github.com/haydenbleasel/ultracite/commit/0bc6cd6): Update the ESLint family: `eslint` 10.11, `eslint-plugin-unicorn` 76, `@typescript-eslint/*` 8.70.1, `eslint-plugin-jsdoc` 64.5, `eslint-plugin-astro` 3.2, `eslint-plugin-n` 18.4, `eslint-plugin-vue` 10.11.1, `@tanstack/eslint-plugin-query` 5.104, `@next/eslint-plugin-next` 16.3.6, `@darraghor/eslint-plugin-nestjs-typed` 7.5.6, `eslint-plugin-qwik` 1.20.1, `eslint-plugin-html` 8.2.1 and `eslint-plugin-react-doctor` / `oxlint-plugin-react-doctor` 0.9.14.
The ESLint core preset picks up unicorn's ten new JavaScript rules automatically: `no-async-iterator-callback`, `no-unused-builtin-method-return` (which replaces the now-deprecated `no-unused-array-method-return`), `no-unused-iterator-helper`, `no-useless-set-construction`, `no-using-resource-escape`, `prefer-combined-guards`, `prefer-iterator-zip`, `prefer-json-import`, `prefer-temporal-conversion` and `prefer-uint8array-hex`. The CSS-only unicorn rules added in v75 stay off for JavaScript files. unicorn 76 makes `no-immediate-mutation` skip conditional mutations and `no-break-in-nested-loop` skip `continue` by default; the preset keeps those defaults because oxlint's `no-immediate-mutation` behaves the same way and checking `continue` would contradict `prefer-continue`.
- [`0bcb2c6`](https://github.com/haydenbleasel/ultracite/commit/0bcb2c6): Update `prettier-plugin-astro` to 1.1.0 (the version `ultracite init` installs for Astro projects with Prettier) and refresh the transitive Prettier lock to 3.9.9. Prettier 3.9.7 through 3.9.9 are formatting fixes with no option changes, so the Prettier preset is unchanged. prettier-plugin-astro 1.1 makes `astroAllowShorthand` tri-state. When it is unset, which is how Ultracite's generated config leaves it, attributes now stay as written instead of `{value}` being expanded to `value={value}`. Set `astroAllowShorthand: false` in your Prettier config to keep the 1.0 behaviour.
- [`8b39375`](https://github.com/haydenbleasel/ultracite/commit/8b39375): Update `@shadcn/lint` to 0.2.0 (the version `ultracite init` installs for the opt-in `ultracite/oxlint/shadcn` preset). The release adds Vue and Svelte support and fixes theme, barrel and class-site discovery; the rules and their options are unchanged, so the preset is too. Under Oxlint the plugin reads only the `<script>` blocks of `.vue` and `.svelte` files, not their templates.
- [`0bc6cd6`](https://github.com/haydenbleasel/ultracite/commit/0bc6cd6): Update `eslint-plugin-sonarjs` to 4.2.1. The ESLint preset adopts its 16 new rules automatically (Vue, Cypress, Playwright, Testing Library, Vitest, Lodash, jQuery and Axios checks). The `ultracite/oxlint/js-plugins` preset now enables every sonarjs rule that does not need type information, adding 35 rules. These include the 15 new non-type-aware rules and nine test rules that older versions of oxlint's JS plugin bridge could not load. sonarjs 4.2.1 also raised the `no-nested-functions` default threshold from 4 to 5; both presets pin `threshold: 4` to keep the previous strictness.
### [`v7.12.0`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.12.0)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.11.1...ultracite@7.12.0)
##### Minor Changes
- [`1f427d7`](https://github.com/haydenbleasel/ultracite/commit/1f427d7): Add an opt-in `ultracite/oxlint/shadcn` preset for [`@shadcn/lint`](https://github.com/shadcn-ui/lint), shadcn's agent-first linter for Tailwind v4 design systems. All six rules (`no-restyle`, `no-raw-colors`, `no-arbitrary-values`, `no-inline-styles`, `no-unknown-classes`, `require-static-classes`) run at `error` with the upstream `allow: ["layout"]` policy, and the component-authoring rules are relaxed inside `**/components/ui/**`. Enable it with `ultracite init --linter oxlint --js-plugins @shadcn/lint`, or extend it alongside `ultracite/oxlint/core` after installing `@shadcn/lint`.
##### Patch Changes
- [`600e991`](https://github.com/haydenbleasel/ultracite/commit/600e991): Accept file-based page route filenames in `github/filenames-match-regex`. Files under `pages/` (Astro, Next.js pages router) now match a bracket-aware pattern, so dynamic, rest and optional parameters such as `[slug].astro`, `[...slug].ts` and `[[...slug]].tsx` pass while non-route names like `BadPage.ts` are still rejected.
- [`66f97bb`](https://github.com/haydenbleasel/ultracite/commit/66f97bb): Allow top-level `return` in Astro frontmatter. The core preset now turns off `unicorn/prefer-module` for `**/*.astro` files (the rule cannot be configured to allow only `return`), and keeps `require()`, `module.exports`, `__dirname` and `__filename` flagged there via `import/no-commonjs` and `no-restricted-globals`.
### [`v7.11.1`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.11.1)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.11.0...ultracite@7.11.1)
##### Patch Changes
- [`24b2797`](https://github.com/haydenbleasel/ultracite/commit/24b2797): Update oxfmt to 0.67.0 and re-target the markdown `:::` fence patch to the new dist chunk. The release changes no options; it fixes CSS/SCSS/Less comment preservation and a few JSDoc-cast edge cases in member chains.
- [`24b2797`](https://github.com/haydenbleasel/ultracite/commit/24b2797): Update oxlint to 1.82.0. The release adds and removes no rules, so the presets are unchanged apart from one new option: `no-unmodified-loop-condition` now runs with `checkConditionalExpressions: true` in the oxlint and ESLint core presets, so each branch of a ternary in a loop condition must be modified inside the loop rather than only the expression as a whole.
- [`24b2797`](https://github.com/haydenbleasel/ultracite/commit/24b2797): Update the ESLint family: `@angular-eslint/eslint-plugin` 22.5, `@typescript-eslint/*` 8.70, `eslint-plugin-solid` 0.18, `eslint-plugin-vue` 10.11, `eslint-plugin-jsdoc` 64.3.8, `eslint-plugin-cypress` 7.0.2, `@darraghor/eslint-plugin-nestjs-typed` 7.5.2 and `prettier-plugin-astro` 1.0. The dynamic presets pick up the seven new rules automatically: `@typescript-eslint/no-generated-empty-object-type`, `@angular-eslint/reactive-context-must-read-signal`, `vue/no-shadow-native-events`, and Solid's `no-boolean-enumerated-attribute`, `no-store-mutation-outside-setter`, `no-unused-signal` and `no-write-in-pure-computation`. Note that `prettier-plugin-astro` 1.0 is a rewrite on the Astro 7 Rust compiler (Node 22.12+); expect whitespace diffs on first run and mirror any custom `compressHTML` via its new `astroCompressHTML` option.
- [`24b2797`](https://github.com/haydenbleasel/ultracite/commit/24b2797): Refresh the stylelint lock to 17.15.0 and enable its new `selector-no-unmatchable` rule, which flags selectors that can never match (for example `label:checked`, `::before:first-child` or `:is(::before)`).
- [`a71dda4`](https://github.com/haydenbleasel/ultracite/commit/a71dda4): Stop sorting object keys across the whole TanStack presets, not just route files. TanStack Query mutation options are order-sensitive too: TypeScript infers the `onMutate` context from context-sensitive callbacks in source order, so moving `onError`/`onSettled` above `onMutate` turns the context into `{}` and `ultracite fix` broke type-correct code (`TS2339: Property '...' does not exist on type '{}'`). Mutation and query options live anywhere (hooks, components, colocated files), so the route-file exemption could not cover them. `ultracite/oxlint/tanstack` now sets `sort-keys` to `off` and `ultracite/biome/tanstack` now sets `useSortedKeys` to `off` for all files.
- [`a9db61c`](https://github.com/haydenbleasel/ultracite/commit/a9db61c): Disable `github/filenames-match-regex` for file-based route directories (`**/routes/**`) in the oxlint js-plugins preset, matching the existing `unicorn/filename-case` exemption in the TanStack preset. TanStack Router filenames such as `__root.tsx`, `$.tsx`, and `posts.$postId.tsx` no longer fail the GitHub filename rule. Resolves [#​799](https://github.com/haydenbleasel/ultracite/issues/799).
### [`v7.11.0`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.11.0)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.8...ultracite@7.11.0)
##### Minor Changes
- [`d5d241f`](https://github.com/haydenbleasel/ultracite/commit/d5d241f): Add `ultracite upgrade`, and make version drift between Ultracite and its linter visible instead of a crash. Bumping `ultracite` without bumping the linter fails on the next `check`/`fix` with Biome's `Found an unknown key` error (and the equivalent for ESLint and Oxlint), because each release enables rules from a specific linter version.
- `ultracite upgrade` installs the latest `ultracite`, prints a release-notes link for the versions you crossed, then hands off to the freshly installed CLI to reinstall your toolchain at the versions that release was verified with: Biome; ESLint, Prettier, Stylelint and the plugins the preset imports; or Oxlint and Oxfmt. Optional extras already in your `package.json` (framework plugins, Oxlint JS plugins, `oxlint-tsgolint`) are bumped as well. Config files are never rewritten, and the run ends with the same diagnostics as `doctor`.
- `ultracite doctor` now checks the installed Biome, ESLint, Prettier, Stylelint, Oxlint and Oxfmt versions against the range this release supports. Too old fails the check and points at `ultracite upgrade`; newer than the verified range only warns.
- The supported ranges are declared as optional peer dependencies so package managers warn when they drift: `@biomejs/biome ^2.5.0`, `eslint ^10.0.0`, `prettier ^3.0.0`, `stylelint ^17.0.0`, `oxlint ^1.79.0`, `oxfmt >=0.40.0`. `ultracite init` now installs Prettier and Stylelint from those ranges instead of `latest`.
Requires Biome >= 2.5.0, ESLint >= 10.0.0, Prettier >= 3.0.0, Stylelint >= 17.0.0, Oxlint >= 1.79.0 and Oxfmt >= 0.40.0.
##### Patch Changes
- [`27d06f2`](https://github.com/haydenbleasel/ultracite/commit/27d06f2): Update dependencies (`@clack/prompts`, `semver`, `zod`, ESLint plugins).
- [`d322691`](https://github.com/haydenbleasel/ultracite/commit/d322691): Default `ultracite init` to Oxlint + Oxfmt when `--linter` is omitted in quiet or non-interactive mode
- [`c677cf5`](https://github.com/haydenbleasel/ultracite/commit/c677cf5): Recommend Oxlint + Oxfmt in the `ultracite init` linter prompt and list it first
- [`c5be848`](https://github.com/haydenbleasel/ultracite/commit/c5be848): Disable `no-use-before-define` in TanStack Router route files for the oxlint tanstack preset. File routes are mutually recursive (`Route` references the component, and the component calls `Route.useParams()`), so no declaration order could satisfy the rule.
### [`v7.10.8`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.8)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.7...ultracite@7.10.8)
##### Patch Changes
- [`2a43fdd`](https://github.com/haydenbleasel/ultracite/commit/2a43fdd): Turn off `unicorn/prefer-reflect-apply` in the `ultracite/oxlint/anti-slop` preset. When extended after `ultracite/oxlint/core`, the core rule recommended rewriting `Function#apply()` to `Reflect.apply()`, which anti-slop's `no-reflect-apply` then rejected, leaving no way to satisfy both. Direct-call guidance for `.apply()` still comes from `eslint/prefer-spread` and `eslint/no-useless-call`.
- [`03196df`](https://github.com/haydenbleasel/ultracite/commit/03196df): Update `@biomejs/biome` to 2.5.12. Releases 2.5.10 through 2.5.12 only add nursery rules, which the Biome presets exclude, so the presets are unchanged. The releases also carry linter and formatter fixes, including fewer `noUnusedVariables` false positives on merged interfaces, namespaces, and overload type parameters, an unsafe-by-default fix for `noThisInStatic`, and a large batch of Astro expression parsing fixes.
- [`031b867`](https://github.com/haydenbleasel/ultracite/commit/031b867): Update oxfmt to 0.66.0. The 0.65 and 0.66 releases are formatter fixes only, mainly around comment placement between a statement head and its body, suppressed statements (decorators, typecast parens, semicolons), custom side-effect groups in `sortImports`, CommonMark list interruption in JSDoc, and trailing whitespace in YAML block scalars. The `:::` container-directive fence patch is re-targeted to the new markdown bundle; the patched code itself is unchanged.
- [`54affee`](https://github.com/haydenbleasel/ultracite/commit/54affee): Update oxlint to 1.81.0. The 1.80 and 1.81 releases add no rules and remove none, so the presets are unchanged and the peer range stays at ^1.79.0. Notable fixes carried in: `no-use-before-define` now runs on JS and JSX files, `object-shorthand` preserves `__proto__` semantics, `unicorn/prefer-math-min-max` no longer applies an unsafe autofix, and JS plugin diagnostics with invalid or reversed locations are clamped instead of crashing.
- [`0b058d1`](https://github.com/haydenbleasel/ultracite/commit/0b058d1): Fix `ultracite fix --codex` failing on recent Codex CLI releases. The Codex adapter invoked `codex exec --full-auto`, a flag Codex has since removed, so every attempt failed at argument parsing. It now runs `codex exec --sandbox workspace-write`, the equivalent non-interactive, workspace-scoped mode, which is also accepted by older Codex versions. Resolves [#​794](https://github.com/haydenbleasel/ultracite/issues/794).
- [`e2346bb`](https://github.com/haydenbleasel/ultracite/commit/e2346bb): Update the ESLint family to latest: eslint 10.9.1, typescript-eslint 8.69.0, eslint-plugin-unicorn 74, eslint-plugin-solid 0.17, eslint-plugin-jsdoc 64.3.5, eslint-plugin-react-doctor and oxlint-plugin-react-doctor 0.9.13, globals 17.12, and the angular, nestjs-typed, next, tanstack-query, cypress, html, jest and storybook plugins. The dynamic presets pick up the new rules automatically: the solid preset gains the eleven rules added in eslint-plugin-solid 0.16 and 0.17 (`removed-api`, `no-single-arg-create-effect`, `no-accessor-as-prop`, `valid-use-server`, `require-async-server-function`, `no-invalid-server-capture`, `no-browser-globals-in-server-function`, `no-module-scope-reactive-primitive`, `no-restated-default-options`, `prefer-onSettled-for-side-effects`, `prefer-structured-class`), which only report Solid 2.0 issues when `settings.solid.version` is set to 2 and stay silent on Solid 1.x code; the nestjs preset gains `forward-ref-injection-should-use-wrapper-type`, `swagger-file-upload-should-be-documented` and `uploaded-file-should-be-validated`. The curated jsdoc and react-doctor lists are unchanged. No enabled rule was removed or deprecated, and the ESLint/oxlint parity check still passes.
- [`7b0456e`](https://github.com/haydenbleasel/ultracite/commit/7b0456e): `ultracite fix` now runs lint fixes before the formatter: oxlint before oxfmt, and ESLint then Stylelint before Prettier. A fixer can insert unformatted code, such as the braces `curly` adds, the imports `consistent-type-specifier-style` splits, or the font names Stylelint requotes, and running the formatter first left that code unformatted until the next run. Fixer-inserted code is now formatted in the same run. The `--claude` and `--codex` agent passes follow the same order.
`unicorn/no-nested-ternary` is now off in the oxlint and ESLint presets: its fixer adds parentheses that oxfmt and Prettier remove, so the two tools rewrote the file on every run. The core `no-nested-ternary` rule still reports nested ternaries.
- [`8ddc078`](https://github.com/haydenbleasel/ultracite/commit/8ddc078): Enable three rules added in Stylelint 17 in the Stylelint preset: `selector-no-deprecated` and `selector-no-invalid` report selectors that CSS has dropped or that cannot parse, and `relative-selector-nesting-notation` is set to `explicit`, so nested relative selectors are written `& > b` rather than `> b`. The `property-layout-mappings`, `unit-layout-mappings` and `value-keyword-layout-mappings` rules are deliberately left off, since they reject every physical property, unit and keyword and can only autofix with a per-project `languageOptions.directionality` setting. The repo's own Stylelint lock is refreshed from 16.26.1 to 17.14.1, which the config packages already required; `ultracite init` already installs `stylelint@latest` so users are unaffected by the lock change.
- [`4609d79`](https://github.com/haydenbleasel/ultracite/commit/4609d79): Update the vendored `anti-slop` Oxlint plugin to upstream v0.1.2 (commit `e8c4880`). The rule set is unchanged; this picks up a round of correctness fixes: `no-known-value-widening` now also reports known values passed into local `unknown`-typed type predicates, `no-object-parameters`, `no-unknown-parameters`, `no-unknown-returns` and `no-unknown-type-aliases` resolve scoped and transparent generic aliases (including block-scoped aliases and forward references), `no-unknown-returns` covers `PromiseLike<unknown>`, `no-unsafe-dictionary-type` allows generic constraints such as `T extends Record<string, unknown>`, `no-runtime-typeof` always allows existence probes against `"undefined"`, `no-shape-in-symbol-names` allows static member reads such as Zod's `schema.shape`, and `require-safety-comment-for-type-assertion` rejects empty justifications and gains a `markers` option (default `["SAFETY"]`). Upstream also added a separate opt-in `anti-slop-effect` plugin for Effect projects, which is not vendored.
### [`v7.10.7`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.7)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.6...ultracite@7.10.7)
##### Patch Changes
- [`15f7ae0`](https://github.com/haydenbleasel/ultracite/commit/15f7ae0): Add support for the Aube package manager: `ultracite init --pm aube` is now accepted and projects with an `aube-lock.yaml` lockfile are detected automatically
- [`1afc8fe`](https://github.com/haydenbleasel/ultracite/commit/1afc8fe): Re-declare selected Oxlint JS plugins on the root config so dependency analyzers see them. `ultracite init --linter oxlint --js-plugins ...` now generates `const jsPlugins = selectJsPlugins([...])` and adds `jsPlugins: jsPlugins.jsPlugins` to the root config (also when the full `ultracite/oxlint/js-plugins` preset is extended). Knip only resolves `jsPlugins` from the root Oxlint config and never walks `extends`, so `eslint-plugin-github`, `eslint-plugin-sonarjs`, and `oxlint-plugin-react-doctor` were reported as unused devDependencies. Oxlint dedupes the plugin between the root and the extended preset, so linting is unchanged.
Also fix `ultracite update` dropping the `js-plugins` preset from `extends` when the config used the documented `import jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins"` form: the import parser only matched bare default imports. The regenerated full-preset config keeps `settings: jsPluginSettings` on the root as well.
- [`20b06bf`](https://github.com/haydenbleasel/ultracite/commit/20b06bf): Ignore `.alchemy` across all linters and formatters. Alchemy (alchemy.run) writes local state and generated bindings to a `.alchemy` directory. It is now part of the shared ignore patterns synced into Biome's `files.includes` and imported by oxlint, oxfmt, and ESLint, and the Stylelint preset ignores it via `ignoreFiles`. Prettier needs no change: it already respects `.gitignore`/`.prettierignore`.
- [`6e9286c`](https://github.com/haydenbleasel/ultracite/commit/6e9286c): Run `lefthook install` during `ultracite init` through the same spawn adapter as every other tool invocation instead of `execSync`, so it no longer goes through a shell and gets the same Windows command resolution as `husky`, the linters, and editor extension installs.
- [`8b24f34`](https://github.com/haydenbleasel/ultracite/commit/8b24f34): Add support for the Nub package manager: `ultracite init --pm nub` is now accepted and projects with a `nub.lock` lockfile are detected automatically (requires nypm ^0.6.9)
- [`240cb6b`](https://github.com/haydenbleasel/ultracite/commit/240cb6b): Install into the workspace root correctly for Nub and Aube monorepos: nypm emits no root selector for them, so `nub add` ran without `-w` and was refused. Ultracite now hands nypm pnpm's flag set for these pnpm-compatible CLIs, producing `nub add --workspace-root --save-dev ultracite`
- [`44ecbba`](https://github.com/haydenbleasel/ultracite/commit/44ecbba): Add Vitest type-test files (`**/*.{test-d,spec-d}.{ts,tsx,js,jsx}`) to the test-file globs so the shared test relaxations and the Vitest rule overrides apply to them, and enable the Vitest plugin's `typecheck` setting in the ESLint preset so `expectTypeOf`/`assertType` count as assertions.
- [`7ba6bb8`](https://github.com/haydenbleasel/ultracite/commit/7ba6bb8): Package the reusable Ultracite agent skill with the npm distribution.
### [`v7.10.6`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.6)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.5...ultracite@7.10.6)
##### Patch Changes
- [`972b946`](https://github.com/haydenbleasel/ultracite/commit/972b946): Update the oxlint presets for oxlint 1.79: replace the removed `react/react-compiler` rule with the 22 new React Compiler rules in the react preset, add `jsdoc/no-blank-blocks` and `one-var` to the core preset (mirrored in the ESLint presets), and require oxlint ^1.79.0
- [`2d5bbdb`](https://github.com/haydenbleasel/ultracite/commit/2d5bbdb): Declare the `jsPluginSettings` named export in the `ultracite/oxlint/js-plugins` type declarations, so the documented (and `ultracite init`-generated) `import jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins"` type-checks ([#​773](https://github.com/haydenbleasel/ultracite/issues/773))
- [`56aef65`](https://github.com/haydenbleasel/ultracite/commit/56aef65): Refresh the toolchain versions that `ultracite init` installs into projects: `@biomejs/biome` 2.5.9, the ESLint plugin family (including eslint-plugin-cypress 7, eslint-plugin-jsdoc 64, eslint-plugin-solid 0.15, and eslint-plugin-unicorn 73 — the dynamic presets adopt their new rules automatically), and `prettier-plugin-svelte` 4.1.1 / `prettier-plugin-tailwindcss` 0.8.1
### [`v7.10.5`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.5)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.4...ultracite@7.10.5)
##### Patch Changes
- [`8df6ad0`](https://github.com/haydenbleasel/ultracite/commit/8df6ad0): Offer the vendored `anti-slop` Oxlint preset during `ultracite init` — it now appears in the JS-plugins prompt when you pick Oxlint, and non-interactive setup accepts it via `--js-plugins anti-slop`. Selecting it adds `ultracite/oxlint/anti-slop` to the generated config's `extends`; since the preset is vendored inside Ultracite, nothing extra is installed.
- [`cd229e9`](https://github.com/haydenbleasel/ultracite/commit/cd229e9): Bump the `oxlint-plugin-react-doctor` pin from `^0.7.1` to `^0.9.12`, so `ultracite init` installs the current plugin. All react-doctor rules enabled by the js-plugins presets still exist in 0.9.12, and the ported rules run in curated mode via the settings shipped alongside this release ([#​771](https://github.com/haydenbleasel/ultracite/issues/771)).
- [`e1ac886`](https://github.com/haydenbleasel/ultracite/commit/e1ac886): Pin React Doctor's ported rules to their framework-aware "curated" mode ([#​771](https://github.com/haydenbleasel/ultracite/issues/771)). react-doctor 0.9.x rewrote its ported oxc/react-refresh rules — notably `only-export-components` — with a stripped-down default mode: no framework detection, no route-file skipping, and `allowConstantExport` off, so Next.js route-segment exports like `export const dynamic = "force-static"` or `metadata` were flagged as non-component exports in every route file. The ESLint react preset now sets `settings["react-doctor"].portedRuleMode: "curated"`, and generated oxlint configs apply a new `jsPluginSettings` export from `ultracite/oxlint/js-plugins` on the root config (oxlint does not merge `settings` from extended configs, so the setting cannot ride along inside the preset). If you extend the js-plugins preset manually, add `settings: jsPluginSettings` to your root oxlint config.
- [`c27fe36`](https://github.com/haydenbleasel/ultracite/commit/c27fe36): Generate oxlint configs that enable a subset of the JS plugins via a new `selectJsPlugins` export from `ultracite/oxlint/js-plugins`, instead of inlining the filtering logic into the generated file. The inlined block contained a `typeof` check that user-side lint presets flagged (`anti-slop/no-runtime-typeof`, [#​770](https://github.com/haydenbleasel/ultracite/issues/770)); the generated config is now a one-line extend, is emitted already formatted (including the previously missing blank line after imports), and re-running `ultracite init` migrates existing configs with the old inlined block automatically.
- [`0616523`](https://github.com/haydenbleasel/ultracite/commit/0616523): Update the vendored `anti-slop` Oxlint plugin to upstream commit `446268e`, picking up fixes to `no-object-parameters` and `no-unknown-returns` (respect lexical type binders in alias resolution) and a new `allowInTypeGuards` option on `no-runtime-typeof`. The `ultracite/oxlint/anti-slop` preset enables `allowInTypeGuards`, so `typeof` checks inside type predicate functions (`(x): x is T`) no longer need disable comments — predicates are the named-boundary pattern the rule pushes toward ([dmmulroy/anti-slop#10](https://github.com/dmmulroy/anti-slop/issues/10)).
### [`v7.10.4`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.4)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.3...ultracite@7.10.4)
##### Patch Changes
- [`417a85a`](https://github.com/haydenbleasel/ultracite/commit/417a85a): Add an opt-in `ultracite/oxlint/anti-slop` preset that ships a vendored, self-contained build of the [anti-slop](https://github.com/dmmulroy/anti-slop) Oxlint plugin — fifteen rules that reject low-evidence TypeScript and JavaScript patterns (unjustified type assertions, `unknown` leaking through signatures, `Reflect`-based access, module mocking, and more). Extend it alongside `ultracite/oxlint/core`; nothing extra to install. The preset also turns off two core rules that conflict with anti-slop's widening checks (`typescript/consistent-indexed-object-style` and `unicorn/no-immediate-mutation`) when extended after core.
- [`4d3fab8`](https://github.com/haydenbleasel/ultracite/commit/4d3fab8): Move `suspicious/useArraySortCompare` from the Biome core config to the opt-in type-aware config. The rule is in Biome's `types` domain — it type-infers the receiver of every method call before checking the method name, which made `ultracite check` up to \~260x slower on projects with expensive library types (zod, better-auth, Prisma). It now only runs when type-aware linting is explicitly enabled, alongside the other type/project-domain rules. Fixes [#​768](https://github.com/haydenbleasel/ultracite/issues/768).
### [`v7.10.3`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.3)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.2...ultracite@7.10.3)
##### Patch Changes
- [`a1fa9c4`](https://github.com/haydenbleasel/ultracite/commit/a1fa9c4): Replace the hand-rolled package `exports` map matching in the config-resolution doctor check with the resolve.exports library, which implements Node's full `PACKAGE_TARGET_RESOLVE` algorithm (wildcard patterns, key-order precedence, conditional exports, and array fallbacks). The manual `node_modules` walk is kept intentionally — it exists to avoid Bun's auto-install cache resolving specifiers the project's own `node_modules` can't.
- [`414ea80`](https://github.com/haydenbleasel/ultracite/commit/414ea80): Replace the hand-rolled monorepo workspace scan in framework detection with the find-workspaces library. Workspace declarations from package.json (array and yarn-classic object form) and pnpm-workspace.yaml — including negated globs — are now resolved by the library instead of manual pattern collection and globbing, and lerna/bolt monorepos are picked up as well.
- [`a9a1989`](https://github.com/haydenbleasel/ultracite/commit/a9a1989): Replace the hand-rolled upward directory walks in `findNearestFile` and `detectLinter` with the empathic library's `find.any`, which checks candidate names in order within each directory before moving to the parent — the same per-directory precedence the previous implementation enforced manually.
- [`27b2707`](https://github.com/haydenbleasel/ultracite/commit/27b2707): Use magicast to update ESM lint-staged config files during `ultracite init`. The config is now edited as an AST instead of being imported and re-serialized, so comments and function-valued entries elsewhere in the config survive the update, and the user's config code is no longer executed. If the Ultracite glob pattern is already owned by a non-array value, or the config isn't a mergeable object literal (e.g. `defineConfig(...)`), init warns and leaves the file untouched instead of rewriting it. CommonJS configs keep the previous behavior.
- [`f2529b8`](https://github.com/haydenbleasel/ultracite/commit/f2529b8): Rewrite the agent-fix progress renderer on top of log-update, cli-truncate, and string-width. log-update now owns the in-place block rewriting that was previously done with manual cursor-up/clear-line escape sequences, and line truncation is measured by display width instead of code units — so lint messages containing emoji or CJK text can no longer overflow the terminal row and corrupt the animated block.
- [`277b9d6`](https://github.com/haydenbleasel/ultracite/commit/277b9d6): Replace the hand-rolled child-process handling in the agent fix runner with execa. The timeout → SIGTERM → grace period → SIGKILL escalation, stderr capture, and spawn-failure handling now use execa's `timeout` and `forceKillAfterDelay` options, which are battle-tested across platforms (including Windows kill semantics the manual implementation didn't cover). Behavior is unchanged: agent runs still time out after 5 minutes, escalate to SIGKILL after a 10-second grace period, and report a capped stderr tail.
- [`1614a80`](https://github.com/haydenbleasel/ultracite/commit/1614a80): Drop the direct cross-spawn dependency: all synchronous process spawning (linter runs, tool version checks, editor extension installs, skill installs) now goes through a small adapter over execa's sync API, which owns the Windows spawn semantics cross-spawn provided. The adapter preserves the spawnSync result shape (`status`/`signal`/`error`/`stdout`), always disables shell interpretation, and always decodes output as UTF-8. execa was already a dependency for the agent fix runner, so this consolidates on one process-spawning library.
- [`ac114b4`](https://github.com/haydenbleasel/ultracite/commit/ac114b4): Replace the glob dependency with fast-glob for the tsconfig.json scan during init. fast-glob was already in the dependency tree via find-workspaces, so this drops glob's transitive dependencies (minipass, path-scurry, etc.) from the install without changing behavior.
### [`v7.10.2`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.2)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.1...ultracite@7.10.2)
##### Patch Changes
- [`1c48c68`](https://github.com/haydenbleasel/ultracite/commit/1c48c68): Enable Tailwind CSS class sorting (`sortTailwindcss`) in the Oxfmt preset. Classes in `class`/`className` attributes and in `clsx`, `cva`, `tw`, `twMerge`, `cn`, `twJoin`, and `tv` calls are now sorted using the same algorithm as `prettier-plugin-tailwindcss`, matching the behavior of the Prettier preset (which always loads the Tailwind plugin) and the Biome preset's `useSortedClasses` rule. Projects without Tailwind installed are unaffected beyond class strings being sorted against the default theme, and oxfmt versions older than 0.35.0 ignore the option.
- [`dc78be4`](https://github.com/haydenbleasel/ultracite/commit/dc78be4): Stop sorting TanStack route option keys in route files. TanStack Router's route option types are order-sensitive (`head`/`component` infer `loaderData` from properties declared before them), so the Biome `useSortedKeys` source action rewrote `createFileRoute` literals into an order that breaks type inference (`loaderData` becomes `never`). The Biome TanStack preset now disables `useSortedKeys` for route files, and the oxlint TanStack preset disables `sort-keys` there so route files aren't caught between it and `react-doctor/tanstack-start-route-property-order`.
### [`v7.10.1`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.1)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.10.0...ultracite@7.10.1)
##### Patch Changes
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix several agent fix mode (`fix --claude`/`--codex`) issues: runs no longer abort with ENOBUFS when linter JSON output exceeds 1MB, user-supplied format/reporter flags can no longer override the JSON reporter and break parsing, a stuck agent process is force-killed 10 seconds after the timeout instead of hanging forever, and the progress renderer no longer garbles TTY output when file paths and rule names exceed the terminal width.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix Biome config migration leaving the legacy bare `"extends": ["ultracite"]` form in place, which breaks Biome's module resolution since the package has no root export. It's now mapped to `ultracite/biome/core`.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix `ultracite doctor` reporting spurious failures: config checks now walk up parent directories (matching `check`/`fix` and the linters themselves) so monorepo packages inheriting a root config pass, `.oxlintrc.json` is accepted as a valid oxlint config (with a migration suggestion), and Prettier/Stylelint configs declared via `package.json` keys are recognized.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix `ultracite init` corrupting existing Prettier/Stylelint/ESLint configs by writing an ESM module into JSON/YAML/TOML/CJS config files (e.g. `.prettierrc`, `eslint.config.cjs`). Updates now write the default `.mjs` config instead and remove the incompatible file so it can't shadow the new one.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix `ultracite check`/`fix` misrouting space-separated flag values (e.g. `--max-warnings 10`) into the file list, which scrambled the underlying linter invocation and made formatters fail on bogus targets. Positional files listed before a `--` separator are also kept as lint targets instead of being reclassified as passthrough, which could silently widen formatter runs to the whole project.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix Lefthook and pre-commit YAML updates silently doing nothing on common config shapes: the Lefthook job is now inserted correctly when `jobs:` isn't the first key under `pre-commit:` (and no longer matches a `jobs:` key in a different hook), `repos: []` in `.pre-commit-config.yaml` is handled, and shapes that can't be safely edited produce a warning instead of writing the file back unchanged.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix the Husky integration overwriting an existing `.husky/pre-commit` hook: `ultracite init` ran `husky init`, which unconditionally replaces the hook with `npm test`. It now runs plain `husky` to set up the hooks infrastructure without touching the hook file.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix `ultracite init` destroying user files it couldn't parse or merge: unparseable `tsconfig.json` files are no longer replaced with a minimal config, unparseable `.vscode`/`.zed` settings are no longer overwritten wholesale, and lint-staged configs with function-valued entries are left untouched — all now warn and skip instead.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix re-running `ultracite init` on an oxlint setup silently enabling the full js-plugins preset — the previously selected JS plugins are now preserved when no new selection is made. Init also no longer flips an explicit `"type": "commonjs"` in package.json to `"module"`; it warns instead.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix switching linters removing `storybook` from the project's dependencies. It was swept into the removal set as a peer of `eslint-plugin-storybook`, but it's a user-facing tool a project may use independently of linting.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix Stylelint target generation dropping directories with a dot in their name (e.g. `app.web`) and producing non-matching globs from Windows-style backslash paths. Framework detection also handles negated workspace patterns (`!packages/legacy`) again.
- [`d018b7f`](https://github.com/haydenbleasel/ultracite/commit/d018b7f): Fix the CLI becoming a silent no-op (exiting 0 without linting anything) when a generic `TEST` environment variable is set, as is common in CI matrices. The internal test guard now uses `ULTRACITE_TEST`.
- [`36c7b80`](https://github.com/haydenbleasel/ultracite/commit/36c7b80): Move [@​typescript-eslint/utils](https://github.com/typescript-eslint/utils) from dependencies to devDependencies. It was accidentally shipped as a runtime dependency in 7.9.0, pulling eslint and the typescript-eslint packages into every consumer's install (including oxlint-only setups) via npm's automatic peer dependency installation. Nothing in the published package imports it — it only exists to support the workspace-internal rule-parity script.
### [`v7.10.0`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.10.0)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.9.4...ultracite@7.10.0)
##### Minor Changes
- [`cd0a36c`](https://github.com/haydenbleasel/ultracite/commit/cd0a36c): Add `--claude` and `--codex` flags to `ultracite fix`. After the normal autofix pass, remaining diagnostics are handed to the Claude Code or Codex CLI non-interactively, one agent run per affected file, with a live per-issue spinner that flips to ✓/✗ once the fix is verified by a re-lint. Fixes that don't survive verification are retried (up to 3 attempts per file) with the fresh diagnostics and feedback that the previous approach failed. Works with all three linter modes (Oxlint, Biome, ESLint); exits non-zero if any issues remain, matching the plain `fix` contract.
- [`e089510`](https://github.com/haydenbleasel/ultracite/commit/e089510): Scope React Doctor's framework-specific rules to per-framework add-on presets ([#​752](https://github.com/haydenbleasel/ultracite/issues/752))
The `ultracite/oxlint/js-plugins` preset no longer enables React Doctor's `nextjs-*` and TanStack (`query-*`, `tanstack-start-*`) rules for every consumer. Rules like `nextjs-no-img-element` and `tanstack-start-no-anchor-element` fire on plain `<img>`/`<a>` JSX and recommend framework replacements, which falsely errored in Vite + React and other non-Next/non-TanStack projects.
Those rules now live in two new add-on presets:
- `ultracite/oxlint/next/js-plugins`
- `ultracite/oxlint/tanstack/js-plugins`
`ultracite init` wires the matching add-on automatically when you select the framework together with `oxlint-plugin-react-doctor`. If you manage `oxlint.config.ts` by hand and use Next.js or TanStack, add the matching add-on preset to `extends` alongside `js-plugins` to keep those rules — or re-run `npx ultracite init`.
Also fixes re-running `init` on a config that already extends `js-plugins` producing a duplicate `import jsPlugins` declaration.
##### Patch Changes
- [`3320cd8`](https://github.com/haydenbleasel/ultracite/commit/3320cd8): Update Biome to 2.5.6. No stable (non-nursery) rules were added, removed, or promoted between 2.5.3 and 2.5.6, so the preset configs are unchanged.
- [`477cd6e`](https://github.com/haydenbleasel/ultracite/commit/477cd6e): Update ESLint to 10.8.0 and all ESLint plugins to their latest versions. Highlights:
- `eslint-plugin-react-doctor` 0.9.3: the react preset expands from 149 to 417 rules, adopting the upstream `recommended` set (react-router, three.js/r3f, ink, motion, remotion, zustand/valtio/mobx, and more) while excluding rules that duplicate already-enabled `react`, `react-hooks`, and `jsx-a11y` rules. The next preset gains `nextjs-async-dynamic-api-not-awaited` and `nextjs-metadata-url-consistency`; the tanstack preset gains `tanstack-start-missing-scripts`, `query-floating-mutate-async`, and `query-no-mutation-in-effect-as-read`.
- `eslint-plugin-unicorn` 72: adds `no-missing-local-resource`, `no-multiple-promise-resolver-calls`, `no-shorthand-property-overrides`, `no-transition-all`, `no-unnecessary-string-trim`, `no-useless-re-export`, `prefer-then-catch`, and `require-frontmatter-fields`. CSS-only rules are excluded from the preset since they fail config validation for JS files.
- `eslint-plugin-sonarjs` 4.2: adds 11 rules including `no-fixed-wait-in-tests`, `parameterized-tests`, `assertions-in-test-cases`, `prefer-native-lodash-alternative`, and `explicit-test-skip`.
- `typescript-eslint` 8.65: `@typescript-eslint/no-loop-func` and `@typescript-eslint/no-restricted-imports` were deprecated upstream in favor of the base rules, which now apply to TypeScript files.
- `eslint-plugin-astro` 3: removes `astro/no-omitted-end-tags` and `astro/valid-compile`.
- `eslint-plugin-svelte` 3.22: adds `no-bind-value-on-checkable-inputs` and `no-conflicting-module-names`; `no-restricted-html-elements` is now off because its schema requires a user-supplied element list.
- `@angular-eslint/eslint-plugin` 22.1: adds `inject-at-top` and `prefer-service-decorator`.
- [`b81578b`](https://github.com/haydenbleasel/ultracite/commit/b81578b): Fix the useSortedPackageJson action not being executed by turning on assist actions for package.json-like files.
- [`9ec454a`](https://github.com/haydenbleasel/ultracite/commit/9ec454a): Update oxlint to 1.76.0 and oxfmt to 0.61.0. New stable rules added to the presets: `oxc/bad-match-all-arg`, `id-denylist`, `node/exports-style` (core), `react/function-component-definition` with arrow-function components (react), and `vitest/padding-around-test-blocks` (vitest). `node/no-top-level-await` is off — top-level await is idiomatic in ESM, Astro frontmatter, and build scripts — and the ESLint preset's `n/no-top-level-await` is now off to match. No rules were removed or promoted out of nursery.
- [`ba61c02`](https://github.com/haydenbleasel/ultracite/commit/ba61c02): Fix generated oxlint.config.ts accessing plugin.name on ExternalPluginEntry without narrowing the string form, which caused a TypeScript error in projects that type-check the config ([#​753](https://github.com/haydenbleasel/ultracite/issues/753))
### [`v7.9.4`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.9.4)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.9.3...ultracite@7.9.4)
##### Patch Changes
- [`f480c12`](https://github.com/haydenbleasel/ultracite/commit/f480c12): Upgrade Biome to 2.5.3 to fix the LSP scanner deadlock ([#​10845](https://github.com/biomejs/biome/issues/10845)) where editors get stuck on "Biome is scanning the project".
- [`1d70c40`](https://github.com/haydenbleasel/ultracite/commit/1d70c40): Fail fast with an actionable message when Biome can't resolve `ultracite/biome/core`. Biome resolves that config out of the project's `node_modules`, so it errors with an opaque "module not found" whenever Ultracite isn't installed there — a state `npx ultracite check` / `bunx ultracite check` hide, because they run the CLI from a temp cache regardless. `check` and `fix` now detect it before invoking Biome and say what's actually wrong, and `doctor` verifies that Ultracite resolves rather than just appearing in `package.json` ([#​750](https://github.com/haydenbleasel/ultracite/issues/750)).
- [`38d10bc`](https://github.com/haydenbleasel/ultracite/commit/38d10bc): Move Biome's `noUnknownAttribute` rule out of the core preset and into the `react` preset. The rule only recognises React's JSX attribute names, so projects using Solid, Svelte, Vue, or Qwik were incorrectly flagged for framework-standard attributes such as `class`.
- [`cf723bd`](https://github.com/haydenbleasel/ultracite/commit/cf723bd): Add interactive and non-interactive selection for optional Oxlint JS plugins during init.
### [`v7.9.3`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.9.3)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.9.2...ultracite@7.9.3)
##### Patch Changes
- [`dc6d760`](https://github.com/haydenbleasel/ultracite/commit/dc6d760): Disable the `n/no-unpublished-import` rule in the ESLint core config. This rule flags imports of packages that aren't listed as published dependencies, but it produces a lot of false positives in practice, so it's now turned off.
- [`15ecfba`](https://github.com/haydenbleasel/ultracite/commit/15ecfba): Fix `ultracite init --linter eslint` installing an unusable toolchain. The generated ESLint config imports `eslint-plugin-storybook` unconditionally (which requires the `storybook` peer) and the generated Stylelint config extends `stylelint-config-standard` / `stylelint-config-idiomatic-order` / `stylelint-prettier`, but none of those packages were installed — so a fresh ESLint setup failed to load with "Cannot find package 'storybook'" or "Could not find stylelint-config-standard". These four packages are now installed with the ESLint linter.
- [`2f73a41`](https://github.com/haydenbleasel/ultracite/commit/2f73a41): Upgrade to oxlint 1.73.0 and oxfmt 0.58.0, and enable the new lint rules they introduce: `no-unreachable-loop`, `unicorn/explicit-timer-delay`, and `unicorn/no-confusing-array-with`.
- [`83b2783`](https://github.com/haydenbleasel/ultracite/commit/83b2783): Add an `[astro]` formatter mapping (`astro-build.astro-vscode`) to the oxlint VS Code editor settings generated by `ultracite init`, since oxfmt doesn't format `.astro` files.
- [`d186c53`](https://github.com/haydenbleasel/ultracite/commit/d186c53): Move every JS-plugin-based rule set out of the Oxlint core and framework presets and into a single opt-in `ultracite/oxlint/js-plugins` preset. This covers `eslint-plugin-github` and `eslint-plugin-sonarjs` (previously in core) as well as `oxlint-plugin-react-doctor` (previously bundled into the `react`, `next`, and `tanstack` presets). The core, `react`, `next`, and `tanstack` presets now run entirely on Oxlint's native Rust rules, so new setups no longer install those dependencies and no longer pay the slower JS-plugin lint pass. To keep the extra ESLint-parity and React Doctor rules, install `eslint-plugin-github`, `eslint-plugin-sonarjs`, and `oxlint-plugin-react-doctor` and extend `ultracite/oxlint/js-plugins` alongside `core` (and your framework preset).
- [`057753e`](https://github.com/haydenbleasel/ultracite/commit/057753e): Add performance benchmarks for `ultracite check` / `ultracite fix` across all three providers (oxlint, biome, eslint). A new CI job builds the PR and its base branch on the same runner, benchmarks them interleaved, and fails on a statistically significant regression (median ratio > 1.25x with Mann-Whitney U p < 0.05) so config changes can't silently slow the linters down again.
### [`v7.9.2`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.9.2)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.9.1...ultracite@7.9.2)
##### Patch Changes
- [`c335a1f`](https://github.com/haydenbleasel/ultracite/commit/c335a1f): Add `**/node_modules` and `**/.git` to the shared ignore patterns. oxlint only skips `node_modules` when a `.gitignore` lists it, so in projects without one, `ultracite fix` would lint and autofix files inside `node_modules` — corrupting installed packages (e.g. rewriting `var` enum wrappers in `typescript/lib/typescript.js` to self-referencing `const`, causing "Cannot access 'Comparison' before initialization" when oxlint loads eslint-plugin-sonarjs). Fixes [#​737](https://github.com/haydenbleasel/ultracite/issues/737).
### [`v7.9.1`](https://github.com/haydenbleasel/ultracite/releases/tag/ultracite%407.9.1)
[Compare Source](https://github.com/haydenbleasel/ultracite/compare/ultracite@7.9.0...ultracite@7.9.1)
##### Patch Changes
- [`ecd10cc`](https://github.com/haydenbleasel/ultracite/commit/ecd10cc): Disable `sonarjs/no-implicit-dependencies` and `github/no-implicit-buggy-globals` in the oxlint and ESLint presets. Both produce false positives through oxlint's JS plugin bridge: `no-implicit-dependencies` has no dependency-manifest resolution so it flags builtin (`bun:test`) and workspace imports as missing dependencies, and `no-implicit-buggy-globals` misreads module-scoped declarations such as Astro frontmatter as implicit globals.
- [`c76f59d`](https://github.com/haydenbleasel/ultracite/commit/c76f59d): Disable `sonarjs/file-name-differ-from-class` in the oxlint and ESLint presets. It fires on any file whose name differs from an exported class, which is noise for the many config and module files that export objects rather than classes.
- [`29e30ce`](https://github.com/haydenbleasel/ultracite/commit/29e30ce): Fold the github and sonarjs rules into the oxlint core preset. The standalone `ultracite/oxlint/github` and `ultracite/oxlint/sonarjs` presets are removed — ultracite ships framework presets, not individual plugins. Their rules now live in `ultracite/oxlint/core`, so every oxlint setup gets eslint-plugin-github and eslint-plugin-sonarjs through oxlint's JS plugin bridge, matching how the ESLint preset already bundles them into core.
This is a breaking change to generated configs: `ultracite/oxlint/github` and `ultracite/oxlint/sonarjs` no longer exist. Re-run `ultracite init` to regenerate `oxlint.config.ts`.
Also fixed: nine sonarjs rules (`async-test-assertions`, `hooks-before-test-cases`, `no-duplicate-test-title`, `no-empty-test-title`, `no-floating-point-equality`, `no-forced-browser-interaction`, `no-trivial-assertions`, `prefer-specific-assertions`, `super-linear-regex`) that exist in eslint-plugin-sonarjs but that oxlint's JS plugin bridge does not register. Naming them made oxlint hard-fail config parsing, which broke `ultracite fix`/`check` for oxlint projects using the sonarjs preset. They are omitted from oxlint core (still enabled in the ESLint preset), and a test now runs oxlint against core to catch this class of regression.
The React Doctor, github, and sonarjs plugins are installed into your project's devDependencies at init (as the ESLint plugins already were), rather than bundled as dependencies of ultracite — oxlint resolves JS plugin specifiers from the project root, so they must be installed there directly.
- [`8a4291f`](https://github.com/haydenbleasel/ultracite/commit/8a4291f): Upgrade to Oxlint 1.72.0 and Oxfmt 0.57.0. Oxfmt 0.57 adds native CSS and GraphQL formatters. The five new non-nursery Oxlint rules from the 1.71/1.72 releases are already covered by the presets (`node/no-sync`, `node/no-mixed-requires`, `unicorn/prefer-number-coercion`, `unicorn/max-nested-calls`, `vue/no-async-in-computed-properties`). The markdown `:::` container-directive fence patch (which keeps `proseWrap: "never"` from folding fences into prose) was regenerated against the 0.57 bundle.
</details>
---
### Configuration
📅 **Schedule**: (in timezone Europe/Warsaw)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box
---
This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC43NC4wIiwidXBkYXRlZEluVmVyIjoiNDQuNzQuMCIsInRhcmdldEJyYW5jaCI6ImRldiIsImxhYmVscyI6WyJkZXBzLWNvZGUiLCJyZW5vdmF0ZSJdfQ==-->
In order to perform the update(s) described in the table above, Renovate ran the go get command, which resulted in the following additional change(s):
2 additional dependencies were updated
Due to Go's usage of Minimal Version Selection (MVS), these packages have been updated to the minimum version available, so will still abide by minimumReleaseAge=3 days
Details:
Package
Change
golang.org/x/sys
v0.47.0 -> v0.48.0
modernc.org/libc
v1.75.7 -> v1.77.1
### ℹ️ Artifact update notice
##### File name: backend/go.mod
In order to perform the update(s) described in the table above, Renovate ran the `go get` command, which resulted in the following additional change(s):
- 2 additional dependencies were updated
Due to Go's usage of [Minimal Version Selection (MVS)](https://go.dev/ref/mod#minimal-version-selection), these packages have been updated to the minimum version available, so will still abide by `minimumReleaseAge=3 days`
Details:
| **Package** | **Change** |
| :----------------- | :--------------------- |
| `golang.org/x/sys` | `v0.47.0` -> `v0.48.0` |
| `modernc.org/libc` | `v1.75.7` -> `v1.77.1` |
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
This PR contains the following updates:
2.5.2→2.5.13v1.59.0→v1.60.17.9.0→7.12.2Release Notes
biomejs/biome (@biomejs/biome)
v2.5.13Compare Source
Patch Changes
#11379
07a0073Thanks @Netail! - Added the nursery ruleuseLayeredStyles, which enforces that style rules are defined within a cascade layer and import rules to import its styles into a cascade layer.#11667
e997900Thanks @devtechedge! - Added the nursery ruleuseBetterDomTraversing, which prefers.firstChild,.firstElementChild,.closest(), and merged.querySelector()calls over positional DOM traversal.#11620
20e513aThanks @jakeleventhal! - Fixed #11610, #11611, #11612, #11615, and #11616: Biome no longer fully infers an imported generic declaration just to apply its type arguments, restoring type-aware lint performance for large libraries such as Zod. This improvesuseRegexpExec,noFloatingPromises,noMisusedPromises,useNullishCoalescing, andnoUnsafePlusOperands.#11657
e322040Thanks @ematipico! - Fixed #7495:noUselessConstructornow ignores TypeScript constructors that forward at least one argument tosuper, preserving constructors that narrow the subclass's accepted parameter types. The exemption also applies when the parent and child signatures are identical; JavaScript and zero-argument forwarding behavior are unchanged.#11670
4969ee1Thanks @ematipico! - Fixed #7076:useAriaPropsForRoleanduseFocusableInteractiveno longer report non-focusable elements withrole="separator". A separator with an explicittabIndexortabindexstill requiresaria-valuenow.#11627
23aad6dThanks @ematipico! - Fixed #6571 so Grit plugins can capture and inspect multiple named import specifiers.#11631
00dbd3aThanks @ematipico! - Reduced unnecessary type inference when type-aware lint rules inspect members of namespace imports from libraries such as Zod. Fixed type inference so blanket re-exports do not expose default exports.#11628
a2f8ff7Thanks @dyc3! - Added the nursery rulenoXorAsExponentiation, which reports the bitwise XOR operator^between two decimal integer literals, where the exponentiation operator**was likely intended.#11670
4969ee1Thanks @ematipico! - Fixed #7192:noUnusedPrivateClassMembersnow considers compound assignments such as??=to read and use private class members.#11676
840a52aThanks @dyc3! - Fixed #11672 and #11671 by disabling the experimental capitalized-call and effect-dependency checks inuseReactCompiler, matching their exclusion from upstream's recommended lint preset. Valid calls such asIntl.NumberFormat()and captures of variables declared inside effects no longer produce these diagnostics.#11660
49485edThanks @ematipico! - Fixed #11653: Astro template suppression comments ({/* biome-ignore lint: reason */}) now suppress matching HTML lint diagnostics on the following line when full HTML support is enabled.#11664
9a73b9cThanks @dyc3! - Improved the performance ofuseRegexpExec.#11661
5341b3fThanks @ematipico! - Fixed #7479.noUnusedVariablesnow treats Unicode escapes in identifiers as the same binding as their decoded spelling.#11630
62e1fc5Thanks @dyc3! - Fixed the HTML formatter inserting whitespace between adjacent Svelte expressions when their combined length exceeds the line width.#11658
ed4bfa4Thanks @fredrikblau! - Fixed #11644:useHeadingContentno longer reports headings that render their text with a directive:set:htmlandset:textin Astro files,v-htmlandv-textin Vue files.#11613
47d7383Thanks @ematipico! - Improved the performance of Biome Formatter up to ~50% in some cases.#11655
fd8fc74Thanks @ematipico! - Fixed #6974, wherenoUnusedPrivateClassMembersincorrectly reported TypeScript private constructor properties read through object destructuring fromthisas unused.#11618
21a10cfThanks @siketyan! - Fixed #11605: Type inference now infers the type of an unannotated callback parameter from the signature of the function the callback is passed to, and honours explicit type arguments on call expressions. This improves type-aware analysis fornoBaseToString,noFloatingPromises,noMisleadingReturnType,noMisusedPromises,noUnnecessaryConditions,noUnsafePlusOperands,noUselessTypeConversion,useArrayFind,useArraySortCompare,useAwaitThenable,useDisposables,useExhaustiveSwitchCases,useIncludes,useNullishCoalescing,useRegexpExec, anduseStringStartsEndsWith. For example,noFloatingPromisescan now detect Promises reached through such parameters:#11698
b019982Thanks @denbezrukov! - Fixed parsing of unquoted CSS URLs beginning with@or!, such asurl(@/assets/icon.svg)andurl(!font.woff2). Preserved escaped and non-ASCII whitespace in raw URLs during formatting.#11622
c23e4c7Thanks @Netail! - Added the nursery rulenoUnsafeIframeSandbox, which reportsiframeelements whosesandboxattribute combinesallow-scriptsandallow-same-origin, since that combination lets the embedded document remove its own sandboxing.#11606
de0528fThanks @dyc3! - Added the recommendednoSvelteAtHtmlTagsnursery rule, which reports Svelte{@html}tags that render unescaped HTML.#11670
4969ee1Thanks @ematipico! - Fixed #6782: GritQL plugins now match captured JSX component names against code snippets such asReact.Fragment.#11687
09d97d9Thanks @hori-design! - Fixed #11678:useReactCompilerno longer panics on files that contain non-ASCII characters. This bumps the React Compiler version.#11595
a64d757Thanks @dyc3! - Added the nursery Vue-domain ruleuseVueBaseImportrule, which enforces importing Vue APIs fromvueinstead of internal@vue/*packages.#11675
353cbaeThanks @dyc3! - FixeduseReactCompilersilently producing no diagnostics in WebAssembly builds, including the playground.#11625
ea20e5aThanks @denbezrukov! - Improved linting performance for large CSS and JSON files.#11670
4969ee1Thanks @ematipico! - Fixed #7527: suppression actions for diagnostics emitted on comments are now inserted before the diagnostic comment. In particular, suppressingnoTsIgnorenow places thebiome-ignorecomment before@ts-ignore.#11655
fd8fc74Thanks @ematipico! - Fixed #8629, wherenoUnusedPrivateClassMembersincorrectly reported used private TypeScript method overload signatures as unused.#11669
579f401Thanks @denbezrukov! - Improved the performance ofnoExcessiveLinesPerFilewhenskipBlankLinesisfalse.v2.5.12Compare Source
Patch Changes
#11440
b88f1eaThanks @Princesseuh! - Fixed Astro attribute expressions rejecting TypeScript and JSX syntax that is accepted in text expressions.#11440
b88f1eaThanks @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
b88f1eaThanks @Princesseuh! - Fixed a bare>in the children of an Astro expression being treated as markup, such as{x && <div>a > b</div>}.#11440
b88f1eaThanks @Princesseuh! - Fixed HTML comments inside an Astro expression failing to parse. They are now read as trivia, wherever they appear among the children.#11440
b88f1eaThanks @Princesseuh! - Fixedis:rawchildren inside an Astro expression being read as JSX, such as{x && <div is:raw>{not js} < & text</div>}.#11440
b88f1eaThanks @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
b88f1eaThanks @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.#11440
b88f1eaThanks @Princesseuh! - Added support for template literal attribute values inside an Astro expression, such as{x && <C data-x=`t${x}` />}.#11440
b88f1eaThanks @Princesseuh! - Fixed unquoted attribute values being rejected inside an Astro expression, such as{x && <a class=foo maxlength=255 href=/about>go</a>}.#11440
b88f1eaThanks @Princesseuh! - Fixed a template literal nested inside${}breaking the rest of an Astro file, such asconst href = `/blog${page === 0 ? '' : `/${page + 1}`}`;.#11440
b88f1eaThanks @Princesseuh! - Fixed a quote inside a regex character class breaking the rest of an Astro file, such asconst unsafe = /[/"]/;.#11508
54f3a2eThanks @dyc3! - Added the nursery ruleuseFlatMathMinMax. BecauseMath.min()andMath.max()accept any number of arguments, the rule reports unnecessary nested calls to the same method:The fix flattens this expression to
Math.max(a, b, c).#11585
c5c8315Thanks @Netail! - Fixed #11475:noUnresolvedImportsno longer reports Bun runtime built-in modules (bun,bun:bundle,bun:ffi,bun:jsc,bun:sqlite,bun:test).#11368
52a57b3Thanks @Austin1serb! - Fixed #6830: Biome now reports a diagnostic for excessively deep syntax instead of overflowing the native stack while releasing the parsed tree.#11596
1fc42edThanks @dyc3! - Added the nursery rulenoThisOutsideOfClass. The rule reportsthisoutside class members and TypeScript functions with an explicitthisparameter.#11555
2516335Thanks @dyc3! - Fixed #11529, wherenoFloatingPromisesmissed 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
0fee70cThanks @HarperZ9! - Fixed #11500: the formatter now prints thedeclaremodifier before accessibility modifiers on class properties.private declare readonly name: stringis now formatted asdeclare private readonly name: string, matching Prettier and TypeScript's canonical modifier order.#11580
1277af2Thanks @ematipico! - Fixed #5091: Biome no longer moves comments next to the<of a generic, which causes invalid TypeScript syntax:#11577
42995d2Thanks @ematipico! - Fixed #4592. Biome no longer crashes while parsing malformeddeleteexpressions.#11590
67963b4Thanks @ematipico! - Fixed #6427 so Grit plugins can usefunction = ...as a node argument.#11600
a689cb5Thanks @ematipico! - Fixed #6644:noUnusedVariablesnow recognizes all interface declarations in a TypeScript declaration-merging group when the interface is referenced.The following snippet no longer triggers the rule.
#11591
d4a0716Thanks @ematipico! - Fixed #6615.noDuplicatePropertiesno longer reports declarations nested in block at-rules as duplicates of declarations in their parent block.#11492
f2a07aaThanks @santichausis! - Fixed #11454:noMisplacedAssertionnow recognises@fast-check/vitest'stest.prop(...)(and.concurrent.prop,.skip.prop, etc.) as a test function, the same way it already recognisestest.each. The JS formatter picks up the same recognition, so a curriedtest.prop(...)(...)call is now formatted with the regular breakable argument layout used fortest.each/test.for, instead of the single-line-hugging layout used for plainit/testcalls.For example, Biome no longer reports the assertion below as misplaced:
#11589
65742b3Thanks @ematipico! - Fixed #4928:noUnusedVariablesno longer reports a value declaration as unused when its merged namespace is referenced.#11559
472dbc2Thanks @levrik! - Fixed a false positive innoVueDuplicateKeyswhere a<script setup>variable initialized frompropswas reported as a duplicate of the prop it derives from. Biome now exempts any variable whose initializer referencesprops, instead of only recognizingdefineProps()andtoRefs(props).For example, Biome no longer reports
foobelow as a duplicate key:#11594
6586cebThanks @ematipico! - Fixed #6640. Biome no longer crashes when linting malformedfor...ofstatements.#11571
85b197dThanks @ematipico! - Fixed #10838:useSortedAttributesno longer corrupts JSX attributes when nested JSX elements also require sorting.#11533
97e76c0Thanks @ematipico! - Fixed #11520, where the Biome scanner would start analysing dependencies multiple times, leading to long and unresponsive sessions.#11564
18a0e1fThanks @Netail! - Fixed the diagnostic range ofnoInferrableTypesso it now highlights only the type instead of including the leading:colon, spaces and comments.#11540
124fdaaThanks @ematipico! - Fixed#11537:noShorthandPropertyOverridesnow compares declarations only within the same block. The rule no longer reports@supportsfeature queries and correctly checks nested,@keyframes, and@pageblocks.#11532
7ceb0eeThanks @dyc3! - Fixed #11528:noFloatingPromisesno longer reports statement-levelawaitexpressions 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 byawait.#11474
3c6412eThanks @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
6c7fd27Thanks @dyc3! - Added the nursery rulenoVueDeprecatedScopedSlots. It reports deprecated$scopedSlotsreferences in Vue templates and component objects, and offers an unsafe replacement with$slots. For example, Biome now reportsthis.$scopedSlots.defaultinside a Vue component.#11440
b88f1eaThanks @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
f4e5ebbThanks @dyc3! - Added the nursery ruleuseModernMathApis. The rule reports legacy mathematical patterns that have direct modernMathequivalents.#11597
a20f44aThanks @Netail! - Added the nursery rulenoBunModules, which forbids the use of Bun builtin modules (e.g.bun:sqlite,bun:ffi).#11545
7d54688Thanks @dyc3! - Fixed #11542: Biome now reports HTML comments between Svelte tag attributes as parse errors.#11582
b6611ddThanks @ematipico! - Fixed #3862. Biome now parses legacy Internet Explorerfilterand-ms-filtervalues such asprogid:DXImageTransform...andalpha(opacity=40).#11575
65da251Thanks @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
0f78499Thanks @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
0fca643Thanks @ematipico! - Fixed #11512, wherestyle/noDescendingSpecificitymissed lower-specificity selectors after a later higher-specificity selector with the same tail selector.#11544
040f867Thanks @dyc3! - Fixed #11541: formatting a Svelte render tag followed by an HTML comment no longer duplicates the comment.#11565
ee69e0eThanks @ematipico! - Fixed #11525. Now the configuration schema correctly provides auto-completion for linter domains.#11583
b19390cThanks @dyc3! - Fixed #11352:useExplicitLengthCheckno longer reportslength-like properties used as value-producing||fallbacks or optional chains, and it no longer offers fixes for value-producing&&checks or unsafe negations.#11562
753e955Thanks @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
7d3ee9cThanks @dyc3! - Fixed handling ofbiome-ignore formatsuppression comments on TypeScript declared class properties with string literal names.#11497
f5d7896Thanks @dyc3! - Added thenoInvalidFileInputAcceptnursery rule. The rule reports invalid literalacceptvalues on file inputs in JSX and HTML, and normalizes common mistakes.#11345
ac58958Thanks @jakeleventhal! - Improved type inference performance by avoiding resolution of unused members in object arguments.#11554
2d55931Thanks @Netail! - Added the new nursery ruleuseReactNamingConvention, which enforces naming conventions for React values assigned fromcreateContext,useId, anduseRef. A value fromcreateContextmust be a PascalCase component name ending withContext, a value fromuseIdmust be namedidor end withId, and a value fromuseRefmust be namedrefor end withRef.#11491
1d6210bThanks @dyc3! - Added the nursery rulenoUnmodifiedLoopCondition, which reports variables in loop conditions that are never modified in the loop.v2.5.11Compare Source
Patch Changes
#11499
9743d0cThanks @scs0209! - Fixed #11496:useValidAnchornow treats Astro JSX shorthand attributes like<a {href}>as a validhref.#11437
88f805eThanks @Princesseuh! - Fixed #9944: adjacent elements inside an Astro expression now parse as an implicit fragment instead of raising an error.#11437
88f805eThanks @Princesseuh! - Fixed Astro templates rejecting unclosed HTML void elements, such as{cond && <br>}.#11507
e2fc036Thanks @dyc3! - Fixed #11157:noUnusedVariablesno longer reports Vue<script setup>bindings used by CSSv-bind()as unused.#11398
afc4615Thanks @dyc3! - Fixed #11389: Files passed through--stdin-file-pathnow use full HTML support for Astro, Svelte, and Vue when it is enabled.#11526
372cd68Thanks @dyc3! - FixednoVueRefAsOperandto track Vue refs through declaration aliases andtoRefs()properties, and to recognizeuseTemplateRef()results. The rule no longer reports false positives such as plain ref transfers, plaintoRefs()property access,defineModel()modifiers, or the supported.effectmember as operands.The refactor enabling these fixes also improves the performance of the rule.
#11458
a7cd286Thanks @dyc3! - Fixed #11436: GritQL snippets such asexport { $specifiers } from $sourcenow match named re-exports with aliases, inlinetypemodifiers, and multiple specifiers.#11515
382b15dThanks @dyc3! - Fixed #11390, wherenoFloatingPromisesperformed 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
6f40e82Thanks @levrik! - FixednoVueRefAsOperandso it no longer reports a callback parameter (e.g. from.find(),.map()) as an unwrapped ref value just because it's nested inside aref(),computed(), or similar call.Previously,
itemhere was incorrectly treated as a ref value because the rule attributed it to the outercomputed()call.#11495
496268dThanks @Netail! - FixeduseGraphqlNamingConventionso it no longer reports GraphQL enum value definitions with comments & descriptions and now displays a more accurate diagnostic range.#11407
6ef52b0Thanks @1678092075! - Fixed #11214:noUnusedVariablesno longer reports type parameters declared by non-default function overload signatures that have an implementation.#11322
5c353e6Thanks @jp-knj! - Added a new nursery rulenoAstroSetHtmlDirective, which disallows Astro'sset:htmldirective because untrusted content can introduce cross-site scripting vulnerabilities.For example, the following snippet triggers the rule:
#11462
18883b7Thanks @dyc3! - Fixed #10776:useVueHyphenatedAttributesno longer reports lowercase attribute names containing punctuation, such aspt:header:data-test-idandsome_attr.#11476
3270ca4Thanks @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.#11191
3e5367fThanks @ematipico! - Added the nursery rulenoUndeclaredCustomProperties, which reports references to custom properties that are not defined in available CSS, static HTML-likestyleattributes, or JSX stringstyleattributes.For example, the following snippet triggers the rule:
#11435
7754894Thanks @levrik! - Fixed: Variables and imports used as custom Vue directives are no longer reported as unused.For example:
#11501
e6acdedThanks @aminya! - Improved the performance ofuseArraySortCompareby skipping type inference for calls to unrelated methods.#11467
66b282cThanks @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
db9aa2aThanks @dyc3! - Fixed #10278: Marked the fix fornoThisInStaticas unsafe by default.#11502
652aedbThanks @levrik! -noGlobalAssignno 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:
v2.5.10Compare Source
Patch Changes
#11403
8f7786fThanks @Princesseuh! - Fixed Astro rejecting JavaScript comments between attributes.#11403
8f7786fThanks @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
3133ffaThanks @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.#11403
8f7786fThanks @Princesseuh! - Fixed #9165: an empty Astro expression such as<div>{}</div>no longer fails to parse. Astro renders{}as nothing.#11403
8f7786fThanks @Princesseuh! - Fixed Astro expressions containing a comment failing to parse.#11403
8f7786fThanks @Princesseuh! - Added support for Astro's fragment shorthand.#11403
8f7786fThanks @Princesseuh! - Fixed an Astro frontmatter block being cut short by a closing tag inside a string or comment.#11403
8f7786fThanks @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.#11403
8f7786fThanks @Princesseuh! - Fixed an Astro frontmatter block ending early on a line that merely starts with a dash.#11403
8f7786fThanks @Princesseuh! - Fixed the children of an Astro element carryingis:rawbeing 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.#11403
8f7786fThanks @Princesseuh! - Fixed Astro rejecting attribute names that start with a colon, such as:href.#11403
8f7786fThanks @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
8f7786fThanks @Princesseuh! - Fixed{inside an Astro<math>element opening an expression. MathML is foreign content where Astro parses no expressions, so LaTeX such asR^{2x}now survives as text.<svg>is unaffected.#11403
8f7786fThanks @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
8f7786fThanks @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.#11403
8f7786fThanks @Princesseuh! - Added support for template literal attribute values in Astro, such as<div class=`a ${b} c`>.#11403
8f7786fThanks @Princesseuh! - Fixed Astro rejecting HTML5 unquoted attribute values that contain`,=,'or", such as<a href=a=b>and<a href=a'b>.#11393
dec5a8fThanks @1678092075! - Fixed #11207:useStrictModeno longer reports Vue event handlers such as@click="count++".#11431
c065f99Thanks @levrik! - Fixed #11429: Variables and imports used by Vue same-name bindings such as:disabledorv-bind:disabledare no longer reported as unused.#11409
405dedbThanks @ematipico! - Fixed a memory leak in the LSP server where memory usage kept growing over long editor sessions.#11422
a51eff7Thanks @dyc3! - Fixed #11416: Biome no longer crashes when parsing incomplete{let}or{const}declarations in Svelte files.#11378
34b715cThanks @Netail! - Added extra rule sources from@eslint/css.biome migrate eslintdetects rules in your eslint configurations more reliably.#11403
8f7786fThanks @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
8d45229Thanks @ematipico! - Fixed #11390:noFloatingPromisesno longer performs unnecessary type inference on call arguments when checking methods of non-generic class instances created withnew.#11425
9c2667bThanks @dyc3! - Fixed #6426: GritQL plugins now match and rewrite metavariables embedded in quoted strings.#11441
00317c3Thanks @dyc3! - Improved performance ofuseNamedCaptureGroup,noMisplacedAssertion,noSkippedTests,noExportsInTest,noDuplicateTestHooks,noIdenticalTestTitle,useTestHooksInOrder, anduseTestHooksOnTop.v2.5.9Compare Source
Patch Changes
#11321
41386f3Thanks @dyc3! - Fixed #11315: The CSS parser now recovers at declaration boundaries after bogus declarations, allowing subsequent valid declarations to be parsed.#11248
57b197eThanks @yanthomasdev! - Expanded the environment variable metadata used bybiome rageto includeBIOME_BINARY,BIOME_LOG_FILE, andRUST_BACKTRACEas well as reworded explanations for better readability.#11377
a8798eaThanks @Netail! - Added a new nursery ruleuseNamedLayerwhich disallows anonymous cascade layers.#11327
6771cf5Thanks @dyc3! - The HTML formatter now preserves meaningful blank lines in HTML, including spacing after elements with trailing spaces and blank lines between comment groups.#10312
ba8aa18Thanks @dyc3! - Added the nursery ruleuseTailwindShorthandClasses, which suggests shorter Tailwind utility classes. For example, the rule suggests replacingw-4 h-4withsize-4.#11333
715e0cdThanks @kkkhs! - Fixed #11328:lint/nursery/useExpectnow recognizes Vitest Browser Modeexpect.element()calls as assertions.#11343
9b98211Thanks @johncarmack1984! - Fixed #11311: the CSS parser now accepts Tailwind container-query variant names in@variant, such as@xland@max-xl. These previously produced a parse error and anoUnknownAtRulesdiagnostic.#11220
3e8c488Thanks @santichausis! - Fixed #9541:noUndeclaredVariables,noUnusedImports, andnoUnusedVariablesnow 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
greetas undeclared in the following Svelte component:#11300
36430ebThanks @dyc3! - Fixed the HTML formatter's whitespace handling formarquee,noscript,video,audio, andobjectelements.#11299
6559e6cThanks @jp-knj! - Added the nursery ruleuseAstroClientOnlyDirectiveValue, which reports Astroclient:onlydirectives without an initializer.For example,
<Component client:only />triggers the rule.#11365
7529811Thanks @MHJahanbakhsh! - Fixed #11229: TheuseGenericFontNamesrule now treatsmathas a valid generic font family.#11346
674f5f4Thanks @Jayllyz! - Fixed #11335:noComponentHookFactoriesnow reports ause-prefixed variable only when a function is assigned to it directly.#11334
c87c46aThanks @zkasuran! - Fixed #11317:noSvgWithoutTitleno longer reports ansvgthat uses the boolean shorthandaria-hidden(equivalent toaria-hidden={true}in React).#11364
13853b1Thanks @ematipico! - Fixed a bug whereuseJsxKeyInIterableincorrectly flagged Astro files.#11321
41386f3Thanks @dyc3! - Fixed #11315: Invalid CSS declarations in HTMLstyleattributes now produce parser diagnostics instead of causing a panic.#11325
67c3bf0Thanks @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.#11367
fe5b5d4Thanks @ematipico! - Fixed TypeScriptcompilerOptions.pathsresolution when mapping targets omit./. Biome now resolves these targets relative to their configured path base.#11316
17e48d6Thanks @wanxiankai! - Fixed #11289: the safe fix fornoExtraBooleanCastnow preserves parentheses around nested conditional expressions.#11254
d25d113Thanks @dyc3! - Fixed #11242: Biome no longer crashes with an access violation when analysing files on Windows ARM64.#11221
85aac73Thanks @freeatnet! - Added the nursery rulenoUnsafeTypeAssertion, which disallows TypeScript type assertions while allowing const assertions.#11314
7ffb677Thanks @ematipico! - Fixed #11310: Restored the performance ofnoMisusedPromisesandnoFloatingPromiseswhen analyzed expressions share deep imported type paths.#11356
6cd3263Thanks @johncarmack1984! - The Tailwind parser now understands modifiers on bare utilities (@container/sidebar,shadow/50).#11318
76059e9Thanks @johncarmack1984! - The Tailwind parser now understands container-query variants (@sm:,@max-lg:,@min-[400px]:) and child and descendant variants (*:,**:).#11357
faa2074Thanks @johncarmack1984! - The Tailwind parser now accepts the legacy leading!important marker (!flex,hover:!p-4).#11344
f34e15cThanks @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
2f5d452Thanks @dyc3! - Fixed HTML formatting that inserted rendered whitespace between an element and touching text when the line wrapped.#11312
e65f07eThanks @xosnos! - Added a new nursery ruleuseControlLabelfor both HTML and JSX, which reports interactive control elements (button,menuitem) without an accessible label.#11364
13853b1Thanks @ematipico! - Fixed SVG parsing for files with an XML declaration followed by aPUBLICdoctype, such as<?xml version="1.0"?><!DOCTYPE svg PUBLIC "a" "b">.#11301
610ee28Thanks @dyc3! - Fixed parent tag wrapping when an HTML element starts or ends with a block-like or hidden child such assource,track, orparam.v2.5.8Compare Source
Patch Changes
#10710
0a0fbc1Thanks @dyc3! - Added a new nursery ruleuseReactCompiler, which reports diagnostics from React Compiler lint mode.#11251
ea9dd8aThanks @dyc3! - Improved performance ofnoImportCycles.#11247
52b44d6Thanks @dyc3! - Added the nursery rulenoSvelteLegacyConst, which disallows legacy Svelte{@const}tags and recommends declaration tags with$derived().Invalid:
Valid:
#11252
d5f5704Thanks @Turtle-Hwan! - Fixed #11250:useAwaitno longer reports async functions that contain anawait usingdeclaration.#11143
6be7be1Thanks @vznh! - Fixed #11017:noUselessUndefinedno longer reportsreturn undefinedwhen the enclosing function has a return type annotation other thanundefinedorvoid.#11234
caefe39Thanks @subotac! - Fixed #11228: CSS block comments between a declaration colon and value now preserve their source indentation.#11285
bca1f73Thanks @denbezrukov! - Fixed #11280: CSS formatting keeps comments inside functional pseudo-classes and pseudo-elements instead of moving them before the function name.#11080
af16a0bThanks @dyc3! - HTMLstyleattribute values are now parsed as CSS. All Biome CSS lint rules are applied to thestyleattributes.#11195
6a85588Thanks @dyc3! - Fixed Svelte files failing to parse when an expression begins with an object literal.Now the following snippet is correctly parsed:
#11173
481d008Thanks @Austin1serb! - Fixed #10242: JavaScript GritQL patterns with multiple metavariables now match snippets consistently in WebAssembly.#11187
23c0369Thanks @ematipico! - Added the nursery rulenoInvalidPropertyInitValue, which reports an@propertywhoseinitial-valuedoes not match itssyntaxdescriptor. For example, the following declaration triggers the rule becauseredis not a<length>:#11272
73896e6Thanks @ematipico! - Improved the diagnostic emitted bynoRootType.#11240
bd0b68dThanks @ematipico! - Fixed #11223: Improved theperformance of
noMisusedPromiseswhen analyzing async class methods that call other methods through
this.#11172
4a0bc5cThanks @saberoueslati! - Fixed #10806:noUselessFragmentsno longer causes Biome to panic when its unsafe fix removes a fragment used as a JSX attribute value.#11227
4d603b0Thanks @saberoueslati! - Fixed #11178:noUndeclaredVariablesno longer reports Vue's built-in instance properties, such as$slotsand$attrs, in template expressions or$eventin inline event-handler expressions. The instance properties are still reported inside<script setup>, where they are not defined.#11187
23c0369Thanks @ematipico! - Fixed CSS parsing of registered custom properties: Biome now correctly validates thesyntaxdescriptor of@propertyrules.v2.5.7Compare Source
Patch Changes
#10822
c171b3bThanks @pkallos! - Added the optionignoreIfStatementsto useNullishCoalescing. Biome now flagsifstatements that only assign to a nullish variable (such asif (!a) { a = b }) and can rewrite them to??=. When enabled, Biome ignores thoseifstatements.#11136
e63354cThanks @AkashNaickar! - Added a new nursery rulenoExtendNative, which reports extending the prototype of a built-in object.#10094
e007143Thanks @THEjacob1000! - Added the nursery rulenoTailwindArbitraryValue. Biome now reports Tailwind CSS arbitrary values such asw-[400px], including in HTML/JSX class attributes, configured utility functions, and tagged templates.#11184
135f476Thanks @subotac! - Fixed #11176:noUnknownPseudoClassnow recognizes Vue's:deep()pseudo-class inside.vuestyle blocks.#8239
a519f9dThanks @cormacrelf! - Fixed #8233, where Biome CLI instdin 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 insubdirectory/biome.json:Now, the nested configuration is correctly picked up and applied.
In addition, Biome now shows a warning if
--stdin-file-pathis provided butthat path is ignored and therefore not formatted or fixed.
#11138
8c2c6bdThanks @ematipico! - FixednoUnnecessaryConditions: 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
() => voidaccepts anasynccallback andscheduletherefore returnsstring:The following code is also now invalid, because
map(() => 42)returns42:#11138
8c2c6bdThanks @ematipico! - Fixed #11087:noUnnecessaryConditionsno 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:
#11118
9c16840Thanks @subotac! - Fixed #11098: The HTML formatter now preserves the configured trailing newline when a file ends with a comment.#11201
0e80610Thanks @Bishwas-py! - Fixed #11182: suppression comments fornoPositiveTabindexnow suppress the rule in HTML files when the attributes of the element span multiple lines.#11079
607afd2Thanks @dyc3! - The HTML formatter now lays out thesrcsetattribute 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:#11156
fed72c7Thanks @saberoueslati! - Fixed #11129:noUnusedVariablesno longer reports Vue bindings as unused when they are assigned through automatically unwrapped template refs.#11124
d890b39Thanks @denbezrukov! - Fixed CSS formatting of line comments between a declaration colon and value to preserve their source indentation.#11113
3d8ab73Thanks @denbezrukov! - Fixed CSS formatting of long block comments between comma-separated property values:#11127
da5c1a5Thanks @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.'and"count as the characters they stand for, and only the character that ends up as the delimiter stays escaped:Entities that are not quotes, such as
&or&[#​39](https://github.com/biomejs/biome/issues/39);, are left exactly as written.#11193
77035bbThanks @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:#11106
ad80f57Thanks @dyc3! - The HTML formatter now writes the HTML5 doctype in lowercase, matching Prettier:This only applies to a plain
.htmlfile 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:A
.vue,.svelte, or.astrofile keeps whatever the author wrote.#11188
60679dbThanks @dyc3! - Fixed the HTML formatter printing a comment twice when it ended the line of the last element in a document:#11077
4dcd0d9Thanks @dyc3! - Fixed a bug where the HTML formatter collapsed the whitespace inside<textarea>,<xmp>and<plaintext>, changing what the page renders.Biome now prints the content of these elements exactly as it appears in the source, matching the existing behavior for
<pre>.#11194
abfbb11Thanks @dyc3! - Fixed the HTML formatter refusing to format a Svelte file containing an array pattern that skips a position:#10094
e007143Thanks @THEjacob1000! - FixeduseSortedClassesto 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
10da30eThanks @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: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
4afd901Thanks @ematipico! - Improved the performance of the following lint rules:noArguments.noGlobalAssign.noUndeclaredVariables.noRestrictedGlobals.noInvalidUseBeforeDeclaration.noShadow.noRedeclare.#11134
2fa0a62Thanks @yanthomasdev! - Clarified the warning emitted when using the experimentaljsonandjson-prettyreporters.#11198
ed88b13Thanks @saberoueslati! - Fixed #11171: variables referenced only inside a Svelte attachment ({@attach ...}) are no longer reported as unused bynoUnusedVariablesandnoUnusedImports.#11155
6ee17eaThanks @dyc3! - Improved performance when printing diagnostics to the console.#11160
217f8adThanks @dyc3! - Improved the performance ofnoFloatingPromisesby skipping type inference for assignment statements, which are always considered handled.#11159
26c23d9Thanks @saberoueslati! - Fixed #11144:noFloatingPromisesno longer reports already-awaited optional Promise values.#11138
8c2c6bdThanks @ematipico! - Fixed #11121:noUnnecessaryConditionsno 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 returnsboolean:#11152
c4fc6a9Thanks @dyc3! - Improved the performance of collecting rule timings with--profile-rulesin heavily multithreaded environments.#11128
4d3ff76Thanks @ematipico! - Fixed #7635:noDeprecatedImportsnow detects deprecated ambient declarations that are exported separately.#11117
01f7ef5Thanks @subotac! - Fixed #11014:noDeleteno longer reportsprocess.env["FOO"]style property deletions.#11168
9847e68Thanks @saberoueslati! - Added the nursery rulenoNonScalableViewport, which reports viewport metadata that disables user scaling withuser-scalable=no.For example:
#11154
a1d6b1fThanks @dyc3! - Improved the performance ofnoImportCyclesby skipping graph traversals for imports that cannot be part of a cycle.#11175
d96d6ddThanks @ematipico! - Fixed CSS parsing of registered custom properties: Biome now correctly validates thesyntaxdescriptor of@propertyrules.v2.5.6Compare Source
Patch Changes
#11035
0e4b03bThanks @ematipico! - Fixed a performance regression innoMisusedPromisesthat caused type inference to run repeatedly while linting a file.#11043
22ec076Thanks @denbezrukov! - Fixed CSS formatting for multiline function arguments preceded by comments:#11007
c9acb25Thanks @BTF-Kabir-2020! - Fixed #9195:useHookAtTopLevelno longer reports hooks in namedforwardRefcomponents that receive arefparameter.#10152
50a9bd8Thanks @Zelys-DFKH! - 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.#11105
8ffe2b9Thanks @dadavidtseng! - Fixed #11092: ThenoUselessTernaryquick fix now preserves operator spacing when simplifying or inverting boolean ternary expressions.#10533
5809875Thanks @Mokto! - Fixed #10515:biome check --writewas 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.#11040
0abb620Thanks @Mokto! - Fixed an issue where the HTML formatter would duplicate a comment placed directly before a Svelte{@const ...}or{@debug ...}block. The duplication compounded on every subsequent--write, causing the file to grow exponentially.#10858
6d18204Thanks @ruidosujeira! - Fixed #10839: Svelte{#each}array destructuring no longer includes spaces inside square brackets, and multiline bind function expressions now indent their getter, setter, and function body correctly.#11009
2c36626Thanks @ematipico! - Improved the accuracy of type-aware lint rules by resolving more inferred types. For example,noFloatingPromisesnow detects floating Promises returned by aliased callbacks and arrays of Promises created by async mapping callbacks.The following statements are now reported:
#10973
9cb044cThanks @ematipico! - Fixed false positives innoMisleadingReturnTypewhen generic-constraint, normalization, substitution, or structural return-type comparison cannot complete. The rule now suppresses diagnostics rather than suggesting a return type derived from partial information. For example, this unresolved return type is no longer reported:#11071
15047a2Thanks @dyc3! - The HTML parser now accepts mixed-casedoctypedeclarations.#11030
cc90e65Thanks @marschattha! - Therdjsonreporter now populates the severity field of each diagnostic (ERROR,WARNING, orINFO), so tools consuming Reviewdog Diagnostic Format output no longer need to assume a default severity.#11009
2c36626Thanks @ematipico! - Fixed a performance regression in type-aware JavaScript lint rules by inferring only requested types and memoizing export resolution.#11056
903b177Thanks @dyc3! - Added support for Svelte declaration tags usingletandconst. Biome can now parse, format, and lint bindings declared in these tags.#11045
89c27c6Thanks @ematipico! - Improved the performance of Biome formatter up to ~7% across the board.#9806
781d68dThanks @dyc3! - Added the nursery rulenoJsRestrictedProperties, which ports ESLint'sno-restricted-propertiesrule. Biome now flags restricted member access and object destructuring, andbiome migrate eslintpreserves the rule's options.v2.5.5Compare Source
Patch Changes
#10972
ab8c21bThanks @ematipico! - FixeduseExhaustiveSwitchCasesfor unions of bigint literals. The rule now reports missing bigint cases and compares bigint literals by value, including binary, octal, hexadecimal, and separator-containing spellings. For example, this switch now reports the missing2ncase:#10972
ab8c21bThanks @ematipico! - Fixed false positives innoBaseToStringanduseNullishCoalescingwhen member, stringification, or nullish inference cannot complete. These rules now suppress diagnostics instead of reporting from partial type information. For example, neither expression is reported when a recursive type cannot be fully resolved:#10977
0bf7486Thanks @ematipico! - Fixed #10922: the actionuseSortedAttributesno longer triggers for HTML instructions.#10957
cf263c4Thanks @dyc3! - FixednoThenPropertyfailing to detectObject.fromEntries,Object.defineProperty, andReflect.definePropertycalls with comments between their tokens.#10983
edc0ed7Thanks @ayaangazali! - Fixed #10980:useAriaPropsSupportedByRoleno longer reports false positives when the attribute that determines an element's implicit ARIA role is written as a shorthand attribute, such as<a {href} aria-label="...">in Astro and Svelte files.Shorthand attributes are now taken into account when computing the implicit role, so the anchor above correctly resolves to the
linkrole instead ofgeneric.#10889
89526e3Thanks @denbezrukov! - Fixed CSS formatter casing for syntax-owned names while preserving author-defined names, including scoped keyframes and container scroll-state queries.#10964
794ccd0Thanks @denbezrukov! - Fixed CSS formatting for comments between declaration values and!important.#10993
b7a9694Thanks @denbezrukov! - Fixed the CSS formatter to preserve comments on the correct side of selector combinators and before declaration blocks.It now also keeps selectors with escaped newlines in attribute values inline when they fit.
#10978
8ebafe1Thanks @ematipico! - Fixed #10870:noUnresolvedImportsno longer reports false positives such asimport type { NextRequest } from "next/server".#10901
68c10e6Thanks @Socialpranker! - Fixed #10622: the HTML/Vue parser no longer panics on the argument-lessv-bindshorthand (:="props").This syntax is valid Vue and equivalent to
v-bind="props", so the parser now accepts it (along with the longhandv-bind:="props") instead of crashing while building a diagnostic for a missing argument.#10936
7df46f5Thanks @ematipico! - Improved generic tuple inference foruseIncludes. The rule now recognizes specialised tuple element types returned through generic aliases.#10941
f787725Thanks @siketyan! - Fixed#10855: Biome now supports parsing and formatting CSS custom media queries declared with@custom-media.#10969
72d309bThanks @ematipico! - Fixed an issue where Biome logs became too verbose, dumping information not relevant to user's operations.e62f6b6Thanks @ematipico! - Fixed #10963: Biome no longer panics when a type-aware rule such asnoFloatingPromiseschecks a call to a function with multiple call signatures imported from another module.#10931
899c60dThanks @ematipico! - Fixedcheck --writecommand. Now the command reports code frame of the formatted code, if the formatter is enabled.#10904
ceee4f4Thanks @qzwxsaedc! - Fixed #10892:noUnnecessaryConditionsno longer reports a false positive when checking a member of a discriminated union that is accessed through a default type-only namespace import. The following code is no longer flagged:#10962
f0a67f2Thanks @ematipico! - Biome no longer removes embedded styles and scripts in HTML files.#11000
5039a1eThanks @ematipico! - Fixed a bug where closing one editor stopped a shared Biome daemon used by other editors. LSP proxy processes now exit when either the editor or daemon disconnects.#10957
cf263c4Thanks @dyc3! - Improved the performance of thenoThenPropertylint rule by about 50%.#10992
4bf9b21Thanks @ematipico! - FixednoMisusedPromises: The rule now reports Promise-returning callbacks where a synchronous callback is expected when calls use tuple spreads or tuple rest parameters, including generic and deeply nested tuples, and when constructor signatures come from interface or object types. Recursive or excessively nested tuple spreads use a conservative fallback so analysis terminates.For example, the following callback is now reported.
#10915
b3b12b3Thanks @Functionhx! - Added the rulenoNegationInEqualityCheck. The rule flags negated expressions on the left side of strict equality checks like!foo === bar— due to operator precedence this evaluates as(!foo) === barwhich is almost always a mistake forfoo !== bar.The rule provides an unsafe fix that flips the operator.
#10970
bd1038bThanks @ematipico! - Improved overload selection fornoMisusedPromises. Biome now handles overloaded calls, overloaded constructors, rest parameters, union arguments, and generic constraints without selecting an incompatible signature. For example,noMisusedPromisesnow reports the async callback passed to the synchronous overload:#10933
48a4abbThanks @ematipico! - FixeduseArrayFindto recognize bigint zero indexes.#10931
899c60dThanks @ematipico! - Fixed an orchestration issue that could lead to deadlocks when type-aware rules are enabled.#10969
72d309bThanks @ematipico! - Hardened the Biome Language Server by improving its synchronisation logic.#10972
ab8c21bThanks @ematipico! - Fixed false positives innoMisusedPromisesanduseAwaitThenablewhen Promise or thenable inference cannot complete. These rules now suppress diagnostics instead of treating incomplete type information as a definite result. For example,useAwaitThenableno longer reportsawait valuewhen the value's thenability is unknown:v2.5.4Compare Source
Patch Changes
#10665
55ff995Thanks @dyc3! - Improved the performance of the HTML parser slightly in our synthetic benchmarks.#10894
f4fb10eThanks @ematipico! - Fixed #6392: On-type formatting no longer moves comments before anifstatement into its body.#10939
f2799dbThanks @Netail! - Fixed #10930:noLabelWithoutControlnow correctly detects text interpolation in Astro, Svelte & Vue as valid accessible content.#10945
ae15d98Thanks @Netail! - Fixed #10942: Svelte directives don't throw an accidental debug log anymore.#10842
5e1abfeThanks @JamBalaya56562! - Fixed #9196:biome check --write --unsafeno longer hangs forever when applying thenoCommentTextcode fix.The rule's fix now wraps the comment in a real JSX expression container (
{/* comment */}) instead of re-inserting the braces as plain JSX text, so the fixed code is no longer reported again by the same rule.#10891
ecca79eThanks @ematipico! - Fixed#10885: prevented a module-inference regression introduced by a housekeeping change.#10886
60c8043Thanks @dyc3! - Fixed #10727: Biome now breaks the arguments of curriedtest.each,it.each,describe.each, andtest.forcalls when they exceed the configured line width.#10895
01a85f0Thanks @ematipico! - Biome will now remove stale Unix daemon sockets from older Biome versions when starting a newer daemon.v2.5.3Compare Source
Patch Changes
#10815
86613d5Thanks @WaterWhisperer! - Fixed a parser panic reported in #10708: Biome now recovers when unsupported CSS Modules@valuerules or scoped@keyframesnames end at EOF.#10534
da9b403Thanks @Mokto! - FixednoUnusedVariablesfalse positives in Svelte files: Svelte store subscriptions ($storereferences in templates now keep the underlyingstorebinding from being flagged), and$bindable()props that are only written to in the script block (write-only is intentional for bindable props) are no longer reported as unused.#10827
098ba41Thanks @Aqu1bp! - Fixed #10698: ThenoUnsafeOptionalChainingrule now reports unsafe optional chains wrapped in TypeScriptas,satisfies, type assertion, and instantiation expressions, such asnew (value?.constructor as Constructor)().#10773
3c6513dThanks @otkrickey! - Fixed #10772:useVueValidVOnno longer reports a missing handler for v-on directives using a verb modifier (.stop/.prevent) without an expression, e.g.<div @click.stop></div>. The rule also accepts the arg-less object syntax<div v-on="$listeners"></div>instead of reporting a missing event name.#10721
d83c66bThanks @minseong0324! - Improved type-aware lint rule inference for built-in globals and indexed function calls. Biome now resolvesError(...),new Error(...), optionalError#stack, and calls through indexed function values such ashandlers[0]()more accurately.#10865
6450276Thanks @ematipico! - Fixed #10845. Biome Language Server no longer goes in deadlock when the scanner is enabled.#10853
93d8e53Thanks @Netail! - Fixed #10840: Astro shorthand attribute syntax is now correctly being parsed from embedded nodes.#10820
bba3092Thanks @JamBalaya56562! - Fixed #10619:noProcessEnvnow also reports computed (bracket) member access. Previously only dot access was checked, soprocess["env"]andenv["NODE_ENV"](whereenvis imported fromnode:process) were missed. Both static and computed accesses are now reported.#10835
3447b2fThanks @dyc3! - Fixed #10824:useDomQuerySelectornow supports anignoreoption for receiver identifiers that should not be reported.#10875
b12e486Thanks @dyc3! - Fixed #10795:--profile-rulesnow reports timings for each plugin separately asplugin/<pluginName>, matching the naming used by plugin suppressions, instead of aggregating all plugins under a singleplugin/pluginentry.#10877
d6bc447Thanks @ematipico! - Fixed biome-zed#164: Biome no longer inserts stray whitespace when format-on-type runs after closing delimiters such as),], and}.#10867
a21463eThanks @dyc3! - Fixed #10864: Biome no longer crashes when checking or linting HTML files with unquoted attribute values such as<textarea rows=4></textarea>.cznic/sqlite (modernc.org/sqlite)
v1.60.1Compare Source
v1.60.0Compare Source
haydenbleasel/ultracite (ultracite)
v7.12.2Compare Source
Patch Changes
0f192a7:ultracite initno longer wipes an existing Biome config it can't read. Previously abiome.jsonorbiome.jsoncwith a syntax error, or a nested monorepo config with"extends": "//", was treated as empty and replaced with just the Ultraciteextends, losing every other setting. Now:init."extends": "//") is left unchanged, since the Ultracite presets belong in the root config.extendsis turned into a list that also includes the Ultracite presets.biome.jsoncare preserved.ultracite doctorand thecheck/fixresolution check now follow a nested config that extends"//"to the root config, instead of warning that the nested config doesn't extendultracite/biome/core.c68ba48:ultracite checkandultracite fixhandle their arguments and missing tools more reliably:checknow treats explicit files the wayfixdoes. Oxlint only gets files it can lint, Prettier runs with--ignore-unknown, and oxfmt with--no-error-on-unmatched-pattern. Before,ultracite check README.mdorultracite check Dockerfile src/index.tsfailed even though nothing was wrong.--tsconfig tsconfig.json,-c .oxlintrc.json,--config-path biome.json,--only lint/suspicious/noDebugger,--since origin/mainand similar. Before, the value was treated as a lint target, soultracite fix --tsconfig tsconfig.jsonskipped Oxlint entirely and only formattedtsconfig.json. Ultracite's own--claude,--codex,--hookand--unsafenever take a value, so the next argument is always a target.ultracite fix --unsafewith the ESLint toolchain no longer fails with ESLint's "Invalid option '--unsafe'". ESLint has no unsafe fixes, so the flag is dropped with a warning.ultracite doctoralready said, so a project without it now skips CSS linting with a warning instead of failing. "No linter configuration found" is also printed without a stack trace.node_modules/.binare found even when Ultracite isn't run through a package manager script,npxorbunx, for example./node_modules/.bin/ultracite check.f61f393:ultracite initnow looks for existing ESLint, Prettier and Stylelint configs in the same order the tools do, so when a project has more than one, init updates the one the tool actually loads. For example, Prettier reads.prettierrc.jsonbeforeprettier.config.mjs, and ESLint readseslint.config.jsbeforeeslint.config.mjs. Before, init could update a config the tool ignored and leave the active one in place. Stylelint's.stylelintrc.tsandstylelint.config.tsare now recognised too.242cd2a:ultracite initnow updates an existing.vscode/settings.jsonor.zed/settings.jsonin place, so your comments and formatting are kept. Before, the file was re-serialised as plain JSON, which stripped every comment. A settings file with a syntax error is now left unchanged with a warning. Before, it was rewritten with whatever part the parser could recover, which dropped the rest.For the ESLint toolchain, init now also installs the Prettier VS Code extension (
esbenp.prettier-vscode), since the settings it writes make Prettier the default formatter. Before, only the ESLint extension was installed, so format-on-save did nothing until you added Prettier yourself.c273393:ultracite initnow checks every flag value before it changes anything in the project. An unknown value for--linter,--pm,--frameworks,--editors,--agents,--hooks,--integrationsor--js-pluginsstops init with a message listing the valid values. Previously a misspelled--linter(for example--linter Biome) deleted every existing Biome, ESLint, Prettier, Stylelint, Oxlint and oxfmt config file and then crashed.When
--linteris not passed and init runs without prompts (because of--quiet,CI, or flags such as--agentsor--pm), it now keeps the linter the project is already set up with and only falls back to Oxlint when there is none. Runningultracite init --agents universalon a Biome project no longer migrates it to Oxlint. The interactive linter prompt also preselects the detected linter.3cb2e71: Clearer wording in the CLI:ultracite init --helpnow lists the valid values for--pm,--linter,--frameworks,--hooksand--integrations, and explains what--type-awaredoes for Biome and for Oxlint.initandupgradesay "Using pnpm (detected from the project)" instead of "Detected lockfile", since the package manager can also come frompackageManager.ultracite doctorspells "unrecognized" consistently, formats commands as code, and describes warnings as "Some checks have warnings" instead of "optional improvements".6ae8be8: The ESLintnestjspreset imports@darraghor/eslint-plugin-nestjs-typed, butultracite init --linter eslint --frameworks nestjsnever installed it, so ESLint failed to load the config with "Cannot find package".initnow installs the plugin with the preset, andultracite upgradeinstalls the plugins of every framework preset youreslint.config.*imports, so existing NestJS projects pick it up on their next upgrade.a8e83bb:ultracite initnow migrates an existing.oxlintrc.json,.oxfmtrc.jsonor.oxfmtrc.jsoncwhen it sets up Oxlint. Oxlint and oxfmt refuse to load any config when a JSON config sits next tooxlint.config.tsoroxfmt.config.ts, and init used to write the TS configs beside the JSON ones, soultracite checkandultracite fixstopped working. Init now moves the JSON config's settings into the TS config and deletes the JSON file:rules,overrides,env,pluginsand other options become properties of the generated config.ignorePatterns,settingsandjsPluginsare added to the ones Ultracite generates instead of replacing them.extendsentries become presets. Otherextendspaths can't be referenced from a TS config, so init names them in a warning.A JSON config that doesn't parse is left in place, and neither file is written.
Re-running
ultracite initalso keeps what you added tooxlint.config.tsandoxfmt.config.ts: customrules,overrides,ignorePatterns,settingsand other properties, extraextendsentries, your own imports and statements, and comments are carried over while the Ultracite parts are regenerated. Before, both files were regenerated from scratch. A config that doesn't parse is now left unchanged with a warning instead of being overwritten.ultracite doctornow fails when a JSON config and a TS config for Oxlint or oxfmt sit side by side, and suggests runninginitto migrate a lone.oxfmtrc.json.981ef2f:ultracite init --linter oxlintno longer adds"type": "module"topackage.json. That field changes how Node loads every.jsfile in the package, so CommonJS files such as anext.config.js,postcss.config.jsorjest.config.jsusingmodule.exportsstopped working after init.Instead, init writes the Oxlint and oxfmt configs as
oxlint.config.mtsandoxfmt.config.mtswhen the package isn't an ES module package (no"type"or"type": "commonjs"). A.mtsfile always loads as an ES module, with noMODULE_TYPELESS_PACKAGE_JSONwarning on every run, and it works under"type": "commonjs". ES module packages ("type": "module") still getoxlint.config.tsandoxfmt.config.ts.Re-running init updates an existing config under the name it already has. The one exception is a
.tsconfig in a"type": "commonjs"package, which Node can't load: init renames it to.mtsand says so.ultracite doctor, linter detection and the stale-config cleanup all recognise the.mtsnames.doctorfails a.tsconfig in a CommonJS package, and fails when a.tsand an.mtsconfig sit side by side.Requires oxfmt >= 0.59.0, the first release that finds
oxfmt.config.mtson its own.9a9beb0:ultracite initis gentler withpackage.json:devDependenciesto the top and pushed keys likeprivate,mainandexportsto the end.checkorfixscript the project already has (for example"check": "tsc --noEmit"). It prints a warning instead, and it only adds the scripts that are missing. Existing scripts that already run Ultracite with extra flags are left as they are.devDependencies. Packages independenciesandpeerDependenciesstay, such asprettierused at runtime oreslintas the peer of a published plugin.--skip-install, the final message now says the dependencies were added topackage.jsoninstead of claiming they were installed.c3027a5: Re-runningultracite initno longer silently discards changes to an ESLint, Prettier or Stylelint config that builds on Ultracite's:eslint.config.*: the framework presets it already spreads are kept alongside any newly selected ones, together with your own config objects, imports and adefineConfig(...)wrapper. Before, the file was regenerated from the selected frameworks only, dropping earlier presets and every custom rule.prettier.config.*: your options, imports and extra plugins are kept. Framework plugins are added, andprettier-plugin-tailwindcssstays last.ultracite/stylelint, including one inpackage.json, is left as it is.A config that doesn't use Ultracite's presets is still replaced, but init now prints a warning naming the file (or the
package.jsonkey) it replaced. A config that doesn't parse is left unchanged with a warning. With the ESLint toolchain, a"prettier"or"stylelint"key inpackage.jsonis now handled by these updates (and reported when replaced) instead of being deleted silently during migration.44d6997:ultracite initno longer creates directories for agent rule files or agent hook configs (such as.claude/or.cursor/) before checking that the path stays inside the project. When one of those directories was a symlink to somewhere outside the project, init created the missing directories there before it refused to write the file.33a1ccd:ultracite init --integrations husky --skip-installno longer replaces an existingpreparescript inpackage.json. It addshuskyto the script, as the installing path already did (for example"prepare": "svelte-kit sync && husky"). The Lefthook setup messages now name the config file init actually updates or creates, such as.lefthook.yaml, instead of always sayinglefthook.yml.923667b:ultracite initno longer overrides a tsconfig that turnsstrictNullChecksoff. It leaves an explicit"strictNullChecks": false, whether set in the file or in a config it extends, as it is and prints a warning. It also followsextends, whether a relative path or a package such as@tsconfig/strictest, so a tsconfig that already inheritsstrictorstrictNullChecksis no longer given a redundant"strictNullChecks": true.199974c:ultracite initandultracite upgradenow work in Yarn 2+ monorepos. The root install used to pass Yarn 1's-Wflag, which Yarn 2+ rejects with "Unsupported option name", whenever the Yarn version wasn't named inpackage.json'spackageManagerfield. That happened with--pm yarn, with lockfile-only detection, and always in the second half ofupgrade, which re-ran the new CLI with a bare--pm yarn. Ultracite now tells Yarn 2+ apart from Yarn 1 by.yarnrc.ymlor the lockfile format.upgradeonly forwards--pmto the newly installed CLI when you passed it.ultracite doctor(and the check at the end ofupgrade) no longer fails in Yarn Plug'n'Play projects just because it can't find Ultracite or the tools innode_modules. It warns that it can't verify them there instead.c33158d: Agent hooks set up byultracite init --hookswork in more places and no longer double up..github/hooks/ultracite.jsonis now written in the format the Copilot CLI and Copilot cloud agent read:"version": 1and a camelCasepostToolUseevent. VS Code reads this format too. The previous file, with a PascalCasePostToolUseand noversion, was only picked up by VS Code. Re-running init moves an existing Ultracite hook to the new format instead of keeping both.fix --hookwith Copilot: the file path is now read from Copilot payloads:toolArgs.pathfrom the Copilot CLI (including whentoolArgsis a JSON string),tool_input.filePathfrom VS Code's edit tools, every file of amulti_replace_string_in_fileedit, and the file headers of anapply_patch. So only the edited files are fixed, not the whole project. These hosts run the hook after every tool, so tools that edit nothing (such asread_file,vieworbash) now skip the fix instead of fixing the whole project.pnpm fix …,bun fix …,yarn fix …, ornpm run fix --skip=…. That older hook is updated in place, so it stops running a whole-project fix after every edit.ultracitesomewhere else, such as a Claude Code permission rule likeBash(npx ultracite check). Only existing hook commands are checked.fix --hooknow fixes a file at the project root whose name starts with.., instead of treating it as outside the project.includePackageJsonAutoImportspreference is now set for vtsls, Zed's default TypeScript and JavaScript language server (lsp.vtsls.settings), for both TypeScript and JavaScript. It was previously set undertypescript-language-server, which Zed doesn't use by default.dc5e18e: Re-runningultracite initnow updates the Ultracite rules inAGENTS.md,.claude/CLAUDE.md,GEMINI.mdand the other shared rule files in place, instead of appending another full copy.npx ultracite fix,yarn ultracite fix,pnpm exec ultracite fix,bunx ultracite fix) instead of a dlx runner. For Deno they readdeno run -A npm:ultracite fix; previously the brokendeno run -A npm: ultracite fixwas written..firebender/rules/ultracite.mdcwithalwaysApply: true, where Firebender reads project rules. They were previously written as Markdown intofirebender.json, which made that file invalid JSON and overwrote an existing Firebender config. Afirebender.jsonthat still holds only those Markdown rules is reset to{}.2ad7704: Fixes forultracite fix --claudeandultracite fix --codex:claude -p,codex exec -) instead of as a command-line argument. On Windows, an npm-installed CLI runs through a.cmdshim, which can cut an argument off at its first newline, so the agent received only the prompt's first line and never saw the file or its issues.--codexnow passes--skip-git-repo-check, so it also works in a project that isn't a git repository. Previouslycodex execrefused to run there and every file failed.0b0775e: The Husky pre-commit hook written byultracite init --integrations huskyis more robust.npx,yarn,pnpm exec,bunx) instead ofyarn dlx/pnpm dlx.yarn dlxdoes not exist in Yarn 1, so every commit failed with "Ultracite found issues that could not be auto-fixed", andpnpm dlxdownloaded the latest release instead of the version the project pins. Husky itself is also initialised with the installed binary.huskyonto an existingpreparescript (for example SvelteKit'ssvelte-kit sync || echo '') instead of replacing it. Choosing Husky and lefthook together no longer leaves only one of them inprepare.3862221:ultracite init --integrations lint-stagedno longer damages or shadows an existing lint-staged config..lintstagedrcwritten in YAML (which is how lint-staged reads it) is now edited as YAML. Previously it was overwritten with a JSON file holding only the Ultracite task, deleting the user's own tasks, and single-quoted YAML was silently skipped.yamlDocument API, so comments and the rest of the file are kept..lintstagedrc.ts,.mts,.ctsandlint-staged.config.ts,.mts,.cts) and thelint-stagedkey ofpackage.yamlare now recognised. Previously init created a.lintstagedrc.jsonnext to them, and because lint-staged uses only the first config in a directory, the user's config silently stopped running.package.jsonkey, matching the file lint-staged actually uses.ultracite fixinstead of being replaced.npx,yarn,pnpm exec,bunx) instead ofyarn dlx/pnpm dlx, which don't exist in Yarn 1 or download the latest release instead of the pinned one. Re-running init upgrades adlxcommand written by an earlier version.bffb8dd: Corrected the bundled Ultracite agent skill (skills/ultracite/SKILL.md):githubandsonarjspresets that init includes by default. Neither exists. The GitHub, SonarJS and React Doctor rules live in theultracite/oxlint/js-pluginspreset (plusnext/js-pluginsandtanstack/js-plugins), next to theanti-slopandshadcnpresets, and all of them are opt-in.--js-pluginsinit flag is now documented.npx,pnpm exec,yarn,bunx) rather thanpnpxoryarn dlx.da2934a: Installing the Ultracite skill fromultracite initnow works when init runs in a normal terminal.Init runs
skills add haydenbleasel/ultracitewithout a terminal attached. When theskillsCLI needed to ask which agents to install to, or whether to install into the project or globally, it could not show the prompt:Init now passes
--yesand names the agents to install for, soskillsinstalls without asking. The skill always goes to the shared.agents/skillsdirectory, which Codex, Cursor, GitHub Copilot, Gemini CLI, Amp, Cline, OpenCode and other agents read. It also goes to the skills directory of any agent whose project folder exists (for example.claude,.windsurf,.codebuddyor.roo, which init creates when you choose those agents).Init names the agents because otherwise, in a project where
skillsdetects no agent,--yeswould install for every agent it knows. That also created a.claude/skillslink and a second copy in a top-levelagent/directory (for the Eve framework). The "install it later" hint still shows the interactiveskills add haydenbleasel/ultracitecommand.4941883:ultracite initnow edits.pre-commit-config.yamland lefthook configs through theyamlDocument API instead of splicing text, so the result is always valid YAML and comments are kept.repos:list written flush with its key (- repo:, the stylepre-commit sample-configgenerates) or with four-space indentation no longer turns into invalid YAML that breaks every commit. The file's sequence style is kept.pre-commit:block, a compactjobs:list, or a column-0 comment inside the block no longer produce invalid YAML or a duplicatejobs:key. A hook that usescommands:gets ajobs:list next to it..lefthook.yml,lefthook.yaml,.config/lefthook.yml(and the other names lefthook reads) and edits it. Previously it createdlefthook.yml, which lefthook picks first, so the user's own config silently stopped running. A JSON or TOML config is left alone with instructions.*.js,*.ts, and so on. With lefthook's default matcher, the previous**/*.jsform skipped root-level files such aspackage.jsonorindex.ts. Projects that setglob_matcher: doublestarget**/*.js.npx,yarn,pnpm exec,bunx) instead ofyarn dlx/pnpm dlx. Re-running init upgrades a hook or job written by an earlier version.lefthook installruns the project's installed lefthook, and thepreparescript is chained onto an existing one (e.g.svelte-kit sync || echo '' && lefthook install) instead of replacing it.9c30c15: Raise theoxlintpeer dependency to^1.82.0. The core Oxlint preset configuresno-unmodified-loop-conditionwithcheckConditionalExpressions, an option Oxlint 1.79 to 1.81 reject, so those releases failed with "Failed to parse oxlint configuration file" even though the old^1.79.0range allowed them.ultracite doctorand your package manager now flag the too-old version instead.Requires oxlint >= 1.82.0
47303f4: The Biomejestpreset now declares Jest's globals (describe,it,test,expect,jest,beforeEachand the rest ofglobals.jest) for test files. Jest injects them without imports, sonoUndeclaredVariablesfrom the core preset reported every one of them in idiomatic Jest tests. The ESLintjestpreset already declared them.The route-file exemptions in the Biome
remixandtanstackpresets (useFilenamingConventionoff inroutes/) now cover.jsand.jsxroute files as well as.tsand.tsx.8f236ee: The ESLint core preset now lints.jsx,.tsx,.mtsand.ctsfiles. Its main block previously matched only.js,.mjs,.cjs,.ts,.jsonand.html, so ESLint skipped.mtsand.ctsfiles entirely ("File ignored because no matching configuration was supplied"), and.jsxand.tsxfiles received only the framework preset's rules..tsxfiles were also parsed by espree, so any TypeScript syntax in them was a parse error. Every TypeScript extension now uses@typescript-eslint/parserwith type information, and the core rules apply to all of them.Type information now comes from the TypeScript project service (
parserOptions.projectService: true) instead of a fixedproject: "./tsconfig.json". Solution-style tsconfigs ("files": []plusreferences, as in the Vite templates) and monorepo packages now resolve their own tsconfig, where previously every file failed to parse. A file that no tsconfig includes still has to be added to one; typescript-eslint reports it as "not found by the project service" (see https://typescript-eslint.io/troubleshooting/typed-linting/#i-get-errors-telling-me-was-not-found-by-the-project-service-consider-either-including-it-in-the-tsconfigjson-or-including-it-in-allowdefaultproject).The JavaScript block no longer matches
**/*.json. espree cannot parse JSON, so everypackage.jsonandtsconfig.jsonfailed withParsing error: Unexpected token :. The ESLint preset does not lint JSON, matching the Oxlint preset.94722fa: Theeslint.config.mjsthatultracite initwrites no longer fails its own lint. Every ESLint preset exported a default namedconfig, soimport core from "ultracite/eslint/core"(and each framework import) trippedimport-x/no-rename-defaulttwice per preset. Each preset's default export is now named after the identifier the generated config imports it as (core,react,tanstack, …).d9803a6: Fix ESLint presets that crashed the whole run or never reached the files they target.@tanstack/start/no-async-client-componentand@tanstack/start/no-client-code-in-server-componentneed type information and aborted ESLint ("You have used a rule which requires type information") on the first.jsxor.tsxfile. They now run only on TypeScript files. The Query, Router and Start rules also apply to plain.ts/.jsmodules, where query and mutation options are usually declared, instead of only to.jsxand.tsx.sort-keysis off, andno-use-before-defineandunicorn/filename-caseare off inroutes/directories, matching the Oxlint and Biome TanStack presets (alphabetical option keys break TanStack's type inference, and file routes are mutually recursive and encode the URL in the filename).qwik/valid-lexical-scopeneeds type information and crashed ESLint on.jsxfiles. It now runs only on.tsxfiles.jest/no-deprecated-functionsthrew "Unable to detect Jest version" and crashed ESLint when thejestpackage was not resolvable from the plugin, for example in bun:test projects. The preset now reads the Jest version from the project and falls back to the latest major.globals.cypress, which does not exist in theglobalspackage, socy,Cypress,describeanditwere reported byno-undef. It now useseslint-plugin-cypress's own globals config. It also matched only*.cy.js, and now covers*.cy.*for every JS and TS extension plus files undercypress/.*.stories.jsand*.stories.ts. It now covers*.stories.*and*.story.*for every JS and TS extension, including.tsxand.jsx.d6ee272: The ESLintvue,svelteandastropresets now parse their files. They registered only the plugin and its rules, never a parser, so ESLint read every.vue,.svelteand.astrofile with espree and reported a parse error (Unexpected token <) instead of linting it.Each preset now starts from its plugin's own base config (
flat/baseineslint-plugin-vue,eslint-plugin-svelteandeslint-plugin-astro), which registersvue-eslint-parser,svelte-eslint-parserorastro-eslint-parserfrom the plugin's dependencies, and parses<script lang="ts">blocks with@typescript-eslint/parser. The Svelte preset also covers.svelte.jsand.svelte.tsrune modules. In Astro files, client-side<script>tags are linted without type information, because those in-memory virtual files cannot be type-checked.f7599ef: The ESLint core preset no longer re-enables formatting rules thateslint-config-prettierturns off. The unicorn rule set was spread aftereslint-config-prettier, which switchedunicorn/number-literal-case,unicorn/template-indentandunicorn/empty-brace-spacesback on.unicorn/number-literal-casedemands0xABCDwhile Prettier prints0xabcd, soprettier/prettierand the unicorn rule reported every hex literal andeslint --fixcould not satisfy both.eslint-config-prettiernow applies after every plugin, sounicorn/number-literal-caseandunicorn/template-indentare off.unicorn/empty-brace-spacesstays on (Prettier prints empty braces as{}too), matching the Oxlint preset.efa80e8: The ESLint core preset no longer reports the same problem two or three times on TypeScript files, and no longer flags ambient type-only globals.no-undefandsonarjs/no-reference-errorare off for.ts,.tsx,.mtsand.ctsfiles. TypeScript already reports unresolved identifiers, and both rules flagged valid ambient types such asNodeJS.Timeout(see the typescript-eslint FAQ).class-methods-use-this,consistent-return,no-unused-private-class-members,prefer-destructuringandprefer-promise-reject-errorsrules are off for TypeScript files, because their@typescript-eslintextension rules are enabled there. The base rules duplicated every report and ignored the extensions' TypeScript-aware options.unused-imports/no-unused-varsis off. It is a copy ofno-unused-vars/@typescript-eslint/no-unused-vars, which stay on as in the Oxlint preset, so every unused variable was reported twice.unused-imports/no-unused-importsstays on for its autofix.@typescript-eslint/max-paramsis off, matchingmax-paramsbeing off in the core presets. It was on with its default limit of 3 parameters for TypeScript files only.9ae95d1: Fix false positives in the ESLintvue,svelteandastropresets that appeared once their files were actually parsed.vue/block-langandsvelte/block-langrejected every<script lang="ts">, because with no options they only allow blocks without alangattribute. They now allow TypeScript scripts, andlessandscssstyles, as well as blocks withoutlang.<script>tags are linted as virtual files named1_1.ts,2_1.jsand so on, whichgithub/filenames-match-regexreported on every page with a script tag. Filename rules are now off for those virtual files.@typescript-eslint/parserfor the frontmatter explicitly. ESLint's config merge dropped the copy ineslint-plugin-astro's base config, so frontmatter TypeScript parsed only when@typescript-eslint/parserhappened to resolve from the working directory, and failed withThe keyword 'interface' is reservedotherwise.ultracite initnow installseslint-plugin-jsx-a11yfor Astro projects using ESLint. The preset enableseslint-plugin-astro'sastro/jsx-a11y/*rules, which load that package and reported "you need to install eslint-plugin-jsx-a11y" on every.astrofile when it was missing.e88e76d: The Prettier preset now setstailwindFunctionstoclsx,cva,tw,twMerge,cn,twJoinandtv, soprettier-plugin-tailwindcsssorts Tailwind classes passed to those helpers anywhere in a file, for exampleconst button = cva("p-4 flex"). Previously only class strings insideclass/classNameattributes were sorted with Prettier, while the Oxfmt (sortTailwindcss.functions) and Biome (useSortedClasses) presets already sorted these helpers. See https://github.com/tailwindlabs/prettier-plugin-tailwindcss#sorting-classes-in-function-calls.3a5885e:github/filenames-match-regexnow accepts file-based routing filenames in the ESLint preset too, and Next.js's special pages in both presets.js-pluginsand now ESLintcore) allows a leading underscore, so the Next.js pages-router files_app,_documentand_error, which cannot be renamed, are no longer reported. Other names inpages/(BadPage.ts) and camelCase route params ([postId], whichunicorn/filename-caserejects too) are still reported.corepreset now carries the same route exemptions as the Oxlintjs-pluginspreset (#799, #804). Previously it applied the plain kebab-case regex everywhere and reported SvelteKit's+page.tsand+server.ts, TanStack Router and React Router files such asposts.$postId.ts, and page routes such as[slug].json.tsand[...auth].ts.github/filenames-match-regexin Oxlintjs-pluginsand ESLintcore, plusunicorn/filename-caseandno-use-before-definein the Oxlint and ESLinttanstackpresets) now cover.jsand.jsxroute files as well as.tsand.tsx.ad2adb0: The Stylelint preset now handles Tailwind CSS v4, SCSS and Less, and skips build output.@theme,@utility,@variant,@custom-variant,@slot,@pluginand@configare allowed byat-rule-no-unknown, alongside the Tailwind v3 at-rules and@sourceand@reference.at-rule-prelude-no-invalidno longer rejects every@apply(or the other Tailwind at-rules' preludes).custom-property-patternaccepts theme namespace resets (--color-*: initial) and sub-properties (--text-xl--line-height).declaration-property-value-no-unknownandfunction-no-unknownaccept--spacing(),--alpha(),--value(),--modifier()andtheme().media-query-no-invalidacceptstheme()breakpoints, andnesting-selector-no-missing-scoping-rootallows&inside@custom-variant,@variantand@utility. See https://tailwindcss.com/docs/functions-and-directives.import-notationis now"string".stylelint-config-standard's"url"autofix rewrote@import "tailwindcss"to@import url("tailwindcss")..scssfiles are parsed withpostcss-scssand.lessfiles withpostcss-less. Previously every SCSS and Less file failed withCssSyntaxError(for example on//comments or mixin calls). The CSS-only rules that cannot understand preprocessor syntax (unknown at-rules such as@use,@includeand@mixin,$variableand@variablevalues, Sass and Less built-in functions,//comments) are relaxed for those files.ultracite initnow installspostcss-scssandpostcss-lesswith the ESLint toolchain..sass: the indented Sass syntax has no maintained PostCSS parser, soultracite checkandultracite fixno longer pass.sassfiles to Stylelint, where every one failed to parse.ignoreFilesnow uses the shared ignore list (dist,build,.next,coverage,storybook-staticand so on). Stylelint does not read.gitignore, so it previously linted generated CSS.prettier/prettieris now enabled. The preset loadedstylelint-prettierbut never turned its rule on.ultracite checkandultracite fixnow escape glob characters in directory arguments before passing them to Stylelint. A directory such asapp/(marketing)previously matched no files.89989df: Test-file overrides in every preset now match.mts,.cts,.mjsand.cjstest files as well as.ts,.tsx,.jsand.jsx. The globs were**/*.{test,spec,test-d,spec-d}.{ts,tsx,js,jsx}and**/__tests__/**/*.{ts,tsx,js,jsx}, so asum.test.mtsfile got none of the test-file relaxations and none of the Jest or Vitest rules. For example,test.onlyin a.test.mtsfile was not reported byvitest/no-focused-testsorjest/no-focused-tests. This covers the Oxlintcore,js-plugins,jestandvitestpresets, the Biomecorepreset, and the ESLintcore,jestandvitestpresets.4d28cf9: The Oxlintvuepreset now lists every non-nursery Vue rule Oxlint implements, like the other framework presets. It previously enabled 18 of them. Among the 27 newly enabled rules arevue/valid-define-props,vue/valid-define-emits,vue/no-export-in-script-setup,vue/no-lifecycle-after-await,vue/no-arrow-functions-in-watch,vue/no-side-effects-in-computed-properties,vue/require-typed-ref,vue/define-props-destructuringand thevue/no-deprecated-*rules.vue/max-propsis off in both the Oxlint and ESLint presets, because its default allows a single prop per component.The Oxlint
vitestpreset now enablesvitest/consistent-test-it, the one Vitest rule it was missing (the ESLint preset already had it).Vue files whose names Nuxt and file-based Vue Router require (
pages/**,layouts/**,error.vueandapp.vue) are exempt fromuseVueMultiWordComponentNamesin the Biome preset and fromvue/multi-word-component-namesin the ESLint preset. Previouslypages/index.vue,layouts/default.vueanderror.vuewere always reported.v7.12.1Compare Source
Patch Changes
00dd547: Agent hooks now fix only the file the agent edited. The generated hook command passes a new--hookflag, andultracite fix --hookreads the agent's payload from stdin: Claude Code and CodeBuddy (tool_input.file_path), Cursor (file_path) and Windsurf (tool_info.file_path). A file outside the project, one that no longer exists, or one the project's linters do not handle is skipped. A payload with no readable file path keeps the previous whole-project run. Re-runningultracite initrewrites a hook from an earlier init to the new command, including one generated before this change or for a different package manager or linter.300a814: Update@biomejs/biometo 2.5.14. Releases 2.5.13 and 2.5.14 add only nursery rules (14 of them, includingnoReturnInFinally,noSvelteAtHtmlTagsanduseValidTestTitle), with no stable-group additions, removals or promotions. The Biome presets deliberately leave nursery rules off, so they are unchanged.b8b183b: Update dependencies (@clack/prompts1.8,empathic2.1,magicast,nypm,string-width8.3,yamlandzod4.6).b59b8aa: Update oxfmt to 0.70.0 and re-target the markdown:::fence patch. Releases 0.68 through 0.70 change no options; they fix suppression-comment handling, comment placement around operators andas/satisfies, and several YAML and CSS edge cases. oxfmt 0.69 also ships a native markdown formatter, but.mdfiles still go through the Prettier-based path, so the fence patch still applies.548c8b0: Update oxlint to 1.85.0. Releases 1.83 through 1.85 add and remove no rules and change no options, so the presets are unchanged. Note thatjest/prefer-strict-equal,vitest/prefer-strict-equal,vitest/prefer-to-be-truthyandvitest/prefer-to-be-falsynow offer suggestions instead of fixes, soultracite fixreports them rather than rewriting them.unicorn/consistent-function-scopingalso now catches functions that only capture variables from an outer ancestor scope.0bc6cd6: Update the ESLint family:eslint10.11,eslint-plugin-unicorn76,@typescript-eslint/*8.70.1,eslint-plugin-jsdoc64.5,eslint-plugin-astro3.2,eslint-plugin-n18.4,eslint-plugin-vue10.11.1,@tanstack/eslint-plugin-query5.104,@next/eslint-plugin-next16.3.6,@darraghor/eslint-plugin-nestjs-typed7.5.6,eslint-plugin-qwik1.20.1,eslint-plugin-html8.2.1 andeslint-plugin-react-doctor/oxlint-plugin-react-doctor0.9.14.The ESLint core preset picks up unicorn's ten new JavaScript rules automatically:
no-async-iterator-callback,no-unused-builtin-method-return(which replaces the now-deprecatedno-unused-array-method-return),no-unused-iterator-helper,no-useless-set-construction,no-using-resource-escape,prefer-combined-guards,prefer-iterator-zip,prefer-json-import,prefer-temporal-conversionandprefer-uint8array-hex. The CSS-only unicorn rules added in v75 stay off for JavaScript files. unicorn 76 makesno-immediate-mutationskip conditional mutations andno-break-in-nested-loopskipcontinueby default; the preset keeps those defaults because oxlint'sno-immediate-mutationbehaves the same way and checkingcontinuewould contradictprefer-continue.0bcb2c6: Updateprettier-plugin-astroto 1.1.0 (the versionultracite initinstalls for Astro projects with Prettier) and refresh the transitive Prettier lock to 3.9.9. Prettier 3.9.7 through 3.9.9 are formatting fixes with no option changes, so the Prettier preset is unchanged. prettier-plugin-astro 1.1 makesastroAllowShorthandtri-state. When it is unset, which is how Ultracite's generated config leaves it, attributes now stay as written instead of{value}being expanded tovalue={value}. SetastroAllowShorthand: falsein your Prettier config to keep the 1.0 behaviour.8b39375: Update@shadcn/lintto 0.2.0 (the versionultracite initinstalls for the opt-inultracite/oxlint/shadcnpreset). The release adds Vue and Svelte support and fixes theme, barrel and class-site discovery; the rules and their options are unchanged, so the preset is too. Under Oxlint the plugin reads only the<script>blocks of.vueand.sveltefiles, not their templates.0bc6cd6: Updateeslint-plugin-sonarjsto 4.2.1. The ESLint preset adopts its 16 new rules automatically (Vue, Cypress, Playwright, Testing Library, Vitest, Lodash, jQuery and Axios checks). Theultracite/oxlint/js-pluginspreset now enables every sonarjs rule that does not need type information, adding 35 rules. These include the 15 new non-type-aware rules and nine test rules that older versions of oxlint's JS plugin bridge could not load. sonarjs 4.2.1 also raised theno-nested-functionsdefault threshold from 4 to 5; both presets pinthreshold: 4to keep the previous strictness.v7.12.0Compare Source
Minor Changes
1f427d7: Add an opt-inultracite/oxlint/shadcnpreset for@shadcn/lint, shadcn's agent-first linter for Tailwind v4 design systems. All six rules (no-restyle,no-raw-colors,no-arbitrary-values,no-inline-styles,no-unknown-classes,require-static-classes) run aterrorwith the upstreamallow: ["layout"]policy, and the component-authoring rules are relaxed inside**/components/ui/**. Enable it withultracite init --linter oxlint --js-plugins @shadcn/lint, or extend it alongsideultracite/oxlint/coreafter installing@shadcn/lint.Patch Changes
600e991: Accept file-based page route filenames ingithub/filenames-match-regex. Files underpages/(Astro, Next.js pages router) now match a bracket-aware pattern, so dynamic, rest and optional parameters such as[slug].astro,[...slug].tsand[[...slug]].tsxpass while non-route names likeBadPage.tsare still rejected.66f97bb: Allow top-levelreturnin Astro frontmatter. The core preset now turns offunicorn/prefer-modulefor**/*.astrofiles (the rule cannot be configured to allow onlyreturn), and keepsrequire(),module.exports,__dirnameand__filenameflagged there viaimport/no-commonjsandno-restricted-globals.v7.11.1Compare Source
Patch Changes
24b2797: Update oxfmt to 0.67.0 and re-target the markdown:::fence patch to the new dist chunk. The release changes no options; it fixes CSS/SCSS/Less comment preservation and a few JSDoc-cast edge cases in member chains.24b2797: Update oxlint to 1.82.0. The release adds and removes no rules, so the presets are unchanged apart from one new option:no-unmodified-loop-conditionnow runs withcheckConditionalExpressions: truein the oxlint and ESLint core presets, so each branch of a ternary in a loop condition must be modified inside the loop rather than only the expression as a whole.24b2797: Update the ESLint family:@angular-eslint/eslint-plugin22.5,@typescript-eslint/*8.70,eslint-plugin-solid0.18,eslint-plugin-vue10.11,eslint-plugin-jsdoc64.3.8,eslint-plugin-cypress7.0.2,@darraghor/eslint-plugin-nestjs-typed7.5.2 andprettier-plugin-astro1.0. The dynamic presets pick up the seven new rules automatically:@typescript-eslint/no-generated-empty-object-type,@angular-eslint/reactive-context-must-read-signal,vue/no-shadow-native-events, and Solid'sno-boolean-enumerated-attribute,no-store-mutation-outside-setter,no-unused-signalandno-write-in-pure-computation. Note thatprettier-plugin-astro1.0 is a rewrite on the Astro 7 Rust compiler (Node 22.12+); expect whitespace diffs on first run and mirror any customcompressHTMLvia its newastroCompressHTMLoption.24b2797: Refresh the stylelint lock to 17.15.0 and enable its newselector-no-unmatchablerule, which flags selectors that can never match (for examplelabel:checked,::before:first-childor:is(::before)).a71dda4: Stop sorting object keys across the whole TanStack presets, not just route files. TanStack Query mutation options are order-sensitive too: TypeScript infers theonMutatecontext from context-sensitive callbacks in source order, so movingonError/onSettledaboveonMutateturns the context into{}andultracite fixbroke type-correct code (TS2339: Property '...' does not exist on type '{}'). Mutation and query options live anywhere (hooks, components, colocated files), so the route-file exemption could not cover them.ultracite/oxlint/tanstacknow setssort-keystooffandultracite/biome/tanstacknow setsuseSortedKeystoofffor all files.a9db61c: Disablegithub/filenames-match-regexfor file-based route directories (**/routes/**) in the oxlint js-plugins preset, matching the existingunicorn/filename-caseexemption in the TanStack preset. TanStack Router filenames such as__root.tsx,$.tsx, andposts.$postId.tsxno longer fail the GitHub filename rule. Resolves #799.v7.11.0Compare Source
Minor Changes
d5d241f: Addultracite upgrade, and make version drift between Ultracite and its linter visible instead of a crash. Bumpingultracitewithout bumping the linter fails on the nextcheck/fixwith Biome'sFound an unknown keyerror (and the equivalent for ESLint and Oxlint), because each release enables rules from a specific linter version.ultracite upgradeinstalls the latestultracite, prints a release-notes link for the versions you crossed, then hands off to the freshly installed CLI to reinstall your toolchain at the versions that release was verified with: Biome; ESLint, Prettier, Stylelint and the plugins the preset imports; or Oxlint and Oxfmt. Optional extras already in yourpackage.json(framework plugins, Oxlint JS plugins,oxlint-tsgolint) are bumped as well. Config files are never rewritten, and the run ends with the same diagnostics asdoctor.ultracite doctornow checks the installed Biome, ESLint, Prettier, Stylelint, Oxlint and Oxfmt versions against the range this release supports. Too old fails the check and points atultracite upgrade; newer than the verified range only warns.@biomejs/biome ^2.5.0,eslint ^10.0.0,prettier ^3.0.0,stylelint ^17.0.0,oxlint ^1.79.0,oxfmt >=0.40.0.ultracite initnow installs Prettier and Stylelint from those ranges instead oflatest.Requires Biome >= 2.5.0, ESLint >= 10.0.0, Prettier >= 3.0.0, Stylelint >= 17.0.0, Oxlint >= 1.79.0 and Oxfmt >= 0.40.0.
Patch Changes
27d06f2: Update dependencies (@clack/prompts,semver,zod, ESLint plugins).d322691: Defaultultracite initto Oxlint + Oxfmt when--linteris omitted in quiet or non-interactive modec677cf5: Recommend Oxlint + Oxfmt in theultracite initlinter prompt and list it firstc5be848: Disableno-use-before-definein TanStack Router route files for the oxlint tanstack preset. File routes are mutually recursive (Routereferences the component, and the component callsRoute.useParams()), so no declaration order could satisfy the rule.v7.10.8Compare Source
Patch Changes
2a43fdd: Turn offunicorn/prefer-reflect-applyin theultracite/oxlint/anti-sloppreset. When extended afterultracite/oxlint/core, the core rule recommended rewritingFunction#apply()toReflect.apply(), which anti-slop'sno-reflect-applythen rejected, leaving no way to satisfy both. Direct-call guidance for.apply()still comes fromeslint/prefer-spreadandeslint/no-useless-call.03196df: Update@biomejs/biometo 2.5.12. Releases 2.5.10 through 2.5.12 only add nursery rules, which the Biome presets exclude, so the presets are unchanged. The releases also carry linter and formatter fixes, including fewernoUnusedVariablesfalse positives on merged interfaces, namespaces, and overload type parameters, an unsafe-by-default fix fornoThisInStatic, and a large batch of Astro expression parsing fixes.031b867: Update oxfmt to 0.66.0. The 0.65 and 0.66 releases are formatter fixes only, mainly around comment placement between a statement head and its body, suppressed statements (decorators, typecast parens, semicolons), custom side-effect groups insortImports, CommonMark list interruption in JSDoc, and trailing whitespace in YAML block scalars. The:::container-directive fence patch is re-targeted to the new markdown bundle; the patched code itself is unchanged.54affee: Update oxlint to 1.81.0. The 1.80 and 1.81 releases add no rules and remove none, so the presets are unchanged and the peer range stays at ^1.79.0. Notable fixes carried in:no-use-before-definenow runs on JS and JSX files,object-shorthandpreserves__proto__semantics,unicorn/prefer-math-min-maxno longer applies an unsafe autofix, and JS plugin diagnostics with invalid or reversed locations are clamped instead of crashing.0b058d1: Fixultracite fix --codexfailing on recent Codex CLI releases. The Codex adapter invokedcodex exec --full-auto, a flag Codex has since removed, so every attempt failed at argument parsing. It now runscodex exec --sandbox workspace-write, the equivalent non-interactive, workspace-scoped mode, which is also accepted by older Codex versions. Resolves #794.e2346bb: Update the ESLint family to latest: eslint 10.9.1, typescript-eslint 8.69.0, eslint-plugin-unicorn 74, eslint-plugin-solid 0.17, eslint-plugin-jsdoc 64.3.5, eslint-plugin-react-doctor and oxlint-plugin-react-doctor 0.9.13, globals 17.12, and the angular, nestjs-typed, next, tanstack-query, cypress, html, jest and storybook plugins. The dynamic presets pick up the new rules automatically: the solid preset gains the eleven rules added in eslint-plugin-solid 0.16 and 0.17 (removed-api,no-single-arg-create-effect,no-accessor-as-prop,valid-use-server,require-async-server-function,no-invalid-server-capture,no-browser-globals-in-server-function,no-module-scope-reactive-primitive,no-restated-default-options,prefer-onSettled-for-side-effects,prefer-structured-class), which only report Solid 2.0 issues whensettings.solid.versionis set to 2 and stay silent on Solid 1.x code; the nestjs preset gainsforward-ref-injection-should-use-wrapper-type,swagger-file-upload-should-be-documentedanduploaded-file-should-be-validated. The curated jsdoc and react-doctor lists are unchanged. No enabled rule was removed or deprecated, and the ESLint/oxlint parity check still passes.7b0456e:ultracite fixnow runs lint fixes before the formatter: oxlint before oxfmt, and ESLint then Stylelint before Prettier. A fixer can insert unformatted code, such as the bracescurlyadds, the importsconsistent-type-specifier-stylesplits, or the font names Stylelint requotes, and running the formatter first left that code unformatted until the next run. Fixer-inserted code is now formatted in the same run. The--claudeand--codexagent passes follow the same order.unicorn/no-nested-ternaryis now off in the oxlint and ESLint presets: its fixer adds parentheses that oxfmt and Prettier remove, so the two tools rewrote the file on every run. The coreno-nested-ternaryrule still reports nested ternaries.8ddc078: Enable three rules added in Stylelint 17 in the Stylelint preset:selector-no-deprecatedandselector-no-invalidreport selectors that CSS has dropped or that cannot parse, andrelative-selector-nesting-notationis set toexplicit, so nested relative selectors are written& > brather than> b. Theproperty-layout-mappings,unit-layout-mappingsandvalue-keyword-layout-mappingsrules are deliberately left off, since they reject every physical property, unit and keyword and can only autofix with a per-projectlanguageOptions.directionalitysetting. The repo's own Stylelint lock is refreshed from 16.26.1 to 17.14.1, which the config packages already required;ultracite initalready installsstylelint@latestso users are unaffected by the lock change.4609d79: Update the vendoredanti-slopOxlint plugin to upstream v0.1.2 (commite8c4880). The rule set is unchanged; this picks up a round of correctness fixes:no-known-value-wideningnow also reports known values passed into localunknown-typed type predicates,no-object-parameters,no-unknown-parameters,no-unknown-returnsandno-unknown-type-aliasesresolve scoped and transparent generic aliases (including block-scoped aliases and forward references),no-unknown-returnscoversPromiseLike<unknown>,no-unsafe-dictionary-typeallows generic constraints such asT extends Record<string, unknown>,no-runtime-typeofalways allows existence probes against"undefined",no-shape-in-symbol-namesallows static member reads such as Zod'sschema.shape, andrequire-safety-comment-for-type-assertionrejects empty justifications and gains amarkersoption (default["SAFETY"]). Upstream also added a separate opt-inanti-slop-effectplugin for Effect projects, which is not vendored.v7.10.7Compare Source
Patch Changes
15f7ae0: Add support for the Aube package manager:ultracite init --pm aubeis now accepted and projects with anaube-lock.yamllockfile are detected automatically1afc8fe: Re-declare selected Oxlint JS plugins on the root config so dependency analyzers see them.ultracite init --linter oxlint --js-plugins ...now generatesconst jsPlugins = selectJsPlugins([...])and addsjsPlugins: jsPlugins.jsPluginsto the root config (also when the fullultracite/oxlint/js-pluginspreset is extended). Knip only resolvesjsPluginsfrom the root Oxlint config and never walksextends, soeslint-plugin-github,eslint-plugin-sonarjs, andoxlint-plugin-react-doctorwere reported as unused devDependencies. Oxlint dedupes the plugin between the root and the extended preset, so linting is unchanged.Also fix
ultracite updatedropping thejs-pluginspreset fromextendswhen the config used the documentedimport jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins"form: the import parser only matched bare default imports. The regenerated full-preset config keepssettings: jsPluginSettingson the root as well.20b06bf: Ignore.alchemyacross all linters and formatters. Alchemy (alchemy.run) writes local state and generated bindings to a.alchemydirectory. It is now part of the shared ignore patterns synced into Biome'sfiles.includesand imported by oxlint, oxfmt, and ESLint, and the Stylelint preset ignores it viaignoreFiles. Prettier needs no change: it already respects.gitignore/.prettierignore.6e9286c: Runlefthook installduringultracite initthrough the same spawn adapter as every other tool invocation instead ofexecSync, so it no longer goes through a shell and gets the same Windows command resolution ashusky, the linters, and editor extension installs.8b24f34: Add support for the Nub package manager:ultracite init --pm nubis now accepted and projects with anub.locklockfile are detected automatically (requires nypm ^0.6.9)240cb6b: Install into the workspace root correctly for Nub and Aube monorepos: nypm emits no root selector for them, sonub addran without-wand was refused. Ultracite now hands nypm pnpm's flag set for these pnpm-compatible CLIs, producingnub add --workspace-root --save-dev ultracite44ecbba: Add Vitest type-test files (**/*.{test-d,spec-d}.{ts,tsx,js,jsx}) to the test-file globs so the shared test relaxations and the Vitest rule overrides apply to them, and enable the Vitest plugin'stypechecksetting in the ESLint preset soexpectTypeOf/assertTypecount as assertions.7ba6bb8: Package the reusable Ultracite agent skill with the npm distribution.v7.10.6Compare Source
Patch Changes
972b946: Update the oxlint presets for oxlint 1.79: replace the removedreact/react-compilerrule with the 22 new React Compiler rules in the react preset, addjsdoc/no-blank-blocksandone-varto the core preset (mirrored in the ESLint presets), and require oxlint ^1.79.02d5bbdb: Declare thejsPluginSettingsnamed export in theultracite/oxlint/js-pluginstype declarations, so the documented (andultracite init-generated)import jsPlugins, { jsPluginSettings } from "ultracite/oxlint/js-plugins"type-checks (#773)56aef65: Refresh the toolchain versions thatultracite initinstalls into projects:@biomejs/biome2.5.9, the ESLint plugin family (including eslint-plugin-cypress 7, eslint-plugin-jsdoc 64, eslint-plugin-solid 0.15, and eslint-plugin-unicorn 73 — the dynamic presets adopt their new rules automatically), andprettier-plugin-svelte4.1.1 /prettier-plugin-tailwindcss0.8.1v7.10.5Compare Source
Patch Changes
8df6ad0: Offer the vendoredanti-slopOxlint preset duringultracite init— it now appears in the JS-plugins prompt when you pick Oxlint, and non-interactive setup accepts it via--js-plugins anti-slop. Selecting it addsultracite/oxlint/anti-slopto the generated config'sextends; since the preset is vendored inside Ultracite, nothing extra is installed.cd229e9: Bump theoxlint-plugin-react-doctorpin from^0.7.1to^0.9.12, soultracite initinstalls the current plugin. All react-doctor rules enabled by the js-plugins presets still exist in 0.9.12, and the ported rules run in curated mode via the settings shipped alongside this release (#771).e1ac886: Pin React Doctor's ported rules to their framework-aware "curated" mode (#771). react-doctor 0.9.x rewrote its ported oxc/react-refresh rules — notablyonly-export-components— with a stripped-down default mode: no framework detection, no route-file skipping, andallowConstantExportoff, so Next.js route-segment exports likeexport const dynamic = "force-static"ormetadatawere flagged as non-component exports in every route file. The ESLint react preset now setssettings["react-doctor"].portedRuleMode: "curated", and generated oxlint configs apply a newjsPluginSettingsexport fromultracite/oxlint/js-pluginson the root config (oxlint does not mergesettingsfrom extended configs, so the setting cannot ride along inside the preset). If you extend the js-plugins preset manually, addsettings: jsPluginSettingsto your root oxlint config.c27fe36: Generate oxlint configs that enable a subset of the JS plugins via a newselectJsPluginsexport fromultracite/oxlint/js-plugins, instead of inlining the filtering logic into the generated file. The inlined block contained atypeofcheck that user-side lint presets flagged (anti-slop/no-runtime-typeof, #770); the generated config is now a one-line extend, is emitted already formatted (including the previously missing blank line after imports), and re-runningultracite initmigrates existing configs with the old inlined block automatically.0616523: Update the vendoredanti-slopOxlint plugin to upstream commit446268e, picking up fixes tono-object-parametersandno-unknown-returns(respect lexical type binders in alias resolution) and a newallowInTypeGuardsoption onno-runtime-typeof. Theultracite/oxlint/anti-sloppreset enablesallowInTypeGuards, sotypeofchecks inside type predicate functions ((x): x is T) no longer need disable comments — predicates are the named-boundary pattern the rule pushes toward (dmmulroy/anti-slop#10).v7.10.4Compare Source
Patch Changes
417a85a: Add an opt-inultracite/oxlint/anti-sloppreset that ships a vendored, self-contained build of the anti-slop Oxlint plugin — fifteen rules that reject low-evidence TypeScript and JavaScript patterns (unjustified type assertions,unknownleaking through signatures,Reflect-based access, module mocking, and more). Extend it alongsideultracite/oxlint/core; nothing extra to install. The preset also turns off two core rules that conflict with anti-slop's widening checks (typescript/consistent-indexed-object-styleandunicorn/no-immediate-mutation) when extended after core.4d3fab8: Movesuspicious/useArraySortComparefrom the Biome core config to the opt-in type-aware config. The rule is in Biome'stypesdomain — it type-infers the receiver of every method call before checking the method name, which madeultracite checkup to ~260x slower on projects with expensive library types (zod, better-auth, Prisma). It now only runs when type-aware linting is explicitly enabled, alongside the other type/project-domain rules. Fixes #768.v7.10.3Compare Source
Patch Changes
a1fa9c4: Replace the hand-rolled packageexportsmap matching in the config-resolution doctor check with the resolve.exports library, which implements Node's fullPACKAGE_TARGET_RESOLVEalgorithm (wildcard patterns, key-order precedence, conditional exports, and array fallbacks). The manualnode_moduleswalk is kept intentionally — it exists to avoid Bun's auto-install cache resolving specifiers the project's ownnode_modulescan't.414ea80: Replace the hand-rolled monorepo workspace scan in framework detection with the find-workspaces library. Workspace declarations from package.json (array and yarn-classic object form) and pnpm-workspace.yaml — including negated globs — are now resolved by the library instead of manual pattern collection and globbing, and lerna/bolt monorepos are picked up as well.a9a1989: Replace the hand-rolled upward directory walks infindNearestFileanddetectLinterwith the empathic library'sfind.any, which checks candidate names in order within each directory before moving to the parent — the same per-directory precedence the previous implementation enforced manually.27b2707: Use magicast to update ESM lint-staged config files duringultracite init. The config is now edited as an AST instead of being imported and re-serialized, so comments and function-valued entries elsewhere in the config survive the update, and the user's config code is no longer executed. If the Ultracite glob pattern is already owned by a non-array value, or the config isn't a mergeable object literal (e.g.defineConfig(...)), init warns and leaves the file untouched instead of rewriting it. CommonJS configs keep the previous behavior.f2529b8: Rewrite the agent-fix progress renderer on top of log-update, cli-truncate, and string-width. log-update now owns the in-place block rewriting that was previously done with manual cursor-up/clear-line escape sequences, and line truncation is measured by display width instead of code units — so lint messages containing emoji or CJK text can no longer overflow the terminal row and corrupt the animated block.277b9d6: Replace the hand-rolled child-process handling in the agent fix runner with execa. The timeout → SIGTERM → grace period → SIGKILL escalation, stderr capture, and spawn-failure handling now use execa'stimeoutandforceKillAfterDelayoptions, which are battle-tested across platforms (including Windows kill semantics the manual implementation didn't cover). Behavior is unchanged: agent runs still time out after 5 minutes, escalate to SIGKILL after a 10-second grace period, and report a capped stderr tail.1614a80: Drop the direct cross-spawn dependency: all synchronous process spawning (linter runs, tool version checks, editor extension installs, skill installs) now goes through a small adapter over execa's sync API, which owns the Windows spawn semantics cross-spawn provided. The adapter preserves the spawnSync result shape (status/signal/error/stdout), always disables shell interpretation, and always decodes output as UTF-8. execa was already a dependency for the agent fix runner, so this consolidates on one process-spawning library.ac114b4: Replace the glob dependency with fast-glob for the tsconfig.json scan during init. fast-glob was already in the dependency tree via find-workspaces, so this drops glob's transitive dependencies (minipass, path-scurry, etc.) from the install without changing behavior.v7.10.2Compare Source
Patch Changes
1c48c68: Enable Tailwind CSS class sorting (sortTailwindcss) in the Oxfmt preset. Classes inclass/classNameattributes and inclsx,cva,tw,twMerge,cn,twJoin, andtvcalls are now sorted using the same algorithm asprettier-plugin-tailwindcss, matching the behavior of the Prettier preset (which always loads the Tailwind plugin) and the Biome preset'suseSortedClassesrule. Projects without Tailwind installed are unaffected beyond class strings being sorted against the default theme, and oxfmt versions older than 0.35.0 ignore the option.dc78be4: Stop sorting TanStack route option keys in route files. TanStack Router's route option types are order-sensitive (head/componentinferloaderDatafrom properties declared before them), so the BiomeuseSortedKeyssource action rewrotecreateFileRouteliterals into an order that breaks type inference (loaderDatabecomesnever). The Biome TanStack preset now disablesuseSortedKeysfor route files, and the oxlint TanStack preset disablessort-keysthere so route files aren't caught between it andreact-doctor/tanstack-start-route-property-order.v7.10.1Compare Source
Patch Changes
d018b7f: Fix several agent fix mode (fix --claude/--codex) issues: runs no longer abort with ENOBUFS when linter JSON output exceeds 1MB, user-supplied format/reporter flags can no longer override the JSON reporter and break parsing, a stuck agent process is force-killed 10 seconds after the timeout instead of hanging forever, and the progress renderer no longer garbles TTY output when file paths and rule names exceed the terminal width.d018b7f: Fix Biome config migration leaving the legacy bare"extends": ["ultracite"]form in place, which breaks Biome's module resolution since the package has no root export. It's now mapped toultracite/biome/core.d018b7f: Fixultracite doctorreporting spurious failures: config checks now walk up parent directories (matchingcheck/fixand the linters themselves) so monorepo packages inheriting a root config pass,.oxlintrc.jsonis accepted as a valid oxlint config (with a migration suggestion), and Prettier/Stylelint configs declared viapackage.jsonkeys are recognized.d018b7f: Fixultracite initcorrupting existing Prettier/Stylelint/ESLint configs by writing an ESM module into JSON/YAML/TOML/CJS config files (e.g..prettierrc,eslint.config.cjs). Updates now write the default.mjsconfig instead and remove the incompatible file so it can't shadow the new one.d018b7f: Fixultracite check/fixmisrouting space-separated flag values (e.g.--max-warnings 10) into the file list, which scrambled the underlying linter invocation and made formatters fail on bogus targets. Positional files listed before a--separator are also kept as lint targets instead of being reclassified as passthrough, which could silently widen formatter runs to the whole project.d018b7f: Fix Lefthook and pre-commit YAML updates silently doing nothing on common config shapes: the Lefthook job is now inserted correctly whenjobs:isn't the first key underpre-commit:(and no longer matches ajobs:key in a different hook),repos: []in.pre-commit-config.yamlis handled, and shapes that can't be safely edited produce a warning instead of writing the file back unchanged.d018b7f: Fix the Husky integration overwriting an existing.husky/pre-commithook:ultracite initranhusky init, which unconditionally replaces the hook withnpm test. It now runs plainhuskyto set up the hooks infrastructure without touching the hook file.d018b7f: Fixultracite initdestroying user files it couldn't parse or merge: unparseabletsconfig.jsonfiles are no longer replaced with a minimal config, unparseable.vscode/.zedsettings are no longer overwritten wholesale, and lint-staged configs with function-valued entries are left untouched — all now warn and skip instead.d018b7f: Fix re-runningultracite initon an oxlint setup silently enabling the full js-plugins preset — the previously selected JS plugins are now preserved when no new selection is made. Init also no longer flips an explicit"type": "commonjs"in package.json to"module"; it warns instead.d018b7f: Fix switching linters removingstorybookfrom the project's dependencies. It was swept into the removal set as a peer ofeslint-plugin-storybook, but it's a user-facing tool a project may use independently of linting.d018b7f: Fix Stylelint target generation dropping directories with a dot in their name (e.g.app.web) and producing non-matching globs from Windows-style backslash paths. Framework detection also handles negated workspace patterns (!packages/legacy) again.d018b7f: Fix the CLI becoming a silent no-op (exiting 0 without linting anything) when a genericTESTenvironment variable is set, as is common in CI matrices. The internal test guard now usesULTRACITE_TEST.36c7b80: Move @typescript-eslint/utils from dependencies to devDependencies. It was accidentally shipped as a runtime dependency in 7.9.0, pulling eslint and the typescript-eslint packages into every consumer's install (including oxlint-only setups) via npm's automatic peer dependency installation. Nothing in the published package imports it — it only exists to support the workspace-internal rule-parity script.v7.10.0Compare Source
Minor Changes
cd0a36c: Add--claudeand--codexflags toultracite fix. After the normal autofix pass, remaining diagnostics are handed to the Claude Code or Codex CLI non-interactively, one agent run per affected file, with a live per-issue spinner that flips to ✓/✗ once the fix is verified by a re-lint. Fixes that don't survive verification are retried (up to 3 attempts per file) with the fresh diagnostics and feedback that the previous approach failed. Works with all three linter modes (Oxlint, Biome, ESLint); exits non-zero if any issues remain, matching the plainfixcontract.e089510: Scope React Doctor's framework-specific rules to per-framework add-on presets (#752)The
ultracite/oxlint/js-pluginspreset no longer enables React Doctor'snextjs-*and TanStack (query-*,tanstack-start-*) rules for every consumer. Rules likenextjs-no-img-elementandtanstack-start-no-anchor-elementfire on plain<img>/<a>JSX and recommend framework replacements, which falsely errored in Vite + React and other non-Next/non-TanStack projects.Those rules now live in two new add-on presets:
ultracite/oxlint/next/js-pluginsultracite/oxlint/tanstack/js-pluginsultracite initwires the matching add-on automatically when you select the framework together withoxlint-plugin-react-doctor. If you manageoxlint.config.tsby hand and use Next.js or TanStack, add the matching add-on preset toextendsalongsidejs-pluginsto keep those rules — or re-runnpx ultracite init.Also fixes re-running
initon a config that already extendsjs-pluginsproducing a duplicateimport jsPluginsdeclaration.Patch Changes
3320cd8: Update Biome to 2.5.6. No stable (non-nursery) rules were added, removed, or promoted between 2.5.3 and 2.5.6, so the preset configs are unchanged.477cd6e: Update ESLint to 10.8.0 and all ESLint plugins to their latest versions. Highlights:eslint-plugin-react-doctor0.9.3: the react preset expands from 149 to 417 rules, adopting the upstreamrecommendedset (react-router, three.js/r3f, ink, motion, remotion, zustand/valtio/mobx, and more) while excluding rules that duplicate already-enabledreact,react-hooks, andjsx-a11yrules. The next preset gainsnextjs-async-dynamic-api-not-awaitedandnextjs-metadata-url-consistency; the tanstack preset gainstanstack-start-missing-scripts,query-floating-mutate-async, andquery-no-mutation-in-effect-as-read.eslint-plugin-unicorn72: addsno-missing-local-resource,no-multiple-promise-resolver-calls,no-shorthand-property-overrides,no-transition-all,no-unnecessary-string-trim,no-useless-re-export,prefer-then-catch, andrequire-frontmatter-fields. CSS-only rules are excluded from the preset since they fail config validation for JS files.eslint-plugin-sonarjs4.2: adds 11 rules includingno-fixed-wait-in-tests,parameterized-tests,assertions-in-test-cases,prefer-native-lodash-alternative, andexplicit-test-skip.typescript-eslint8.65:@typescript-eslint/no-loop-funcand@typescript-eslint/no-restricted-importswere deprecated upstream in favor of the base rules, which now apply to TypeScript files.eslint-plugin-astro3: removesastro/no-omitted-end-tagsandastro/valid-compile.eslint-plugin-svelte3.22: addsno-bind-value-on-checkable-inputsandno-conflicting-module-names;no-restricted-html-elementsis now off because its schema requires a user-supplied element list.@angular-eslint/eslint-plugin22.1: addsinject-at-topandprefer-service-decorator.b81578b: Fix the useSortedPackageJson action not being executed by turning on assist actions for package.json-like files.9ec454a: Update oxlint to 1.76.0 and oxfmt to 0.61.0. New stable rules added to the presets:oxc/bad-match-all-arg,id-denylist,node/exports-style(core),react/function-component-definitionwith arrow-function components (react), andvitest/padding-around-test-blocks(vitest).node/no-top-level-awaitis off — top-level await is idiomatic in ESM, Astro frontmatter, and build scripts — and the ESLint preset'sn/no-top-level-awaitis now off to match. No rules were removed or promoted out of nursery.ba61c02: Fix generated oxlint.config.ts accessing plugin.name on ExternalPluginEntry without narrowing the string form, which caused a TypeScript error in projects that type-check the config (#753)v7.9.4Compare Source
Patch Changes
f480c12: Upgrade Biome to 2.5.3 to fix the LSP scanner deadlock (#10845) where editors get stuck on "Biome is scanning the project".1d70c40: Fail fast with an actionable message when Biome can't resolveultracite/biome/core. Biome resolves that config out of the project'snode_modules, so it errors with an opaque "module not found" whenever Ultracite isn't installed there — a statenpx ultracite check/bunx ultracite checkhide, because they run the CLI from a temp cache regardless.checkandfixnow detect it before invoking Biome and say what's actually wrong, anddoctorverifies that Ultracite resolves rather than just appearing inpackage.json(#750).38d10bc: Move Biome'snoUnknownAttributerule out of the core preset and into thereactpreset. The rule only recognises React's JSX attribute names, so projects using Solid, Svelte, Vue, or Qwik were incorrectly flagged for framework-standard attributes such asclass.cf723bd: Add interactive and non-interactive selection for optional Oxlint JS plugins during init.v7.9.3Compare Source
Patch Changes
dc6d760: Disable then/no-unpublished-importrule in the ESLint core config. This rule flags imports of packages that aren't listed as published dependencies, but it produces a lot of false positives in practice, so it's now turned off.15ecfba: Fixultracite init --linter eslintinstalling an unusable toolchain. The generated ESLint config importseslint-plugin-storybookunconditionally (which requires thestorybookpeer) and the generated Stylelint config extendsstylelint-config-standard/stylelint-config-idiomatic-order/stylelint-prettier, but none of those packages were installed — so a fresh ESLint setup failed to load with "Cannot find package 'storybook'" or "Could not find stylelint-config-standard". These four packages are now installed with the ESLint linter.2f73a41: Upgrade to oxlint 1.73.0 and oxfmt 0.58.0, and enable the new lint rules they introduce:no-unreachable-loop,unicorn/explicit-timer-delay, andunicorn/no-confusing-array-with.83b2783: Add an[astro]formatter mapping (astro-build.astro-vscode) to the oxlint VS Code editor settings generated byultracite init, since oxfmt doesn't format.astrofiles.d186c53: Move every JS-plugin-based rule set out of the Oxlint core and framework presets and into a single opt-inultracite/oxlint/js-pluginspreset. This coverseslint-plugin-githubandeslint-plugin-sonarjs(previously in core) as well asoxlint-plugin-react-doctor(previously bundled into thereact,next, andtanstackpresets). The core,react,next, andtanstackpresets now run entirely on Oxlint's native Rust rules, so new setups no longer install those dependencies and no longer pay the slower JS-plugin lint pass. To keep the extra ESLint-parity and React Doctor rules, installeslint-plugin-github,eslint-plugin-sonarjs, andoxlint-plugin-react-doctorand extendultracite/oxlint/js-pluginsalongsidecore(and your framework preset).057753e: Add performance benchmarks forultracite check/ultracite fixacross all three providers (oxlint, biome, eslint). A new CI job builds the PR and its base branch on the same runner, benchmarks them interleaved, and fails on a statistically significant regression (median ratio > 1.25x with Mann-Whitney U p < 0.05) so config changes can't silently slow the linters down again.v7.9.2Compare Source
Patch Changes
c335a1f: Add**/node_modulesand**/.gitto the shared ignore patterns. oxlint only skipsnode_moduleswhen a.gitignorelists it, so in projects without one,ultracite fixwould lint and autofix files insidenode_modules— corrupting installed packages (e.g. rewritingvarenum wrappers intypescript/lib/typescript.jsto self-referencingconst, causing "Cannot access 'Comparison' before initialization" when oxlint loads eslint-plugin-sonarjs). Fixes #737.v7.9.1Compare Source
Patch Changes
ecd10cc: Disablesonarjs/no-implicit-dependenciesandgithub/no-implicit-buggy-globalsin the oxlint and ESLint presets. Both produce false positives through oxlint's JS plugin bridge:no-implicit-dependencieshas no dependency-manifest resolution so it flags builtin (bun:test) and workspace imports as missing dependencies, andno-implicit-buggy-globalsmisreads module-scoped declarations such as Astro frontmatter as implicit globals.c76f59d: Disablesonarjs/file-name-differ-from-classin the oxlint and ESLint presets. It fires on any file whose name differs from an exported class, which is noise for the many config and module files that export objects rather than classes.29e30ce: Fold the github and sonarjs rules into the oxlint core preset. The standaloneultracite/oxlint/githubandultracite/oxlint/sonarjspresets are removed — ultracite ships framework presets, not individual plugins. Their rules now live inultracite/oxlint/core, so every oxlint setup gets eslint-plugin-github and eslint-plugin-sonarjs through oxlint's JS plugin bridge, matching how the ESLint preset already bundles them into core.This is a breaking change to generated configs:
ultracite/oxlint/githubandultracite/oxlint/sonarjsno longer exist. Re-runultracite initto regenerateoxlint.config.ts.Also fixed: nine sonarjs rules (
async-test-assertions,hooks-before-test-cases,no-duplicate-test-title,no-empty-test-title,no-floating-point-equality,no-forced-browser-interaction,no-trivial-assertions,prefer-specific-assertions,super-linear-regex) that exist in eslint-plugin-sonarjs but that oxlint's JS plugin bridge does not register. Naming them made oxlint hard-fail config parsing, which brokeultracite fix/checkfor oxlint projects using the sonarjs preset. They are omitted from oxlint core (still enabled in the ESLint preset), and a test now runs oxlint against core to catch this class of regression.The React Doctor, github, and sonarjs plugins are installed into your project's devDependencies at init (as the ESLint plugins already were), rather than bundled as dependencies of ultracite — oxlint resolves JS plugin specifiers from the project root, so they must be installed there directly.
8a4291f: Upgrade to Oxlint 1.72.0 and Oxfmt 0.57.0. Oxfmt 0.57 adds native CSS and GraphQL formatters. The five new non-nursery Oxlint rules from the 1.71/1.72 releases are already covered by the presets (node/no-sync,node/no-mixed-requires,unicorn/prefer-number-coercion,unicorn/max-nested-calls,vue/no-async-in-computed-properties). The markdown:::container-directive fence patch (which keepsproseWrap: "never"from folding fences into prose) was regenerated against the 0.57 bundle.Configuration
📅 Schedule: (in timezone Europe/Warsaw)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
This PR has been generated by Mend Renovate CLI.
ℹ️ Artifact update notice
File name: backend/go.mod
In order to perform the update(s) described in the table above, Renovate ran the
go getcommand, which resulted in the following additional change(s):Due to Go's usage of Minimal Version Selection (MVS), these packages have been updated to the minimum version available, so will still abide by
minimumReleaseAge=3 daysDetails:
golang.org/x/sysv0.47.0->v0.48.0modernc.org/libcv1.75.7->v1.77.1