import * as fs from 'node:fs'
import nodeHttp from 'node:http'
import { chromium } from '@playwright/test'
import { createTeardown } from 'fs-teardown'
import { build, createServer, createServerModuleRunner, preview } from 'vite'
import { fromRoot, mswExports } from '../../test/support/alias'
import { fromTemp } from '../../test/support/utils'
import type { network } from 'virtual:msw'
import { msw } from './plugin'

const fsMock = createTeardown({
  rootDir: fromTemp('vite/plugin'),
})

beforeAll(async () => {
  await fsMock.prepare()
})

beforeEach(async () => {
  await fsMock.reset()
})

afterEach(() => {
  vi.unstubAllEnvs()
})

afterAll(async () => {
  await fsMock.cleanup()
})

test('loads the packaged plugin without installing its development dependencies', async () => {
  await fsMock.create({
    'plugin.mjs': fs.readFileSync(fromRoot('lib/vite/index.js'), 'utf8'),
    'verify.mjs': `
import { msw } from './plugin.mjs'
console.log(msw().name)
`,
  })

  const { stdout } = await fsMock.exec('node ./verify.mjs')

  expect(stdout.trim()).toBe('msw')
})

test('excludes user-guarded mocking from a production client bundle', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  await fsMock.create({
    'index.html':
      '<output></output><script type="module" src="/entry.ts"></script>',
    'handlers.ts': `
import { http, HttpResponse } from 'msw/http'
console.log('MOCK_ONLY_HANDLER_MODULE')
export const handlers = [http.get('/resource', () => HttpResponse.text('MOCK_ONLY_RESPONSE'))]
`,
    'shared.ts': `
import { http, HttpResponse } from 'msw/http'
export const appValue = 'Application ready'
export function createHandler() {
  return http.get('/override', () => HttpResponse.text('MOCK_ONLY_OVERRIDE'))
}
`,
    'side-effect.ts': "document.body.dataset.sideEffect = 'preserved'",
    'entry.ts': `
import { appValue, createHandler } from './shared'
import './side-effect'

if (import.meta.env.DEV) {
  const { network: mocking } = await import('virtual:msw')
  const { handlers } = await import('./handlers')

  mocking.configure({ handlers })
  await mocking.enable()
  mocking.use(createHandler())
}

function start(network: { enable(): string }) {
  return network.enable()
}

document.querySelector('output')!.textContent = appValue + start({
  enable() {
    return ' with local network'
  },
})
`,
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw({ mode: 'auto' })],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    build: {
      minify: false,
      rollupOptions: { output: { entryFileNames: 'app.js' } },
    },
  })

  const output = fs.readFileSync(fsMock.resolve('dist/app.js'), 'utf8')

  expect(output).not.toContain('MOCK_ONLY')
  expect(output).not.toContain('virtual:msw')
  expect(output).not.toContain('defineNetwork')
  expect(output).not.toContain('mockServiceWorker')
  expect(output).not.toContain('HttpResponse')
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(fs.existsSync(fsMock.resolve('dist/mockServiceWorker.js'))).toBe(false)

  const server = await preview({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    preview: { host: '127.0.0.1', port: 0 },
  })
  await using serverResource = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  const browser = await chromium.launch()
  await using browserResource = {
    [Symbol.asyncDispose]: browser.close.bind(browser),
  }
  const page = await browser.newPage()
  await page.goto(server.resolvedUrls.local[0])

  await expect
    .poll(() => page.locator('output').textContent())
    .toBe('Application ready with local network')
  await expect(
    page.locator('body').getAttribute('data-side-effect'),
  ).resolves.toBe('preserved')
}, 20_000)

