···77import { setupBrowserRpc } from './rpc'
88import { BrowserServer } from './server'
991010+export { distRoot } from './constants'
1011export { createBrowserPool } from './pool'
1212+1113export type { BrowserServer } from './server'
12141315export async function createBrowserServer(
+27-3
packages/browser/src/node/plugin.ts
···44import type { BrowserServer } from './server'
55import { lstatSync, readFileSync } from 'node:fs'
66import { createRequire } from 'node:module'
77-import { fileURLToPath } from 'node:url'
87import { dynamicImportPlugin } from '@vitest/mocker/node'
98import { toArray } from '@vitest/utils'
109import MagicString from 'magic-string'
···1211import sirv from 'sirv'
1312import { coverageConfigDefaults, type Plugin } from 'vitest/config'
1413import { getFilePoolName, resolveApiServerConfig, resolveFsAllow, distDir as vitestDist } from 'vitest/node'
1414+import { distRoot } from './constants'
1515import BrowserContext from './plugins/pluginContext'
1616import { resolveOrchestrator } from './serverOrchestrator'
1717import { resolveTester } from './serverTester'
···1919export { defineBrowserCommand } from './commands/utils'
2020export type { BrowserCommand } from 'vitest/node'
21212222+const versionRegexp = /(?:\?|&)v=\w{8}/
2323+2224export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
2323- const pkgRoot = resolve(fileURLToPath(import.meta.url), '../..')
2424- const distRoot = resolve(pkgRoot, 'dist')
2525 const project = browserServer.project
26262727 function isPackageExists(pkg: string, root: string) {
···160160 res.end(buffer)
161161 })
162162 }
163163+ server.middlewares.use((req, res, next) => {
164164+ // 9000 mega head move
165165+ // Vite always caches optimized dependencies, but users might mock
166166+ // them in _some_ tests, while keeping original modules in others
167167+ // there is no way to configure that in Vite, so we patch it here
168168+ // to always ignore the cache-control set by Vite in the next middleware
169169+ if (req.url && versionRegexp.test(req.url) && !req.url.includes('chunk-')) {
170170+ res.setHeader('Cache-Control', 'no-cache')
171171+ const setHeader = res.setHeader.bind(res)
172172+ res.setHeader = function (name, value) {
173173+ if (name === 'Cache-Control') {
174174+ return res
175175+ }
176176+ return setHeader(name, value)
177177+ }
178178+ }
179179+ next()
180180+ })
163181 },
164182 },
165183 {
···325343 BrowserContext(browserServer),
326344 dynamicImportPlugin({
327345 globalThisAccessor: '"__vitest_browser_runner__"',
346346+ filter(id) {
347347+ if (id.includes(distRoot)) {
348348+ return false
349349+ }
350350+ return true
351351+ },
328352 }),
329353 {
330354 name: 'vitest:browser:config',
+8-29
packages/mocker/src/browser/interceptor-msw.ts
···3434export class ModuleMockerMSWInterceptor implements ModuleMockerInterceptor {
3535 protected readonly mocks: MockerRegistry = new MockerRegistry()
36363737- private started = false
3838- private startPromise: undefined | Promise<unknown>
3737+ private startPromise: undefined | Promise<SetupWorker>
3838+ private worker: undefined | SetupWorker
39394040 constructor(
4141 private readonly options: ModuleMockerMSWInterceptorOptions = {},
···7878 })
7979 }
80808181- protected async init(): Promise<unknown> {
8282- if (this.started) {
8383- return
8181+ protected async init(): Promise<SetupWorker> {
8282+ if (this.worker) {
8383+ return this.worker
8484 }
8585 if (this.startPromise) {
8686 return this.startPromise
···101101 http.get(/.+/, async ({ request }) => {
102102 const path = cleanQuery(request.url.slice(location.origin.length))
103103 if (!this.mocks.has(path)) {
104104- // do not cache deps like Vite does for performance
105105- // because we want to be able to update mocks without restarting the server
106106- // TODO: check if it's still neded - we invalidate modules after each test
107107- if (path.includes('/deps/')) {
108108- return fetch(bypass(request))
109109- }
110110-111104 return passthrough()
112105 }
113106···126119 }
127120 }),
128121 )
129129- return worker.start(this.options.mswOptions)
122122+ return worker.start(this.options.mswOptions).then(() => worker)
130123 }).finally(() => {
131131- this.started = true
124124+ this.worker = worker
132125 this.startPromise = undefined
133126 })
134134- await this.startPromise
127127+ return await this.startPromise
135128 }
136129}
137130···149142 'x-msw-intention': 'passthrough',
150143 },
151144 })
152152-}
153153-154154-function bypass(request: Request) {
155155- const clonedRequest = request.clone()
156156- clonedRequest.headers.set('x-msw-intention', 'bypass')
157157- const cacheControl = clonedRequest.headers.get('cache-control')
158158- if (cacheControl) {
159159- clonedRequest.headers.set(
160160- 'cache-control',
161161- // allow reinvalidation of the cache so mocks can be updated
162162- cacheControl.replace(', immutable', ''),
163163- )
164164- }
165165- return clonedRequest
166145}
167146168147const replacePercentageRE = /%/g
+1-2
packages/mocker/src/browser/mocker.ts
···7788const { now } = Date
991010-// TODO: define an interface thath both node.js and browser mocker can implement
1110export class ModuleMocker {
1211 protected registry: MockerRegistry = new MockerRegistry()
1312···6261 const resolved = await this.rpc.resolveId(id, importer)
6362 if (resolved == null) {
6463 throw new Error(
6565- `[vitest] Cannot resolve ${id} imported from ${importer}`,
6464+ `[vitest] Cannot resolve "${id}" imported from "${importer}"`,
6665 )
6766 }
6867 const ext = extname(resolved.id)
+4
packages/mocker/src/node/dynamicImportPlugin.ts
···1111 * @default `"__vitest_mocker__"`
1212 */
1313 globalThisAccessor?: string
1414+ filter?: (id: string) => boolean
1415}
15161617export function dynamicImportPlugin(options: DynamicImportPluginOptions = {}): Plugin {
···2021 transform(source, id) {
2122 // TODO: test is not called for static imports
2223 if (!regexDynamicImport.test(source)) {
2424+ return
2525+ }
2626+ if (options.filter && !options.filter(id)) {
2327 return
2428 }
2529 return injectDynamicImport(source, id, this.parse, options)