Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116import type { Plugin } from 'vite'import type { MockedModuleSerialized } from '../registry'import { readFile } from 'node:fs/promises'import { join } from 'node:path/posix'import { ManualMockedModule, MockerRegistry } from '../registry'import { cleanUrl } from '../utils'import { automockModule } from './automockPlugin'
export interface InterceptorPluginOptions { /** * @default "__vitest_mocker__" */ globalThisAccessor?: string}
export function interceptorPlugin(options: InterceptorPluginOptions): Plugin { const registry = new MockerRegistry() return { name: 'vitest:mocks:interceptor', enforce: 'pre', async load(id) { const mock = registry.get(id) if (!mock) { return } if (mock.type === 'manual') { const exports = Object.keys(await mock.resolve()) const accessor = options.globalThisAccessor || '"__vitest_mocker__"' const serverUrl = (mock as any).serverUrl as string const module = `const module = globalThis[${accessor}].getFactoryModule("${serverUrl}");` const keys = exports .map((name) => { if (name === 'default') { return `export default module["default"];` } return `export const ${name} = module["${name}"];` }) .join('\n') return `${module}\n${keys}` } if (mock.type === 'redirect') { return readFile(mock.redirect, 'utf-8') } }, transform: { order: 'post', handler(code, id) { const mock = registry.get(id) if (!mock) { return } if (mock.type === 'automock' || mock.type === 'autospy') { const m = automockModule(code, mock.type, this.parse, { globalThisAccessor: options.globalThisAccessor, })
return { code: m.toString(), map: m.generateMap({ hires: 'boundary', source: cleanUrl(id) }), } } }, }, configureServer(server) { server.ws.on('vitest:interceptor:register', (event: MockedModuleSerialized) => { const serverUrl = event.url // the browsers stores the url relative to the root // but on the server "id" operates on the file paths event.url = join(server.config.root, event.url) if (event.type === 'manual') { const module = ManualMockedModule.fromJSON(event, async () => { const keys = await getFactoryExports(serverUrl) return Object.fromEntries(keys.map(key => [key, null])) }) Object.assign(module, { serverUrl, }) registry.add(module) } else { if (event.type === 'redirect') { const redirectUrl = new URL(event.redirect) event.redirect = join(server.config.root, redirectUrl.pathname) } registry.register(event) } server.ws.send('vitest:interceptor:register:result') }) server.ws.on('vitest:interceptor:delete', (id: string) => { registry.delete(id) server.ws.send('vitest:interceptor:register:delete') }) server.ws.on('vitest:interceptor:invalidate', () => { registry.clear() server.ws.send('vitest:interceptor:register:invalidate') })
function getFactoryExports(url: string) { server.ws.send('vitest:interceptor:resolve', url) let timeout: NodeJS.Timeout return new Promise<string[]>((resolve, reject) => { timeout = setTimeout(() => { reject(new Error(`Timeout while waiting for factory exports of ${url}`)) }, 10_000) server.ws.on('vitest:interceptor:resolved', ({ url: resolvedUrl, keys }: { url: string; keys: string[] }) => { if (resolvedUrl === url) { clearTimeout(timeout) resolve(keys) } }) }) } }, }}