vkashti / scripts / optimize-build.js
optimize-build.js
Raw
/**
 * Optimized Build Script
 * ----------------------
 * This script runs a sequence of optimizations before building the Next.js app:
 * 1. Optimizes images (resize, compress, convert to modern formats)
 * 2. Analyzes and reduces unused JavaScript
 * 3. Optimizes CSS
 * 4. Runs the production build
 */

const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');

// Check if running in CI environment (like Vercel)
const isCI = process.env.CI === 'true' || process.env.VERCEL === '1';

console.log('๐Ÿš€ Starting optimized build process...');
console.log(isCI ? 'Running in CI environment (Vercel)' : 'Running in local environment');

// Create directory for build reports
const REPORTS_DIR = path.join(__dirname, '../build-reports');
if (!fs.existsSync(REPORTS_DIR)) {
  fs.mkdirSync(REPORTS_DIR, { recursive: true });
}

// Utility to run commands and handle errors
function runCommand(command, name) {
  console.log(`\n๐Ÿ”„ Running ${name}...`);
  try {
    execSync(command, { stdio: 'inherit' });
    console.log(`โœ… ${name} completed successfully`);
    return true;
  } catch (error) {
    console.error(`โŒ ${name} failed:`, error.message);
    return false;
  }
}

// Conditional execution based on environment
if (!isCI) {
  // 1. Install required dependencies if they don't exist
  console.log('\n๐Ÿ“ฆ Checking build dependencies...');
  try {
    // Check if sharp and glob are installed
    require.resolve('sharp');
    require.resolve('glob');
    console.log('โœ… All dependencies installed');
  } catch (error) {
    console.log('๐Ÿ”„ Installing missing dependencies...');
    runCommand('npm install --save-dev sharp glob', 'Install dependencies');
  }

  // 2. Run Image Optimization - Skip in CI environment
  runCommand('node scripts/optimize-images.js', 'Image Optimization');

  // 3. Analyze bundle size - Skip in CI environment
  const shouldAnalyze = process.env.ANALYZE === 'true';
  if (shouldAnalyze) {
    console.log('\n๐Ÿ“Š Analyzing initial bundle size...');
    runCommand(
      'ANALYZE=true next build', 
      'Bundle analysis'
    );
  }
}

// 4. Run Next.js production build with all optimizations
console.log('\n๐Ÿ—๏ธ Building optimized production bundle...');
runCommand(
  'next build', 
  'Production build'
);

// 5. Verify build output
const BUILD_OUTPUT_DIR = path.join(__dirname, '../.next');
if (fs.existsSync(BUILD_OUTPUT_DIR)) {
  console.log(`\nโœ… Build completed successfully in ${BUILD_OUTPUT_DIR}`);
  
  if (!isCI) {
    // Get build stats - Skip in CI environment
    const buildSize = getFolderSize(BUILD_OUTPUT_DIR);
    console.log(`๐Ÿ“Š Build size: ${(buildSize / 1024 / 1024).toFixed(2)} MB`);
  
    console.log('\n๐Ÿ” Next steps:');
    console.log('1. Run "npm run start" to test the optimized build');
    console.log('2. Check for any performance issues');
    console.log('3. Deploy to production');
  }
} else {
  console.error('โŒ Build output not found. Build may have failed.');
  process.exit(1);
}

// Helper function to calculate folder size
function getFolderSize(folderPath) {
  let totalSize = 0;
  
  function readDir(dirPath) {
    const items = fs.readdirSync(dirPath);
    
    for (const item of items) {
      const itemPath = path.join(dirPath, item);
      const stats = fs.statSync(itemPath);
      
      if (stats.isFile()) {
        totalSize += stats.size;
      } else if (stats.isDirectory()) {
        readDir(itemPath);
      }
    }
  }
  
  readDir(folderPath);
  return totalSize;
}