diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 28b1fdbe6d..e80ef8b0ee 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -294,6 +294,11 @@ jobs: command: | vp fmt vp run validate + - name: vite-plus-vitest-global-type-minimal-repro + node-version: 24 + command: | + vp test + vp check --fix exclude: # frm-stack uses Docker (testcontainers) which doesn't work the same way on Windows - os: windows-latest diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index ba94fcd98d..63fb7b3f26 100644 --- a/ecosystem-ci/repo.json +++ b/ecosystem-ci/repo.json @@ -102,5 +102,11 @@ "repository": "https://github.com/why-reproductions-are-required/bun-vite-template.git", "branch": "master", "hash": "d84099b4a153c7e0f35e510725b2ceb24c6e09ad" + }, + "vite-plus-vitest-global-type-minimal-repro": { + "repository": "https://github.com/why-reproductions-are-required/vite-plus-vitest-global-type-minimal-repro.git", + "branch": "main", + "hash": "419653665e4f0688ad3cac68a34673fdd0632b55", + "forceFreshMigration": true } } diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/package.json b/packages/cli/snap-tests/check-vitest-globals-typecheck/package.json new file mode 100644 index 0000000000..c992d4d0a5 --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/package.json @@ -0,0 +1,5 @@ +{ + "name": "check-vitest-globals-typecheck", + "version": "0.0.0", + "private": true +} diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/snap.txt b/packages/cli/snap-tests/check-vitest-globals-typecheck/snap.txt new file mode 100644 index 0000000000..e17c51b30c --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/snap.txt @@ -0,0 +1,3 @@ +> vp check +pass: All 6 files are correctly formatted (ms, threads) +pass: Found no warnings, lint errors, or type errors in 3 files (ms, threads) diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.test.ts b/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.test.ts new file mode 100644 index 0000000000..81faad4669 --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.test.ts @@ -0,0 +1,5 @@ +import { fn } from "./index.ts"; + +test("fn", () => { + expect(fn()).toBe("Hello, world!"); +}); diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.ts b/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.ts new file mode 100644 index 0000000000..b6b6588250 --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/src/index.ts @@ -0,0 +1,3 @@ +export function fn() { + return "Hello, world!"; +} diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/steps.json b/packages/cli/snap-tests/check-vitest-globals-typecheck/steps.json new file mode 100644 index 0000000000..d9c26d5a29 --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/steps.json @@ -0,0 +1,6 @@ +{ + "env": { + "VITE_DISABLE_AUTO_INSTALL": "1" + }, + "commands": ["vp check"] +} diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/tsconfig.json b/packages/cli/snap-tests/check-vitest-globals-typecheck/tsconfig.json new file mode 100644 index 0000000000..c11910b88e --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "nodenext", + "moduleResolution": "nodenext", + "types": ["vite-plus/test/globals"], + "strict": true, + "noEmit": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/packages/cli/snap-tests/check-vitest-globals-typecheck/vite.config.ts b/packages/cli/snap-tests/check-vitest-globals-typecheck/vite.config.ts new file mode 100644 index 0000000000..227508e08e --- /dev/null +++ b/packages/cli/snap-tests/check-vitest-globals-typecheck/vite.config.ts @@ -0,0 +1,14 @@ +export default { + test: { + globals: true, + }, + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + rules: { + "typescript/no-unsafe-call": "error", + }, + }, +}; diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 4f27411f22..2057aa5843 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -720,6 +720,7 @@ export function rewriteStandaloneProject( overrides: { ...pkg.pnpm?.overrides, ...VITE_PLUS_OVERRIDE_PACKAGES, + ...(isForceOverrideMode() ? { [VITE_PLUS_NAME]: VITE_PLUS_VERSION } : {}), }, }; // remove packages from `resolutions` field if they exist @@ -734,7 +735,7 @@ export function rewriteStandaloneProject( extractedStagedConfig = rewritePackageJson(pkg, packageManager, false, skipStagedMigration); // ensure vite-plus is in devDependencies - if (!pkg.devDependencies?.[VITE_PLUS_NAME]) { + if (!pkg.devDependencies?.[VITE_PLUS_NAME] || isForceOverrideMode()) { pkg.devDependencies = { ...pkg.devDependencies, [VITE_PLUS_NAME]: VITE_PLUS_VERSION, diff --git a/packages/test/BUNDLING.md b/packages/test/BUNDLING.md index 2ab49f9541..64b9b67ae1 100644 --- a/packages/test/BUNDLING.md +++ b/packages/test/BUNDLING.md @@ -113,9 +113,12 @@ For maintainers developing the vitest/vite migration feature, here are the trans | `from "@vitest/browser-preview"` | `from "@voidzero-dev/vite-plus-test/browser-preview"` | | `from "vite"` | `from "@voidzero-dev/vite-plus-core"` | | `from "vite/module-runner"` | `from "@voidzero-dev/vite-plus-core/module-runner"` | +| `import('vitest')` | `import('@voidzero-dev/vite-plus-test')` | **Note**: `@voidzero-dev/vite-plus-core` is the bundled version of upstream vite (Vite v8 beta). See [Core Package Bundling](../core/BUNDLING.md) for details on what it contains. +**Note**: The `import('vitest')` → `import('@voidzero-dev/vite-plus-test')` rewrite is critical for `globals.d.ts`, which declares global types like `typeof import('vitest')['test']`. Without this rewrite, `vitest` is not resolvable from the `@voidzero-dev/vite-plus-test` package context in pnpm's strict `node_modules` layout. TypeScript silently treats unresolved dynamic type imports as `any`, but oxlint's type-aware linting treats them as `error` types, causing `no-unsafe-call` errors. The rewrite turns this into a self-reference that resolves correctly via Node.js package self-referencing. + **Note:** When using pnpm overrides, you have three options for browser provider imports: - `vitest/browser-playwright` (or `vitest/browser-webdriverio`, `vitest/browser-preview`) - works when `vitest` is overridden to our package (Recommended) diff --git a/packages/test/build.ts b/packages/test/build.ts index f96cd14f33..0f16824d38 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -65,6 +65,7 @@ const distDir = resolve(projectDir, 'dist'); const vendorDir = resolve(distDir, 'vendor'); const CORE_PACKAGE_NAME = '@voidzero-dev/vite-plus-core'; +const TEST_PACKAGE_NAME = '@voidzero-dev/vite-plus-test'; // @vitest/* packages to copy (not bundle) to preserve browser/Node.js separation // These are copied from node_modules to dist/@vitest/ to avoid shared chunks @@ -480,7 +481,8 @@ async function bundleVitest() { .replaceAll(/require\("vite"\)/g, `require("${CORE_PACKAGE_NAME}")`) .replaceAll(`import 'vite';`, `import '${CORE_PACKAGE_NAME}';`) .replaceAll(`'vite/module-runner'`, `'${CORE_PACKAGE_NAME}/module-runner'`) - .replaceAll(`declare module "vite"`, `declare module "${CORE_PACKAGE_NAME}"`); + .replaceAll(`declare module "vite"`, `declare module "${CORE_PACKAGE_NAME}"`) + .replaceAll(/import\(['"]vitest['"]\)/g, `import('${TEST_PACKAGE_NAME}')`); console.log(`Replaced vite imports in ${destPath}`); await writeFile(destPath, content, 'utf-8'); } else {