Skip to content

Proposal: Drop vite from runtime dependencies #197

@fi3ework

Description

@fi3ework

Clear and concise description of the problem

Background

unplugin-vue currently lists vite in its dependencies:

// package.json
"dependencies": {
  // ...
  "vite": "^8.0.0-beta.9"
}

As a project built on top of unplugin — whose whole premise is "one plugin, many bundlers" — this feels a bit at odds with the goal. Users who consume unplugin-vue via the webpack, rspack, rollup, esbuild, or farm entries still end up pulling vite (and transitively rolldown, its native binding, esbuild, postcss, …) into their node_modules, even though they never touch the Vite entry.

Beyond the install footprint, depending on vite as a runtime dep also couples unplugin-vue to Vite's internal module layout. A concrete example today: transformWithOxc in current Vite is a re-export of rolldown/experimental's transformSync, which is itself a thin wrapper around the @rolldown/binding-* native binding. So importing this one helper from Vite transitively binds us to the rolldown native runtime, which is a fairly heavy implementation detail to inherit.

What the source actually uses from vite

Looking at src/, the runtime surface borrowed from vite is quite small:

symbol used in nature
normalizePath core/main.ts, core/index.ts, core/utils/descriptorCache.ts 2-line helper (path.posix.normalize + slash)
createFilter core/index.ts re-export of @rollup/pluginutils
isCSSRequest core/handleHotUpdate.ts one regex test
formatPostcssSourceMap core/style.ts ~10 lines
transformWithOxc core/main.ts TS→JS for <script lang="ts">

Plus a few import type-only references (HmrContext, ModuleNode, ViteDevServer).

In other words, none of these strictly require Vite as a runtime dependency — they either are tiny utilities, or can be served directly by the underlying package (oxc-transform for transformWithOxc, @rollup/pluginutils for createFilter).

Suggested solution

Proposal

Move vite from dependencies to devDependencies, and declare it as an optional peerDependency so that the type-only imports continue to resolve for downstream users who do have Vite installed:

"peerDependencies": {
  "vite": "^8.0.0-beta.9",
  "vue": "^3.2.25"
},
"peerDependenciesMeta": {
  "vite": { "optional": true }
}

Concretely, the change would:

  1. Add a small internal module (e.g. src/core/utils/vite.ts) that locally implements / re-exports the handful of helpers above:
    • normalizePath, isCSSRequest, formatPostcssSourceMap — inlined (a few lines each).
    • createFilter — re-exported from @rollup/pluginutils (added as a regular dep).
    • transformWithOxc — implemented directly on top of oxc-transform's transformSync (added as a regular dep), with sourcemap chaining done via the existing @jridgewell/{gen,trace}-mapping deps. We'd intentionally drop Vite's automatic tsconfig.json loading here, since callers (e.g. main.ts) already forward options.devServer?.config.oxc, which is the path most users rely on.
  2. Switch the vite runtime imports in src/core/** to import from this internal module. import type { … } from 'vite' references stay as-is and are covered by the optional peer dep.
  3. Move vite to devDependencies for local development, tests and type-checking.

Net effect on the dependency tree

Users of the non-Vite entries (webpack, rspack, rollup, esbuild, farm) would no longer transitively install vite and everything it brings along — rolldown and its platform-specific @rolldown/binding-* native binary, esbuild, postcss and its plugin chain, picomatch, tinyglobby, tsconfck, sirv, ws, and so on. For a project that just wants to compile Vue SFCs through, say, the webpack entry, this is a sizable amount of disk space, install time, and lockfile churn that goes away — and importantly, removes a platform-specific native binary from environments that have no use for it (CI images, Docker layers, Lambda bundles, etc.).

In return, two small focused packages enter dependencies:

  • @rollup/pluginutils — already ubiquitous in the rollup/vite plugin ecosystem and almost certainly already in most users' lockfiles.
  • oxc-transform — a single-purpose napi package that's strictly smaller than the rolldown native binding it replaces in this code path.

The Vite entry (unplugin-vue/vite) keeps working unchanged: users of that entry naturally already have Vite installed, which satisfies the optional peer dep, and the type-only import type references continue to resolve as before. The net delta for Vite users is essentially zero, while the delta for every other backend's users is strictly positive.

Alternative

No response

Additional context

Would a PR be welcome?

If this direction sounds reasonable, I'd be happy to open a PR with the full changes (I already have a working local prototype where pnpm build and pnpm test both pass, and the published dist/ no longer contains any Vite code). Of course, very open to feedback on the approach — including "we'd rather not, here's why", in which case I'll happily drop it. Thanks for maintaining unplugin-vue!

@fi3ework w/ @claude

Validations

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions