From 0101d36c83986d21111c31701676a5bc248500d6 Mon Sep 17 00:00:00 2001
From: Arthur Albuquerque <20231694+troclaux@users.noreply.github.com>
Date: Thu, 22 Oct 2026 16:25:15 -0300
Subject: [PATCH] fix: report CLI read/write errors instead of crashing (#894)
---
spec/cli_spec.js | 40 ++++++++++++++++++++++++++++++++++++++++
src/cli/cli.js | 18 ++++++++++++++----
2 files changed, 54 insertions(+), 4 deletions(-)
create mode 100644 spec/cli_spec.js
diff --git a/spec/cli_spec.js b/spec/cli_spec.js
new file mode 100644
index 00000000..eb6215fd
--- /dev/null
+++ b/spec/cli_spec.js
@@ -0,0 +1,40 @@
+"use strict";
+
+import { spawnSync } from "child_process";
+import path from "path";
+import { fileURLToPath } from "url";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const cliPath = path.join(__dirname, "../src/cli/cli.js");
+const missingPath = path.join(__dirname, "assets/does-not-exist.xml");
+
+function runCli(args, input) {
+ return spawnSync(process.execPath, [cliPath, ...args], { input, encoding: "utf8" });
+}
+
+describe("CLI", function() {
+
+ it("should parse xml from stdin", function() {
+ const result = runCli([], "1");
+
+ expect(result.status).toBe(0);
+ expect(JSON.parse(result.stdout)).toEqual({ a: 1 });
+ });
+
+ it("should report a missing input file instead of crashing", function() {
+ const result = runCli([missingPath]);
+
+ expect(result.status).toBe(1);
+ expect(result.stdout).toContain("Seems an invalid file or stream.");
+ expect(result.stdout).toContain("ENOENT");
+ expect(result.stderr).not.toContain("throw err");
+ });
+
+ it("should report an unwritable output file instead of crashing", function() {
+ const result = runCli(["-o", path.join(missingPath, "out.json")], "1");
+
+ expect(result.status).toBe(1);
+ expect(result.stdout).toContain("Seems an invalid file or stream.");
+ expect(result.stderr).not.toContain("throw err");
+ });
+});
diff --git a/src/cli/cli.js b/src/cli/cli.js
index b4773e40..6b3effba 100755
--- a/src/cli/cli.js
+++ b/src/cli/cli.js
@@ -70,27 +70,37 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
if (!fileName) {
ReadToEnd.readToEnd(process.stdin, function(err, data) {
if (err) {
- throw err;
+ onError(err);
+ return;
}
callback(data.toString());
});
} else {
fs.readFile(fileName, function(err, data) {
if (err) {
- throw err;
+ onError(err);
+ return;
}
callback(data.toString());
});
}
} catch (e) {
- console.log('Seems an invalid file or stream.' + e);
+ onError(e);
}
}
+// errors raised inside async callbacks are not caught by the try/catch above,
+// so they are reported here instead of being thrown
+function onError(err) {
+ console.log('Seems an invalid file or stream.' + err);
+ process.exitCode = 1;
+}
+
function writeToFile(fileName, data) {
fs.writeFile(fileName, data, function(err) {
if (err) {
- throw err;
+ onError(err);
+ return;
}
console.log('JSON output has been written to ' + fileName);
});