import * as esbuild from 'esbuild'; import ora from 'ora'; import fs from 'fs'; import path from 'path'; import { promisify } from 'util'; import UploadService from './uploadService'; import FileService from './fileService'; import { v4 as uuidv4 } from 'uuid'; // Convert fs.readdir and fs.stat to return promises const readdir = promisify(fs.readdir); const stat = promisify(fs.stat); // Temporary storage for file data const tempFileData = new Map(); class BuildService { // Singleton instance of BuildService private static _instance: BuildService; // Singleton accessor for BuildService public static getInstance(): BuildService { if (!this._instance) { this._instance = new BuildService(); } return this._instance; } // Method to build a single file async buildFile(file: string, filename: string, environment: string, projectid: string, secretkey: string) { const startTime = performance.now(); const spinner = ora(`Building ${filename}`).start(); const fileid = uuidv4(); const splitFilepath = file.split('\\'); const operatorFolder = await FileService.findOperatorFolder(); operatorFolder.split('\\').forEach(() => splitFilepath.shift()); const relativePath = splitFilepath.join('\\'); const splitFilename = filename.split('.'); // Determine the HTTP method from the filename let method = 'GET'; if (splitFilename.length === 3) { switch (splitFilename[1]) { case 'post': method = 'POST'; break; case 'put': method = 'PUT'; break; case 'delete': method = 'DELETE'; break; case 'patch': method = 'PATCH'; break; } splitFilename.splice(1, 1); // Remove the method from the filename } // Remove the file extension splitFilename.splice(1, 1); // Modify the file path based on certain conditions if (splitFilename[0] === 'index') { splitFilepath.splice(splitFilepath.length - 1, 1); } if (!splitFilepath.includes(splitFilename[0])) { splitFilepath.push(splitFilename[0]); } // Build the file using esbuild await esbuild.build({ entryPoints: [file], bundle: true, minify: true, platform: 'node', target: 'node14', format: 'esm', external: ['apilib-npm', 'api-lib', 'node:fs', 'node:path', 'node:util', 'node:crypto'], outfile: path.join(process.cwd(), `/.apilib/build/${fileid}.js`), }); // Replace apilib-npm with actual path await FileService.replaceContentInFile( path.join(process.cwd(), `/.apilib/build/${fileid}.js`), /api-lib/g, '../../src/apilib/index.ts', ); // Record the build time const endTime = performance.now(); const elapsedTime = endTime - startTime; spinner.text = `Built ${filename} - ${elapsedTime.toFixed(2)}ms`; spinner.succeed(); // Upload the built file await UploadService.uploadFileToFileserver( fileid, filename, splitFilepath, method, environment, projectid, secretkey, ); } // Method to build all files in a given folder async buildAllFilesInFolder(folderPath: string, environment: string, projectid: string, secretkey: string) { // Clear all files in the build folder try { const files = await readdir(path.join(process.cwd(), '/.apilib/build/')); files.forEach(async file => { await fs.unlinkSync(path.join(process.cwd(), `/.apilib/build/${file}`)); }); } catch (error) { console.error('Error clearing build folder:', error); } // Process each file in the folder try { const files = await readdir(folderPath); for (const file of files) { const filePath = path.join(folderPath, file); const stats = await stat(filePath); if (stats.isDirectory()) { // Recursively process subfolders await this.buildAllFilesInFolder(filePath, environment, projectid, secretkey); } else { // Build individual file const filename = path.basename(filePath); await this.buildFile(filePath, filename, environment, projectid, secretkey); } } } catch (err) { console.error('Error reading folder:', err); } } } // Export singleton instance of BuildService export default BuildService.getInstance();