Skip to content

The coverage.autoUpdate rewrite strips intended newlines from config files #9227

Closed

Description

@kaspar-p

Describe the bug

Using coverage.autoUpdate in a configuration to update the thresholds will strip intended whitespace/newlines from the bottom of configuration files.

This goes against common linting rules like eslint/prettier-prettier that enforce newlines at the end of files, meaning that on-update the build will generally fail CI builds, if linting is enabled there.

The workaround is to ignore the role in the second-to-last line:

import { configDefaults, defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,

    coverage: {
      enabled: true,
      provider: 'istanbul',
      thresholds: {
        autoUpdate: (newThreshold: number): number =>
          newThreshold >= Math.floor(newThreshold) + 0.2 ? Math.floor(newThreshold) : Math.floor(newThreshold) - 1,
        statements: 56,
        branches: 47,
        functions: 58,
        lines: 56,
      },
      reporter: ['json', 'json-summary', 'text', 'cobertura', 'html'],
      include: ['lib/**/*.ts'],
    },

    exclude: [...configDefaults.exclude, '**/dist/**/*'],
  },
  // eslint-disable-next-line prettier/prettier
});

but it'd be nice if only the thresholds part of the file was replaced, preferably just the numbers. In general it's hard to conform to all linters, since something like 56.<many digits>1 may want to wrap to a new line:

lines:
  56.0000000000000000000000000000000000001

so maybe the real solution is to offer a post-update script that could be run to format. Appending to our "test" script to format is ugly and also doesn't work in Monorepos that lint/format from the top-level, not individually in each package.

Reproduction

Configure eslint with the prettier plugin.

Run tests, the coverage doesn't even have to change.

See that the newline is stripped off of vitest.config.ts.

System Info

System:
    OS: Linux 5.10 Amazon Linux 2
    CPU: (32) arm64 unknown
    Memory: 101.18 GB / 123.55 GB
    Container: Yes
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 20.18.0 - ~/.local/share/mise/installs/node/20/bin/node
    npm: 10.8.2 - ~/.local/share/mise/installs/node/20/bin/npm
  npmPackages:
    vitest: ^3 => 3.2.4

Used Package Manager

npm

Validations

Activity

  1. added theissue type on Dec 10, 2025
  2. AriPerkkio commented on Dec 11, 2025

    @AriPerkkio
    Member

    magicast seems to have bunch of format options. We need to check if using these fixes the issue. https://github.com/unjs/magicast/blob/main/src/format.ts

  3. nami8824 commented on Dec 13, 2025

    @nami8824
    Contributor

    I took a closer look at the formatting options exposed by the code generator currently used by Vitest (via Vite/magicast), and it appears that only the following CodeFormatOptions are available:

    interface CodeFormatOptions {
      tabWidth?: number;
      useTabs?: boolean;
      wrapColumn?: number;
      quote?: "single" | "double";
      trailingComma?: boolean;
      arrayBracketSpacing?: boolean;
      objectCurlySpacing?: boolean;
      arrowParensAlways?: boolean;
      useSemi?: boolean;
    }

    From what I can tell, there doesn’t seem to be an option to preserve or explicitly insert a final newline at EOF.

    Given this, I was wondering if there might be a couple of possible directions to address this issue:

    1. Updating the magicast dependency, if a newer version provides support for preserving a final newline, though this might have a broader impact on config generation.
    2. Handling the final newline within Vitest itself, for example by wrapping generate() in parseConfigModule and appending a newline when the generated code does not already end with one.

    The second approach seems like it could be a minimal and self-contained way to avoid breaking common linting rules, but I’d appreciate feedback on which direction would be preferred.

  4. AriPerkkio commented on Dec 13, 2025

    @AriPerkkio
    Member

    Looking at recast (used by magicast), trailing new lines should already be preserved:

    No idea why it doesn't work here. 🤔

  5. AriPerkkio commented on Jan 12, 2026

    @AriPerkkio
    Member

    Figured out a way to work-around this with magicast:

  6. self-assigned this
    on Jan 12, 2026
  7. kaspar-p commented on Jan 15, 2026

    @kaspar-p
    Author

    That's awesome, thank you!

  8. locked and limited conversation to collaborators on Jan 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

feat: coverageIssues and PRs related to the coverage feature

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions