Skip to content

Commit 4d33441

Browse files
author
Juan Castaño
committed
Improved error detection
1 parent 4b77f60 commit 4d33441

File tree

4 files changed

+220
-85
lines changed

4 files changed

+220
-85
lines changed

app/api/generate/route.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,28 @@ const benchify = new Benchify({
1010
apiKey: process.env.BENCHIFY_API_KEY,
1111
});
1212

13+
const debug = false;
14+
const buggyCode = [
15+
{
16+
path: "src/App.tsx",
17+
content: `import React from 'react';
18+
19+
const App = () => {
20+
const message = "Hello World; // Missing closing quote
21+
const title = 'Welcome to my app';
22+
23+
return (
24+
<div>
25+
<h1>{title}</h1>
26+
<p>{message}</p>
27+
</div>
28+
);
29+
};
30+
31+
export default App;`
32+
}
33+
];
34+
1335
export async function POST(request: NextRequest) {
1436
try {
1537
const body = await request.json();
@@ -27,7 +49,12 @@ export async function POST(request: NextRequest) {
2749
const { description } = validationResult.data;
2850

2951
// Generate the Vue app using OpenAI
30-
const generatedFiles = await generateApp(description);
52+
let generatedFiles;
53+
if (debug) {
54+
generatedFiles = buggyCode;
55+
} else {
56+
generatedFiles = await generateApp(description);
57+
}
3158

3259
// Repair the generated code using Benchify's API
3360
// const { data } = await benchify.fixer.run({

components/ui-builder/preview-card.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ export function PreviewCard({
129129
</div>
130130
<div className="flex-1 min-w-0">
131131
<p className={`text-sm font-medium ${isCompleted ? 'text-green-700 dark:text-green-400' :
132-
isCurrent ? 'text-primary' :
133-
'text-muted-foreground'
132+
isCurrent ? 'text-primary' :
133+
'text-muted-foreground'
134134
}`}>
135135
{step.label}
136136
</p>

lib/e2b.ts

Lines changed: 41 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { benchifyFileSchema } from './schemas';
33
import { z } from 'zod';
44
import { fetchAllSandboxFiles } from './file-filter';
55
import { applyTransformations } from './sandbox-helpers';
6+
import { detectCodeErrors, parseTypeScriptErrors } from './error-detection';
67

78
const E2B_API_KEY = process.env.E2B_API_KEY;
89

@@ -59,8 +60,11 @@ export async function createSandbox({ files }: { files: z.infer<typeof benchifyF
5960
console.log('New packages installed successfully:', result.stdout);
6061
if (result.stderr) {
6162
console.warn('npm install warnings:', result.stderr);
62-
// Parse npm install errors
63-
if (result.stderr.includes('npm ERR!')) {
63+
// Only treat critical npm errors as build errors (not warnings or peer dep issues)
64+
if (result.stderr.includes('npm ERR!') &&
65+
(result.stderr.includes('ENOTFOUND') ||
66+
result.stderr.includes('ECONNREFUSED') ||
67+
result.stderr.includes('permission denied'))) {
6468
buildErrors.push({
6569
type: 'build',
6670
message: 'Package installation failed: ' + result.stderr.split('npm ERR!')[1]?.trim()
@@ -79,39 +83,47 @@ export async function createSandbox({ files }: { files: z.infer<typeof benchifyF
7983
}
8084
}
8185

82-
// Run TypeScript check to catch type errors
86+
// Start the dev server and check logs for errors (let Vite handle error detection)
8387
try {
84-
console.log('Running TypeScript check...');
85-
const tscResult = await sandbox.commands.run('cd /app && npx tsc --noEmit --skipLibCheck');
86-
87-
if (tscResult.exitCode !== 0 && tscResult.stderr) {
88-
console.log('TypeScript errors found:', tscResult.stderr);
89-
const tsErrors = parseTypeScriptErrors(tscResult.stderr);
90-
buildErrors.push(...tsErrors);
88+
console.log('Starting dev server...');
89+
// Start dev server in background
90+
const devServerResult = await sandbox.commands.run('cd /app && npm run dev', { background: true });
91+
92+
console.log('Dev server command executed');
93+
console.log('Dev server exit code:', devServerResult.exitCode);
94+
console.log('Dev server stderr:', devServerResult.stderr || 'No stderr');
95+
console.log('Dev server stdout:', devServerResult.stdout || 'No stdout');
96+
97+
// Give it a moment to start and potentially fail
98+
console.log('Waiting 3 seconds for dev server to start...');
99+
await new Promise(resolve => setTimeout(resolve, 3000));
100+
101+
// Check the initial output for immediate errors
102+
if (devServerResult.stderr || devServerResult.stdout) {
103+
const allOutput = (devServerResult.stderr || '') + '\n' + (devServerResult.stdout || '');
104+
105+
// Use the error detection module
106+
const errorResult = detectCodeErrors(allOutput);
107+
108+
if (errorResult.hasErrors) {
109+
console.log('🔴 CODE ERRORS DETECTED!');
110+
buildErrors.push(...errorResult.errors);
111+
} else if (errorResult.isInfrastructureOnly) {
112+
console.log('⚠️ Only infrastructure errors detected (ignoring)');
113+
} else {
114+
console.log('✅ No errors detected');
115+
}
116+
} else {
117+
console.log('⚠️ No stderr or stdout from dev server command');
91118
}
92-
} catch (error) {
93-
console.error('TypeScript check failed:', error);
94-
buildErrors.push({
95-
type: 'typescript',
96-
message: `TypeScript check failed: ${error instanceof Error ? error.message : String(error)}`
97-
});
98-
}
99119

100-
// Try to build the project to catch build-time errors
101-
try {
102-
console.log('Running build check...');
103-
const buildResult = await sandbox.commands.run('cd /app && npm run build');
104-
105-
if (buildResult.exitCode !== 0) {
106-
console.log('Build errors found:', buildResult.stderr);
107-
const viteErrors = parseViteBuildErrors(buildResult.stderr);
108-
buildErrors.push(...viteErrors);
109-
}
120+
console.log('Dev server started, output checked');
121+
console.log('Total build errors found:', buildErrors.length);
110122
} catch (error) {
111-
console.error('Build check failed:', error);
123+
console.error('Dev server check failed:', error);
112124
buildErrors.push({
113125
type: 'build',
114-
message: `Build failed: ${error instanceof Error ? error.message : String(error)}`
126+
message: `Dev server failed to start: ${error instanceof Error ? error.message : String(error)}`
115127
});
116128
}
117129

@@ -130,60 +142,7 @@ export async function createSandbox({ files }: { files: z.infer<typeof benchifyF
130142
};
131143
}
132144

133-
function parseTypeScriptErrors(stderr: string): BuildError[] {
134-
const errors: BuildError[] = [];
135-
const lines = stderr.split('\n');
136-
137-
for (const line of lines) {
138-
// Match TypeScript error pattern: file(line,column): error TS####: message
139-
const match = line.match(/(.+)\((\d+),(\d+)\): error TS\d+: (.+)/);
140-
if (match) {
141-
const [, file, line, column, message] = match;
142-
errors.push({
143-
type: 'typescript',
144-
message: message.trim(),
145-
file: file.replace('/app/', ''),
146-
line: parseInt(line),
147-
column: parseInt(column)
148-
});
149-
}
150-
}
151145

152-
// If no specific errors found but stderr has content, add generic error
153-
if (errors.length === 0 && stderr.trim()) {
154-
errors.push({
155-
type: 'typescript',
156-
message: 'TypeScript compilation failed: ' + stderr.trim()
157-
});
158-
}
159-
160-
return errors;
161-
}
162-
163-
function parseViteBuildErrors(stderr: string): BuildError[] {
164-
const errors: BuildError[] = [];
165-
const lines = stderr.split('\n');
166-
167-
for (const line of lines) {
168-
// Match Vite build error patterns
169-
if (line.includes('error') || line.includes('Error')) {
170-
errors.push({
171-
type: 'build',
172-
message: line.trim()
173-
});
174-
}
175-
}
176-
177-
// If no specific errors found but stderr has content, add generic error
178-
if (errors.length === 0 && stderr.trim()) {
179-
errors.push({
180-
type: 'build',
181-
message: 'Build failed: ' + stderr.trim()
182-
});
183-
}
184-
185-
return errors;
186-
}
187146

188147
function extractNewPackages(packageJsonContent: string): string[] {
189148
try {

lib/error-detection.ts

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
interface BuildError {
2+
type: 'typescript' | 'build' | 'runtime';
3+
message: string;
4+
file?: string;
5+
line?: number;
6+
column?: number;
7+
}
8+
9+
export interface ErrorDetectionResult {
10+
hasErrors: boolean;
11+
errors: BuildError[];
12+
isInfrastructureOnly: boolean;
13+
}
14+
15+
/**
16+
* Detects if output contains code-related errors (not infrastructure issues)
17+
*/
18+
export function detectCodeErrors(output: string): ErrorDetectionResult {
19+
console.log('=== CHECKING OUTPUT FOR CODE ERRORS ===');
20+
console.log('Output length:', output.length);
21+
console.log('Full output:', output);
22+
23+
// Check for actual code errors that users care about
24+
const hasSyntaxError = output.includes('SyntaxError');
25+
const hasUnexpectedToken = output.includes('Unexpected token');
26+
const hasParseError = output.includes('Parse error');
27+
const hasUnterminatedString = output.includes('Unterminated string');
28+
const hasModuleError = output.includes('Cannot resolve module') || output.includes('Module not found');
29+
const hasImportError = output.includes('Cannot resolve import');
30+
31+
// Check for infrastructure errors that should be ignored
32+
const isInfrastructureError = output.includes('EACCES: permission denied') ||
33+
output.includes('failed to load config from /app/vite.config.ts') ||
34+
output.includes('error when starting dev server') ||
35+
output.includes('/app/node_modules/.vite-temp/');
36+
37+
console.log('Error pattern checks (focusing on code errors):');
38+
console.log('- Has "SyntaxError":', hasSyntaxError);
39+
console.log('- Has "Unexpected token":', hasUnexpectedToken);
40+
console.log('- Has "Parse error":', hasParseError);
41+
console.log('- Has "Unterminated string":', hasUnterminatedString);
42+
console.log('- Has module/import errors:', hasModuleError || hasImportError);
43+
console.log('- Is infrastructure error (ignoring):', isInfrastructureError);
44+
45+
const hasCodeErrors = hasSyntaxError || hasUnexpectedToken || hasParseError ||
46+
hasUnterminatedString || hasModuleError || hasImportError;
47+
48+
// Only report actual code errors, not infrastructure issues
49+
if (hasCodeErrors && !isInfrastructureError) {
50+
console.log('🔴 CODE ERRORS DETECTED! Parsing...');
51+
const errors = parseErrorsFromOutput(output);
52+
console.log('Parsed errors:', errors);
53+
54+
return {
55+
hasErrors: true,
56+
errors,
57+
isInfrastructureOnly: false
58+
};
59+
} else if (isInfrastructureError && !hasCodeErrors) {
60+
console.log('⚠️ Only infrastructure errors detected (ignoring)');
61+
return {
62+
hasErrors: false,
63+
errors: [],
64+
isInfrastructureOnly: true
65+
};
66+
} else {
67+
console.log('✅ No code errors detected');
68+
return {
69+
hasErrors: false,
70+
errors: [],
71+
isInfrastructureOnly: false
72+
};
73+
}
74+
}
75+
76+
/**
77+
* Parses TypeScript compilation errors
78+
*/
79+
export function parseTypeScriptErrors(stderr: string): BuildError[] {
80+
const errors: BuildError[] = [];
81+
const lines = stderr.split('\n');
82+
83+
for (const line of lines) {
84+
// Match TypeScript error pattern: file(line,column): error TS####: message
85+
const match = line.match(/(.+)\((\d+),(\d+)\): error TS\d+: (.+)/);
86+
if (match) {
87+
const [, file, line, column, message] = match;
88+
// Filter out common non-critical errors that might be false positives
89+
const lowerMessage = message.toLowerCase();
90+
if (!lowerMessage.includes('deprecated') &&
91+
!lowerMessage.includes('unused') &&
92+
!lowerMessage.includes('implicit any')) {
93+
errors.push({
94+
type: 'typescript',
95+
message: message.trim(),
96+
file: file.replace('/app/', ''),
97+
line: parseInt(line),
98+
column: parseInt(column)
99+
});
100+
}
101+
}
102+
}
103+
104+
return errors;
105+
}
106+
107+
/**
108+
* Parses errors from build/dev server output
109+
*/
110+
function parseErrorsFromOutput(output: string): BuildError[] {
111+
console.log('🔍 parseErrorsFromOutput called with input length:', output.length);
112+
const errors: BuildError[] = [];
113+
const lines = output.split('\n');
114+
console.log('Total lines to process:', lines.length);
115+
116+
for (let i = 0; i < lines.length; i++) {
117+
const line = lines[i];
118+
console.log(`Line ${i + 1}: "${line}"`);
119+
120+
// Only match actual code errors, not infrastructure issues
121+
const hasCodeError = line.includes('SyntaxError') ||
122+
line.includes('Unexpected token') ||
123+
line.includes('Parse error') ||
124+
line.includes('Unterminated string') ||
125+
line.includes('Cannot resolve module') ||
126+
line.includes('Module not found') ||
127+
line.includes('Cannot resolve import');
128+
129+
// Skip infrastructure errors
130+
const isInfrastructureError = line.includes('EACCES: permission denied') ||
131+
line.includes('failed to load config') ||
132+
line.includes('error when starting dev server') ||
133+
line.includes('/app/node_modules/.vite-temp/');
134+
135+
console.log(` - Has code error: ${hasCodeError}`);
136+
console.log(` - Is infrastructure error (skip): ${isInfrastructureError}`);
137+
138+
if (hasCodeError && !isInfrastructureError) {
139+
console.log(` ✅ FOUND ERROR: "${line}"`);
140+
errors.push({
141+
type: 'build',
142+
message: line.trim()
143+
});
144+
}
145+
}
146+
147+
console.log(`🎯 parseErrorsFromOutput found ${errors.length} errors:`, errors);
148+
return errors;
149+
}

0 commit comments

Comments
 (0)