import type { Plugin } from 'vite'
import { stripLiteral } from 'strip-literal'

const metaUrlLength = 'import.meta.url'.length
const locationString = 'self.location'.padEnd(metaUrlLength, ' ')

// Vite transforms new URL('./path', import.meta.url) to new URL('/path.js', import.meta.url)
// This makes "href" equal to "http://localhost:3000/path.js" in the browser, but if we keep it like this,
// then in tests the URL will become "file:///path.js".
// To battle this, we replace "import.meta.url" with "self.location" in the code to keep the browser behavior.
export function NormalizeURLPlugin(): Plugin {
  return {
    name: 'vitest:normalize-url',
    enforce: 'post',
    transform(code) {
      if (
        this.environment.name !== 'client'
        || !code.includes('new URL')
        || !code.includes('import.meta.url')
      ) {
        return
      }

      const cleanString = stripLiteral(code)
      const assetImportMetaUrlRE
      // vite injects new URL(path, import.meta.url) in the code
      // rolldown-vite injects new URL(path, '' + import.meta.url) in the code
        = /\bnew\s+URL\s*\(\s*(?:'[^']+'|"[^"]+"|`[^`]+`)\s*,\s*(?:'' \+ )?import\.meta\.url\s*(?:,\s*)?\)/g

      let updatedCode = code
      let match: RegExpExecArray | null
      // eslint-disable-next-line no-cond-assign
      while ((match = assetImportMetaUrlRE.exec(cleanString))) {
        const { 0: exp, index } = match
        const metaUrlIndex = index + exp.indexOf('import.meta.url')
        updatedCode
          = updatedCode.slice(0, metaUrlIndex)
            + locationString
            + updatedCode.slice(metaUrlIndex + metaUrlLength)
      }

      return {
        code: updatedCode,
        map: null,
      }
    },
  }
}
