diff --git a/packages/mocker/src/node/hoistMocks.ts b/packages/mocker/src/node/hoistMocks.ts index ee73a95fd..9b7a09c93 100644 --- a/packages/mocker/src/node/hoistMocks.ts +++ b/packages/mocker/src/node/hoistMocks.ts @@ -83,6 +83,12 @@ const regexpHoistable = /\b(?:vi|vitest)\s*\.\s*(?:mock|unmock|hoisted|doMock|doUnmock)\s*\(/ const hashbangRE = /^#!.*\n/ +// Public redistributions of Vitest that re-export its mocking API (`vi`) +// verbatim under their own specifier. Imports from these are treated as the +// hoisted module so `vi.mock()` is hoisted for e.g. +// `import { vi } from 'vite-plus/test'`, exactly as it is for `vitest`. +const REDISTRIBUTED_HOISTED_MODULES = ['vite-plus/test'] + // this is a fork of Vite SSR transform export function hoistMocks( code: string, @@ -141,8 +147,9 @@ export function hoistMocks( ) { const source = importNode.source.value as string // always hoist vitest import to top of the file, so - // "vi" helpers can access it - if (hoistedModule === source) { + // "vi" helpers can access it. Vitest redistributions that re-export the + // mocking API under their own specifier are recognized the same way. + if (hoistedModule === source || REDISTRIBUTED_HOISTED_MODULES.includes(source)) { hoistedModuleImported = true return } diff --git a/test/unit/test/injector-mock.test.ts b/test/unit/test/injector-mock.test.ts index a0ae2dc8f..8d2a5107f 100644 --- a/test/unit/test/injector-mock.test.ts +++ b/test/unit/test/injector-mock.test.ts @@ -60,6 +60,21 @@ import { test } from 'vitest' `) }) +test('recognizes the vite-plus/test redistribution as the hoisted module', () => { + expect(hoistSimpleCode(` +import { vi } from 'vite-plus/test' +vi.mock('path', () => {}) +vi.unmock('path') +vi.hoisted(() => {}) + `)).toMatchInlineSnapshot(` + "vi.mock('path', () => {}) + vi.unmock('path') + vi.hoisted(() => {}) + + import { vi } from 'vite-plus/test'" + `) +}) + test('always hoists all imports but they are under mocks', () => { expect(hoistSimpleCode(` import { vi } from 'vitest'