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
JavaScript
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();
}
}