-
Notifications
You must be signed in to change notification settings - Fork 2
feat: enhance error database with common runtime errors #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,151 +1,33 @@ | ||
| #!/usr/bin/env node | ||
| const database = require('../lib/database'); | ||
| const Fuse = require('fuse.js'); | ||
| const { Command } = require('commander'); | ||
|
|
||
| const { Command } = require("commander"); | ||
| const { spawn } = require("child_process"); | ||
| const path = require("path"); | ||
| const { findError } = require("../lib/matcher"); | ||
| const { formatError } = require("../lib/formatter"); | ||
| const { version } = require("../package.json"); | ||
| const fuse = new Fuse(database, { keys: ['pattern'] }); | ||
|
|
||
| const program = new Command(); | ||
|
|
||
| program | ||
| .name("errlens") | ||
| .description("Professional JS Error Analytics") | ||
| .version(version) | ||
| .option("--json", "Output JSON instead of pretty UI"); // ✅ GLOBAL OPTION | ||
| .name('errlens') | ||
| .description('Translate errors to human-readable insights') | ||
| .version('1.0.9'); | ||
|
|
||
| // ----------------- RUN COMMAND ----------------- | ||
| program | ||
| .command("run <file>") | ||
| .description("Run a Javascript file and analyze crashes") | ||
| .action(async (file) => { | ||
| const { default: ora } = await import("ora"); | ||
| const { default: chalk } = await import("chalk"); | ||
|
|
||
| const isJson = Boolean(program.opts().json); | ||
| const filePath = path.resolve(process.cwd(), file); | ||
| const spinner = isJson | ||
| ? null | ||
| : ora(`Running ${chalk.yellow(file)}...`).start(); | ||
|
|
||
| const child = spawn(process.execPath, [filePath], { | ||
| stdio: ["inherit", "pipe", "pipe"], | ||
| }); | ||
|
|
||
| let errorOutput = ""; | ||
|
|
||
| // Stream stdout only in pretty mode | ||
| child.stdout.on("data", (data) => { | ||
| if (!isJson) { | ||
| spinner.stop(); | ||
| process.stdout.write(data); | ||
| spinner.start(); | ||
| } | ||
| }); | ||
|
|
||
| // Capture stderr (DO NOT print in JSON mode) | ||
| child.stderr.on("data", (data) => { | ||
| errorOutput += data.toString(); | ||
|
|
||
| if (!isJson) { | ||
| process.stderr.write(data); | ||
| } | ||
| }); | ||
|
|
||
| child.on("close", (code, signal) => { | ||
| if (!isJson && spinner) { | ||
| spinner.stop(); | ||
| } | ||
|
|
||
| const { count, matches } = findError(errorOutput); | ||
|
|
||
| // Process killed by signal | ||
| if (code === null) { | ||
| const result = { code: 1, count, matches }; | ||
|
|
||
| if (isJson) { | ||
| console.log(JSON.stringify(result, null, 2)); | ||
| } else { | ||
| console.log( | ||
| chalk.red.bold(`\n⚠️ Process killed by signal: ${signal}`) | ||
| ); | ||
| } | ||
|
|
||
| process.exit(1); | ||
| } | ||
|
|
||
| // JSON MODE | ||
| if (isJson) { | ||
| console.log(JSON.stringify({ code, count, matches }, null, 2)); | ||
| process.exit(code ?? 1); | ||
| } | ||
|
|
||
| // PRETTY MODE | ||
| if (code === 0) { | ||
| console.log(chalk.green.bold("\n✨ Process finished successfully.")); | ||
| } else { | ||
| if (count > 0) { | ||
| console.log( | ||
| chalk.bold.cyan(`\n🚀 ErrLens Analysis (${count} Issue(s)):`) | ||
| ); | ||
| matches.forEach((m) => console.log(formatError(m))); | ||
| } else { | ||
| console.log( | ||
| chalk.red.bold("\n❌ Crash detected (No known fix in database):") | ||
| ); | ||
| console.log(chalk.gray(errorOutput)); | ||
| } | ||
| } | ||
|
|
||
| process.exit(code ?? 1); | ||
| }); | ||
|
|
||
| child.on("error", (err) => { | ||
| const result = { code: 1, count: 0, matches: [] }; | ||
|
|
||
| if (isJson) { | ||
| console.log(JSON.stringify(result, null, 2)); | ||
| .command('explain') | ||
| .argument('<message>', 'error message to explain') | ||
| .option('--json', 'output in json format') | ||
| .action((message, options) => { | ||
| const results = fuse.search(message); | ||
| const result = results.length > 0 ? results[0].item : null; | ||
|
|
||
| if (options.json) { | ||
| console.log(JSON.stringify(result || { message: "No explanation found" })); | ||
| } else { | ||
| if (result) { | ||
| console.log(`Explanation: ${result.explanation}\nCause: ${result.cause}\nFix: ${result.fix}`); | ||
| } else { | ||
| if (spinner) spinner.stop(); | ||
| console.log(chalk.red(`System Error: ${err.message}`)); | ||
| console.log("No specific explanation found for this error."); | ||
| } | ||
|
|
||
| process.exit(1); | ||
| }); | ||
| }); | ||
|
|
||
| // ----------------- ANALYZE COMMAND ----------------- | ||
| program | ||
| .command("analyze <errorString>") | ||
| .description("Analyze a specific error string") | ||
| .action(async (errorString) => { | ||
| const { default: chalk } = await import("chalk"); | ||
| const isJson = Boolean(program.opts().json); | ||
| const { count, matches } = findError(errorString); | ||
| const exitCode = count > 0 ? 1 : 0; | ||
|
|
||
| if (isJson) { | ||
| console.log( | ||
| JSON.stringify({ code: exitCode, count, matches }, null, 2) | ||
| ); | ||
| process.exit(exitCode); | ||
| } | ||
|
|
||
| if (count > 0) { | ||
| console.log( | ||
| chalk.bold.cyan(`\n🚀 ErrLens Analysis (${count} Issue(s)):`) | ||
| ); | ||
| matches.forEach((m) => console.log(formatError(m))); | ||
| } else { | ||
| console.log( | ||
| chalk.red.bold("\n❌ Crash detected (No known fix in database):") | ||
| ); | ||
| console.log(chalk.gray(errorString)); | ||
| } | ||
|
|
||
| process.exit(exitCode); | ||
| }); | ||
|
|
||
| // ----------------- PARSE ----------------- | ||
| program.parse(process.argv); | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||
| module.exports = [ | ||||||||||||||||||
| { | ||||||||||||||||||
| pattern: "is not defined", | ||||||||||||||||||
| explanation: "You are trying to use a variable that hasn't been declared or is out of scope.", | ||||||||||||||||||
| cause: "Commonly caused by typos, missing variable declarations (var/let/const), or trying to access a variable before it is defined.", | ||||||||||||||||||
| fix: "Check for typos in the variable name. Ensure the variable is declared in the correct scope before accessing it." | ||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| pattern: "is not a function", | ||||||||||||||||||
| explanation: "You are trying to call a value as if it were a function, but the value is of a different type (like undefined, null, or a string).", | ||||||||||||||||||
| cause: "Often happens when an object property is missing, or an asynchronous function returns an unexpected result.", | ||||||||||||||||||
| fix: "Verify that the object or property you are calling is actually a function. Use 'typeof' to check if necessary." | ||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| pattern: "Cannot read property of undefined", | ||||||||||||||||||
| explanation: "You are trying to access a property on an object that is currently undefined.", | ||||||||||||||||||
| cause: "The object you are accessing hasn't been initialized or the API response you expected is missing.", | ||||||||||||||||||
| fix: "Use optional chaining (e.g., obj?.property) or add a null check before accessing the property." | ||||||||||||||||||
|
Comment on lines
+15
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's explore the repository structure
find . -type f -name "*.js" | head -20Repository: BeyteFlow/errlens Length of output: 161 🏁 Script executed: # Check if lib/database.js and bin/index.js exist and examine them
ls -la lib/ bin/ 2>/dev/null || echo "Checking with find..."
git ls-files | grep -E "(lib|bin).*\.js"Repository: BeyteFlow/errlens Length of output: 830 🏁 Script executed: # Read lib/database.js to see the current content at lines 15-18
cat -n lib/database.jsRepository: BeyteFlow/errlens Length of output: 2210 🏁 Script executed: # Read bin/index.js to understand how patterns are searched
cat -n bin/index.jsRepository: BeyteFlow/errlens Length of output: 1258 🏁 Script executed: # Check Node.js version requirements
cat package.json | grep -A 5 -B 5 "engines\|node"Repository: BeyteFlow/errlens Length of output: 634 🏁 Script executed: # Also check for .nvmrc or other version files
ls -la | grep -E "\.nvmrc|\.node-version|\.tool-versions"
cat .nvmrc 2>/dev/null || echo "No .nvmrc file"Repository: BeyteFlow/errlens Length of output: 75 🌐 Web query:
💡 Result: The difference is just the JavaScript engine (V8) / Node.js version, not a different underlying error.
This message wording change is why the same failing code (or the same test) can produce different text on different machines/CI images—pinning the Node version makes the message consistent. [1] Rollbar’s examples also show both variants in the wild. [2] Practical takeaway: if you have tests/snapshots that assert the full error message, prefer asserting on Sources: [1] [2] Update error pattern for Node.js 20 compatibility. Line 15 uses Fix- pattern: "Cannot read property of undefined",
+ pattern: "Cannot read properties of undefined",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| pattern: "Maximum call stack size exceeded", | ||||||||||||||||||
| explanation: "Your code is hitting the recursion limit of the JavaScript engine.", | ||||||||||||||||||
| cause: "Typically caused by an infinite loop in a recursive function or missing a base case.", | ||||||||||||||||||
| fix: "Review your recursive function to ensure there is a clear exit condition and that it is reachable." | ||||||||||||||||||
| }, | ||||||||||||||||||
| { | ||||||||||||||||||
| pattern: "Unexpected token", | ||||||||||||||||||
| explanation: "The JavaScript engine encountered a character it did not expect while parsing your code.", | ||||||||||||||||||
| cause: "Missing closing braces, parentheses, or commas in your syntax.", | ||||||||||||||||||
| fix: "Use an IDE with linting (like ESLint) to identify missing brackets or incorrect syntax patterns." | ||||||||||||||||||
| } | ||||||||||||||||||
| ]; | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: BeyteFlow/errlens
Length of output: 75
🏁 Script executed:
cat -n ./bin/index.js | head -30Repository: BeyteFlow/errlens
Length of output: 1201
🌐 Web query:
Commander.js variadic arguments syntax <message...>💡 Result:
In Commander.js, appending
...to an argument placeholder makes it variadic (captures multiple values into an array).<message...>= required variadic positional argument namedmessage(1+ values), parsed as an array. Only the last positional argument may be variadic. [1][message...]= optional variadic positional argument (0+ values), also parsed as an array. [3]Example:
Sources: Commander documentation describing variadic command arguments and
...syntax. [1] [3]🏁 Script executed:
Repository: BeyteFlow/errlens
Length of output: 1258
Accept pasted error messages without forcing quotes.
Line 16 captures only one argv token. Most real errors contain spaces, so
errlens explain ReferenceError: foo is not definedgets split into extra args unless the user remembers to quote the whole message.🧩 Variadic message capture
🤖 Prompt for AI Agents