import frameworkUI from "@framework/ui/vite"; import vue from "@vitejs/plugin-vue"; import vueJsx from "@vitejs/plugin-vue-jsx"; import path from "path"; import { defineConfig } from "vite"; import { VitePWA } from "vite-plugin-pwa"; import { getLocalFrappeUIDevConfig, importFrappeUIPlugin, } from "./vite-helpers"; export default defineConfig(async ({ mode }) => { const { useLocalFrappeUI, localFrappeUIAliases } = getLocalFrappeUIDevConfig({ mode, rootDir: __dirname, }); const frappeui = await importFrappeUIPlugin({ useLocalFrappeUI }); const config = { plugins: [ frappeui({ frappeProxy: true, lucideIcons: true, jinjaBootData: true, buildConfig: { outDir: `../opendesk/public/desk`, emptyOutDir: true, indexHtmlPath: "../opendesk/www/opendesk/index.html", }, frappeTypes: { input: { opendesk: [ "od_ticket_status", "od_ticket", "od_service_holiday_list", "od_service_level_agreement", "od_agent", "od_team", "od_customer", ], frappe: ["assignment_rule", "contact"], }, }, }), frameworkUI(), vue(), vueJsx(), VitePWA({ registerType: "autoUpdate", devOptions: { enabled: true, }, workbox: { cleanupOutdatedCaches: true, maximumFileSizeToCacheInBytes: 5 * 1024 * 1024, }, manifest: { display: "standalone", name: "Opendesk", short_name: "Opendesk", start_url: "/opendesk", description: "Modern, Streamlined, Free and Open Source Customer Service Software", icons: [ { src: "/assets/opendesk/desk/manifest/manifest-icon-192.maskable.png", sizes: "192x192", type: "image/png", purpose: "any", }, { src: "/assets/opendesk/desk/manifest/manifest-icon-192.maskable.png", sizes: "192x192", type: "image/png", purpose: "maskable", }, { src: "/assets/opendesk/desk/manifest/manifest-icon-512.maskable.png", sizes: "512x512", type: "image/png", purpose: "any", }, { src: "/assets/opendesk/desk/manifest/manifest-icon-512.maskable.png", sizes: "512x512", type: "image/png", purpose: "maskable", }, ], }, }), ], server: { allowedHosts: true, fs: { // ".." = apps/opendesk; "../.." = apps/, so the linked @framework/ui // (apps/frappe/ui) can be served from source in dev. allow: ["..", "../.."], }, }, resolve: { alias: { "@": path.resolve(__dirname, "src"), "tailwind.config.js": path.resolve(__dirname, "tailwind.config.js"), // @framework/ui still imports the `frappe-ui/editor-style.css` subpath, // dropped in frappe-ui 1.0.0-beta.63 (the editor entrypoint pulls its // own stylesheet now). Point it at the real file until frappe drops it. "frappe-ui/editor-style.css": path.resolve( __dirname, "node_modules/frappe-ui/src/molecules/editor/style.css" ), // ...localFrappeUIAliases, }, // frappe-ui is served from source (excluded from optimizeDeps) and the // submodule ships its own node_modules with older tiptap/ProseMirror. // Force a single copy of each so the editor doesn't load two // prosemirror-state instances (RangeError: different instances of a keyed plugin). dedupe: [ // @framework/ui imports from vue/frappe-ui; force a single instance of // each so its Combobox shares opendesk's frappe-ui, not a second copy. // vue-router and dompurify are peers of @framework/ui, so they have // to come from opendesk's copies "vue", "vue-router", "frappe-ui", // studio's NewTicketForm imports it from outside desk/ "@framework/ui", "dompurify", "reka-ui", "@tiptap/core", "@tiptap/pm", // shared/ imports these from outside desk/ "@tiptap/extension-table", "@tiptap/extension-text-align", "@tiptap/extension-text-style", "@tiptap/starter-kit", "prosemirror-state", "prosemirror-model", "prosemirror-transform", "prosemirror-view", "prosemirror-keymap", "prosemirror-commands", "prosemirror-history", "prosemirror-gapcursor", "prosemirror-tables", // shared/ imports it from outside desk/ "vuedraggable", ], }, optimizeDeps: { include: [ "tailwind.config.js", "prosemirror-state", "prosemirror-view", "prosemirror-gapcursor", "prosemirror-tables", "lowlight", "interactjs", ], exclude: ["frappe-ui", "@framework/ui", "@opendesk/shared"], }, }; return config; });