test('excludes user-guarded mocking from a production server bundle', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  const { msw: builtMsw } = await import('../../lib/vite/index.js')
  await fsMock.create({
    'package.json': '{"type":"module"}',
    'handlers.js': `
import { http, HttpResponse } from 'msw/http'
console.log('MOCK_ONLY_HANDLER_MODULE')
export const handlers = [http.get('/resource', () => HttpResponse.text('MOCK_ONLY_RESPONSE'))]
`,
    'entry.js': `
if (import.meta.env.DEV) {
  const { network } = await import('virtual:msw')
  const { handlers } = await import('./handlers.js')

  network.configure({ handlers })
  await network.enable()
}

console.log('Application ready')
`,
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [builtMsw()],
    build: { ssr: 'entry.js', outDir: 'server', minify: false },
  })
  const output = fs.readFileSync(fsMock.resolve('server/entry.js'), 'utf8')

  expect(output).not.toContain('MOCK_ONLY')
  expect(output).not.toMatch(/(?:from|import)\s*["'](?:msw|virtual:msw)/)
  expect(output).not.toContain('defineNetwork')
  expect(output).not.toContain('network')
  expect(output).not.toContain('handlers')
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(fs.existsSync(fsMock.resolve('server/mockServiceWorker.js'))).toBe(
    false,
  )

  const { stdout } = await fsMock.exec('node ./server/entry.js')

  expect(stdout.trim()).toBe('Application ready')
})

test('serves the worker when the dev server runs in production', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  const server = await createServer({
    configFile: false,
    root: fsMock.resolve('.'),
    appType: 'custom',
    logLevel: 'silent',
    plugins: [msw()],
    server: { host: '127.0.0.1', port: 0 },
  })
  await using serverResource = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  await server.listen()

  const response = await fetch(
    new URL('/mockServiceWorker.js', server.resolvedUrls?.local[0]),
  )

  expect(response.status).toBe(200)
})

