Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 78 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
- 🔄 **Live Monitoring** – Catch errors in real-time using the `errlens run` command.
- 🧠 **Fuzzy Logic Engine** – Matches messy stack traces and typos using `Fuse.js`.
- 🎨 **Beautiful UI** – High-visibility terminal output powered by `boxen` and `chalk`.
- 🎭 **Color Themes** – 5 built-in themes (`default`, `minimal`, `high-contrast`, `dracula`, `monokai`).
- 🔍 **Verbose Mode** – Extra diagnostic details with `--verbose` for debugging custom patterns.
- 🤖 **CI/CD Ready** – Export raw data via `--json` for automated error reporting.

---
Expand Down Expand Up @@ -84,6 +86,12 @@ errlens analyze "Cannot read properties of undefined" --lang hi

# Run a script with output in Spanish
errlens run app.js --lang es

# Use a color theme
errlens analyze "is not a function" --theme dracula

# Show verbose diagnostic info
errlens analyze "Cannot read properties of undefined" --verbose
```

---
Expand All @@ -93,12 +101,21 @@ errlens run app.js --lang es
### Available Commands

```bash
errlens run <file> [--json] # Run a script and analyze any crashes
errlens analyze <error> [--json] # Analyze a specific error message
errlens --version # Show version information
errlens --help # Show help
errlens run <file> [options] # Run a script and analyze any crashes
errlens analyze <error> [options] # Analyze a specific error message
errlens --version # Show version information
errlens --help # Show help
```

### Options

| Flag | Description |
|------|-------------|
| `--json` | Output raw JSON (no colors, no boxes) — perfect for CI/CD pipelines |
| `--theme <name>` | Pick a color theme (see the Color Themes section below) |
| `--verbose` | Show extra diagnostic info for each matched error |
| `--lang <code>` | Output language (e.g. `hi`, `es`, `fr`) — available on `run` and `analyze` |

### 1️⃣ Automatic Monitoring (The "Pro" Way)

Run your script through ErrLens. If it crashes, ErrLens intercepts the error and explains the fix before the process exits.
Expand Down Expand Up @@ -176,7 +193,61 @@ This follows Unix conventions where `0` means success and non-zero means failure

---

### 4️⃣ Multilingual Support
### 4️⃣ Color Themes

ErrLens ships with several built-in color themes. Use the `--theme` flag to switch between them:

```bash
errlens analyze "TypeError: Cannot read properties of undefined" --theme dracula
errlens run app.js --theme high-contrast
errlens analyze "is not a function" --theme monokai
```

#### Available Themes

| Theme | Description |
|-------|-------------|
| `default` | Cyan-bordered, colorful theme (the classic ErrLens look) |
| `minimal` | Grayscale — clean, distraction-free output |
| `high-contrast` | Bright colors on dark terminals for accessibility |
| `dracula` | Magenta/pink palette inspired by the Dracula color scheme |
| `monokai` | Yellow/green palette inspired by Monokai |

> **Tip:** Combine `--theme` with `--verbose` for the most detailed output:
> ```bash
> errlens analyze "Cannot read properties of undefined" --theme dracula --verbose
> ```

When using `--json`, themes are ignored — you always get clean machine-readable output.

#### Runtime Injection Themes

When using `errlens/injector` or `errlens/auto` in your own code, set themes via environment variables:

```bash
ERRLENS_THEME=monokai ERRLENS_VERBOSE=1 node app.js
```

---

### 5️⃣ Verbose Mode

Add `--verbose` to see extra diagnostic details alongside each error match:

```bash
errlens analyze "Cannot read properties of undefined" --verbose
```

Verbose output adds:
- **MATCH:** the exact substring that triggered the database lookup
- **LENGTH:** character count of the match phrase
- **KEY:** the internal database key used

This is useful when debugging custom error patterns or contributing new entries to the database.

---

### 6️⃣ Multilingual Support

ErrLens supports error explanations in multiple languages using the `--lang` flag.

Expand Down Expand Up @@ -230,7 +301,9 @@ errlens/
├── lib/
│ ├── matcher.js # Fuzzy search & Logic engine
│ ├── formatter.js # UI & Terminal styling
│ ├── themes.js # Color theme definitions & resolver
│ ├── auto.js # Automation & Error interception
│ ├── injector.js # Runtime injection with theme support
│ └── database.json # The "Knowledge Base" (Dictionary)
├── package.json # Dependencies & Metadata
└── README.md # Documentation
Expand Down
118 changes: 85 additions & 33 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#!/usr/bin/env node

"use strict";

const { Command } = require("commander");
const { spawn } = require("child_process");
const path = require("path");
const { findError } = require("../lib/matcher");
const { formatError } = require("../lib/formatter");
const { THEME_NAMES, isValidTheme, resolveTheme } = require("../lib/themes");
const { version } = require("../package.json");

const program = new Command();
Expand All @@ -13,19 +16,43 @@ program
.name("errlens")
.description("Professional JS Error Analytics")
.version(version)
.option("--json", "Output JSON instead of pretty UI"); // ✅ GLOBAL OPTION
.option("--json", "Output JSON instead of pretty UI")
.option(
"--theme <name>",
`Color theme (${THEME_NAMES.join(", ")})`,
"default"
)
.option("--verbose", "Show extra diagnostic info for each match");

// ----------------- HELPERS -----------------
function validateTheme(name) {
if (name && !isValidTheme(name)) {
console.error(
`Unknown theme "${name}". Available: ${THEME_NAMES.join(", ")}`
);
process.exit(1);
}
}

function sanitizeMatches(matches) {
return matches.map(({ matchedText, ...rest }) => rest);
}

// ----------------- RUN COMMAND -----------------
program
.command("run <file>")
.description("Run a Javascript file and analyze crashes")
.option("--lang <code>", "output language (e.g. hi, es, fr)", "en")

.action(async (file,options) => {
.action(async (file, options) => {
const { default: ora } = await import("ora");
const { default: chalk } = await import("chalk");

const isJson = Boolean(program.opts().json);
const themeName = program.opts().theme;
const verbose = Boolean(program.opts().verbose);

validateTheme(themeName);

const filePath = path.resolve(process.cwd(), file);
const spinner = isJson
? null
Expand All @@ -37,7 +64,6 @@ program

let errorOutput = "";

// Stream stdout only in pretty mode
child.stdout.on("data", (data) => {
if (!isJson) {
spinner.stop();
Expand All @@ -46,10 +72,8 @@ program
}
});

// Capture stderr (DO NOT print in JSON mode)
child.stderr.on("data", (data) => {
errorOutput += data.toString();

if (!isJson) {
process.stderr.write(data);
}
Expand All @@ -60,90 +84,118 @@ program
spinner.stop();
}

const { count, matches } = findError(errorOutput,options.lang);
const { count, matches } = findError(errorOutput, options.lang);

// Process killed by signal
if (code === null) {
const result = { code: 1, count, matches };

if (isJson) {
console.log(JSON.stringify(result, null, 2));
console.log(
JSON.stringify({ code: 1, count, matches: sanitizeMatches(matches) }, null, 2)
);
} else {
const theme = resolveTheme(themeName);
console.log(
chalk.red.bold(`\n⚠️ Process killed by signal: ${signal}`)
chalk[theme.signalText].bold(`\n⚠️ Process killed by signal: ${signal}`)
);
}

process.exit(1);
}

// JSON MODE
if (isJson) {
console.log(JSON.stringify({ code, count, matches }, null, 2));
console.log(
JSON.stringify({ code, count, matches: sanitizeMatches(matches) }, null, 2)
);
process.exit(code ?? 1);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

// PRETTY MODE
const theme = resolveTheme(themeName);

if (code === 0) {
console.log(chalk.green.bold("\n✨ Process finished successfully."));
console.log(chalk[theme.successText].bold("\n✨ Process finished successfully."));
} else {
if (count > 0) {
console.log(
chalk.bold.cyan(`\n🚀 ErrLens Analysis (${count} Issue(s)):`)
chalk[theme.headerText].bold(`\n🚀 ErrLens Analysis (${count} Issue(s)):`)
);
matches.forEach((m) =>
console.log(
formatError(m, { theme: themeName, verbose })
)
);
matches.forEach((m) => console.log(formatError(m)));
} else {
console.log(
chalk.red.bold("\n❌ Crash detected (No known fix in database):")
chalk[theme.unknownText].bold(
"\n❌ Crash detected (No known fix in database):"
)
);
console.log(chalk.gray(errorOutput));
console.log(chalk[theme.rawText](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));
console.log(
JSON.stringify({ code: 1, count: 0, matches: [] }, null, 2)
);
} else {
if (spinner) spinner.stop();
console.log(chalk.red(`System Error: ${err.message}`));
const theme = resolveTheme(themeName);
console.log(chalk[theme.errorText](`System Error: ${err.message}`));
}

process.exit(1);
});
});

// ----------------- ANALYZE COMMAND -----------------
program
program
.command("analyze <errorString>")
.description("Analyze a specific error string")
.option("--lang <code>", "output language (e.g. hi, es, fr)", "en")
.action(async (errorString, options) => {
const { default: chalk } = await import("chalk");
const isJson = Boolean(program.opts().json);
const themeName = program.opts().theme;
const verbose = Boolean(program.opts().verbose);

validateTheme(themeName);

const { count, matches } = findError(errorString, options.lang);
const exitCode = count > 0 ? 1 : 0;

if (isJson) {
console.log(JSON.stringify({ code: exitCode, count, matches }, null, 2));
console.log(
JSON.stringify({ code: exitCode, count, matches: sanitizeMatches(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)));
const theme = resolveTheme(themeName);
console.log(
chalk[theme.headerText].bold(`\n🚀 ErrLens Analysis (${count} Issue(s)):`)
);
matches.forEach((m) =>
console.log(formatError(m, { theme: themeName, verbose }))
);
} else {
console.log(chalk.red.bold("\n❌ Crash detected (No known fix in database):"));
console.log(chalk.gray(errorString));
const theme = resolveTheme(themeName);
console.log(
chalk[theme.unknownText].bold(
"\n❌ Crash detected (No known fix in database):"
)
);
console.log(chalk[theme.rawText](errorString));
}

process.exit(exitCode);
});


// ----------------- PARSE -----------------
program.parse(process.argv);
program.parseAsync(process.argv).catch((err) => {
console.error(err.message || err);
process.exit(1);
});
39 changes: 30 additions & 9 deletions lib/auto.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
"use strict";

const { findError } = require("./matcher");
const { formatError } = require("./formatter");
const chalk = require("chalk");
const { resolveTheme } = require("./themes");
const chalkImport = require("chalk");
const chalk = chalkImport.default || chalkImport;

const themeName = process.env.ERRLENS_THEME || "default";
const verbose = process.env.ERRLENS_VERBOSE === "1";
const theme = resolveTheme(themeName);

function safeStringify(err) {
try {
if (err !== null && typeof err === "object") {
return err.stack || err.message || String(err);
}
return String(err ?? "unknown error");
} catch {
return "unknown error";
}
}

process.on("uncaughtException", (err) => {
// Use the message for the search, guard against non-Error throws
const msg = (err && err.message) ? err.message : String(err);
const msg = safeStringify(err);
const { count, matches } = findError(msg);

if (count > 0) {
console.log(chalk.cyan(`\n💡 ErrLens detected ${count} issue(s):`));
matches.forEach(match => console.log(formatError(match)));
console.log(
chalk[theme.infoLabel]( `\n💡 ErrLens detected ${count} issue(s):`)
);
matches.forEach((match) =>
console.log(formatError(match, { theme: themeName, verbose }))
);
} else {
// If no match, show the raw error so you know what happened
console.error(chalk.red.bold("\n--- Raw System Error ---"));
console.error(chalk[theme.errorText].bold("\n--- Raw System Error ---"));
console.error(err);
}
process.exit(1);
});
});
Loading
Loading