Repository navigation
vp create doesn't rewrite vite imports when adding a third-party template to a monorepo #3281
Description
Describe the bug
When vp create adds a third-party template to an existing Vite+ monorepo, the new package's
vite.config.ts keeps import { defineConfig } from "vite" while lazyPlugins is imported from
vite-plus, so vp check fails on vite-plus/prefer-vite-plus-imports. vp migrate rewrites this
import; vp create doesn't.
Expected: defineConfig is imported from vite-plus, as after vp migrate.
Actual (apps/web/vite.config.ts):
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { lazyPlugins } from "vite-plus";
Not framework-specific: adding a React app the same way
(vp create vite --no-interactive -- react-app --template react-ts) gives
import { defineConfig } from "vite" + import { lazyPlugins } from "vite-plus" too.
Outside a monorepo, the same vp create svelte@1.1.1 … command is correct
(import { defineConfig, lazyPlugins } from "vite-plus"), which points at the monorepo path.
Cause: rewriteMonorepoProject (packages/cli/src/migration/migrator/orchestrators.ts) never calls
rewriteAllImports. rewriteStandaloneProject does, and rewriteMonorepo runs it once for the whole
workspace (it calls rewriteMonorepoProject with deferLazyPluginWrapping = true). vp create adding a
package to a monorepo only calls rewriteMonorepoProject (create/bin.ts), which wraps lazy plugins
without having rewritten imports.
Suggested fix: in rewriteMonorepoProject, when deferLazyPluginWrapping is false, call
rewriteAllImports(projectPath, …) before wrapLazyPluginsInViteConfig, as rewriteStandaloneProject
does. Happy to send a PR if that helps.
Reproduction
https://github.com/DaniilKubarenko/vp-create-monorepo-vite-imports
Steps to reproduce
vp create vite:monorepo --no-interactive --directory repro --no-hooks --package-manager pnpm
cd repro
vp create svelte@1.1.1 --no-interactive -- web --template minimal --types ts --no-add-ons
cd apps/web
vp check
The linked repo is the unmodified output of the first three commands; `apps/website` and
`packages/utils` are the monorepo template's samples and unrelated.
### System Info
```shell
Node.js:
Version 26.13.0
Source engines.node
Bin Path C:\Users\<user>\AppData\Local\vite-plus\data\js_runtime\node\26.13.0\node.exe
Installed true
Mode managed
Package Manager:
Name pnpm
Version 12.16.1
Source devEngines.packageManager
Installed true
Mode managed
vp v1.4.0
Local vite-plus:
vite-plus v1.4.0
Tools:
vite v8.3.5
rolldown v1.2.15
vitest v5.0.5
oxfmt v0.74.0
oxlint v1.89.0
oxlint-tsgolint v7.0.2004
tsdown v0.23.1
Used Package Manager
pnpm
Logs
x vite-plus(prefer-vite-plus-imports): Use 'vite-plus' instead of 'vite' in Vite+ projects.
,-[vite.config.ts:3:30]
2 | import { sveltekit } from "@sveltejs/kit/vite";
3 | import { defineConfig } from "vite";
: ^^^^^^Validations
- Read the Contributing Guidelines.
- Check that there isn't already an issue for the same bug.
- Confirm this is a Vite+ issue and not an upstream issue (Vite, Vitest, tsdown, Rolldown, or Oxc).
- The provided reproduction is a minimal reproducible example.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Effort
Target date
Start date
Activity
voidzero-bot commentedon Oct 29, 2026
packages/cli/src/migration/__tests__/migrator.spec.tsvite.config.tscan be fixed manually or by runningvp migrateon the package, so it doesn't block core installs or builds.The root cause is in
packages/cli/src/migration/migrator/orchestrators.ts.vp createcallsrewriteMonorepoProject(...)when integrating a new package into a monorepo, but that function only wraps plugins and never runsrewriteAllImports. Standalonevp createandvp migrateboth rewrite imports before wrapping, so this path is the only one missing the step.I found a potential fix for this issue. The branch adds an
oxlintOwnerDirscollection beforerewritePackageJsonand callsrewriteAllImports(projectPath, ...)beforewrapLazyPluginsInViteConfigwhendeferLazyPluginWrappingisfalse. That mirrors the standalone and full-monorepo orchestrators, and it includes a regression test that fails before the fix and passes after.Full Triage Report
Triage Report: #3281
Issue Summary
When
vp createadds a third-party template package to an existing Vite+ monorepo, the newpackage's
vite.config.tskeepsimport { defineConfig } from "vite"while also importinglazyPluginsfrom"vite-plus". This causesvp checkto fail on thevite-plus/prefer-vite-plus-importsrule. The same template outside of a monorepo rewrites theimport to
"vite-plus"correctly.Environment
vpbinary: v1.4.0 from PATH (the repository's currentpackages/cli/package.jsonis also v1.4.0)Reproduction Path
Clone the reporter's reproduction repository:
Install dependencies from npm (the lockfile pins
vite-plus@1.4.0, matching the currentcheckout's package version):
Run
vp checkin the newly added Svelte app:cd apps/web vp checkObserved Result
apps/web/vite.config.tscontains:vp checkexits with code 1 and reports the exact rule violation described in the issue:Control:
packages/utils/vite.config.tsin the same monorepo (a Vite+ template package, not athird-party template) imports
{ defineConfig } from "vite-plus". Runningvp checkinpackages/utilspasses with noprefer-vite-plus-importserror, confirming the failure is scoped tothe newly added third-party project.
Source-Code Observation
packages/cli/src/migration/migrator/orchestrators.tsconfirms the reporter's analysis:rewriteStandaloneProjectcallsrewriteAllImports(...)beforewrapLazyPluginsInViteConfig(...).rewriteMonorepocallsrewriteAllImports(...)once for the whole workspace, then loops overpackages calling
rewriteMonorepoProject(..., deferLazyPluginWrapping = true)before wrappingplugins per-project.
rewriteMonorepoProjectdoes not callrewriteAllImports(...)whendeferLazyPluginWrappingisfalse.vp createcalls this function with the defaultdeferLazyPluginWrapping = false, so the third-party project's imports are wrapped but neverrewritten.
Diagnosis
Affected files and lines
packages/cli/src/create/bin.ts:1261and:1325rewriteMonorepoProject(...).rewriteStandaloneProject(...), which does rewrite imports.packages/cli/src/migration/migrator/orchestrators.ts:344-346rewriteStandaloneProjectrunsrewriteAllImportsand thenwrapLazyPluginsInViteConfig.packages/cli/src/migration/migrator/orchestrators.ts:505-515rewriteMonoreporunsrewriteAllImportsonce for the whole workspace and then passesdeferLazyPluginWrapping = trueto each per-projectrewriteMonorepoProjectcall.packages/cli/src/migration/migrator/orchestrators.ts:529-649rewriteMonorepoProjectperforms package.json/tsconfig merging and then runswrapLazyPluginsInViteConfigat line 642, but it never callsrewriteAllImports.packages/cli/src/create/templates/monorepo.ts:123and:158rewriteMonorepoProjectwithoutdeferLazyPluginWrapping, so default apps/libraries are affected too (latent).Incorrect logic
rewriteMonorepoProjectis designed to work in two modes:rewriteMonorepowithdeferLazyPluginWrapping = true— the workspace-widerewriteAllImportshas already run, so the project only needs plugin wrapping.vp createor the monorepo template generator) with the defaultdeferLazyPluginWrapping = false— there is no precedingrewriteAllImportscall anywhere, sothe project's Vite imports are left untouched.
Because plugin wrapping (
wrapLazyPluginsInViteConfig) only rewrites the plugin array and adds alazyPluginsimport fromvite-plus, the originaldefineConfigimport stays pointed atvite.The resulting file exactly matches the reproduction:
Why this causes the observed behavior
The monorepo root enables
vite-plus/prefer-vite-plus-imports(seen in the reproduction's rootvite.config.ts). The newly added package is a workspace member, so the rule applies to it. BecauserewriteMonorepoProjectdid not rewrite theviteimport tovite-plus,vp checkreports aviolation on the generated
vite.config.ts.Suggested fix approach
In
rewriteMonorepoProject, collectoxlintOwnerDirsand, whendeferLazyPluginWrappingisfalse, callrewriteAllImports(projectPath, silent, report, true, oxlintOwnerDirs)beforewrapLazyPluginsInViteConfig. This mirrorsrewriteStandaloneProjectand preserves the existingrewriteMonorepobehavior becausedeferLazyPluginWrappingstaystruethere.Edge cases
library created by the
vite:monorepotemplate. This is desirable — every Vite+ workspace membershould use
vite-plusimports.rewriteMonorepoProjectisshared.
deferLazyPluginWrapping = truepath unchanged to avoidredundant or conflicting rewrites from the workspace-level call.
Regression evidence
packages/cli/src/migration/__tests__/migrator.spec.tshas a test'wraps direct monorepo project rewrites used by create-monorepo flows', but its fixture alreadyimports
defineConfigfromvite-plus, so the missingrewriteAllImportscall is not exercised.No existing test drives a
defineConfigimport fromvitethroughrewriteMonorepoProject.Verification
Claim
The reporter observed that
vp create, when adding a third-party template to an existing Vite+monorepo, leaves
defineConfigimported from"vite"in the new package'svite.config.ts. Theexpected behavior is the same migration that
vp migrateperforms: rewriting the import to"vite-plus"sovp checkpasses.Evidence of intent
docs/guide/migrate.mdstates thatvp migrate"Rewrites imports where needed" as a coremigration step.
packages/cli/src/migration/migrator/orchestrators.ts:343and:504both contain the explicitcomment:
This comment precedes
rewriteAllImportsin bothrewriteStandaloneProjectandrewriteMonorepo,but the equivalent call is missing in
rewriteMonorepoProject.rewriteStandaloneProject(standalonevp createpath) andrewriteMonorepo(vp migratepath)already perform the rewrite; only the direct
rewriteMonorepoProjectpath used byvp createinside an existing monorepo skips it.
The default monorepo root config in the reproduction enables
vite-plus/prefer-vite-plus-imports: "error", so leaving theviteimport is a clear lintviolation.
git blameshows the per-projectwrapLazyPluginsInViteConfigcall at line 515-516 was added ina later commit (
688fe4aa, Charles Wang, 2026-09-25), while the import-rewrite step was neverbrought into
rewriteMonorepoProject. This looks like an oversight, not an intentional omission.gh search issues --repo voidzero-dev/vite-plusfinds no prior issue or design decision statingthat third-party monorepo apps should keep upstream
viteimports.Verdict
Bug. The behavior contradicts the established migration pattern, the explicit code comments, and
the lint rules that
vp createitself generates for Vite+ monorepos.Confidence
high
Fix
Changed files
packages/cli/src/migration/migrator/orchestrators.tspackages/cli/src/migration/__tests__/migrator.spec.tsRationale
rewriteMonorepoProjectis used both as a per-project helper insiderewriteMonorepo(
deferLazyPluginWrapping = true) and as a direct integration step whenvp createadds a packageto a monorepo (
deferLazyPluginWrapping = false). In the latter case there is no precedingcall to
rewriteAllImports, so the new package'sviteimports remain unchanged.the fix adds an
oxlintOwnerDirscollection beforerewritePackageJson(to preserve Oxlint pluginownership signals) and, when
deferLazyPluginWrappingisfalse, callsrewriteAllImportsbeforewrapLazyPluginsInViteConfig. This mirrors the standalone and full-monorepo orchestrators.Diff
Regression test
A new unit test in
migrator.spec.tssets up avite.config.tswithimport { defineConfig } from 'vite'and an inlineplugins: [react()]array, then callsrewriteMonorepoProjectdirectly. Itexpects:
'vite-plus';'vite'module specifier imports;lazyPlugins;report.rewrittenImportFileCountto be1andreport.wrappedPluginConfigCountto be1.This test fails before the fix (the import stays
'vite') and passes after.Validation
pnpm -F vite-plus build-tsrebuilt the CLI package without errors.pnpm test:unit -- packages/cli/src/migration/__tests__/migrator.spec.ts -t "rewrites vite imports"passed.pnpm test:unit -- packages/cli/src/migrationpassed — 28 files, 1670 tests.vp check packages/cli/src/migration/migrator/orchestrators.ts packages/cli/src/migration/__tests__/migrator.spec.tspassed.git status --short --untracked-files=allshows only the intended two source files modified.Alternatives considered
rewriteAllImportsunconditionally insiderewriteMonorepoProjectregardless ofdeferLazyPluginWrapping. Rejected becauserewriteMonorepoalready rewrites the whole workspacebefore per-project
wrapLazyPluginsInViteConfig; duplicating the rewrite per package would beinefficient and could re-process already-relocated files.
Final Result
This report was made by an LLM. The analysis may be wrong, and the potential fix might not work, but is intended as a starting point for exploring the issue.
I've opened a pull request with this fix: #3282