UNPKG

gebeya-dala-error-fixer

Version:

Automatic runtime error detection and fix suggestions for Next.js applications with config-based setup

441 lines (413 loc) 16.5 kB
export class ErrorDetector { constructor(onError, config) { this.isDestroyed = false; this.onError = onError; this.config = config || {}; this.errorPatterns = new Map(); this.originalConsoleError = console.error; this.originalConsoleWarn = console.warn; this.initializeErrorPatterns(); this.setupGlobalErrorHandlers(); } initializeErrorPatterns() { // Common Next.js and React error patterns with solutions this.errorPatterns.set(/Cannot read propert(y|ies) of undefined/i, { title: "Undefined Property Access", description: "You're trying to access a property on an undefined value. This usually happens when data hasn't loaded yet or when an object is unexpectedly undefined.", code: `// Use optional chaining or check if the value exists const value = obj?.property || 'default'; // Or use conditional rendering in React return ( <div> {data ? <p>{data.name}</p> : <p>Loading...</p>} </div> );`, actions: [ "Add optional chaining (?.)", "Check if object exists before accessing", "Provide default values", "Use null checking", "Add loading states in React components" ] }); this.errorPatterns.set(/Cannot read propert(y|ies) of null/i, { title: "Null Property Access", description: "You're trying to access a property on a null value. This often occurs when API calls return null or when DOM elements aren't found.", code: `// Check for null before accessing if (obj !== null && obj !== undefined) { const value = obj.property; } // Or use optional chaining const value = obj?.property; // In React components return ( <div> {user && <p>Welcome, {user.name}!</p>} </div> );`, actions: [ "Add null checking", "Use optional chaining (?.)", "Initialize with default values", "Check data loading state", "Verify API response handling" ] }); this.errorPatterns.set(/Hydration failed|Text content does not match/i, { title: "Hydration Mismatch", description: "Server-side and client-side rendering don't match. This is common in Next.js when client-only code runs during SSR.", code: `// Use useEffect for client-only code import { useEffect, useState } from 'react'; function MyComponent() { const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); }, []); if (!mounted) return null; return <div>{/* Client-only content */}</div>; } // Or use dynamic imports with ssr: false const ClientOnlyComponent = dynamic( () => import('./ClientOnlyComponent'), { ssr: false } );`, actions: [ "Move client-only code to useEffect", "Use dynamic imports with ssr: false", "Check for browser-specific code in SSR", "Ensure consistent data between server and client", "Use suppressHydrationWarning sparingly" ] }); this.errorPatterns.set(/Invalid hook call|Hooks can only be called inside/i, { title: "Invalid Hook Usage", description: "Hooks can only be called inside React function components or custom hooks, and they must be called at the top level.", code: `// Hooks must be at the top level of components function MyComponent() { const [state, setState] = useState(null); // Don't call hooks inside loops, conditions, or nested functions // ❌ Wrong // if (someCondition) { // const [badState] = useState(); // } // ✅ Correct const [conditionalState, setConditionalState] = useState( someCondition ? 'value' : null ); return <div>{state}</div>; }`, actions: [ "Move hooks to top level of component", "Don't call hooks in loops or conditions", "Ensure component is a function component", "Check for hooks in regular functions", "Use custom hooks for reusable logic" ] }); this.errorPatterns.set(/Maximum update depth exceeded|Too many re-renders/i, { title: "Infinite Re-render Loop", description: "A component is causing infinite re-renders, usually due to state updates in render or incorrect useEffect dependencies.", code: `// Use useCallback to prevent infinite loops const handleClick = useCallback(() => { // handler logic }, [dependency]); // Fix useEffect dependencies useEffect(() => { // effect logic }, [prop1, prop2]); // Include all dependencies // Don't set state directly in render function MyComponent({ data }) { // ❌ Wrong - causes infinite loop // if (data) { // setProcessedData(processData(data)); // } // ✅ Correct - use useEffect useEffect(() => { if (data) { setProcessedData(processData(data)); } }, [data]); }`, actions: [ "Use useCallback for event handlers", "Check useEffect dependencies", "Avoid setting state in render", "Use useMemo for expensive calculations", "Review component lifecycle methods" ] }); this.errorPatterns.set(/Module not found|Cannot resolve module/i, { title: "Module Import Error", description: "The imported module cannot be found. This could be due to incorrect paths, missing packages, or case sensitivity issues.", code: `// Check import path and module installation import { Component } from './correct/path'; // For npm packages npm install missing-package // or yarn add missing-package // For relative imports, check file structure import MyComponent from '../components/MyComponent'; // For absolute imports in Next.js import { utils } from '@/utils/helpers';`, actions: [ "Check import path spelling", "Verify file exists", "Install missing npm package", "Check relative vs absolute paths", "Verify case sensitivity", "Check tsconfig.json path aliases" ] }); this.errorPatterns.set(/Unexpected token|Syntax error/i, { title: "Syntax Error", description: "There's a syntax error in your code, such as missing brackets, incorrect JSX, or typos in keywords.", code: `// Check for missing brackets, semicolons, or typos const obj = { key: 'value' }; // Make sure brackets match // Common JSX issues return ( <div> <p>Hello World</p> {/* Make sure tags are closed */} </div> ); // Check for proper string quotes const message = "Hello World"; // or 'Hello World'`, actions: [ "Check for missing brackets", "Verify semicolons and commas", "Check for typos in keywords", "Validate JSX syntax", "Check string quotes and escaping" ] }); this.errorPatterns.set(/Objects are not valid as a React child/i, { title: "Invalid React Child", description: "You're trying to render an object directly in JSX. React can only render strings, numbers, or valid React elements.", code: `// Convert object to string or render specific properties function MyComponent({ data }) { return ( <div> {/* ❌ Wrong - renders object directly */} {/* {data} */} {/* ✅ Correct options */} {JSON.stringify(data)} {/* or */} <p>{data.message}</p> {/* or */} <pre>{JSON.stringify(data, null, 2)}</pre> </div> ); }`, actions: [ "Convert object to string with JSON.stringify", "Render specific object properties", "Use proper JSX elements", "Check data structure", "Add conditional rendering for objects" ] }); this.errorPatterns.set(/Failed to compile|Build failed/i, { title: "Compilation Error", description: "There's an error preventing your code from compiling. This could be TypeScript errors, missing imports, or syntax issues.", code: `// Check for common compilation issues: // 1. Missing imports import React from 'react'; import { NextPage } from 'next'; // 2. Type errors in TypeScript interface Props { name: string; } const MyComponent: React.FC<Props> = ({ name }) => { return <div>{name}</div>; }; // 3. Missing dependencies // Run: npm install missing-package`, actions: [ "Check import statements", "Verify syntax", "Fix TypeScript type errors", "Install missing dependencies", "Check for unused imports", "Verify file extensions" ] }); this.errorPatterns.set(/Network request failed|Failed to fetch/i, { title: "Network Request Error", description: "A network request failed. This could be due to CORS issues, API endpoint problems, or network connectivity.", code: `// Handle network errors properly try { const response = await fetch('/api/data'); if (!response.ok) { throw new Error('Network response was not ok'); } const data = await response.json(); return data; } catch (error) { console.error('Fetch error:', error); // Handle error appropriately return { error: 'Failed to load data' }; } // In Next.js API routes, check CORS export default function handler(req, res) { // Set CORS headers if needed res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // Your API logic here }`, actions: [ "Check API endpoint URLs", "Verify CORS configuration", "Test network connectivity", "Check request headers", "Implement proper error handling", "Verify API route exists" ] }); // Add custom patterns from config if (this.config.customPatterns) { this.config.customPatterns.forEach(pattern => { this.errorPatterns.set(new RegExp(pattern.pattern, 'i'), { title: pattern.title, description: pattern.description, code: pattern.code, actions: pattern.actions }); }); } } setupGlobalErrorHandlers() { if (typeof window === 'undefined') return; // Global JavaScript error handler if (this.config.enableConsoleOverride !== false) { window.addEventListener('error', (event) => { var _a; if (this.isDestroyed) return; const errorInfo = { message: event.message, stack: (_a = event.error) === null || _a === void 0 ? void 0 : _a.stack, line: event.lineno, column: event.colno, filename: event.filename, timestamp: Date.now() }; const suggestion = this.findSuggestion(errorInfo.message); this.onError(errorInfo, suggestion); }); } // Unhandled promise rejection handler if (this.config.enableUnhandledRejection !== false) { window.addEventListener('unhandledrejection', (event) => { var _a, _b; if (this.isDestroyed) return; const errorInfo = { message: ((_a = event.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Unhandled Promise Rejection', stack: (_b = event.reason) === null || _b === void 0 ? void 0 : _b.stack, timestamp: Date.now() }; const suggestion = this.findSuggestion(errorInfo.message); this.onError(errorInfo, suggestion); }); } // Console error override if (this.config.enableConsoleOverride !== false) { console.error = (...args) => { if (this.isDestroyed) { this.originalConsoleError.apply(console, args); return; } const message = args.join(' '); // Check if this looks like a React or Next.js error if (this.shouldProcessConsoleError(message)) { const errorInfo = { message, timestamp: Date.now() }; const suggestion = this.findSuggestion(message); this.onError(errorInfo, suggestion); } this.originalConsoleError.apply(console, args); }; // Also override console.warn for hydration warnings console.warn = (...args) => { if (this.isDestroyed) { this.originalConsoleWarn.apply(console, args); return; } const message = args.join(' '); // Check for hydration warnings if (message.includes('Hydration') || message.includes('hydration')) { const errorInfo = { message, timestamp: Date.now() }; const suggestion = this.findSuggestion(message); this.onError(errorInfo, suggestion); } this.originalConsoleWarn.apply(console, args); }; } } shouldProcessConsoleError(message) { // Skip if message matches exclude patterns if (this.config.excludePatterns) { for (const pattern of this.config.excludePatterns) { if (new RegExp(pattern, 'i').test(message)) { return false; } } } // Process React/Next.js specific errors return (message.includes('Warning:') || message.includes('Error:') || message.includes('React') || message.includes('Next.js') || message.includes('Hydration') || message.includes('Cannot read') || message.includes('undefined') || message.includes('null') || message.includes('Invalid') || message.includes('Failed')); } findSuggestion(errorMessage) { for (const [pattern, suggestion] of Array.from(this.errorPatterns.entries())) { if (pattern.test(errorMessage)) { return suggestion; } } // Default suggestion for unknown errors return { title: "Unknown Error", description: "An unexpected error occurred. This might be a new type of error that hasn't been cataloged yet.", code: `// General debugging steps: console.log('Error details:', error); // Check the browser console for more information // Review recent code changes // Check network requests in DevTools // Verify all dependencies are installed`, actions: [ "Check the browser console for more details", "Review recent code changes", "Check network requests in DevTools", "Verify all dependencies are installed", "Search for the error message online", "Check component props and state" ] }; } addCustomPattern(pattern, suggestion) { this.errorPatterns.set(pattern, suggestion); } destroy() { this.isDestroyed = true; // Restore original console methods if (typeof window !== 'undefined') { console.error = this.originalConsoleError; console.warn = this.originalConsoleWarn; } // Clear patterns this.errorPatterns.clear(); } }