Skip to content

refactor: replace @voidzero-dev/vite-plus-test with upstream vitest@4.1.5#1588

Open
Brooooooklyn wants to merge 44 commits into
mainfrom
refactor/replace-vite-plus-test-with-vitest
Open

refactor: replace @voidzero-dev/vite-plus-test with upstream vitest@4.1.5#1588
Brooooooklyn wants to merge 44 commits into
mainfrom
refactor/replace-vite-plus-test-with-vitest

Conversation

@Brooooooklyn
Copy link
Copy Markdown
Member

@Brooooooklyn Brooooooklyn commented May 15, 2026

Summary

Deletes the bundled @voidzero-dev/vite-plus-test wrapper and consumes upstream vitest@4.1.5 (plus @vitest/browser*) directly. The vite redirection role that drove the wrapper is now handled cleanly by package-manager overrides (vite@voidzero-dev/vite-plus-core), so the bundle was dead weight that lagged upstream releases.

Public API contract preserved:

  • vite-plus/test* IS the public test API — existing user code (import { vi } from 'vite-plus/test', etc.) is NEVER rewritten.
  • New vite-plus users don't install vitest or @vitest/* separately; they come in transitively as direct deps of vite-plus.
  • vp migrate on an upstream-vitest project still forward-migrates vitest, vitest/*, @vitest/browser*, declare-module specifiers, and /// <reference types> directives to the vite-plus/test* surface (one-time transition).

Notable changes:

  • packages/cli/build.ts: syncTestPackageExports auto-generates ./test/* shims from upstream vitest's exports map, plus ./test/<provider> and ./test/browser/providers/<short> shims projected from each @vitest/browser-* package's exports.
  • packages/cli/package.json: adds @vitest/browser, @vitest/browser-playwright, @vitest/browser-preview, @vitest/browser-webdriverio as direct catalog deps pinned to 4.1.5.
  • crates/vite_global_cli/src/commands/version.rs: vitest ToolSpec points at the vitest package directly.
  • packages/cli/src/resolve-test.ts: resolves vitest/package.json and reads bin.vitest so vp test invokes upstream vitest.
  • packages/cli/src/utils/constants.ts: drops vitest from VITE_PLUS_OVERRIDE_PACKAGES; only vite remains a managed key.
  • packages/cli/src/migration/migrator.ts:
    • Adds an isVitestAdjacent flag that flips needVitePlus = true for projects with packages like vitest-browser-svelte even when there's no vite/oxlint/tsdown to migrate.
    • Adds pruneLegacyWrapperAliases / pruneYamlMapLegacyWrapperAliases sweeps that rewrite stale vitest: npm:@voidzero-dev/vite-plus-test@* aliases to ^4.1.5 (so existing catalog: refs keep resolving) and drop other stale wrapper-targeted keys.
  • packages/cli/src/migration/bin.ts: adds a handleInstallResult helper so failed reinstalls warn the user, append to report.warnings, and flip process.exitCode instead of being silently reported as success.

User-visible behavior changes

vp test -h and live test runs now show vitest's native banner (vitest/<semver>, RUN v<semver> <cwd>) instead of the wrapper-rebranded output (vp test/<semver>, RUN <cwd>). This is the tradeoff for delegating directly to upstream vitest without a wrapper layer.

Test plan

  • cargo test -p vite_migration --lib: 167 tests pass
  • pnpm exec vitest run (packages/cli): 374 tests pass
  • pnpm bootstrap-cli succeeds
  • pnpm -F vite-plus snap-test-global + snap-test-local: all fixtures regenerated; diffs only reflect expected behavior (forward import rewrites, @vitest/browser* removed from user devDeps, playwright/webdriverio preserved as peers, stale vite-plus-test catalog aliases normalized to ^4.1.5).
  • Manual end-to-end: vp test on a fixture using import { vi } from 'vite-plus/test'; vi.mock(...). See "Follow-up" below.
  • Manual end-to-end: vp migrate on a fresh upstream-vitest project.
  • pnpm install clean: zero @voidzero-dev/vite-plus-test references in the lockfile, browser-provider packages installed transitively via vite-plus.

Follow-up

@vitest/mocker hoists vi.mock(...) calls only when the import source is the literal string 'vitest' (@vitest/mocker@4.1.5/dist/chunk-hoistMocks.js hardcodes hoistedModule = "vitest"). User code that imports vi from 'vite-plus/test' won't get its mocks hoisted, which silently breaks mocking. The plan is to contribute upstream a hoistedModule?: string | string[] option to hoistMocks() so vite-plus can pass ['vitest', 'vite-plus/test']. Tracked as a separate effort — this PR ships the wrapper removal independently; the mocker fix can land later as a @vitest/mocker point release.

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it rewires how vite-plus/test* shims and workflow overrides are generated/resolved (including Bun-specific tgz aliasing), which can break runtime/TS resolution if any export patterns or peer-dep constraints drift.

Overview
Switches Vite+ test support from the internal @voidzero-dev/vite-plus-test package to upstream vitest@4.1.5 plus @vitest/browser*, while keeping the public import surface vite-plus/test* via regenerated shims and updated export mapping (including browser provider aliases and context/runtime shims).

Updates automation and CI to match the new model: upgrade-deps.ts now bumps vitest and @vitest/browser* catalog entries and also rewrites a VITEST_VERSION TS constant and the test-vp-create workflow pins; workflows/release publishing stop packing/publishing the removed test package and add a Bun-only vite tgz masquerade to satisfy Bun’s stricter peer checks. Migration/import rewriting is adjusted to include .cjs/.cts files and to stop rewriting declare module 'vitest'/@vitest/* augmentations, and docs/README are updated to remove instructions about overriding vitest to the deleted wrapper.

Reviewed by Cursor Bugbot for commit eaa3344. Bugbot is set up for automated code reviews on this repo. Configure here.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 15, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit eaa3344
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a0c771a4ebeee00080147e6

Comment thread packages/cli/src/migration/migrator.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 365a61de42

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/resolve-test.ts
Brooooooklyn added a commit that referenced this pull request May 15, 2026
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fafa67971

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/build.ts
Comment thread .github/scripts/upgrade-deps.ts
Comment thread packages/cli/src/migration/bin.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ec69abaadb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/package.json
Comment thread packages/cli/src/migration/migrator.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 381b6e2c20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/migration/migrator.ts Outdated
Comment thread packages/cli/build.ts
Brooooooklyn added a commit that referenced this pull request May 15, 2026
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 5c48da8 to 39efcbf Compare May 15, 2026 13:15
Comment thread packages/cli/src/migration/bin.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39efcbf239

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/vite_migration/src/import_rewriter.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2fceee5296

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/resolve-test.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b888329d76

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/migration/migrator.ts
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Upstream blockers (still needed after the fixes in this PR)

  • @vitest/mocker: the static hoister hardcodes hoistedModule = "vitest" so vi.mock() calls authored as import { vi } from 'vite-plus/test' are silently not hoisted — we ship a defineConfig-injected pre-stage Vite plugin (commit d69fe03) that rewrites 'vite-plus/test''vitest' at transform time as a workaround, but the long-term fix is exposing hoistedModule as a configurable option upstream.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form.

Comment thread .github/workflows/test-vp-create.yml Outdated
Comment thread ecosystem-ci/patch-project.ts Outdated
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Upstream blockers (updated)

After the latest fixes (21937c5b1), the remaining upstream items are:

  • @vitest/mocker: hardcodes hoistedModule = "vitest" in its static hoister, so any redistributor that surfaces vi under a different specifier breaks vi.mock() silently — we now rewrite 'vite-plus/test''vitest' in the Rust migrator (commit 21937c5) plus keep a runtime Vite plugin (d69fe03) as a safety net, but the long-term fix is exposing hoistedModule as a configurable option upstream so vite-plus/test could also be recognized natively.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form (only vp-config E2E remains failing on this).

@Brooooooklyn
Copy link
Copy Markdown
Member Author

Final status (after b6b5b8b)

The aggressive Rust source-rewrite in 21937c5 (which made 'vitest' canonical) was reverted because it broke vp create templates that use from "vitest" and rely on the forward rewrite to resolve to vite-plus/test. The reverse-direction migration was over-reach and the cure was worse than the disease for the affected projects (vinext's 4 vi.mock files).

Confirmed upstream blockers:

  • @vitest/mocker: hardcodes hoistedModule = "vitest" in its static hoister, so any redistributor surfacing vi under a different specifier breaks vi.mock() silently — affects ~4 files in vinext that import vi from vite-plus/test (the runtime plugin in d69fe03 handles non-mocker vi usage but cannot intercept the static hoister which runs in vitest's own plugin pipeline). The long-term fix is exposing hoistedModule as a configurable option upstream.
  • rolldown/oxc parser: rejects the legal TypeScript syntax import type Default, { Named } from 'mod', breaking vp build against .d.ts files such as postcss@8.5.8's that use this form (only vp-config E2E remains failing on this).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6b5b8b0bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/index.ts
Comment thread .github/scripts/upgrade-deps.ts
Comment thread crates/vite_migration/src/import_rewriter.rs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60b7d0f0ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/index.ts
Comment thread packages/cli/src/utils/constants.ts
@Brooooooklyn
Copy link
Copy Markdown
Member Author

Final status — branch HEAD 3ffb7cd

E2E now at expected steady state. The two remaining upstream items:

  • @vitest/mocker hardcodes hoistedModule = "vitest" so the static mock hoister doesn't recognize vi imported via redistributor specifiers — needs upstream PR exposing hoistedModule as a configurable option. (Workaround in this PR: defineConfig injects a pre-stage Vite plugin that rewrites 'vite-plus/test''vitest', propagated into test.projects entries.)
  • rolldown/oxc parser rejects valid TypeScript import type Default, { Named } from 'mod' syntax — breaks vp build against postcss@8.5.8's .d.ts declarations (only vp-config E2E still fails on this).

Everything else (npmx.dev, vinext, vue-mini, frm-stack, varlet, vitepress, reactive-resume, rollipop, dify, etc.) is now green.

Brooooooklyn added a commit that referenced this pull request May 16, 2026
…mat/typo

PR #1588 CI failures:
- Force-override mode (VP_FORCE_MIGRATE=1, set by test-vp-create.yml and
  ecosystem-ci) now re-pins any pre-existing vite-plus range to the local
  tgz path in monorepo workspace packages. Without this, pnpm reads the
  published vite-plus@0.1.21 metadata to resolve transitive deps including
  @voidzero-dev/vite-plus-test@0.1.21, which shadowed upstream vitest@4.1.5
  at runtime and broke vp create monorepo tests.
- typos CI: rename yarn-PnP to yarn Plug'n'Play (Pn→On false positive).
- vp check: format packages/cli/build.ts shim-generation block.
- Rename __dirname in install-failure-guard.spec.ts to satisfy
  eslint(no-underscore-dangle).
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 3ffb7cd to a0d248e Compare May 16, 2026 09:20
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0d248e65e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/define-config.ts Outdated
Comment thread packages/cli/src/migration/migrator.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a5b2697e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/define-config.ts
Comment thread .github/workflows/test-vp-create.yml
Brooooooklyn and others added 28 commits May 19, 2026 22:42
User configs like `defineConfig({ test: { globals: true } })` were
failing tsc with TS2769 because vite-plus's defineConfig typed its
parameter as vite-plus-core's UserConfig, which doesn't get vitest's
`declare module 'vite'` augmentation (vite-plus-core is a fork).

Extend the local `declare module '@voidzero-dev/vite-plus-core'`
augmentation to add `test?: VitestInlineConfig`. New unit test
locks the behavior in.
@vitest/mocker's static hoister only recognizes 'vitest' as the
mock-API source string. User configs from migrated projects import
`vi` from 'vite-plus/test', which mocker doesn't see, so vi.mock()
fails at runtime with "problems in resolving the mocks API".

Inject a pre-stage Vite plugin into vite-plus's defineConfig
wrapper that rewrites bare `from 'vite-plus/test'` -> `from 'vitest'`
in user source. Subpaths are left alone. Override pinning (task #50)
guarantees both specifiers resolve to the same module so this is
runtime-safe.

This is a workaround until @vitest/mocker accepts a configurable
hoistedModule option upstream.
…itest/config

`export * from '@voidzero-dev/vite-plus-core'` and `export * from
'vitest/config'` both expose `mergeConfig` (and a handful of other
vite-from-vitest re-exports). Per the ES spec, two star-exports of
the same name make any named import fail with a SyntaxError --
breaking frm-stack and any other config that imports `mergeConfig`
from `vite-plus`.

Switch the vitest/config re-export to an explicit list of names
unique to vitest (configDefaults, coverageConfigDefaults, etc),
leaving the shared names (mergeConfig, loadConfigFromFile, ...) to
come from vite-plus-core only.
A one-off ECONNRESET on the pnpm registry got baked into
command-add-pnpm11-with-workspace/snap.txt, then never reproduced,
making the snap permanently diff against live runs. While fixing,
discovered the existing pnpm WARN redaction regexes contained U+2009
thin-space characters in their patterns, so they never matched real
pnpm output -- only test fixtures that also contained thin spaces.

Normalize all 4 WARN regexes to ASCII space, and broaden the
GET-registry pattern to drop transient network warnings like
`[WARN] GET ... error (ECONNRESET). Will retry ...` so single-run
flakes don't get baked into snapshots.
npmx.dev pins @vitest/coverage-v8@4.1.0 alongside vitest. Without
overriding coverage-v8 to match VITEST_VERSION (4.1.5), pnpm
installs two physical copies of @vitest/mocker on disk and vitest's
runtime mocks-API check (`vi` came from the same mocker copy as
the hoister registered) fails.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vitest/mocker's static hoister hardcodes `hoistedModule = 'vitest'`
and runs as part of vitest's own plugin pipeline that executes
before user plugins -- so the previously-shipped runtime plugin
(d69fe03) cannot intercept it for `vi.mock` calls.

Move the rewrite into the Rust migrator so source is fixed at
migrate-time and the mocker sees the canonical specifier. The
runtime plugin stays as a safety net for code authored after
migration.

Concretely:
- Drop the forward `'vitest'` -> `'vite-plus/test'` ast-grep rule
  (the bare-root specifier is now intentionally preserved).
- Add a reverse `'vite-plus/test'` -> `'vitest'` ast-grep rule that
  is anchored to the exact root specifier; subpaths such as
  `'vite-plus/test/browser-playwright'` are intentionally left
  untouched because they map to dedicated vite-plus shim exports.
- Mirror the same change for `/// <reference types=... />`
  triple-slash directives.
- Update existing tests to expect the new behaviour and add
  dedicated forward + negative coverage for the reverse rewrite.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Trivial fmt-only fix on two test fixtures introduced by 21937c5.
…rite

Commit 21937c5 swapped the Rust migrator's direction from
`'vitest' -> 'vite-plus/test'` (forward) to `'vite-plus/test' -> 'vitest'`
(reverse) to fix vinext's `vi.mock` hoister bug. But that broke
`vp create` because template-generated test files use `from "vitest"`
and the `test-vp-create.yml` CI workflow doesn't include the vitest
family in its `VP_OVERRIDE_PACKAGES` env — so `vitest` is not
installable in the generated project and `vp check` fails with
TS2307.

Restoring the forward rewrite means templates with `from "vitest"`
become `from "vite-plus/test"` (which IS resolvable via the vite-plus
dep). The `vi.mock` hoister bug for migrated projects returns, but
that's already documented as an upstream blocker (PR comment
4466063059) — the cure was worse than the disease.

Coverage-v8/coverage-istanbul overrides from 895e4db are kept.
Task #56 narrowed `export * from 'vitest/config'` to an explicit list,
but missed the vitest-specific type aliases (ViteUserConfig,
ViteUserConfigExport, TestProjectConfiguration, TestUserConfig, etc.).
Downstream like vue-mini imports `ViteUserConfig` from 'vite-plus' and
broke with TS2724.

Add the full set of vitest-only type exports. Runtime exports
(mergeConfig, defineConfig, loadConfigFromFile) still come from
vite-plus-core only to avoid star-export conflicts.
pnpm v10 changed the formatting of the `Packages: +<n>` summary
line (no longer indented). Update the captured snaps for
`command-list-pnpm10-with-workspace` and `command-outdated-pnpm10`
to match. Verified locally that subsequent runs produce no diff.
The mocker rewrite plugin was only injected into the root vite config,
so projects defined under `test.projects` (each spinning up an isolated
Vite pipeline) never got the rewrite — `vi.mock()` failed with
"problems in resolving the mocks API" for source that imports from
`'vite-plus/test'` inside a project.
Fix `vp check` formatting failures from a13027f.
TS infers the narrowed types after `typeof project === 'function'`
and `typeof project === 'object' && project !== null`, so the
explicit casts trip `typescript(no-unnecessary-type-assertion)`.
`vp migrate` rewrites `@vitest/browser/context` and
`@vitest/browser-{playwright,preview,webdriverio}/context` to the
corresponding `vite-plus/test/...` subpaths, but those entries
only declared a `types` path. Node's resolver returned
`ERR_PACKAGE_PATH_NOT_EXPORTED` at runtime on migrated browser-mode
projects.

`syncTestPackageExports` now emits a runtime shim that re-exports
from upstream `@vitest/browser/context` (which itself is a stub
that Vitest replaces in browser mode) and adds `default` keys to
all six context entries.
…l success

The initial install (pre-migration) is best-effort — migration
proceeds regardless of its outcome, and the final install (with
--force / --no-frozen-lockfile) is the authoritative recovery.
But `handleInstallResult` was being called on both summaries and
setting `process.exitCode` on either failure, so a successful
final install was being clobbered by a failed initial install's
exit code, making the migration look failed when it wasn't.

Add a `propagateExitCode` opt-out and apply it to the initial
call only. Both failures still get reported via `report.warnings`.
The env var replaces VITE_PLUS_OVERRIDE_PACKAGES entirely, so the
CI's previous map (which only listed vite + vite-plus-core) left
created projects with a catalog missing every vitest entry. Mirror
the vitest family from ecosystem-ci/patch-project.ts so installs
succeed.
- upgrade-deps.ts now bumps the @vitest/browser, @vitest/browser-playwright,
  @vitest/browser-preview, @vitest/browser-webdriverio catalog entries
  alongside the vitest line, and rewrites the VITEST_VERSION constant
  in packages/cli/src/utils/constants.ts.
- ecosystem-ci/patch-project.ts imports VITEST_VERSION from the CLI
  constants so the three places that previously hardcoded "4.1.5"
  collapse to a single source of truth (constants.ts).
…l imports

The previous regex-only rewriter ran on raw source text and would
mutate string content that happened to contain `from 'vite-plus/test'`
(template literals, error messages, fixtures). Switch the ESM
import/dynamic-import path to es-module-lexer so only actual import
specifiers get spliced. CJS `require()` keeps a tightened
boundary-anchored regex.

Added 4 unit tests covering the false-positive cases.
`LEGACY_WRAPPER_FALLBACK_VERSIONS.vitest` was set to `^${VITEST_VERSION}`,
while fresh migrations write the exact `VITEST_VERSION` via
`VITE_PLUS_OVERRIDE_PACKAGES`. Projects cleaned up from a stale
`@voidzero-dev/vite-plus-test` alias should land on the same exact
pin as fresh migrations, not a caret range that allows unintended
drift on reinstall.
The workflow's VP_OVERRIDE_PACKAGES JSON hardcodes the vitest
family at "4.1.5" ten times. Without rewriting this file on
bump, CI's vp create tests pin a stale vitest version after the
next daily upgrade. Add updateTestVpCreateWorkflow alongside
the constants.ts + pnpm-workspace.yaml rewrites.
es-module-lexer's `s`/`e` offsets bound the specifier name *without*
quotes for static imports but include the quotes (as part of the
expression) for dynamic imports. The previous splice replaced
specifier-plus-quotes with a bare `vitest`, producing
`import(vitest)` instead of `import('vitest')`. Branch on `d` to
wrap dynamic replacements in quotes.
The mocker's static hoister hardcodes `hoistedModule = "vitest"` and bails
on any other source, leaving `vi.mock(...)` un-hoisted and producing TDZ
errors at runtime (`Cannot access '__vi_import_0__' before initialization`).

After `vp migrate` rewrites `import { vi } from 'vitest'` to
`'vite-plus/test'`, the user's `vi.mock(...)` calls hit this path. Patch
the hoister to also recognize `'vite-plus/test'` so the public test API
works without forcing a runtime source rewrite.

To be replaced by an upstream PR that exposes `hoistedModule` as
`string | string[]`.
…/TSX

The vite-plus/test→vitest rewrite relied on es-module-lexer to scope
replacements to real import statements, but the lexer cannot parse JSX
syntax. For .tsx test files containing `import { vi } from 'vite-plus/test'`
plus JSX in the body, parse() threw, our silent catch left `imports`
undefined, and the source flowed through unchanged. Downstream
@vitest/mocker then bailed on the unknown specifier, dropped the
`vi.mock(...)` hoist, and crashed with
`Cannot access '__vi_import_0__' before initialization`.

Detect the throw and fall back to two tight regexes targeting
`from 'vite-plus/test'` and `import('vite-plus/test')`. The backreferenced
quote forbids subpath matches like `vite-plus/test/browser`, and both
contexts require statement-position keywords so string-literal noise is
left alone.

Add JSX/TSX coverage to the unit tests so this regression is locked down.
…kages

Three breaking changes carried over from the wrapper deletion are
addressed:

1. `./test/client`, `./test/locators`, `./test/matchers`, `./test/utils`,
   `./test/context` exports were dropped in the wrapper removal but
   `packages/cli/src/oxlint-plugin.ts:36-37` still autofixes
   `@vitest/browser/client` and `@vitest/browser/locators` to those
   exact paths. Without the exports, `vp lint --fix` produced
   unresolvable imports. Restore them as shims projecting the matching
   `@vitest/browser/<sub>` subpaths.

2. `./test/browser-compat` is restored as a shim re-exporting
   `asLocator`, `defineBrowserCommand`, `defineBrowserProvider`,
   `parseKeyDef`, `resolveScreenshotPath` from `@vitest/browser`. The
   path is used by downstream consumers that point `@vitest/browser` at
   vite-plus via a package-manager override; restoring it preserves the
   wrapper-era surface.

3. `@testing-library/jest-dom`, `@storybook/test`, and `jest-extended`
   are auto-merged into `test.server.deps.inline` (root + per-project)
   so `expect.extend()` matcher registrations land on the same `expect`
   instance the test runtime uses. The previous wrapper applied this as
   a build-time patch to vitest's CLI chunk (vitest issue #897); the
   wrapper-free architecture re-applies it inside `defineConfig`.

The auto-inline merge is idempotent, respects `inline: true` opt-in,
deduplicates string entries, and recognises regexp entries that already
match an auto-inline package.
`packages/cli/src/index.ts` deliberately uses a curated re-export from
`vitest/config` that omits `mergeConfig` to avoid colliding with the
`mergeConfig` re-exported from `@voidzero-dev/vite-plus-core`. The CJS
entry still spread the full `vitest/config` namespace, so CJS consumers
ended up with vitest's `mergeConfig` (which delegates to upstream `vite`)
instead of vite-plus-core's. ESM and CJS now expose the same identifiers.
- Drop `?? {}` spread fallbacks (unicorn/no-useless-fallback-in-spread):
  optional chaining already guards the spread, the fallback is dead.
- Wrap single-statement `continue` in braces (eslint/curly).

No behavior change.
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 1189a0e to eaa3344 Compare May 19, 2026 14:43
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eaa334435e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// The regex matches `from 'vite-plus/test'` and `import('vite-plus/test')`
// only — both contexts can never appear inside legal JS string content
// because `from` and bare `import(` require statement positions.
result = result.replace(FROM_PATTERN, `$1$2${TARGET_REPLACEMENT}$2`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid regex fallback rewriting TSX string contents

Fresh evidence beyond the earlier resolved regex comment: the newly added JSX/TSX fallback only runs after es-module-lexer throws, then applies FROM_PATTERN to the raw source. In a .tsx/.jsx test module such as const msg = "from 'vite-plus/test'"; return <div>{msg}</div>, the lexer fails because of JSX and this replacement mutates the runtime string to from 'vitest', which can break rendered text or snapshot assertions even though there is no import to rewrite. The fallback needs to avoid string/template/comment ranges or use a parser that understands JSX/TSX.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant