svm-pay
Version:
A payment solution for SVM networks (Solana, Sonic SVM, Eclipse, s00n)
213 lines (175 loc) • 6.2 kB
JavaScript
/**
* Async build optimization script for svm-pay
* This script optimizes the built package for distribution with improved CI performance
*/
const fs = require('fs').promises;
const fsSync = require('fs');
const path = require('path');
async function optimizeBuild() {
console.log('Optimizing build for distribution...');
try {
// Ensure the CLI binary is executable
const cliPath = path.join(__dirname, '../dist/bin/svm-pay.js');
if (await fileExists(cliPath)) {
await fs.chmod(cliPath, '755');
console.log('✓ CLI binary permissions set');
}
// Remove source map files in production builds (async)
if (process.env.NODE_ENV === 'production' || process.argv.includes('--no-source-maps')) {
const removedCount = await removeSourceMaps(path.join(__dirname, '../dist'));
if (removedCount > 0) {
console.log(`✓ Removed ${removedCount} source map files`);
}
}
// Create a package summary (async)
await createBuildSummary();
console.log('✓ Build optimization complete');
} catch (error) {
console.error('❌ Build optimization failed:', error.message);
process.exit(1);
}
}
async function createBuildSummary() {
const packagePath = path.join(__dirname, '../package.json');
const distPath = path.join(__dirname, '../dist');
if (!(await fileExists(packagePath)) || !(await fileExists(distPath))) {
console.warn('⚠ Package.json or dist directory not found, skipping summary');
return;
}
try {
// Read package.json asynchronously
const packageData = await fs.readFile(packagePath, 'utf8');
const packageJson = JSON.parse(packageData);
// Count files in dist directory asynchronously
const fileCount = await countFilesAsync(distPath);
// Calculate total size asynchronously
const totalSize = await calculateDirectorySizeAsync(distPath);
const sizeInMB = (totalSize / (1024 * 1024)).toFixed(2);
console.log('📦 Build Summary:');
console.log(` Package: ${packageJson.name}@${packageJson.version}`);
console.log(` Files built: ${fileCount}`);
console.log(` Total size: ${sizeInMB} MB`);
console.log(` Main entry: ${packageJson.main}`);
console.log(` CLI binary: ${packageJson.bin['svm-pay']}`);
console.log(` Types: ${packageJson.types}`);
} catch (error) {
console.warn('⚠ Failed to create build summary:', error.message);
}
}
async function countFilesAsync(dir) {
let count = 0;
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
// Process entries in parallel for better performance
const promises = entries.map(async (entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return await countFilesAsync(fullPath);
} else {
return 1;
}
});
const counts = await Promise.all(promises);
count = counts.reduce((sum, c) => sum + c, 0);
} catch (error) {
console.warn(`Warning: Could not count files in ${dir}:`, error.message);
}
return count;
}
async function calculateDirectorySizeAsync(dir) {
let totalSize = 0;
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
// Process entries in parallel
const promises = entries.map(async (entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return await calculateDirectorySizeAsync(fullPath);
} else {
try {
const stats = await fs.stat(fullPath);
return stats.size;
} catch (error) {
console.warn(`Warning: Could not stat file ${fullPath}:`, error.message);
return 0;
}
}
});
const sizes = await Promise.all(promises);
totalSize = sizes.reduce((sum, size) => sum + size, 0);
} catch (error) {
console.warn(`Warning: Could not calculate size of ${dir}:`, error.message);
}
return totalSize;
}
async function removeSourceMaps(dir) {
let removedCount = 0;
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
// Process entries in parallel for better performance
const promises = entries.map(async (entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
return await removeSourceMaps(fullPath);
} else if (entry.name.endsWith('.map')) {
try {
await fs.unlink(fullPath);
return 1;
} catch (error) {
console.warn(`Warning: Could not remove ${fullPath}:`, error.message);
return 0;
}
} else {
return 0;
}
});
const counts = await Promise.all(promises);
removedCount = counts.reduce((sum, count) => sum + count, 0);
} catch (error) {
console.warn(`Warning: Could not process directory ${dir}:`, error.message);
}
return removedCount;
}
async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
// Performance monitoring
async function measurePerformance(operation, fn) {
const startTime = process.hrtime.bigint();
const result = await fn();
const endTime = process.hrtime.bigint();
const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds
console.log(`⏱ ${operation} completed in ${duration.toFixed(2)}ms`);
return result;
}
// Enhanced main execution with performance monitoring
async function main() {
if (require.main === module) {
try {
await measurePerformance('Build optimization', optimizeBuild);
} catch (error) {
console.error('❌ Build script failed:', error);
process.exit(1);
}
}
}
// Error handling for unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
process.exit(1);
});
// Run if this script is executed directly
main();
module.exports = {
optimizeBuild,
removeSourceMaps,
countFilesAsync,
calculateDirectorySizeAsync,
measurePerformance
};