electron.vite.config.ts

import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

// Removes script-src 'unsafe-inline' from the renderer CSP in production builds
// only. Dev keeps it because @vitejs/plugin-react injects an inline refresh
// preamble and Vite's HMR client needs inline execution; the production bundle
// has no inline scripts (theme bootstrap is externalized to public/theme-init.js
// and modulePreload.polyfill is off), so 'self' alone is sufficient there.
const strictProdCsp = {
  name: 'strict-prod-csp',
  transformIndexHtml: {
    order: 'post' as const,
    handler(html: string, ctx: { server?: unknown }): string {
      if (ctx.server) return html
      const out = html.replace("script-src 'self' 'unsafe-inline'", "script-src 'self'")
      // Fail the build loudly if the CSP string in index.html drifted and left
      // script-src allowing 'unsafe-inline' — the exact-match replace above
      // would otherwise no-op silently and ship an unsafe policy.
      const scriptSrc = out.match(/script-src[^;]*/)?.[0] ?? ''
      if (scriptSrc.includes("'unsafe-inline'")) {
        throw new Error(
          `strict-prod-csp: production script-src still allows 'unsafe-inline' — ` +
            `update the CSP rewrite to match index.html. Got: ${scriptSrc}`
        )
      }
      return out
    }
  }
}

export default defineConfig({
  main: {
    plugins: [externalizeDepsPlugin()],
    resolve: {
      alias: {
        '@main': resolve('src/main'),
        '@shared': resolve('src/shared')
      }
    }
  },
  preload: {
    plugins: [externalizeDepsPlugin()],
    resolve: {
      alias: {
        '@shared': resolve('src/shared')
      }
    }
  },
  renderer: {
    resolve: {
      alias: {
        '@renderer': resolve('src/renderer'),
        '@shared': resolve('src/shared')
      }
    },
    build: {
      // Electron's Chromium supports modulepreload natively, so skip Vite's
      // inline polyfill script — that keeps the production HTML free of inline
      // scripts so the strict CSP above holds even if code-splitting is added.
      modulePreload: { polyfill: false }
    },
    plugins: [react(), tailwindcss(), strictProdCsp]
  }
})