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

/**
 * This is an ultra-minimal build script for Vercel
 * that creates a temporary minimal Next.js config
 * to avoid common build issues.
 */

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

console.log('๐Ÿš€ Starting minimal build process...');

// Function to patch the require-hook.js file to fix the __non_webpack_require__ error
function patchRequireHook() {
  const hookPath = path.join(process.cwd(), 'node_modules/next/dist/server/require-hook.js');
  
  if (fs.existsSync(hookPath)) {
    // Backup the original file if not already backed up
    const backupPath = `${hookPath}.original`;
    if (!fs.existsSync(backupPath)) {
      fs.copyFileSync(hookPath, backupPath);
      console.log('โœ… Backed up original require-hook.js');
    }
    
    // Read the current content
    let content = fs.readFileSync(hookPath, 'utf8');
    
    // Check if the file is already patched
    if (content.includes('// PATCHED TO FIX __non_webpack_require__ ERROR')) {
      console.log('โœ… require-hook.js already patched');
      return true;
    }
    
    // Add a patch at the beginning of the file to define __non_webpack_require__
    const patchedContent = `// PATCHED TO FIX __non_webpack_require__ ERROR
if (typeof __non_webpack_require__ === 'undefined') {
  global.__non_webpack_require__ = require;
}

// Make sure eval is available (sometimes used by next)
if (typeof globalThis.eval !== 'function') {
  globalThis.eval = eval;
}

${content}`;
    
    // Write the patched file
    fs.writeFileSync(hookPath, patchedContent);
    console.log('โœ… Patched require-hook.js to fix __non_webpack_require__ error');
    
    // Also create a global patch file to ensure these definitions are available
    createGlobalNextPatch();
    
    return true;
  } else {
    console.error('โŒ Could not find require-hook.js at', hookPath);
    return false;
  }
}

// Create a global patch file for Next.js modules
function createGlobalNextPatch() {
  try {
    // Create a directory for our patches
    const patchDir = path.join(process.cwd(), 'patches');
    if (!fs.existsSync(patchDir)) {
      fs.mkdirSync(patchDir, { recursive: true });
    }
    
    // Create a patch file that will be loaded before anything else
    const patchPath = path.join(patchDir, 'next-globals.js');
    
    const patchContent = `
// Global patches for Next.js to fix common build errors
console.log('Applying Next.js global patches...');

// Fix for __non_webpack_require__ not defined
if (typeof global.__non_webpack_require__ === 'undefined') {
  global.__non_webpack_require__ = require;
  console.log('Patched global.__non_webpack_require__');
}

// Fix for missing process.env in some contexts
if (typeof process.env !== 'object') {
  process.env = {};
  console.log('Patched process.env');
}

// Fix for missing globalThis
if (typeof globalThis === 'undefined') {
  global.globalThis = global;
  console.log('Patched globalThis');
}

// Fix for eval not being available
if (typeof globalThis.eval !== 'function') {
  globalThis.eval = eval;
  console.log('Patched globalThis.eval');
}

// Other potential fixes can be added here

console.log('Next.js global patches applied successfully');
`;
    
    fs.writeFileSync(patchPath, patchContent);
    console.log('โœ… Created global Next.js patch file');
    
    // Also patch the node_options to preload our patch
    process.env.NODE_OPTIONS = process.env.NODE_OPTIONS || '';
    if (!process.env.NODE_OPTIONS.includes('-r')) {
      process.env.NODE_OPTIONS += ` -r ${patchPath}`;
      console.log(`โœ… Updated NODE_OPTIONS to preload patch: ${process.env.NODE_OPTIONS}`);
    }
    
    return true;
  } catch (err) {
    console.error('โŒ Failed to create global patch:', err.message);
    return false;
  }
}

