import chalk from "chalk"; import express, { Request, Response } from "express"; import { invokeAura } from "../utils/invokeAura.js"; import { createServer } from "net"; import { type Plugin } from "vite"; import path from "path"; import * as fs from "fs"; export function addMetaXmlFilePlugin(apiVersion: string): Plugin { return { name: "add-meta-xml-file", generateBundle(_options: any, bundle: any) { for (const fileName in bundle) { if (fileName.endsWith(".js")) { const componentName = fileName.split("/")[0]; const metaXml = ` ${apiVersion} false `; this.emitFile({ type: "asset", fileName: `${componentName}/${componentName}.js-meta.xml`, source: metaXml, }); } } }, }; } export function pathAliasPlugin(): Plugin { return { name: "path-alias-plugin", renderChunk(code) { // replace code like "../_assets_1/_assets_1.js" or "./_assets_2/_assets_2.js" const updatedCode = code.replace( /["']\.{1,2}\/(assets_\w+)\/\1\.js["']/g, '"c/$1"' ); return { code: updatedCode, map: null, }; }, }; } // Function to get entry points from pages directory export function getEntryPoints(reactAppRoot?: string) { if (!reactAppRoot) { console.error( chalk.red("❌ Error reading 'reactAppRoot' in miso.config.json.") ); process.exit(1); } const pagesDir = path.resolve(process.cwd(), `./${reactAppRoot}/src/pages`); const entries: Record = {}; // Read first level directories const dirs = fs .readdirSync(pagesDir, { withFileTypes: true }) .filter((dirent) => dirent.isDirectory()) .map((dirent) => dirent.name); for (const dir of dirs) { const mainFile = path.resolve(pagesDir, dir, "main.tsx"); if (fs.existsSync(mainFile)) { entries[dir] = mainFile; } } return entries; } export async function getmisoConfig(rootDir: string) { try { const configPath = path.join(rootDir, "miso.config.mjs"); // Convert path to URL format that works on both Windows and Unix const fileUrl = new URL(`file://${path.resolve(configPath)}`).href; const ts = await import(fileUrl); return ts.default; } catch (error) { console.error(chalk.red("❌ Error reading miso.config.json:"), error); process.exit(1); } } export function getSfdxProjectConfig(rootDir: string): any { const sfdxProjectPath = path.join(rootDir, "sfdx-project.json"); // Check if sfdx-project.json exists if (!fs.existsSync(sfdxProjectPath)) { console.error( chalk.red( "❌ This is not a Salesforce project. Please run this command in a Salesforce project directory." ) ); process.exit(1); } try { const sfdxProject = JSON.parse(fs.readFileSync(sfdxProjectPath, "utf-8")); // Check if sourceApiVersion exists if (!sfdxProject.sourceApiVersion) { console.error( chalk.red( "❌ sourceApiVersion is missing in sfdx-project.json. Please add it to your project configuration." ) ); process.exit(1); } console.info( chalk.green("Detected partial required config from sfdx-project.json:"), { namespace: sfdxProject.namespace, apiVersion: sfdxProject.sourceApiVersion, }, "\n" ); return sfdxProject; } catch (error) { console.error(chalk.red("❌ Error reading sfdx-project.json:"), error); process.exit(1); } } const isPortInUse = (port: number): Promise => { return new Promise((resolve) => { const server = createServer(); server.once("error", () => { resolve(true); }); server.once("listening", () => { server.close(); resolve(false); }); server.listen(port); }); }; const findAvailablePort = async (startPort: number): Promise => { let port = startPort; while (await isPortInUse(port)) { port++; } return port; }; export const startServerProxy = async () => { const app = express(); const initialPort = 4000; const port = await findAvailablePort(initialPort); app.use(express.json()); app.post("/aura", async (req: Request, res: Response) => { try { const { classname, method, namespace, params } = req.body; const response = await invokeAura({ classname, method, namespace, params, }); res.status(response.status); if (response.status >= 200 && response.status < 300) { const responseBody = await response.json(); const actionResponse = responseBody.actions[0]; if (actionResponse.error?.length) { res.json({ code: -1, message: "error", error: actionResponse.error, }); } else { const returnValue = actionResponse.returnValue.returnValue; res.json({ code: 0, message: "success", data: returnValue, }); } } else { res.send({ code: -2, message: "error", error: await response.text(), }); } } catch (error) { console.error(chalk.red("Error forwarding Aura request:"), error); res.status(500).json({ success: false, error: error instanceof Error ? error.message : "Unknown error", }); } }); app.listen(port); return port; }; export function gitignorePlugin(rootDir: string, entries: Record): Plugin { return { name: "gitignore-plugin", buildStart() { const gitignorePath = path.join(rootDir, ".gitignore"); const lwcPath = "force-app/main/default/lwc"; // Read existing .gitignore content let gitignoreContent = ""; try { gitignoreContent = fs.readFileSync(gitignorePath, "utf-8"); } catch (error) { // If .gitignore doesn't exist, create it fs.writeFileSync(gitignorePath, ""); } // Get all entry keys const entryKeys = Object.keys(entries); if (entryKeys.length === 0) return; let hasChanges = false; let newContent = gitignoreContent; // Check each entry and add if not present for (const entryKey of entryKeys) { const entryPath = `${lwcPath}/${entryKey}`; if (!gitignoreContent.includes(entryPath)) { newContent += (newContent ? "\n" : "") + entryPath; hasChanges = true; console.log(`Added ${entryPath} to .gitignore`); } } // Only write to file if there were changes if (hasChanges) { fs.writeFileSync(gitignorePath, newContent); } } }; }