ultracite@8.2.1
Patch Changes
-
7c4d3d3: The Biome
cypresspreset now declares the globals Cypress injects:cy,Cypress, and the Mocha and Chai ones (describe,it,beforeEach,expect, and the rest). The preset was empty, sonoUndeclaredVariablesreported every one of them in each*.cy.*spec and each file undercypress/. The ESLint preset already declared them. -
e1ac218: The Biome
nestjsandangularpresets now parse parameter decorators. Biome rejects them unlessjavascript.parser.unsafeParameterDecoratorsEnabledis on, so everycreate(@Body() cat: Cat)in a NestJS controller, and everyconstructor(@Inject(TOKEN) value)in Angular, was reported as “Decorators are not valid here”, andultracite fixapplied no lint fixes to those files. Both presets now turn the option on. -
e4702e2:
ultracite initno longer leaves Biome unable to start when it adds the presets to abiome.jsonthat already existed. The core preset reads ignore files from version control, and Biome only looks for them in the folder that holds its config, so a config in a package below the repository root, or in a folder with no repository at all, failed afterwards with “Biome couldn’t find an ignore file”. A new config already gotvcs.rootpointing at the repository, or a.gitignoreoutside one; an existing config now gets the same, unless it setsvcsitself. -
e8c2207:
ultracite fixno longer breaks dependency injection in NestJS and Angular projects. With thenestjsorangularpreset, a class a constructor injects, as inconstructor(private readonly cats: CatsService) {}, is only ever named as a type, sotypescript/consistent-type-imports(Oxlint and ESLint) anduseImportType(Biome) rewrote its import toimport type { CatsService }. The code still compiled, but the class was gone at runtime:emitDecoratorMetadatarecorded the parameter asFunction, and the framework could no longer resolve the dependency. Both presets now turn the rule off on all three linters. If a previousfixalready rewrote these imports, change them back to plain imports. -
0aecdd6:
ultracite doctornow fails abiome.jsonorbiome.jsoncthat has a syntax error. It read the file with a parser that recovers from errors, so a truncated config that still listedultracite/biome/corepassed with “Everything looks good!” whileultracite checkfailed with “Biome exited because the configuration resulted in errors”. -
14c0377: The ESLint
astropreset no longer reports areturnin a page’s frontmatter, such asreturn Astro.redirect("/404");, as"return" should be used inside a function(unicorn/prefer-module). Astro compiles frontmatter into the page’s render function, so the statement is valid. The Oxlint preset has allowed it since #805; the ESLint preset now does the same, and still reportsrequire,module.exports,__dirnameand__filenamein.astrofiles throughimport-x/no-commonjsandno-restricted-globals. -
fdc424e: Re-running
ultracite initon an ESLint project no longer discards or rewrites parts of aneslint.config.*that already uses Ultracite’s presets:- A config that passes the presets to
defineConfig()without spreading them, as indefineConfig(core, react, { rules }), was replaced by a bare[...core], losing its other presets, ignores and rules. Those presets are now read like spread ones. - A config init can’t regenerate (one exported through a variable, loaded with
require, or with the presets inside an entry’sextends) was replaced the same way. It is now left unchanged, with a warning to add new presets by hand. - A preset the config only applied to some files, such as
{ files: ["**/*.test.ts"], extends: [vitest] }, was also spread at the top level, so it applied to every file. It now stays scoped. - Presets were reordered to the order of their
importstatements. They now keep the order the config lists them in. - An
eslint.config.tsin a package without"type": "module"was rewritten aseslint.config.mjswith its type annotations still in it, which ESLint could not parse. ESLint loads a TypeScript config whatever the package’s module type, so it now stays a.tsfile.
- A config that passes the presets to
-
6a76431: The ESLint
vitestpreset no longer aborts the run on adescribe()whose title is the name of something the file imports, such asdescribe("sum", …)next toimport { sum } from "./sum". For that casevitest/prefer-describe-function-titleasks the type checker what the import is, which threw “You have used a rule which requires type information” in a config without the type-aware presets, so nothing was linted. The rule now runs inultracite/eslint/vitest/type-aware, with the plugin’s other type-checked rules. -
3f9fffd: The type declarations for
ultracite/oxfmtand theultracite/oxlint/*presets now describe what each preset sets, instead of the genericOxfmtConfigorOxlintConfig, where every field is optional. A config that spreads a preset’s fields now type-checks, such assortTailwindcss: { ...ultracite.sortTailwindcss, stylesheet: "./app/globals.css" }inoxfmt.config.ts, oroverrides: [...core.overrides, ...]andplugins: [...core.plugins, ...react.plugins]inoxlint.config.ts.selectJsPlugins()fromultracite/oxlint/js-pluginsnow declares theoverrides,rulesandsettingsit returns as always set, as it already didjsPlugins.Because the types are exact, two kinds of code that used to type-check now report errors: reading a field a preset doesn’t set, such as
react.overrides, and looking up a rule the preset doesn’t list, such ascore.rules["eslint/no-console"](core lists it asno-console). The field isundefinedat runtime, and the rule usually lives in another preset or under another name. The types are checked withexactOptionalPropertyTypesandnoUncheckedIndexedAccesson, so configs that use those options type-check as well. -
800bc5d:
ultracite checkandultracite fixnow expand a glob target on Biome and Oxlint setups, such asultracite check "src/**/*.ts"in apackage.jsonscript, where no shell expands the quoted pattern. Neither linter reads a glob as one. Biome looked for a file with that literal name, checked nothing and exited 0, so the script passed without linting anything; Oxlint exited 1 with “No files found to lint” after the formatter had run. A path that exists is still passed as it is, soapp/[slug]/page.tsxis unaffected, and ESLint setups are unchanged because ESLint, Prettier and Stylelint expand globs themselves. -
a5d7a16:
ultracite init --hooksno longer rewrites an agent settings file that has a syntax error. The file was read with a parser that recovers from errors and then written back whole, so a.claude/settings.jsonwith unresolved merge conflict markers, for example, came out with the markers gone and one side’sdenyrules silently chosen. Such a file is now left unchanged with a warning to fix it and re-runinit, as editor settings files already were. -
01c8ad3:
ultracite init --integrations husky lint-stagedno longer adds its own section to a.husky/pre-committhat already runs lint-staged. The section runs lint-staged too, so every commit ran it twice. The existing line is left to run the Ultracite taskinitadds to the lint-staged config. -
92db28c: With the
eslint-plugin-jsdocJS plugin enabled on Oxlint,ultracite fixno longer adds an empty/** */block above every undocumented export. The block was the fix forjsdoc-js/require-jsdoc, andultracite checkthen reported each one asjsdoc/no-blank-blocks, along with a missing@paramand@returns. The rule still reports a missing comment; writing it is left to you. -
ee6b830:
ultracite init --integrations lint-stagednow adds its entry to a CommonJS lint-staged config (lint-staged.config.jsin a package without"type": "module", or a.cjsfile) without rewriting the rest of it. It used to load the module and write backmodule.exports =followed by the evaluated value as JSON, which deleted comments and variables and froze every expression at its value on the machine that raninit: a task built fromprocess.env.CIkept the local branch, and one built from__dirnamekept that machine’s absolute path. A config that isn’t an object literal is left unchanged, with a warning naming the entry to add. -
467e264: The Oxlint workspace config
ultracite init --workspace-frameworkwrites now repeats only the properties the root config sets. It always readroot.env,root.ignorePatternsandroot.settings, but a root config has nosettingsunless a JS plugin needs them, so the generated file had a type error (Property 'settings' does not exist), and with--type-awarethe project failedultracite checkstraight afterinitwithtypescript(no-unsafe-assignment). If you addsettingsto the root config later, addsettings: root.settingsto the workspace config as well. -
8297ff4: Ultracite now recognises
.oxlintrc.jsonc, which Oxlint loads like.oxlintrc.json. A project whose only Oxlint config had that name got “No linter configuration found” fromcheck,fix,doctorandupgrade.ultracite initwrote anoxlint.config.tsnext to it without migrating it, after which Oxlint refused to run with both, anddoctorreported that setup as healthy.initnow moves its settings into the TypeScript config and removes it, as it does for.oxlintrc.json, anddoctorfails when it sits beside another Oxlint config. -
6c29ae8:
ultracite checkandultracite fixno longer fail on an ESLint setup when given a file with no extension, such as aDockerfile,Makefile,LICENSE,.gitignoreor.env. Ultracite took any target without an extension for a directory and handed Stylelint<file>/**/*.{css,scss,less}, which crashed withENOTDIR: not a directory, scandir. That blocked commits through the Husky, Lefthook and pre-commit hooks whenever one of those files was staged, and madefix --hookreport a failure to the agent that had edited one. Stylelint now skips them, as ESLint and Prettier already did. -
d100a28:
ultracite upgrade --versionnow refuses a release older than 7.11.0, the first with anupgradecommand, before it installs anything. It used to install that release and then hand off to itsupgrade, which failed witherror: unknown command 'upgrade'and left the oldultraciteinpackage.jsonbeside a linter nothing had synced to it. A major or dist-tag that still resolves to such a release is installed with a warning instead of the failed hand-off. -
1eea8d7: The
vitestpresets for Oxlint and ESLint no longer runvitest/prefer-importing-vitest-globals. The rule treats atestorexpectimported from anywhere butvitestas a global, and its fix addsimport { expect, test } from "vitest"above the existing import. The presets cover every*.test.*and*.spec.*file, so in a project that uses Vitest for unit tests and Playwright for end-to-end tests,ultracite fixrewrote each Playwright spec into a file that declaresexpectandtesttwice and no longer parses. Files importing frombun:testandnode:testwere reported the same way. If a previousfixadded the import, delete it.