import { randomUUID } from 'crypto' import { resolve } from 'path' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import tailwindcss from '@tailwindcss/vite' import Icons from 'unplugin-icons/vite' import IconsResolver from 'unplugin-icons/resolver' import Components from 'unplugin-vue-components/vite' import { VitePWA } from 'vite-plugin-pwa' import { copyFileSync, existsSync, mkdirSync } from 'fs' import { automationPlugin } from './src/automation/vite-plugin' const devAutomationAuthToken = randomUUID() // @ts-expect-error process is a nodejs global const host = process.env.TAURI_DEV_HOST const devAutomationCorsOrigin = host ? `http://${host}:1420` : 'http://localhost:1420' export default defineConfig(async ({ command }) => ({ resolve: { alias: { '@': resolve(__dirname, 'src'), '@open-pencil/vue': resolve(__dirname, 'packages/vue/src'), '@open-pencil/core': resolve(__dirname, 'packages/core/src'), 'opentype.js': resolve(__dirname, 'node_modules/opentype.js/dist/opentype.module.js'), // vue-stream-markdown eagerly loads mermaid/beautiful-mermaid as optional peer deps. // Alias to empty shims to avoid runtime errors and reduce bundle size. mermaid: resolve(__dirname, 'src/shims/mermaid.ts'), 'beautiful-mermaid': resolve(__dirname, 'src/shims/mermaid.ts') } }, define: { __OPENPENCIL_LOCAL_AUTOMATION_TOKEN__: JSON.stringify( command === 'serve' ? devAutomationAuthToken : null ) }, plugins: [ { name: 'raw-md', transform(code: string, id: string) { if (id.endsWith('.md')) { return { code: `export default ${JSON.stringify(code)}`, map: null } } } }, { name: 'copy-canvaskit-wasm', buildStart() { const src = 'node_modules/canvaskit-wasm/bin/canvaskit.wasm' const dest = 'public/canvaskit.wasm' if (existsSync(src) && !existsSync(dest)) { copyFileSync(src, dest) } const webgpuSrc = 'packages/core/vendor/canvaskit-webgpu/canvaskit.wasm' const webgpuDir = 'public/canvaskit-webgpu' const webgpuDest = `${webgpuDir}/canvaskit.wasm` if (existsSync(webgpuSrc) && !existsSync(webgpuDest)) { mkdirSync(webgpuDir, { recursive: true }) copyFileSync(webgpuSrc, webgpuDest) } const webgpuJsSrc = 'packages/core/vendor/canvaskit-webgpu/canvaskit.js' const webgpuJsDest = `${webgpuDir}/canvaskit.js` if (existsSync(webgpuJsSrc) && !existsSync(webgpuJsDest)) { mkdirSync(webgpuDir, { recursive: true }) copyFileSync(webgpuJsSrc, webgpuJsDest) } } }, tailwindcss(), Icons({ compiler: 'vue3' }), Components({ resolvers: [IconsResolver({ prefix: 'icon' })] }), automationPlugin(command === 'serve' ? devAutomationAuthToken : null, devAutomationCorsOrigin), vue(), VitePWA({ registerType: 'autoUpdate', devOptions: { enabled: false }, workbox: { maximumFileSizeToCacheInBytes: 12 * 1024 * 1024, globPatterns: ['**/*.{js,css,html,wasm,png,ico,ttf,webmanifest}'], navigateFallback: '/index.html' }, manifest: { name: 'OpenPencil', short_name: 'OpenPencil', description: 'Open-source design editor', display: 'standalone', orientation: 'any', start_url: '/', scope: '/', theme_color: '#1e1e1e', background_color: '#1e1e1e', categories: ['design', 'productivity'], icons: [ { src: '/pwa-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' }, { src: '/pwa-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' }, { src: '/pwa-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' } ] } }) ], clearScreen: false, server: { port: 1420, strictPort: true, host: host || false, hmr: host ? { protocol: 'ws', host, port: 1421 } : undefined, watch: { ignored: [ '**/desktop/**', '**/packages/cli/**', '**/packages/mcp/**', '**/packages/docs/**', '**/tests/**', '**/.worktrees/**', '**/.github/**', '**/.pi/**' ] } } }))