import { createServer, build, InlineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import path from "path"; import { getEntryPoints, addMetaXmlFilePlugin, pathAliasPlugin, getSfdxProjectConfig, getmisoConfig, gitignorePlugin, } from "../utils/vite.js"; import fs from "fs"; interface ViteOptions { mode?: "dev" | "build"; port?: number; } let chunkId = 0; async function ensureAssetFiles( outDir: string, extraAssetsCount: number, apiVersion: string ) { for (let i = 1; i <= extraAssetsCount; i++) { const assetDir = path.join(outDir, `assets_${i}`); const jsFile = path.join(assetDir, `assets_${i}.js`); const xmlFile = path.join(assetDir, `assets_${i}.js-meta.xml`); if (!fs.existsSync(assetDir)) { console.log(`Created empty lwc component: assets_${i}, since the 'extraAssetsCount' in miso.config.mjs is ${extraAssetsCount}`); fs.mkdirSync(assetDir, { recursive: true }); } if (!fs.existsSync(jsFile)) { fs.writeFileSync( jsFile, "// This file is intentionally left empty because Salesforce does not allow component deletion\n" ); } if (!fs.existsSync(xmlFile)) { const xmlContent = ` ${apiVersion} false `; fs.writeFileSync(xmlFile, xmlContent); } } } export async function vite(options: ViteOptions = {}) { const { mode = "dev", port } = options; try { // Get the directory where the command is executed const rootDir = process.env.INIT_CWD || process.cwd(); // Get API version from sfdx-project.json const sfdxProjectConfig = getSfdxProjectConfig(rootDir); const misoConfig = await getmisoConfig(rootDir); const apiVersion = sfdxProjectConfig.sourceApiVersion; const entries = getEntryPoints(misoConfig.reactAppRoot); const viteConfig: InlineConfig = { resolve: { dedupe: ["react", "react-dom"], alias: [ { find: /^@salesforce\/apex\/(.*)/, replacement: path.resolve( process.cwd(), `.miso/salesforce/apex?path=$1` ), }, { find: /^@salesforce\/schema(\/.*)?$/, replacement: path.resolve(process.cwd(), ".miso/salesforce/schema"), }, { find: /^lightning\/uiRecordApi(\/.*)?$/, replacement: path.resolve( process.cwd(), ".miso/lightning/uiRecordApi" ), }, { find: /^lightning\/toast(\/.*)?$/, replacement: path.resolve(process.cwd(), ".miso/salesforce/toast"), }, { find: /^@\/(.*)/, replacement: path.resolve( process.cwd(), `${misoConfig.reactAppRoot}/src/$1` ), }, ], }, root: path.join(process.cwd(), ".miso"), plugins: [ react(), addMetaXmlFilePlugin(apiVersion), pathAliasPlugin(), gitignorePlugin(rootDir, entries), ], define: { "process.emit": "null", "process.env.miso_SFDX_NAMESPACE": JSON.stringify( sfdxProjectConfig.namespace || "" ), "process.env.miso_REACT_APP_ROOT": `'${misoConfig.reactAppRoot}'`, }, esbuild: { minifyIdentifiers: false, keepNames: true, }, build: { outDir: "../force-app/main/default/lwc", emptyOutDir: false, rollupOptions: { preserveEntrySignatures: "strict", input: entries, treeshake: { moduleSideEffects: false, }, external: [ /^@salesforce\/apex(\/.*)?$/, /^@salesforce\/user(\/.*)?$/, /^@salesforce\/schema(\/.*)?$/, "lightning/uiRecordApi", "lightning/toast", ], output: { format: "es", exports: "named", entryFileNames: "[name]/[name].js", preserveModules: false, manualChunks: { client: ["react", "react-dom"], }, chunkFileNames: ({ name }) => { if (name === "index") { chunkId++; return `assets_${chunkId}/assets_${chunkId}.js`; } // Check if name already starts with underscore const assetName = name.startsWith("_") ? name.substring(1) : name; return `assets_${assetName}/assets_${assetName}.js`; }, }, }, }, server: { proxy: { "/aura": { target: `http://localhost:${port || 4000}`, }, }, }, }; if (mode === "dev") { const server = await createServer(viteConfig); await server.listen(); server.printUrls(); } else if (mode === "build") { viteConfig.publicDir = false; await build(viteConfig); // Ensure all required asset files exist if (misoConfig.extraAssetsCount) { const outDir = path.resolve( process.cwd(), "force-app/main/default/lwc" ); await ensureAssetFiles(outDir, misoConfig.extraAssetsCount, apiVersion); } console.log("Build completed successfully!"); } } catch (error) { console.error(`Error in ${mode} mode:`, error); process.exit(1); } }