Skip to content

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

Open
Brooooooklyn wants to merge 67 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 67 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

High Risk
Large cross-cutting change to test packaging, migration import maps, pnpm patches, and release/CI install paths; incorrect shim or override behavior can break vp test, vp migrate, and ecosystem installs.

Overview
Removes the @voidzero-dev/vite-plus-test wrapper and wires vite-plus to upstream vitest and @vitest/browser* while keeping vite-plus/test* as the public import surface via generated shims in packages/cli/build.ts.

Packaging & release: Stops building/publishing packages/test; vite-plus depends on catalog-pinned vitest packages; release/docs/README only document a vite → core alias (no vitest → wrapper). vp version reports vitest from the real vitest package.

Migration & tooling: vite_migration gains require() rewrites, .cjs/.cts, finer @vitest/browser*vite-plus/test* mappings (shared browser/context, browser/providers/*, flat **client/locators/etc.), and leaves declare module 'vitest' / vitest /// <reference types> on upstream identities. upgrade-deps.ts bumps catalog vitest, VITEST_VERSION, test-vp-create pins, and renames the @vitest/mocker pnpm patch.

CI: E2E / vp create pack core/cli at 0.0.0, drop test tgz, pin vitest family in overrides, and build a vite-7.99.0.tgz masquerade for Bun peer checks on vite.

Follow-up risk: @vitest/mocker still hoists mocks for the literal 'vitest' specifier; imports from vite-plus/test may not hoist until upstream/config changes (noted in PR description).

Reviewed by Cursor Bugbot for commit 3ae8f5a. 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 3ae8f5a
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a15a12426fe51000844dcf4

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 Outdated
@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 Outdated
Brooooooklyn and others added 26 commits May 26, 2026 19:54
…/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.
The previous regex fallback (`/from\s+['"]vite-plus\/test['"]/g`) ran on
raw source when `es-module-lexer` threw on JSX/TSX, so it could mutate
the same substring inside string literals or JSX text. Switched to
`oxc-parser` which understands TSX and exposes precise specifier offsets
for static imports, dynamic imports, and `export … from` re-exports —
all three are needed because the fast-path lexer reports them in one
list while oxc separates them into staticImports / dynamicImports /
staticExports.
Consolidates the @emnapi/core 1.9.2 / 1.10.0 split that the oxc-parser
install introduced, so `pnpm dedupe --check` passes in CI.
…ojects

Browser-mode tests failed in established vite-plus projects (those that
depend only on `vite-plus`, with `vitest`/`@vitest/browser` transitive).
pnpm's isolated layout only exposes a package's direct deps, so the
browser-mode Vite dev server — rooted at the consumer project — could not
resolve `vitest/internal/browser` and the `@vitest/*` family, crashing
with "Failed to resolve import vitest/internal/browser".

Adds `vite-plus:vitest-resolver`, an enforce:'pre' resolveId plugin
injected alongside the specifier-rewrite plugin. It is a pure fallback:
it defers to the default resolver first (skipSelf) so projects that
already resolve fine see zero change, and only when that returns null
does it resolve from vite-plus's own dependency tree — `vitest` /
`@vitest/browser*` via a require anchored at this module, and the nested
`@vitest/*` family (deps of vitest) via a require anchored at vitest.
Bumps the `vitest` / `@vitest/browser*` catalog pins, the `VITEST_VERSION`
constant (which drives migration-injected overrides), and the
`VP_OVERRIDE_PACKAGES` workflow env from 4.1.5 to the latest stable 4.1.7.
The `@vitest/mocker` hoist patch is renamed to track the new version and
still applies cleanly (the patched context is unchanged in 4.1.7).
… exports

The blanket `@vitest/` → `vite-plus/test/` swap produced targets that
do not exist in the `vite-plus` package `exports` map, so migrated code
failed at runtime with ERR_PACKAGE_PATH_NOT_EXPORTED:

- `@vitest/browser/{client,locators,matchers,utils}` mapped to
  `vite-plus/test/browser/*` (only the bare `./test/*` keys exist).
- `@vitest/browser-<provider>/provider` mapped to
  `vite-plus/test/browser-<provider>/provider` (no such export).

Replace the single naive ast-grep rule with 12 mutually-exclusive
enumerated rules and split the triple-slash reference regex into five,
so `vp migrate` produces the same canonical targets as the
`oxlint-plugin.ts` autofix. Add the missing `matchers`/`utils` entries
to `oxlint-plugin.ts` so both implementations stay in sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The tgz-based create/e2e workflows reference fixed `*-0.0.0.tgz`
filenames (`repack-vite-tgz` input, `VP_VERSION`, `install-global-cli`,
`VP_OVERRIDE_PACKAGES`). A release commit bumped `packages/core` and
`packages/cli` to a published version, so `pnpm pack` emitted
`*-0.1.22.tgz` and the `Build vite-plus packages` job failed with
ENOENT on `voidzero-dev-vite-plus-core-0.0.0.tgz`.

Pin both packages to 0.0.0 with `pnpm pkg set` right before packing so
the emitted tgz names stay stable regardless of the committed version.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`replaceInstalledCheckoutPackages` symlinked `node_modules/vitest` and
`node_modules/@voidzero-dev/vite-plus-test` to `packages/test`, which
this branch deletes. The entries are never matched by the current snap
suite (only `create-framework-shim-vue` sets `linkCheckoutPackages` and
it installs neither package), but a future global snap test that does
install vitest would `rmSync` the real package and symlink it to the
now-missing `packages/test`. Remove both stale entries.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`pnpm pkg set` is not implemented (ERR_PNPM_NOT_IMPLEMENTED); pnpm
points to the npm CLI for `pkg` operations. Switch the version pin in
the tgz pack steps to `npm pkg set version=0.0.0`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The ecosystem-ci pack step pins packages/cli to 0.0.0 before packing, so
the installed local build reports 0.0.0. verify-install.ts still compared
against the committed packages/cli/package.json version (0.1.22 after the
release bump), failing every ecosystem E2E project with
"expected version 0.1.22, got 0.0.0". Pin the expected version to 0.0.0,
matching the fixed `vite-plus-0.0.0.tgz` that patch-project.ts references.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`externalDtsTypeOnlyPlugin` blindly prepended `import type ` to every
import line in postcss/lightningcss `.d.ts` files. For the
`import D, { N } from 'x'` shape that produced `import type D, { N }`,
which is invalid — a type-only import statement cannot mix a default
binding with named bindings. The dts bundler then failed with
`[PARSE_ERROR]` (surfaced by the `vp-config` ecosystem-ci project,
whose dependency graph routes postcss's `.d.ts` through this plugin).

Fold the default binding into the named clause as `{ default as D, N }`
before the generic prepend, in both the internal-file and
consumer-file branches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`vp pack --dts` inlined postcss's `.d.ts` into the declaration bundle
and failed with 61 MISSING_EXPORT errors for postcss type names.
postcss exposes its public types via `declare namespace postcss {
export {...} }` + `export = postcss` — a CJS namespace shape the dts
bundler cannot map named imports onto.

`externalDtsTypeOnlyPlugin` previously rewrote postcss/lightningcss
imports to type-only via regex, which neither parsed reliably for
default+named imports nor addressed the `export =` mismatch. Replace
it with a `resolveId` hook that marks postcss/lightningcss external
whenever imported from a `.d.ts` file, so emitted declarations keep
`import type { X } from 'postcss'` instead of inlining postcss's
`.d.ts` — standard dts-bundler treatment of third-party packages.
Runtime bundling is unaffected (the hook only fires for
declaration-file importers).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The daily upgrade script bumped the vitest catalog, VITEST_VERSION, and
the test-vp-create.yml pins, but never touched the @vitest/mocker entry
in patchedDependencies. pnpm keys patchedDependencies by exact version
and fails ERR_PNPM_PATCHED_PKG_DOES_NOT_MATCH on drift, so each vitest
bump broke the auto-upgrade PR. Rewrite the key + patch path and rename
the version-suffixed patch file as part of the upgrade run.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`vitest@4.1.7`'s dist/index.d.ts re-exports `ExpectPollOptions` from
`@vitest/expect`, which does not export that name. tsc tolerates the
dangling re-export (lazy resolution) but the rolldown dts bundler
resolves every re-export eagerly while inlining, so `vp pack --dts`
fails with MISSING_EXPORT. Extend externalDtsTypeOnlyPlugin to keep
vitest and @vitest/* external for .d.ts importers, the same treatment
already applied to postcss/lightningcss.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
vitest browser mode injects `optimizeDeps.include` entries like
`vitest > expect-type` that Vite resolves from the config root. In a
pnpm strict (non-hoisted) layout, a `vitest` pulled in only transitively
via `vite-plus` is not resolvable from the package directory, so the
optimizer fails and the browser test page hangs forever — this broke the
`vibe-dashboard` E2E job across the whole branch.

`vp migrate` now detects vitest browser mode (recursive scan for
`@vitest/browser` / `vite-plus/test/browser` specifiers, stopping at
nested package.json boundaries) and adds `vitest` as a direct
devDependency so pnpm hoists it next to the package.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ch sync

- Switch `replace('/', '\\/')` to `replaceAll` in both regex builders so a
  future vitest sub-key with multiple slashes can't silently produce a
  broken pattern.
- Always persist `updateVitestMockerPatch`'s in-memory rewrite when the
  content changed, not only when the key version moved — fixes a case
  where the key already matches `vitestVersion` but the value's patch-file
  suffix is stale, which would otherwise leak through and trigger
  ERR_PNPM_PATCHED_PKG_DOES_NOT_MATCH on install.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… PnP

- Regenerate pnpm-lock.yaml after rebase onto main so the catalog
  configuration matches; without this `pnpm install --frozen-lockfile`
  errors with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH in CI.
- Add `PnP` (Yarn Plug'n'Play) to .typos.toml so the new migrator
  comment doesn't trip the typos linter (Pn -> On false positive).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…thin-space WARN

- sync-remote-deps.ts: the patchedDependencies merge previously reset
  result.patchedDependencies = {} before copying vite's patches, which
  dropped any vite-plus root-level patches (notably the new
  @vitest/mocker@4.1.7 patch added on this branch) on every sync. Seed
  the merge from main.patchedDependencies so root patches survive.
- utils.ts: the pnpm warning scrub patterns only matched an ASCII space
  before `WARN`, but pnpm output frequently prefixes warnings with a
  U+2009 thin space; broaden the prefix class to `[  ]?` for the
  Skip-adding, Request-took, registry GET, and Tarball patterns.
- pnpm-lock.yaml: regenerate against the now-pinned rolldown@f2757ed7
  clone (where @rolldown/pluginutils is a `catalog:` dep instead of a
  `workspace:*` package), fixing the ERR_PNPM_OUTDATED_LOCKFILE that
  was tanking every CI job after the rebase.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…plugin

`prefer-vite-plus-imports` previously autofixed `TSModuleDeclaration` string
literals like `declare module '@vitest/browser-playwright' { ... }` to
`declare module 'vite-plus/test/browser-playwright' { ... }`. That contradicts
`vp migrate`'s deliberate behavior (and the migration guide's instructions)
of preserving `declare module 'vitest'` / `declare module '@vitest/browser*'`
on the upstream identity so type augmentations merge with what
`vite-plus/test*` actually re-exports. `vp lint --fix` could therefore undo
the augmentation targets users had just been told to keep.

Skip the rewrite in the `TSModuleDeclaration` visitor when the literal is in
the vitest/@vitest-browser family; `declare module 'vite'` and other
non-vitest specifiers continue to be rewritten as before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…are-module fix

- typos: `extend-words` matches whole identifiers via the tokenizer, but
  `PnP` is split into the word `Pn` (which typos wants to autocorrect to
  `On`), so the previous entry didn't apply. Move to
  `extend-identifiers` so the full token is whitelisted.
- snap-tests/lint-vite-plus-imports: reflect the oxlint plugin change
  from 42beb51 — `declare module '@vitest/browser-playwright'` and
  `declare module '@vitest/browser-playwright/context'` are no longer
  reported (7 errors → 5) and `vp lint --fix` no longer rewrites them
  in src/types.ts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`pnpm dedupe --check` in the Lint workflow flagged duplicate resolutions
in the regenerated lockfile from 52577b1037. Run `pnpm dedupe` to
consolidate them (e.g. yaml 2.8.2/2.9.0 split in packages/cli) and clear
ERR_PNPM_DEDUPE_CHECK_ISSUES on CI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The file walker visits `.cjs` / `.cts` files, but the ast-grep rewrite
rules only matched `inside: { kind: import_statement }` and
`kind: module`. That left `require('vitest')`, `require('vitest/config')`,
`require('@vitest/browser*')`, `require('vite')`, and `require('tsdown')`
unrewritten in CommonJS configs like `vitest.config.cjs`, even when
sibling ESM files in the same project were migrated — producing mixed
specifiers and resolution failures.

Mirror every existing import-statement rule with a require-shape sibling
that matches `inside: { kind: arguments, inside: { kind: call_expression,
has: { field: function, regex: ^require$ } } }`. The callee anchor keeps
`my_require(...)`, `require.cache[...]`, `require.resolve(...)`, and
dynamic `import(...)` correctly out of scope. `declare module` (`kind:
module`) is intentionally not mirrored — CJS has no equivalent.

19 new require-shape rules (vite ×2, vitest family ×15, tsdown ×2) and
25 new tests covering positive rewrites + negative cases (my_require,
require.cache, require.resolve) + skip_packages parity.
`cargo test -p vite_migration --lib`: 212 passed, 0 failed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@Brooooooklyn Brooooooklyn force-pushed the refactor/replace-vite-plus-test-with-vitest branch from 4d0f21d to e34f1c5 Compare May 26, 2026 11:54
Brooooooklyn and others added 2 commits May 26, 2026 21:16
The push of e34f1c5 happened during a GitHub Actions outage and the
synchronize event was lost — only third-party checks fired. Empty
commit to force a fresh pull_request synchronize so the main CI / E2E /
Test vp create workflows enqueue.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI's Lint→Deduplicate step caught another mismatch where
rolldown-plugin-dts had two peer-resolved variants in the lockfile
(one with `oxc-resolver`, one without). The previous local dedupe ran
before this drift surfaced; rerun resolves it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3ae8f5a. Configure here.

}
return 0;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Install success shown after failure

Medium Severity

When the pre-migration install succeeds but the post-migration reinstall fails, handleInstallResult still adds the initial install duration to installDurationMs. showMigrationSummary treats any positive duration as success and prints a green “Dependencies installed” line even though process.exitCode was set from the failed final install.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3ae8f5a. Configure here.

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