// Create temporary minimal next.config.js
function createMinimalNextConfig() {
  const nextConfigPath = path.join(process.cwd(), 'next.config.js');
  
  // Backup original config if not already backed up
  const backupPath = path.join(process.cwd(), 'next.config.js.original');
  if (fs.existsSync(nextConfigPath) && !fs.existsSync(backupPath)) {
    fs.copyFileSync(nextConfigPath, backupPath);
    console.log('โœ… Backed up original next.config.js');
  }
  
  // Create a simplified next.config.js that works with dynamic routes
  const minimalConfig = `
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Use standalone output instead of export for dynamic routes
  output: 'standalone',
  
  // Basic image configuration
  images: {
    formats: ['image/webp'],
    deviceSizes: [640, 750, 828, 1080, 1200, 1920],
    imageSizes: [16, 32, 48, 64, 96, 128, 256],
    dangerouslyAllowSVG: true,
    remotePatterns: [],
  },
  
  // Disable problematic experimental features
  experimental: {
    // Disable features that might cause stack issues
    optimizeCss: false,
    memoryBasedWorkersCount: false,
  },
  
  // Simplified webpack config
  webpack: (config) => {
    return config;
  }
};

module.exports = nextConfig;
`;
  
  // Write minimal config
  fs.writeFileSync(nextConfigPath, minimalConfig);
  console.log('โœ… Created minimal next.config.js with standalone output');
  return true;
}

// Create temporary env file to disable problematic features
function createTempEnv() {
  const envPath = path.join(process.cwd(), '.env.production.local');
  const envContent = `
# Disable problematic Next.js features for build
NEXT_DISABLE_SOURCEMAPS=true
NEXT_TELEMETRY_DISABLED=1
NEXT_DISABLE_OPTIMIZATION=1
NEXT_MINIMAL=1
`;
  
  fs.writeFileSync(envPath, envContent);
  console.log('โœ… Created temporary environment variables');
  return true;
}

// Create a patched bootstrap file to handle non-webpack require
function createBootstrapPatch() {
  const patchDir = path.join(process.cwd(), 'patches');
  if (!fs.existsSync(patchDir)) {
    fs.mkdirSync(patchDir, { recursive: true });
  }
  
  const bootstrapPath = path.join(patchDir, 'next-bootstrap.js');
  const bootstrapContent = `
// This is a patched bootstrap file to be used in place of Next.js's default bootstrap
// It fixes the __non_webpack_require__ error

// Define __non_webpack_require__ globally
global.__non_webpack_require__ = require;

// Now load the actual Next.js bootstrap
require('next/dist/bin/next');
`;
  
  fs.writeFileSync(bootstrapPath, bootstrapContent);
  console.log('โœ… Created bootstrap patch file');
  return bootstrapPath;
}

// Run the actual build
function runBuild() {
  console.log('๐Ÿ—๏ธ Running minimal Next.js build...');
  
  try {
    // Set a higher memory limit
    process.env.NODE_OPTIONS = `--max-old-space-size=3072 ${process.env.NODE_OPTIONS || ''}`;
    
    // Try to run the build using our patched bootstrap
    const bootstrapPath = createBootstrapPatch();
    
    // Try different build approaches in sequence
    const buildApproaches = [
      {
        name: "Patched bootstrap build",
        command: `node ${bootstrapPath} build`,
        env: { SKIP_POSTINSTALL: 'true' }
      },
      {
        name: "Standard Next.js build",
        command: "next build",
        env: { SKIP_POSTINSTALL: 'true' }
      },
      {
        name: "Direct server build",
        command: "node node_modules/next/dist/bin/next build",
        env: { SKIP_POSTINSTALL: 'true', NODE_OPTIONS: process.env.NODE_OPTIONS }
      }
    ];
    
    // Try each approach in sequence
    for (const approach of buildApproaches) {
      console.log(`\n๐Ÿ”„ Trying build approach: ${approach.name}`);
      
      try {
        execSync(approach.command, { 
          stdio: 'inherit',
          env: {
            ...process.env,
            ...approach.env
          }
        });
        
        console.log(`โœ… Build successful with approach: ${approach.name}`);
        return true;
      } catch (err) {
        console.error(`โŒ Build failed with approach: ${approach.name}`);
        console.error(`   Error: ${err.message}`);
        
        // If this is the last approach, we'll exit with failure
        if (approach === buildApproaches[buildApproaches.length - 1]) {
          return false;
        }
        
        // Otherwise, continue to the next approach
        console.log(`Trying next build approach...`);
      }
    }
    
    return false;
  } catch (err) {
    console.error('โŒ All build approaches failed:', err.message);
    return false;
  }
}

