Skip to content

Should vite be a peerDependency instead of a full dependency? #4142

Description

@thebanjomatic

Clear and concise description of the problem

Currently Vitest lists vite as a full dependency using:

"vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0",

In my project which uses Vite 4.4.9:

"vite": "^4.4.9"

If I then run yarn add vitest, yarn will somewhat unhelpfully install both:
vite v5 and vite v4:

"vite@npm:^3.0.0 || ^4.0.0, vite@npm:^4.4.9":
  version: 4.4.9

"vite@npm:^3.1.0 || ^4.0.0 || ^5.0.0-0":
  version: 5.0.0-beta.2

The fact that it doesn't see that 4.4.9 satisfies the new condition and just re-use the existing version of vite is annoying, but it winds up causing issues type-checking the vite configuration because there is a type mismatch and two versions of vite are used. The plugins (which use vite as a peer dependency) are using vite 4 types, and "vitest/config" is using the vite 5 types:

image

Suggested solution

If vitest's vite dependency was listed as a peerDependency, then it would always use the version of vite that is referenced in the project's package.json file and resolutions would not be required to align the two versions.

In terms of potential disadvantages, using vitest would now also require explicitly installing vite. For many users, vite is already installed as a devDependency and no extra work is required. There may be some usage of vitest in projects that don't use vite to build, and for those consumers, this would be a breaking change as it would require adding both vitest and vite under devDependencies. This could be mitigated by shipping vite as both a dependency and peerDependency, but then users would occasionally have two versions of vite installed in node_modules if the version mismatched (currently the case today as well), but only one consistent version would be used (better than today's case).

I'm also not sure what the expected behavior is when doing something like using vitest at the root of a monorepo and specifying a vitest.workspace.ts to find and run tests in different packages. Each of those packages could have their own vite version. With the current behavior, a single version of vite is used for all the different packages, but that version is somewhat opaque to the user and hidden in the lock-file. If vite was a peerDependency then the behavior would be essentially identical, but the version of vite used would be enforced and controlled by the vite dependency at the root level, and would be more transparent to the user.

Generally, I feel like this behavior will be less surprising to users as I would venture to guess that if you polled consumers, they would expect vitest to already be using the same version of vite that is installed in the project, and the fact that they don't necessarily match has the potential for a number of headaches as to why your test behavior doesn't match your build behavior.

Alternative

You can work around the issues by manually massaging the lock file like:

"vite@npm:^3.0.0 || ^4.0.0, vite@npm:^4.4.9, vite@npm:^3.1.0 || ^4.0.0 || ^5.0.0-0":
  version: 4.4.9

But if you use a tool like renovate to do periodic lock file maintenance, this will get blown away each time it does so.

A more resilient workaround is to use a resolution:

"resolutions": {
  "vite": "^4.4.9"
}

Additional context

No response

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions