import { existsSync } from "node:fs"; import path from "path"; import type { PluginOption } from "vite"; interface LocalFrappeUIDevConfigParams { mode: string; rootDir: string; } interface LocalFrappeUIDevConfig { useLocalFrappeUI: boolean; localFrappeUIPath: string; localFrappeUIAliases: Record; } interface ImportFrappeUIPluginParams { useLocalFrappeUI: boolean; } type FrappeUIPluginFactory = (...args: any[]) => PluginOption; type FrappeUIPluginModule = { default: FrappeUIPluginFactory }; async function loadFrappeUIPluginModule( modulePath: string ): Promise { return (await import(modulePath)) as FrappeUIPluginModule; } export function getLocalFrappeUIDevConfig({ mode, rootDir, }: LocalFrappeUIDevConfigParams): LocalFrappeUIDevConfig { const isDev = mode === "development"; const localFrappeUIPath = path.resolve(rootDir, "../frappe-ui"); const useLocalFrappeUI = isDev && existsSync(path.join(localFrappeUIPath, "node_modules")); if (isDev && existsSync(localFrappeUIPath) && !useLocalFrappeUI) { console.warn("⚠️ Local frappe-ui found but dependencies not installed."); console.warn(" Run: cd ../frappe-ui && yarn install"); } const localFrappeUIAliases: Record = useLocalFrappeUI ? { "frappe-ui/style.css": path.resolve( localFrappeUIPath, "src", "style.css" ), // The `frappe-ui/editor` subpath maps to src/molecules/editor via the // package's exports map, which the bare `frappe-ui` directory alias // bypasses. Map it explicitly (same pattern as `frappe-ui/style.css`) // so the v1 editor resolves when developing against local frappe-ui. "frappe-ui/editor": path.resolve( localFrappeUIPath, "src", "molecules", "editor", "index.ts" ), "frappe-ui": localFrappeUIPath, } : {}; return { useLocalFrappeUI, localFrappeUIPath, localFrappeUIAliases, }; } export async function importFrappeUIPlugin({ useLocalFrappeUI, }: ImportFrappeUIPluginParams): Promise { const npmModulePath = "frappe-ui/vite"; const modulePath = useLocalFrappeUI ? "../frappe-ui/vite/index.js" : npmModulePath; try { const module = await loadFrappeUIPluginModule(modulePath); return module.default as FrappeUIPluginFactory; } catch (error) { if (useLocalFrappeUI) { console.warn( "⚠️ Failed to import local frappe-ui plugin, falling back to npm package" ); console.warn( " Error:", error instanceof Error ? error.message : String(error) ); const fallbackModule = await loadFrappeUIPluginModule(npmModulePath); return fallbackModule.default as FrappeUIPluginFactory; } throw error; } }