Clear and concise description of the problem
Background
unplugin-vue currently lists vite in its dependencies:
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:
Concretely, the change would:
- 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.
- 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.
- 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
Clear and concise description of the problem
Background
unplugin-vuecurrently listsvitein itsdependencies: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 consumeunplugin-vuevia thewebpack,rspack,rollup,esbuild, orfarmentries still end up pullingvite(and transitivelyrolldown, its native binding,esbuild,postcss, …) into theirnode_modules, even though they never touch the Vite entry.Beyond the install footprint, depending on
viteas a runtime dep also couplesunplugin-vueto Vite's internal module layout. A concrete example today:transformWithOxcin current Vite is a re-export ofrolldown/experimental'stransformSync, 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
viteLooking at
src/, the runtime surface borrowed fromviteis quite small:normalizePathcore/main.ts,core/index.ts,core/utils/descriptorCache.tspath.posix.normalize+slash)createFiltercore/index.ts@rollup/pluginutilsisCSSRequestcore/handleHotUpdate.tsformatPostcssSourceMapcore/style.tstransformWithOxccore/main.ts<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-transformfortransformWithOxc,@rollup/pluginutilsforcreateFilter).Suggested solution
Proposal
Move
vitefromdependenciestodevDependencies, and declare it as an optionalpeerDependencyso that the type-only imports continue to resolve for downstream users who do have Vite installed:Concretely, the change would:
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 ofoxc-transform'stransformSync(added as a regular dep), with sourcemap chaining done via the existing@jridgewell/{gen,trace}-mappingdeps. We'd intentionally drop Vite's automatictsconfig.jsonloading here, since callers (e.g.main.ts) already forwardoptions.devServer?.config.oxc, which is the path most users rely on.viteruntime imports insrc/core/**to import from this internal module.import type { … } from 'vite'references stay as-is and are covered by the optional peer dep.vitetodevDependenciesfor 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 installviteand everything it brings along —rolldownand its platform-specific@rolldown/binding-*native binary,esbuild,postcssand 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-onlyimport typereferences 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 buildandpnpm testboth pass, and the publisheddist/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