Skip to content

Releases: haydenbleasel/ultracite

ultracite@7.8.3

10 Jun 04:45
Immutable release. Only release title and notes can be modified.
f48ea13

Choose a tag to compare

Patch Changes

  • c863d09: Fix automatic editor extension installation during ultracite init.

    The whole command line (e.g. code --install-extension) was passed to
    spawnSync as the executable name, which always failed with ENOENT and
    silently fell back to the "install manually" message. The command is now split
    into the binary and its arguments, so the linter extension actually installs
    for VS Code-based editors.

  • 6888129: Enable the eslint/no-await-in-loop rule as an error in the core Oxlint
    preset.

    Awaiting inside a loop forces each iteration to run sequentially, which can
    lead to serious performance issues when the asynchronous operations could
    otherwise run concurrently. Promoting this rule to an error encourages
    collecting promises and resolving them together (e.g. with Promise.all)
    instead of blocking on each one in turn.

  • 62a9b5c: Fix the generated Husky pre-commit hook's error handling and section
    replacement.

    The standalone hook script set set -e and then tried to capture the
    formatter's exit code, re-stage files, and print a failure message — but a
    non-zero formatter exit terminated the script immediately, so none of that
    ever ran. The script now captures the exit code with || FORMAT_EXIT_CODE=$?
    so files are re-staged and failures are reported with the right exit code.

    Re-running ultracite init also deleted everything from the # ultracite
    marker to the end of the hook, including commands the user added after the
    ultracite section. The section is now terminated with an explicit
    # ultracite end marker and updates replace only the section between the
    markers (legacy sections without an end marker are detected by their closing
    echo line).

  • 6608ceb: Make the lint-staged integration idempotent and respect dedicated config
    files.

    package.json was always treated as the lint-staged config because the file
    exists in every project, so ultracite init wrote the lint-staged config into
    package.json even when a dedicated .lintstagedrc.* or
    lint-staged.config.* file was present — leaving two conflicting configs.
    package.json now only counts when it actually has a lint-staged key;
    otherwise the dedicated config file is updated (or .lintstagedrc.json is
    created).

    Re-running ultracite init also appended another npx ultracite fix entry on
    every run because the merge concatenates arrays. Updates are now skipped when
    the existing config already references ultracite.

  • 4e847f7: Insert -- before script arguments in npm hook commands.

    The post-edit hook command generated for npm projects was
    npm run fix --skip=correctness/noUnusedImports, where npm consumes the
    --skip flag itself instead of forwarding it to the script — so agent hooks
    ran a plain ultracite fix, including the unused-import removal the flag
    exists to prevent mid-edit. The generated command is now
    npm run fix -- --skip=correctness/noUnusedImports, matching the documented
    form.

  • ecb0d5b: Scope the Stylelint step of ultracite check and ultracite fix (ESLint mode)
    to style files.

    Stylelint was previously given the same targets as ESLint and Prettier (or .
    when no files were passed), so it tried to parse .ts/.json files as CSS and
    failed with CssSyntaxError. Style files now pass through unchanged, directory
    targets become **/*.{css,scss,sass,less} globs, other files are dropped, and
    the step is skipped entirely when no style targets remain.
    --allow-empty-input is passed so projects without CSS still succeed.

  • 61ea0a1: Fix the project-path write guard's error message and ordering.

    The "Refusing to write through directory outside project" error interpolated
    the node:path module instead of the offending file path, printing
    [object Object]. It now reports the actual path.

    writeProjectFile also created directories (mkdir -p) before running the
    path-escape check, so directories could be created outside the project before
    the guard threw. Validation now happens first; the parent-directory check
    resolves the nearest existing ancestor so writes into not-yet-created nested
    directories still work.

ultracite@7.8.2

07 Jun 21:42
Immutable release. Only release title and notes can be modified.
a9d704b

Choose a tag to compare

Patch Changes

  • 30971a8: Enable newly available Oxlint and Stylelint rules in the shared configs.

    For Oxlint, the core preset now enables eslint/prefer-named-capture-group,
    jsdoc/require-yields-description, node/callback-return,
    typescript/method-signature-style, and unicorn/import-style.

    The Vue preset now enables vue/component-definition-name-casing,
    vue/no-computed-properties-in-data, vue/no-deprecated-props-default-this,
    vue/no-expose-after-await, vue/no-reserved-component-names,
    vue/no-shared-component-data, vue/no-watch-after-await,
    vue/require-prop-type-constructor, vue/require-render-return,
    vue/require-slots-as-functions, vue/return-in-emits-validator,
    vue/valid-define-options, and vue/valid-next-tick.

    The Stylelint preset now enables display-notation with the short option.

ultracite@7.8.1

31 May 08:16
Immutable release. Only release title and notes can be modified.
e98d16d

Choose a tag to compare

Patch Changes

  • 8335be7: Build the CLI with bun build and a tsgo type-check gate instead of tsup.
  • f747449: Fix the Oxlint TanStack preset so route files under routes/ and app/routes/ are exempt from unicorn/filename-case, matching the documented 7.8.0 behavior.
  • 092597e: Fix generated oxlint.config.ts to be pre-formatted according to oxfmt rules, so ultracite check passes immediately after ultracite init without requiring a separate format step.
  • 81da6e8: Generate agent and editor hook commands through nypm's package-manager script helper.
  • 81da6e8: Keep hyphen-prefixed file operands from being forwarded to linters as options.

ultracite@7.8.0

26 May 06:25
Immutable release. Only release title and notes can be modified.
02e79d9

Choose a tag to compare

Minor Changes

  • 4e2fea0: Add a dedicated tanstack framework preset for Biome, ESLint, and Oxlint. The ESLint preset layers @tanstack/eslint-plugin-query, @tanstack/eslint-plugin-router, and @tanstack/eslint-plugin-start, while the Biome and Oxlint presets relax file-naming conventions for routes/ directories and the generated routeTree.gen.ts. Framework detection now maps @tanstack/react-query, @tanstack/react-router, and @tanstack/react-start to the new tanstack preset.

    Two behavior changes for existing consumers: TanStack Query rules now live in the tanstack preset instead of react, so projects that relied on Query rules must opt into tanstack; and TanStack Router projects now resolve to the tanstack preset rather than remix.

Patch Changes

  • 51a2af0: Recognize .biome.json and .biome.jsonc as valid Biome config files across the CLI. detectLinter, the doctor command, and the Biome config resolver now match the dot-prefixed names alongside biome.json/biome.jsonc, following Biome's documented configuration file resolution order. Closes #700.

  • 14b557c: Harden the generated standalone Husky hook by using git add -- "$file" when restaging formatted files. This prevents option-shaped filenames from being interpreted as Git options during the hook.

  • baa3dd0: Add ignorePatterns to the generated oxlint config at the root level so they are actually applied. Oxlint does not merge ignorePatterns through extends (see oxc-project/oxc#10223), so patterns set in the core preset were silently ignored. The generated config now sets ignorePatterns: core.ignorePatterns at the top level, reusing the patterns from the imported core preset.

  • bd27fd4: Add newly supported Oxlint rules from the latest release to the core, React, and Vitest presets:

    • Core: id-match, no-implicit-globals, no-implied-eval, prefer-arrow-callback, prefer-regex-literals, import/newline-after-import, jsdoc/require-throws-description, jsdoc/require-throws-type, and jsdoc/require-yields-type
    • React: jsx-a11y/control-has-associated-label, jsx-a11y/no-interactive-element-to-noninteractive-role, jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-element-to-interactive-role, react/no-object-type-as-default-prop, and react/no-unstable-nested-components
    • Vitest: vitest/padding-around-after-all-blocks
  • 14b557c: Reject symlinked generated config targets before writing project files. CLI config writers now route through a shared project-file write guard that checks for symlinks and project-root escapes before mutating files.

  • 14b557c: Validate package-manager names before generating agent and editor hook commands. Hook configuration now only uses supported package-manager prefixes, preventing unsafe values from being persisted into later-executed hook commands.

  • 14b557c: Reject unsupported package-manager names during ultracite init. Explicit --pm values and detected packageManager metadata are now runtime-validated against the supported package managers before dependency installation, preventing malicious project metadata from selecting an arbitrary executable.

ultracite@7.7.0

10 May 22:21
Immutable release. Only release title and notes can be modified.
129e754

Choose a tag to compare

Minor Changes

  • 24b0d27: Wire up the nestjs ESLint preset to actually enforce rules. Previously the preset exported an empty const config = [], meaning users who imported ultracite/eslint/nestjs got nothing. It now layers @darraghor/eslint-plugin-nestjs-typed (22 rules covering NestJS conventions, dependency injection correctness, and class-validator/Swagger usage) using the same dynamic-enable pattern as the other framework presets.

    Consumers who already had the empty preset in their config may see new violations on first run.

Patch Changes

  • 161418a: Add missing Biome stable rules to the core config:

    • suspicious/noDuplicateDependencies"error" — flags a dependency listed multiple times in the same group, or across dependencies and devDependencies, in package.json.
    • suspicious/useDeprecatedDate"off" — GraphQL-only convention requiring a deletionDate argument on @deprecated; too opinionated for the default preset.
  • 9a2b548: Pin @angular-eslint/eslint-plugin to ^21.3.1 in packages/cli/package.json. Previously declared as "latest", which defeats lockfile reproducibility and means each bun install could pull a newer version than what was tested at publish time. The current resolved version (21.3.1) is unchanged.

  • 44f6d7f: Align ESLint presets with the oxlint configs (the maintained source of truth). Mostly tightens ESLint where oxlint was stricter; a few documented behavioural exceptions oxlint carries (rule conflicts, bun:test compat) are mirrored back.

    coreeslint.mjs now enforces complexity, no-unused-private-class-members, sort-keys, sort-vars, and full prefer-destructuring (object + array). typescript.mjs now enforces no-confusing-void-expression, no-misused-promises, prefer-readonly, strict-boolean-expressions, and sets return-await: ["error", "always"]. import.mjs now sets consistent-type-specifier-style: ["error", "prefer-top-level"].

    next — added next-env.d.ts override that disables import-x/no-unassigned-import on the generated file.

    remix — added routeTree.gen.ts override that disables unicorn/filename-case and unicorn/no-abusive-eslint-disable on the generated file.

    react — disabled react/jsx-boolean-value, react/no-unknown-property, and react/only-export-components to match oxlint.

    jest — broadened test globs to **/*.{test,spec}.{ts,tsx,js,jsx} + **/__tests__/**/*.{ts,tsx,js,jsx} (previously missed *.spec.* and __tests__/). Disabled no-empty-function and promise/prefer-await-to-then in test scope. Disabled jest/require-hook, jest/no-conditional-in-test, jest/no-hooks, jest/prefer-expect-assertions to mirror oxlint's bun:test/mocking accommodations.

    vitest — same test-glob broadening; same no-empty-function / promise/prefer-await-to-then test-scope disables. Removed the prefer-importing-vitest-globals and prefer-to-have-been-called-times disables (oxlint enforces these). Added prefer-lowercase-title: off and valid-title: off to resolve the documented conflict with prefer-describe-function-title (#665).

  • 63f6a18: Drop the redundant react-hooks/exhaustive-deps: "error" override in config/eslint/react/rules/react-hooks.mjs. The dynamic-enable pattern already sets every non-deprecated react-hooks/* rule to "error", so the override was dead code. No behavior change.

  • 5a0ce67: Refresh the misleading header comment in config/eslint/core/rules/eslint-typescript.mjs. The disables for the formatting rules (brace-style, comma-dangle, indent, etc.) used to defer to @typescript-eslint's typed equivalents, but those rules were removed in v8. They're now disabled because Prettier/Oxfmt owns formatting. Updated the comment to reflect the actual rationale.

  • d681f70: Clean up config/eslint/core/rules/typescript.mjs: remove 22 stale overrides that referenced rules no longer present in @typescript-eslint/eslint-plugin v8.

    Most were formatting rules moved out to @stylistic (block-spacing, brace-style, comma-dangle, comma-spacing, func-call-spacing, indent, key-spacing, keyword-spacing, lines-around-comment, lines-between-class-members, member-delimiter-style, no-extra-parens, object-curly-spacing, padding-line-between-statements, quotes, semi, space-before-blocks, space-before-function-paren, space-infix-ops, type-annotation-spacing). The remaining two (no-type-alias, sort-type-union-intersection-members) were removed/deprecated upstream. All were dead no-ops — no behavior change.

ultracite@7.6.5

08 May 15:47
Immutable release. Only release title and notes can be modified.
d421d12

Choose a tag to compare

Patch Changes

  • 3e08c25: Fix ultracite init failing with npm error No workspaces found! in npm monorepos. When isMonorepo() was true, nypm was passed workspace: true, which translates to --workspaces for npm — that installs in every workspace package and errors when patterns match nothing. We now skip the workspace flag for npm (the default root install is what we want) while preserving the flag for pnpm (--workspace-root) and yarn classic (-W). Applies to ultracite, husky, lefthook, and lint-staged installs.

ultracite@7.6.4

08 May 01:11
Immutable release. Only release title and notes can be modified.
b4e4649

Choose a tag to compare

Patch Changes

  • aba89bb: Add new oxlint 1.63.0 rules:

    • eslint/logical-assignment-operators"error" — prefer ||=, &&=, ??= over their longhand equivalents; aligns with the modern-JS baseline.
    • eslint/require-unicode-regexp"error" — require the u (or v) flag on regex literals for correct Unicode handling.
    • eslint/no-restricted-properties"off" — purely a project-specific allowlist; no useful default to enforce.
    • unicorn/no-negated-condition"error" — newly split from the eslint version; the unicorn variant additionally covers ternary expressions and complements the existing eslint/no-negated-condition.
    • jsx-a11y/interactive-supports-focus"error" — interactive elements (click handlers, role="button", etc.) must be keyboard-focusable; matches the rest of the a11y baseline.
    • vue/return-in-computed-property"error" — computed properties must return a value; missing return silently breaks reactivity.
    • vue/no-deprecated-model-definition"error" — flags Vue 2 model: { ... } usage; Vue 3 is the supported target.
    • vitest/prefer-mock-return-shorthand"error", vitest/no-unneeded-async-expect-function"error", vitest/prefer-to-have-been-called-times"error", vitest/prefer-snapshot-hint"error" — newly split out from the jest plugin; mirrors the existing jest config which has all four enabled.
    • vitest/require-hook"off" — newly split out from jest; disabled to mirror jest config (bun:test mock.module() must be called at top level).
  • 522155e: Set typescript/return-await to ["error", "always"] to resolve a circular conflict between eslint/require-await, typescript/promise-function-async, and typescript/return-await on Promise-returning functions outside try/catch. With the default in-try-catch mode, autofixers chase each other: promise-function-async adds async, require-await then demands an await, and return-await removes any return await outside a try/catch — leaving no resolvable state. The "always" mode keeps return await everywhere, breaking the cycle while preserving consistent stack traces.

ultracite@7.6.3

03 May 18:08
Immutable release. Only release title and notes can be modified.
f27d226

Choose a tag to compare

Patch Changes

  • f584d93: Disable unicorn/number-literal-case due to oxc-project/oxc#21949.

  • ef5c3ae: Fix ultracite check and ultracite fix short-circuiting after the formatter step. Previously, when the formatter (oxfmt or Prettier) exited non-zero, the linter (oxlint, ESLint, Stylelint) was never invoked, hiding lint errors until formatting was clean. The commands now run every step, accumulate failures, and exit with the first failing tool's status. Fixes #690.

  • 3ecb159: Fix the generated oxfmt.config.ts template, which used extends: [ultracite] — a key oxfmt does not recognize, so the preset was silently dropped and built-in options like sortImports never took effect. The template now spreads the preset (...ultracite) so its options are actually applied. Fixes #689.

  • 5a18ec8: Add new oxlint 1.61.0 and 1.62.0 rules:

    • eslint/func-name-matching"error" — function names should match the variable they're assigned to; matches the project's strict baseline.
    • eslint/no-underscore-dangle"off" — common patterns like _id (Mongo) and _internal make this rule too noisy in practice.
    • typescript/explicit-member-accessibility"off" — forcing public/private on every class member is verbose and not idiomatic in modern TS.
    • jest/prefer-expect-assertions"off" and vitest/prefer-expect-assertions"off" — requiring expect.assertions(n) in every test is too strict for general use; not all tests need explicit assertion counts.
    • vitest/max-expects"error" and vitest/max-nested-describe"error" — newly split out from the jest plugin; mirrors the existing jest config which has both enabled.
    • vitest/no-conditional-in-test"off" — newly split out from jest; disabled to mirror jest config (mock factories use conditionals for path-based routing).
    • vitest/no-hooks"off" — newly split out from jest; disabled to mirror jest config (bun:test uses beforeEach for mock.restore()).
    • react/forbid-component-props"off" — parity with the ESLint config, which already disables this rule.

ultracite@7.6.2

26 Apr 01:04
Immutable release. Only release title and notes can be modified.
019d7ab

Choose a tag to compare

Patch Changes

  • 5be860c: Automatically detect frameworks during the init process.
  • 10d9e95: Support -v as a short alias for --version on the CLI (previously only -V worked).
  • 8ff1b96: Fix update command not migrating legacy ultracite/<name> extends entries to ultracite/biome/<name> (e.g. ultracite/core, ultracite/react, ultracite/type-aware, etc.).
  • 5e055ce: Ignore Cloudflare Workers' generated worker-configuration.d.ts (produced by wrangler types), matching the existing handling of next-env.d.ts.
  • 9cc7416: Add a universal editor target that creates .vscode/settings.json for every VS Code-based editor (VS Code, Cursor, Windsurf, CodeBuddy, Antigravity, IBM Bob, Kiro, Trae, Void) with a single selection. The init prompt now offers a "Universal" option, and --editors universal works as an alias on the CLI.

ultracite@7.6.1

22 Apr 16:10
7b9577e

Choose a tag to compare

Patch Changes

  • 2fbded9: Disable the typescript/prefer-readonly-parameter-types Oxlint rule. While the rule is useful for user-authored types, it fires on virtually every parameter that touches a third-party type (Express Request/Response, React events, Node Buffer, ORM models, DOM APIs) because those types aren't deeply readonly internally — leaving users with unfixable violations. Matches the existing ESLint config, which already has this rule off.
  • 617affd: Fix dist/, .next/, **/*.gen.*, and other strong-negation (!!) ignore globs being dropped when a consumer's biome.jsonc extends ultracite/biome/core and also defines its own files.includes. The globs moved into config/shared/ignores.jsonc in 7.5.9 were transitively extended through biome/core, and Biome's extend merge doesn't carry files.includes through a two-level chain when the middle config lacks its own entry. The patterns are now inlined directly in biome/core's files.includes (still generated from config/shared/ignores.mjs), matching the pre-7.5.9 behavior.
  • d681e08: Remove the nonexistent import-x/enforce-node-protocol-usage rule from the ESLint core config, which caused ESLint 9 to throw Could not find "enforce-node-protocol-usage" in plugin "import-x". Node protocol enforcement is already covered by unicorn/prefer-node-protocol.