errlens 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +47 -0
  2. package/bin/index.js +73 -21
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -84,6 +84,53 @@ Get machine-readable results for your own tooling or automated reports:
84
84
  errlens "is not a function" --json
85
85
  ```
86
86
 
87
+ Run a script and write the JSON report directly to a file in CI:
88
+
89
+ ```bash
90
+ errlens run test.js --json > ci-report.json
91
+ ```
92
+
93
+ In `--json` mode, ErrLens prints only JSON (no spinner, colors, or terminal boxes).
94
+
95
+ Example response from `run`:
96
+
97
+ ```json
98
+ {
99
+ "code": 0,
100
+ "count": 0,
101
+ "matches": []
102
+ }
103
+ ```
104
+
105
+ Example response from `analyze <errorString>` (match found):
106
+
107
+ ```json
108
+ {
109
+ "code": 1,
110
+ "count": 1,
111
+ "matches": [
112
+ {
113
+ "name": "TypeError: Cannot read properties of undefined",
114
+ "match": "Cannot read properties of undefined",
115
+ "explanation": "You are trying to access a property on a variable that is currently empty.",
116
+ "why": "The variable wasn't initialized, or an API call hasn't finished yet.",
117
+ "fixes": [
118
+ "Use optional chaining: user?.name",
119
+ "Set a default value: data || []"
120
+ ],
121
+ "example": "const name = user?.name || 'Guest';"
122
+ }
123
+ ]
124
+ }
125
+ ```
126
+
127
+ Exit codes (useful for CI):
128
+
129
+ - `run <file>` exits with the child process exit code.
130
+ - `analyze <errorString>` exits with `1` when matches are found (intentional, so CI can fail when known errors are detected), otherwise `0`.
131
+
132
+ This follows Unix conventions where `0` means success and non-zero means failure. If you prefer success-on-detection in CI, invert the check in your pipeline logic (for example, treat exit code `1` from `analyze <errorString>` as a pass condition).
133
+
87
134
  ---
88
135
 
89
136
  ## 🧠 System Architecture
package/bin/index.js CHANGED
@@ -1,75 +1,127 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const { Command } = require("commander");
4
4
  const { spawn } = require("child_process");
5
- const ora = require("ora");
5
+
6
6
  const chalk = require("chalk");
7
7
  const path = require("path");
8
8
  const { findError } = require("../lib/matcher");
9
9
  const { formatError } = require("../lib/formatter");
10
10
 
11
+ const { version } = require("../package.json");
11
12
  const program = new Command();
12
13
 
13
14
  program
14
15
  .name("errlens")
15
16
  .description("Professional JS Error Analytics")
16
- .version("1.3.1");
17
+ .version(version);
17
18
 
19
+ // ----------------- RUN COMMAND -----------------
18
20
  program
19
21
  .command("run <file>")
22
+ .option('--json', 'Output JSON instead of pretty UI')
20
23
  .description("Run a Javascript file and analyze crashes")
21
- .action((file) => {
24
+ .action(async (file, options) => {
25
+ const { default: ora } = await import("ora");
22
26
  const filePath = path.resolve(process.cwd(), file);
23
- const spinner = ora(`Running ${chalk.yellow(file)}...`).start();
24
-
25
- // stdio: ['inherit', 'pipe', 'pipe']
26
- // This allows us to pipe stdout and stderr while keeping the process interactive
27
+ const isJson = Boolean(options.json);
28
+ const spinner = isJson ? null : ora(`Running ${chalk.yellow(file)}...`).start();
29
+
27
30
  const child = spawn(process.execPath, [filePath], { stdio: ["inherit", "pipe", "pipe"] });
28
31
 
29
32
  let errorOutput = "";
30
33
 
31
- // Stream logs to terminal in REAL-TIME
34
+ // Stream logs to terminal in real-time
32
35
  child.stdout.on("data", (data) => {
33
- // Clear spinner temporarily to print log, then restart (better UX)
34
- spinner.stop();
35
- process.stdout.write(data);
36
- spinner.start();
36
+ if (!isJson) {
37
+ spinner.stop();
38
+ process.stdout.write(data);
39
+ spinner.start();
40
+ }
37
41
  });
38
42
 
39
43
  // Capture stderr for analysis
40
44
  child.stderr.on("data", (data) => {
41
45
  errorOutput += data.toString();
46
+
47
+ if (!isJson) {
48
+ process.stderr.write(data);
49
+ }
42
50
  });
43
51
 
44
52
  child.on("close", (code, signal) => {
45
- spinner.stop();
53
+ if (!isJson) {
54
+ spinner.stop();
55
+ }
56
+
57
+ const { count, matches } = findError(errorOutput);
46
58
 
47
59
  if (code === null) {
48
- console.log(chalk.red.bold(`\n⚠️ Process killed by signal: ${signal}`));
60
+ if (isJson) {
61
+ const result = { code: 1, count, matches };
62
+ console.log(JSON.stringify(result, null, 2));
63
+ } else {
64
+ console.log(chalk.red.bold(`\n⚠️ Process killed by signal: ${signal}`));
65
+ }
49
66
  process.exit(1);
50
67
  return;
51
68
  }
52
69
 
53
- if (code === 0) {
54
- console.log(chalk.green.bold("\n✨ Process finished successfully."));
70
+ if (isJson) {
71
+ const result = { code, count, matches };
72
+ console.log(JSON.stringify(result, null, 2));
73
+ } else if (code === 0) {
74
+ console.log(chalk.green.bold("\n✨ Process finished successfully."));
55
75
  } else {
56
- const { count, matches } = findError(errorOutput);
57
-
58
76
  if (count > 0) {
59
77
  console.log(chalk.bold.cyan(`\n🚀 ErrLens Analysis (${count} Issue(s)):`));
60
- matches.forEach(m => console.log(formatError(m)));
78
+ matches.forEach(m => { console.log(formatError(m)); }); // Pretty UI only here
61
79
  } else {
62
80
  console.log(chalk.red.bold("\n❌ Crash detected (No known fix in database):"));
63
81
  console.log(chalk.gray(errorOutput));
64
82
  }
65
83
  }
84
+
66
85
  process.exit(code ?? 1);
67
86
  });
68
87
 
69
88
  child.on("error", (err) => {
70
- spinner.fail(chalk.red(`System Error: ${err.message}`));
89
+ if (isJson) {
90
+ const result = { code: 1, count: 0, matches: [] };
91
+ console.log(JSON.stringify(result, null, 2));
92
+ } else {
93
+ spinner.fail(chalk.red(`System Error: ${err.message}`));
94
+ }
71
95
  process.exit(1);
72
96
  });
73
97
  });
74
98
 
99
+ // ----------------- ANALYZE COMMAND -----------------
100
+ program
101
+ .arguments("<errorString>") // default command if no "run"
102
+ .description("Analyze a specific error string")
103
+ .option('--json', 'Output result in JSON format')
104
+ .action((errorString, options) => {
105
+ const { count, matches } = findError(errorString);
106
+ const exitCode = count > 0 ? 1 : 0;
107
+ const isJson = Boolean(options.json);
108
+
109
+ if (isJson) {
110
+ console.log(JSON.stringify({ code: exitCode, count, matches }, null, 2));
111
+ process.exit(exitCode);
112
+ return;
113
+ }
114
+
115
+ if (count > 0) {
116
+ console.log(chalk.bold.cyan(`\n🚀 ErrLens Analysis (${count} Issue(s)):`));
117
+ matches.forEach(m => { console.log(formatError(m)); }); // Pretty UI
118
+ } else {
119
+ console.log(chalk.red.bold("\n❌ Crash detected (No known fix in database):"));
120
+ console.log(chalk.gray(errorString));
121
+ }
122
+
123
+ process.exit(exitCode);
124
+ });
125
+
126
+ // ----------------- PARSE ARGUMENTS -----------------
75
127
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "errlens",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Professional CLI tool that explains JavaScript and Node.js errors in plain English with actionable fixes directly in your terminal.",
5
5
  "main": "./bin/index.js",
6
6
  "bin": {
7
- "errlens": "./bin/index.js"
7
+ "errlens": "bin/index.js"
8
8
  },
9
9
  "author": "BeyteFlow (https://github.com/BeyteFlow)",
10
10
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/BeyteFlow/errlens.git"
39
+ "url": "git+https://github.com/BeyteFlow/errlens.git"
40
40
  },
41
41
  "bugs": {
42
42
  "url": "https://github.com/BeyteFlow/errlens/issues"