test('restores cached server mocking after hot updates and replaces it on full reloads', async () => {
  const { msw: builtMsw } = await import('../../lib/vite/index.js')
  await fsMock.create({
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'

network.configure({
  handlers: [http.get('http://msw.test/resource', () => HttpResponse.text('mocked'))],
})
await network.enable()

export let completedUpdates = 0
export let disabledBeforeUpdate = false

if (import.meta.hot) {
  import.meta.hot.on('vite:beforeUpdate', () => {
    disabledBeforeUpdate = network.readyState === 0
  })
  import.meta.hot.on('vite:afterUpdate', () => {
    completedUpdates += 1
  })
}

export { network }
`,
  })

  const originalClientRequest = nodeHttp.ClientRequest
  const server = await createServer({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [builtMsw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    server: { middlewareMode: true, watch: null },
  })
  await using serverResource = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  const error = vi.fn()
  const runner = createServerModuleRunner(server.environments.ssr, {
    hmr: { logger: { debug: vi.fn(), error } },
  })
  await using runnerResource = {
    [Symbol.asyncDispose]: runner.close.bind(runner),
  }
  const integration = await runner.import<{
    network: typeof network
    completedUpdates: number
    disabledBeforeUpdate: boolean
  }>('/entry.js')
  await using interception = {
    [Symbol.asyncDispose]: async () => {
      const current = await runner.import<{ network: typeof network }>(
        '/entry.js',
      )

      if (current.network.readyState === 1) {
        await current.network.disable()
      }
    },
  }

  expect(nodeHttp.ClientRequest).not.toBe(originalClientRequest)

  server.environments.ssr.hot.send({ type: 'update', updates: [] })

  await expect.poll(() => integration.completedUpdates).toBe(1)
  expect(integration.disabledBeforeUpdate).toBe(true)
  await expect.poll(() => integration.network.readyState).toBe(1)
  expect(nodeHttp.ClientRequest).not.toBe(originalClientRequest)

  const updatedResponse = await fetch('http://msw.test/resource')

  await expect(updatedResponse.text()).resolves.toBe('mocked')
  await integration.network.disable()
  server.environments.ssr.hot.send({ type: 'update', updates: [] })

  await expect.poll(() => integration.completedUpdates).toBe(2)
  expect(integration.network.readyState).toBe(0)
  expect(nodeHttp.ClientRequest).toBe(originalClientRequest)

  await integration.network.enable()
  server.environments.ssr.hot.send({ type: 'full-reload' })

  await expect
    .poll(async () => {
      const current = await runner.import<{ network: typeof network }>(
        '/entry.js',
      )

      return (
        current.network !== integration.network &&
        current.network.readyState === 1
      )
    })
    .toBe(true)
  expect(integration.network.readyState).toBe(0)
  expect(error).not.toHaveBeenCalled()

  const response = await fetch('http://msw.test/resource')

  await expect(response.text()).resolves.toBe('mocked')
})

test('replays server setup and retires replaced networks during reloads', async () => {
  const { msw: builtMsw } = await import('../../lib/vite/index.js')
  await fsMock.create({
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'
network.configure({ handlers: [http.get('http://msw.test/resource', () => HttpResponse.text('mocked'))] })
await network.enable()
export { network }
`,
  })
  const server = await createServer({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [builtMsw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    server: { middlewareMode: true, watch: null },
  })
  await using serverResource = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  const runner = createServerModuleRunner(server.environments.ssr)
  await using runnerResource = {
    [Symbol.asyncDispose]: runner.close.bind(runner),
  }
  const original = await runner.import<{ network: typeof network }>('/entry.js')
  await using originalInterception = {
    [Symbol.asyncDispose]: async () => {
      if (original.network.readyState === 1) {
        await original.network.disable()
      }
    },
  }

  const entryModule = runner.evaluatedModules.getModuleByUrl('/entry.js')
  expect(entryModule).toBeDefined()
  runner.evaluatedModules.invalidateModule(entryModule!)
  const replayed = await runner.import<{ network: typeof network }>('/entry.js')

  expect(replayed.network).toBe(original.network)

  // An overlapping SSR import can evaluate the replacement before HMR cleanup runs.
  runner.evaluatedModules.clear()
  const replacement = await runner.import<{ network: typeof network }>(
    '/entry.js',
  )
  await using replacementInterception = {
    [Symbol.asyncDispose]: async () => {
      if (replacement.network.readyState === 1) {
        await replacement.network.disable()
      }
    },
  }

  expect(replacement.network).not.toBe(original.network)
  expect(original.network.readyState).toBe(0)
  await original.network.enable()
  expect(original.network.readyState).toBe(0)
  expect(replacement.network.readyState).toBe(1)
  const response = await fetch('http://msw.test/resource')

  await expect(response.text()).resolves.toBe('mocked')
})

test('creates one disabled network on the server and lets the user enable it', async () => {
  await fsMock.create({
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'

network.configure({
  handlers: [http.get('http://msw.test/resource', () => HttpResponse.text('mocked'))],
})

export { network }
`,
  })

  const originalFetch = globalThis.fetch
  const server = await createServer({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    server: { middlewareMode: true },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  const integration: { network: typeof network } =
    await server.ssrLoadModule('/entry.js')
  const repeatedImport: { network: typeof network } =
    await server.ssrLoadModule('virtual:msw')

  expect(integration.network).toBe(repeatedImport.network)
  expect(integration.network.readyState).toBe(0)
  expect(globalThis.fetch).toBe(originalFetch)

  await integration.network.enable()
  await using interception = {
    [Symbol.asyncDispose]: async () => {
      await integration.network.disable()
    },
  }
  const response = await fetch('http://msw.test/resource')

  await expect(response.text()).resolves.toBe('mocked')
})

test('preserves browser interception through hot updates under a base path', async () => {
  const { msw: builtMsw } = await import('../../lib/vite/index.js')
  await fsMock.create({
    'index.html':
      '<button>Enable</button><output></output><script type="module" src="/entry.js"></script>',
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'

network.configure({
  handlers: [http.get('/resource', () => HttpResponse.text('mocked'))],
})

document.querySelector('output').textContent = String(network.readyState)
let completedUpdates = 0

if (import.meta.hot) {
  import.meta.hot.on('vite:beforeUpdate', () => {
    document.body.dataset.disabledBeforeUpdate = String(network.readyState === 0)
  })
  import.meta.hot.on('vite:afterUpdate', () => {
    document.body.dataset.completedUpdates = String(++completedUpdates)
  })
}

document.querySelector('button').onclick = async () => {
  await network.enable()
  const response = await fetch('/resource')
  document.querySelector('output').textContent = await response.text()
}
`,
  })

  const server = await createServer({
    configFile: false,
    base: '/app/',
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [builtMsw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    server: { host: '127.0.0.1', port: 0, watch: null },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  await server.listen()

  const serverIntegration: { network: typeof network } =
    await server.ssrLoadModule('virtual:msw')

  const browser = await chromium.launch()
  await using browserResource = {
    [Symbol.asyncDispose]: browser.close.bind(browser),
  }
  const page = await browser.newPage()
  const connected = page.waitForEvent('console', {
    predicate: (message) => message.text().includes('[vite] connected.'),
  })
  await page.goto(new URL('/app/nested/', server.resolvedUrls!.local[0]).href, {
    waitUntil: 'networkidle',
  })
  await connected

  await expect.poll(() => page.locator('output').textContent()).toBe('0')
  server.environments.client.hot.send({ type: 'update', updates: [] })

  await expect
    .poll(() => page.locator('body').getAttribute('data-completed-updates'))
    .toBe('1')
  await expect(
    page.evaluate(async () => {
      const registrations = await navigator.serviceWorker.getRegistrations()

      return registrations.length
    }),
  ).resolves.toBe(0)

  await page.getByRole('button', { name: 'Enable' }).click()

  await expect.poll(() => page.locator('output').textContent()).toBe('mocked')
  await page.evaluate(() => {
    delete document.body.dataset.disabledBeforeUpdate
  })
  server.environments.client.hot.send({ type: 'update', updates: [] })

  await expect
    .poll(() => page.locator('body').getAttribute('data-completed-updates'))
    .toBe('2')
  await expect(
    page.locator('body').getAttribute('data-disabled-before-update'),
  ).resolves.toBe('true')
  /**
   * @note The runtime re-enables the network after the update asynchronously:
   * the worker has to be re-activated before it can respond with mocks.
   * Until then, requests reach the dev server. Poll for the mocked response.
   */
  await expect
    .poll(
      () =>
        page.evaluate(async () => {
          const response = await fetch('/resource')

          return response.text()
        }),
      { timeout: 10_000 },
    )
    .toBe('mocked')
  await expect(
    page.evaluate(async () => {
      const registrations = await navigator.serviceWorker.getRegistrations()

      return registrations[0].active?.scriptURL
    }),
  ).resolves.toBe(
    new URL('/app/mockServiceWorker.js', server.resolvedUrls!.local[0]).href,
  )
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(serverIntegration.network.readyState).toBe(0)
}, 20_000)

test('creates a server network in development builds', async () => {
  vi.stubEnv('NODE_ENV', 'development')
  await fsMock.create({
    'package.json': '{"type":"module"}',
    'verify.js': `
import assert from 'node:assert/strict'

const originalFetch = globalThis.fetch
const { network } = await import('./server/entry.js')
assert.equal(network.readyState, 0)
assert.equal(globalThis.fetch, originalFetch)

await network.enable()
const response = await fetch('http://msw.test/resource')
console.log(await response.text())
await network.disable()
assert.equal(globalThis.fetch, originalFetch)
`,
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'

network.configure({
  handlers: [http.get('http://msw.test/resource', () => HttpResponse.text('mocked'))],
})

export { network }
`,
  })

  fs.mkdirSync(fsMock.resolve('node_modules'))
  fs.symlinkSync(fromRoot('.'), fsMock.resolve('node_modules/msw'), 'dir')
  using dependencyLink = {
    [Symbol.dispose]: () => {
      fs.unlinkSync(fsMock.resolve('node_modules/msw'))
    },
  }

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    ssr: { external: ['msw/experimental', 'msw/node', 'msw/http'] },
    build: { ssr: 'entry.js', outDir: 'server' },
  })
  const { stdout } = await fsMock.exec('node ./verify.js')

  expect(stdout.trim()).toBe('mocked')
})

test('creates a browser network in development builds without enabling it automatically', async () => {
  vi.stubEnv('NODE_ENV', 'development')
  await fsMock.create({
    'index.html':
      '<button>Enable</button><output></output><script type="module" src="/entry.js"></script>',
    'entry.js': `
import { network } from 'virtual:msw'
import { http, HttpResponse } from 'msw/http'

network.configure({
  handlers: [http.get('/resource', () => HttpResponse.text('mocked'))],
})

document.querySelector('output').textContent = String(network.readyState)
document.querySelector('button').onclick = async () => {
  await network.enable()
  network.use(http.get('/resource', () => HttpResponse.text('override')))
  const response = await fetch('/resource')
  document.querySelector('output').textContent = await response.text()
}
`,
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
  })
  const server = await preview({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    preview: { host: '127.0.0.1', port: 0 },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }
  const browser = await chromium.launch()
  await using browserResource = {
    [Symbol.asyncDispose]: browser.close.bind(browser),
  }
  const page = await browser.newPage()
  await page.goto(server.resolvedUrls.local[0])

  await expect.poll(() => page.locator('output').textContent()).toBe('0')
  await expect(
    page.evaluate(async () => {
      const registrations = await navigator.serviceWorker.getRegistrations()

      return registrations.length
    }),
  ).resolves.toBe(0)

  await page.getByRole('button', { name: 'Enable' }).click()

  await expect.poll(() => page.locator('output').textContent()).toBe('override')
}, 20_000)

test('serves the worker script without writing files during development', async () => {
  const server = await createServer({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    server: {
      host: '127.0.0.1',
      port: 0,
    },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }

  await server.listen()

  const serverUrl = server.resolvedUrls?.local[0]
  expect(serverUrl).toBeDefined()

  const response = await fetch(new URL('/mockServiceWorker.js', serverUrl))

  expect(response.status).toBe(200)
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(response.headers.get('content-type')).toBe(
    'application/javascript; charset=utf-8',
  )
  await expect(response.text()).resolves.toBe(
    fs.readFileSync(
      new URL('../mockServiceWorker.js', import.meta.url),
      'utf8',
    ),
  )
})

test('serves the worker at the root for a relative development base', async () => {
  const server = await createServer({
    configFile: false,
    base: './',
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    server: {
      host: '127.0.0.1',
      port: 0,
    },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }

  await server.listen()

  const serverUrl = server.resolvedUrls?.local[0]
  expect(serverUrl).toBeDefined()

  const response = await fetch(new URL('/mockServiceWorker.js', serverUrl))

  expect(response.status).toBe(200)
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(response.headers.get('content-type')).toBe(
    'application/javascript; charset=utf-8',
  )
  await expect(response.text()).resolves.toBe(
    fs.readFileSync(
      new URL('../mockServiceWorker.js', import.meta.url),
      'utf8',
    ),
  )
})

test('serves only the worker in worker-only mode with an absolute base URL', async () => {
  const server = await createServer({
    configFile: false,
    base: 'https://cdn.example.com/app/',
    root: fsMock.resolve('.'),
    publicDir: 'static',
    logLevel: 'silent',
    plugins: [msw({ mode: 'worker-only' })],
    server: {
      host: '127.0.0.1',
      port: 0,
    },
  })
  await using _ = {
    [Symbol.asyncDispose]: server.close.bind(server),
  }

  await server.listen()

  const serverUrl = server.resolvedUrls?.local[0]
  expect(serverUrl).toBeDefined()

  const response = await fetch(new URL('/app/mockServiceWorker.js', serverUrl))

  expect(response.status).toBe(200)
  expect(response.headers.get('service-worker-allowed')).toBe('/')
  await expect(
    fetch(new URL('/mockServiceWorker.js', serverUrl)),
  ).resolves.toHaveProperty('status', 404)
  expect(fs.existsSync(fsMock.resolve('static'))).toBe(false)
  await expect(response.text()).resolves.toContain('* Mock Service Worker.')
  await expect(
    server.environments.client.pluginContainer.resolveId('virtual:msw'),
  ).resolves.toBeNull()
  await expect(
    server.environments.ssr.pluginContainer.resolveId('virtual:msw/options'),
  ).resolves.toBeNull()
})

test('preserves user integrations during production builds in worker-only mode', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  await fsMock.create({
    'package.json': '{"type":"module"}',
    'entry.js': `
import { network } from 'virtual:msw'
import { handlers } from './handlers.js'
network.configure({ handlers })
await network.enable()
`,
    'handlers.js': "export const handlers = ['USER_DEFINED_HANDLERS']",
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw({ mode: 'worker-only' })],
    build: {
      ssr: 'entry.js',
      minify: false,
      rollupOptions: { external: ['virtual:msw'] },
    },
  })
  const output = fs.readFileSync(fsMock.resolve('dist/entry.js'), 'utf8')

  expect(output).toContain('virtual:msw')
  expect(output).toContain('network.configure(')
  expect(output).toContain('await network.enable()')
  expect(output).toContain('USER_DEFINED_HANDLERS')
  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
})

test('does not emit the worker script for builds without the network', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  await fsMock.create({
    'index.html': '<html><body>Example app</body></html>',
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
  })

  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(fs.existsSync(fsMock.resolve('dist/mockServiceWorker.js'))).toBe(false)
})

test('emits the worker script for production builds that import the network', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  await fsMock.create({
    'index.html': '<script type="module" src="/entry.js"></script>',
    'entry.js': `
import { network } from 'virtual:msw'
await network.enable()
`,
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    logLevel: 'silent',
    plugins: [msw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
  })

  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(
    fs.readFileSync(fsMock.resolve('dist/mockServiceWorker.js'), 'utf8'),
  ).toBe(
    fs.readFileSync(
      new URL('../mockServiceWorker.js', import.meta.url),
      'utf8',
    ),
  )
})

test('emits the worker to the client output instead of the public directory', async () => {
  vi.stubEnv('NODE_ENV', 'development')
  await fsMock.create({
    'index.html': '<script type="module" src="/entry.js"></script>',
    'entry.js': "import 'virtual:msw'",
    'static/example.txt': 'Public asset',
  })

  await build({
    configFile: false,
    base: '/app/',
    root: fsMock.resolve('.'),
    publicDir: 'static',
    logLevel: 'silent',
    plugins: [msw()],
    resolve: {
      alias: {
        'msw/experimental': fromRoot('lib/core/experimental/index.js'),
        ...mswExports,
      },
    },
    build: {
      outDir: 'build/client',
      assetsDir: 'bundled',
      copyPublicDir: false,
    },
  })

  expect(fs.existsSync(fsMock.resolve('static/mockServiceWorker.js'))).toBe(
    false,
  )
  expect(fs.existsSync(fsMock.resolve('static/app'))).toBe(false)
  expect(
    fs.readFileSync(
      fsMock.resolve('build/client/mockServiceWorker.js'),
      'utf8',
    ),
  ).toBe(
    fs.readFileSync(
      new URL('../mockServiceWorker.js', import.meta.url),
      'utf8',
    ),
  )
})

test('emits the worker in worker-only mode without the virtual modules', async () => {
  vi.stubEnv('NODE_ENV', 'production')
  await fsMock.create({
    'index.html': '<html><body>Example app</body></html>',
  })

  await build({
    configFile: false,
    root: fsMock.resolve('.'),
    publicDir: false,
    logLevel: 'silent',
    plugins: [msw({ mode: 'worker-only' })],
  })

  expect(fs.existsSync(fsMock.resolve('public'))).toBe(false)
  expect(fs.existsSync(fsMock.resolve('dist/mockServiceWorker.js'))).toBe(true)
})
