vkashti / scripts / fix-nextjs-ws.js
fix-nextjs-ws.js
Raw
#!/usr/bin/env node

/**
 * This script fixes the "wsServer.handleUpgrade is not a function" error in Next.js
 * by applying a simple monkey patch to the hot-reloader-webpack.js file.
 */
const fs = require('fs');
const path = require('path');

// Path to the Next.js hot-reloader-webpack.js file
const filePath = path.join(
  process.cwd(),
  'node_modules/next/dist/server/dev/hot-reloader-webpack.js'
);

// Check if the file exists
if (!fs.existsSync(filePath)) {
  console.error(`File not found: ${filePath}`);
  process.exit(1);
}

// Create a backup of the original file if it doesn't exist
const backupPath = `${filePath}.backup`;
if (!fs.existsSync(backupPath)) {
  fs.copyFileSync(filePath, backupPath);
  console.log(`Backup created at ${backupPath}`);
} else {
  // Always restore from backup to ensure clean state
  fs.copyFileSync(backupPath, filePath);
  console.log('Restored from backup to ensure clean state');
}

// Create a simple patched file with a monkey-patched require for 'ws'
const patchedContent = `"use strict";
// Monkey patched module - original is at hot-reloader-webpack.js.backup
Object.defineProperty(exports, "__esModule", {
    value: true
});

// Monkey patch the ws module to provide a working handleUpgrade method
const originalWs = require('ws');
const originalRequire = module.require;
module.require = function(id) {
  if (id === 'ws') {
    // Create a patched version of the ws Server
    const patchedWs = {...originalWs};
    const OriginalServer = originalWs.Server;
    
    patchedWs.Server = function(options) {
      const server = new OriginalServer(options);
      
      // Ensure handleUpgrade is always available
      if (!server.handleUpgrade) {
        server.handleUpgrade = function(request, socket, head, callback) {
          console.log('Using patched handleUpgrade method');
          
          // Create a new WebSocket and call the callback with it
          const ws = new originalWs('ws://localhost');
          callback(ws);
          
          // Prevent socket timeouts
          socket.on('error', () => {});
          socket.on('close', () => {});
        };
      }
      
      return server;
    };
    
    return patchedWs;
  }
  
  return originalRequire.apply(this, arguments);
};

// Load the original file
const originalFile = require(${JSON.stringify(backupPath)});

// Export everything from the original file
Object.keys(originalFile).forEach(key => {
  exports[key] = originalFile[key];
});
`;

// Write the patched file
fs.writeFileSync(filePath, patchedContent);
console.log('Next.js WebSocket patch completed successfully');

// Create a simple test file to verify if the patch works
const testPath = path.join(
  process.cwd(),
  'scripts/test-ws-patch.js'
);

const testContent = `
// Test script to verify if the WebSocket patch works
const hotReloader = require('${filePath}');
console.log('Hot reloader loaded successfully');

// Create a mock request, socket and head
const req = {};
const socket = {
  on: (event, callback) => console.log('Socket event:', event)
};
const head = {};

// Create a fake HotReloaderWebpack instance
const fakeHotReloader = {
  onHMR: hotReloader.HotReloaderWebpack.prototype.onHMR
};

// Try to call the patched onHMR method
try {
  fakeHotReloader.onHMR(req, socket, head);
  console.log('WebSocket patch works!');
} catch (err) {
  console.error('WebSocket patch failed:', err);
}
`;

fs.writeFileSync(testPath, testContent);
console.log(`Test script created at ${testPath}`);
console.log('Run with "node scripts/test-ws-patch.js" to verify the patch');

console.log('\nIMPORTANT: Restart your Next.js server for changes to take effect');