// Execute the build process
(async function main() {
  try {
    console.log('๐Ÿ”ง Applying build fixes...');
    
    // Apply patches to fix require-hook.js and other issues
    patchRequireHook();
    
    // Apply configuration changes
    createMinimalNextConfig();
    createTempEnv();
    
    // Patch known problematic modules
    patchProblemModules();
    
    console.log('๐Ÿ”ง All patches and fixes applied successfully');
    
    // Run the build
    console.log('\n๐Ÿš€ Starting build process...');
    if (runBuild()) {
      console.log('\nโœ… Build completed successfully!');
      process.exit(0);
    } else {
      console.error('\nโŒ Build failed after trying all approaches.');
      console.log('Falling back to static export...');
      
      // Try to import and run the static export script as a last resort
      try {
        require('./static-export.js');
        // If we reach here, the static export was successful
        console.log('\nโœ… Fallback static export completed successfully!');
        process.exit(0);
      } catch (exportErr) {
        console.error('\nโŒ Even static export failed:', exportErr.message);
        process.exit(1);
      }
    }
  } catch (err) {
    console.error('\nโŒ Critical error:', err);
    process.exit(1);
  }
})();

// Function to patch other known problematic modules
function patchProblemModules() {
  // List of other modules to patch
  const modulesToPatch = [
    {
      path: 'node_modules/next/dist/server/lib/patch-fetch.js',
      check: '// PATCHED',
      patch: (content) => `// PATCHED
${content}`
    },
    {
      path: 'node_modules/next/dist/compiled/react-server-dom-webpack/client.edge.js',
      check: '// PATCHED FOR __non_webpack_require__',
      patch: (content) => {
        if (!content.includes('__non_webpack_require__')) return content;
        return `// PATCHED FOR __non_webpack_require__
if (typeof __non_webpack_require__ === 'undefined') {
  global.__non_webpack_require__ = require;
}
${content}`;
      }
    },
    {
      path: 'node_modules/next/dist/compiled/react-server-dom-webpack/server.edge.js',
      check: '// PATCHED FOR __non_webpack_require__',
      patch: (content) => {
        if (!content.includes('__non_webpack_require__')) return content;
        return `// PATCHED FOR __non_webpack_require__
if (typeof __non_webpack_require__ === 'undefined') {
  global.__non_webpack_require__ = require;
}
${content}`;
      }
    }
  ];
  
  // Apply patches to each module
  for (const moduleInfo of modulesToPatch) {
    const modulePath = path.join(process.cwd(), moduleInfo.path);
    
    if (fs.existsSync(modulePath)) {
      try {
        // Backup the file if it's not already backed up
        const backupPath = `${modulePath}.original`;
        if (!fs.existsSync(backupPath)) {
          fs.copyFileSync(modulePath, backupPath);
        }
        
        // Read the current content
        const content = fs.readFileSync(modulePath, 'utf8');
        
        // Check if already patched
        if (content.includes(moduleInfo.check)) {
          console.log(`โœ… Module ${moduleInfo.path} already patched`);
          continue;
        }
        
        // Apply the patch
        const patchedContent = moduleInfo.patch(content);
        fs.writeFileSync(modulePath, patchedContent);
        console.log(`โœ… Patched module ${moduleInfo.path}`);
      } catch (err) {
        console.error(`โš ๏ธ Failed to patch ${moduleInfo.path}:`, err.message);
      }
    }
  }
  
  return true;
}