vkashti / scripts / fix-vercel-build.js
fix-vercel-build.js
Raw
#!/usr/bin/env node

/**
 * This script fixes the "Maximum call stack size exceeded" error
 * that occurs during Next.js build on Vercel, specifically in the
 * micromatch module used by Next.js.
 */
const fs = require('fs');
const path = require('path');

// Only run this fix in Vercel environment
if (process.env.VERCEL !== '1') {
  console.log('Not in Vercel environment, skipping fix');
  process.exit(0);
}

console.log('Applying critical fixes for Vercel build...');

// Set Node.js options to increase memory limit
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '';
if (!process.env.NODE_OPTIONS.includes('--max-old-space-size')) {
  process.env.NODE_OPTIONS += ' --max-old-space-size=3072';
  console.log(`Increased Node.js memory limit: ${process.env.NODE_OPTIONS}`);
}

// Modify next.config.js to disable problematic optimizations
try {
  const nextConfigPath = path.join(process.cwd(), 'next.config.js');
  
  if (fs.existsSync(nextConfigPath)) {
    // Create a backup if it doesn't exist
    const nextConfigBackup = `${nextConfigPath}.original`;
    if (!fs.existsSync(nextConfigBackup)) {
      fs.copyFileSync(nextConfigPath, nextConfigBackup);
      console.log(`Created backup of next.config.js at ${nextConfigBackup}`);
    }
    
    // Read the current config
    let nextConfig = fs.readFileSync(nextConfigPath, 'utf8');
    
    // Simplify complex regexes in the config
    nextConfig = nextConfig.replace(
      /source: '\/\(.*\)\\+\.\((?:[^()]*\|)+[^()]*\)\$'/g,
      'source: \'/(.*)\''
    );
    
    // Disable problematic experimental features
    if (nextConfig.includes('experimental:')) {
      console.log('Disabling problematic experimental features in next.config.js...');
      
      // Disable optimizeCss experimental feature
      if (nextConfig.includes('optimizeCss:')) {
        nextConfig = nextConfig.replace(/optimizeCss:\s*true/, 'optimizeCss: false');
      }
      
      // Disable memoryBasedWorkersCount
      if (nextConfig.includes('memoryBasedWorkersCount:')) {
        nextConfig = nextConfig.replace(/memoryBasedWorkersCount:\s*true/, 'memoryBasedWorkersCount: false');
      }
    }
    
    // Simplify webpack configuration if it exists
    if (nextConfig.includes('webpack:')) {
      console.log('Simplifying webpack configuration in next.config.js...');
      
      // Replace the complex webpack config with a simpler one
      nextConfig = nextConfig.replace(
        /webpack:\s*\(config,\s*\{[^}]*\}\)\s*=>\s*\{[\s\S]*?(?=return config|config\.resolve)[\s\S]*?return config;?\s*\}/,
        `webpack: (config) => {
    // Simplified webpack config to avoid call stack issues
    return config;
  }`
      );
    }
    
    // Write the simplified config
    fs.writeFileSync(nextConfigPath, nextConfig);
    console.log('Successfully simplified next.config.js');
  }
} catch (err) {
  console.error('Warning: Could not modify next.config.js:', err.message);
}

// Create a temporary .env.production.local file to disable certain features
try {
  const envPath = path.join(process.cwd(), '.env.production.local');
  
  // Add environment variables to disable problematic features
  const envContent = `
# Temporary fixes to prevent Maximum call stack size exceeded
NEXT_DISABLE_SOURCEMAPS=true
NEXT_DISABLE_OPTIMIZATION=1
NEXT_TELEMETRY_DISABLED=1
NODE_ENV=production
`;
  
  fs.writeFileSync(envPath, envContent, { flag: 'a' });
  console.log('Added environment variables to disable problematic features');
} catch (err) {
  console.error('Warning: Could not create .env.production.local file:', err.message);
}

// Override the micromatch module if it exists
try {
  const micromatchPath = path.join(
    process.cwd(),
    'node_modules/next/dist/compiled/micromatch/index.js'
  );
  
  if (fs.existsSync(micromatchPath)) {
    // Create a backup if it doesn't exist
    const micromatchBackup = `${micromatchPath}.original`;
    if (!fs.existsSync(micromatchBackup)) {
      fs.copyFileSync(micromatchPath, micromatchBackup);
      console.log(`Created backup of micromatch at ${micromatchBackup}`);
    }
    
    // Read the file content
    const content = fs.readFileSync(micromatchPath, 'utf8');
    
    // Skip if already patched
    if (content.includes('// PATCHED TO PREVENT CALL STACK OVERFLOW')) {
      console.log('Micromatch module already patched');
    } else {
      // Patch known problematic functions
      const patchedContent = content
        // Add patched flag
        .replace('!function(', '// PATCHED TO PREVENT CALL STACK OVERFLOW\n!function(')
        // Patch the create function to prevent deep recursion
        .replace(
          /(function create\(glob, options\) \{)/,
          `$1
  // Handle recursion depth
  if (!options) options = {};
  options._depth = (options._depth || 0) + 1;
  if (options._depth > 50) {
    return { pattern: glob, regex: new RegExp('.*') };
  }`
        )
        // Patch the makeRe function
        .replace(
          /(makeRe: function\(string, options\) \{)/,
          `$1
    // Prevent excessive pattern complexity
    if (string && string.length > 100) {
      return /.*/;
    }`
        );
      
      // Write the patched file
      fs.writeFileSync(micromatchPath, patchedContent);
      console.log('Successfully patched micromatch to prevent stack overflow');
    }
  }
} catch (err) {
  console.error('Warning: Could not patch micromatch module:', err.message);
}

console.log('Vercel build fixes completed successfully!');