import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import path from 'path' import { VitePWA } from 'vite-plugin-pwa' // https://vitejs.dev/config/ export default defineConfig(async ({ mode }) => { const isDev = mode === 'development' const config = { plugins: [ vue(), vueJsx(), VitePWA({ registerType: 'autoUpdate', workbox: { maximumFileSizeToCacheInBytes: 8 * 1024 * 1024, }, devOptions: { enabled: true, }, manifest: { display: 'standalone', name: 'Frappe CRM', short_name: 'Frappe CRM', start_url: '/crm', description: 'Modern & 100% Open-source CRM tool to supercharge your sales operations', icons: [ { src: '/assets/crm/manifest/manifest-icon-192.maskable.png', sizes: '192x192', type: 'image/png', purpose: 'any', }, { src: '/assets/crm/manifest/manifest-icon-192.maskable.png', sizes: '192x192', type: 'image/png', purpose: 'maskable', }, { src: '/assets/crm/manifest/manifest-icon-512.maskable.png', sizes: '512x512', type: 'image/png', purpose: 'any', }, { src: '/assets/crm/manifest/manifest-icon-512.maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable', }, ], }, }), ], resolve: { alias: { '@': path.resolve(import.meta.dirname, 'src'), // point at the package src dir (not index.ts) so subpath imports like // `@framework/ui/components/Notifications` resolve. Importing subpaths avoids the // barrel, which `export *`s components (Grid/Phone/FormLayout) that need a newer // frappe-ui (`frappe-ui/internals`) than this app pins. '@framework/ui': path.resolve( import.meta.dirname, '../../frappe/ui/src', ), }, // ensure the linked framework package reuses the host app's single copy of each peer. // `dompurify` is an implicit dep of @framework/ui's sanitize util (not declared in its // package.json); dedupe resolves it to the host's copy since the symlinked source has // no node_modules of its own. // the editor packages must resolve to one copy each: tiptap imports // `@tiptap/pm/model` while prosemirror-state/transform/tables import bare // `prosemirror-model`, so a nested install of either throws "multiple // versions of prosemirror-model were loaded" on mention insert. Unlike // optimizeDeps (dev-only) this also applies to the production build. dedupe: [ 'vue', 'vue-router', 'frappe-ui', 'dompurify', '@tiptap/core', '@tiptap/pm', '@tiptap/vue-3', 'prosemirror-model', 'prosemirror-state', 'prosemirror-view', 'prosemirror-transform', ], }, optimizeDeps: { include: [ 'feather-icons', 'tailwind.config.js', 'prosemirror-state', 'prosemirror-view', 'lowlight', 'interactjs', ], }, server: { fs: { // allow the bench `apps/` dir so Vite can serve linked local packages // (frappe-ui, @framework/ui) that live in sibling app repos allow: [path.resolve(import.meta.dirname, '../..')], }, }, } const frappeui = await importFrappeUIPlugin(isDev, config) config.plugins.unshift( frappeui({ frappeProxy: true, lucideIcons: true, jinjaBootData: true, buildConfig: { indexHtmlPath: '../crm/www/crm.html', emptyOutDir: true, sourcemap: true, }, }), ) return config }) async function importFrappeUIPlugin(isDev, config) { if (isDev) { try { // Check if local frappe-ui has the vite plugin file const fs = await import('node:fs') const localVitePluginPath = path.resolve( import.meta.dirname, '../frappe-ui/vite/index.js', ) if (fs.existsSync(localVitePluginPath)) { const module = await import('../frappe-ui/vite/index.js') console.info('Local frappe-ui vite plugin found, using local plugin') config.resolve.alias = getAliases(config) return module.default } else { console.warn('Local frappe-ui vite plugin not found, using npm package') } } catch (error) { console.warn( 'Local frappe-ui not found, falling back to npm package:', error.message, ) } } // Fall back to npm package if local import fails const module = await import('frappe-ui/vite') return module.default } function getAliases(config) { return { ...config.resolve.alias, 'frappe-ui/tailwind': path.resolve( import.meta.dirname, '../frappe-ui/tailwind/preset.js', ), 'frappe-ui/style.css': path.resolve( import.meta.dirname, '../frappe-ui/src/style.css', ), 'frappe-ui/frappe': path.resolve( import.meta.dirname, '../frappe-ui/frappe/index.js', ), // subpath entries must precede the bare `frappe-ui` key: a plain string alias // matches by prefix, so without these subpaths would rewrite under // `.../src/index.ts`. `internals` is pulled in by @framework/ui. 'frappe-ui/icons': path.resolve( import.meta.dirname, '../frappe-ui/icons/index.ts', ), 'frappe-ui/editor': path.resolve( import.meta.dirname, '../frappe-ui/src/molecules/editor/index.ts', ), 'frappe-ui/editor-style.css': path.resolve( import.meta.dirname, '../frappe-ui/src/molecules/editor/style.css', ), 'frappe-ui/internals': path.resolve( import.meta.dirname, '../frappe-ui/internals.ts', ), 'frappe-ui': path.resolve(import.meta.dirname, '../frappe-ui/src/index.ts'), } }