Skip to content
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ A comprehensive modernization of all Angular templates to align with Angular v21
* **ci:** mark `Util.sanitizeShellArg(x)` as command injection sanitizer for CodeQL ([#1524](https://github.com/IgniteUI/igniteui-cli/pull/1524))
* **deps:** bump minimatch, ajv, immutable, and lodash ([#1549](https://github.com/IgniteUI/igniteui-cli/pull/1549))
* **deps:** bump flatted ([#1559](https://github.com/IgniteUI/igniteui-cli/pull/1559))
* **CLI error handling:** added `.fail()` handler to yargs to gracefully handle command validation errors (e.g., missing required subcommands) instead of showing raw stack traces ([#1614](https://github.com/IgniteUI/igniteui-cli/pull/1614))
* **Unknown command detection:** running `ig <unknown>` now prints an error message and available commands instead of silently falling through to the step-by-step interactive mode ([#1614](https://github.com/IgniteUI/igniteui-cli/pull/1614))
* **Unhandled promise rejection:** added `.catch()` in the CLI entry point to catch and display unexpected errors cleanly ([#1614](https://github.com/IgniteUI/igniteui-cli/pull/1614))

---

Expand Down
5 changes: 4 additions & 1 deletion packages/cli/bin/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ resolve("igniteui-cli", { basedir: process.cwd() }, function (err, res) {
} else {
cli = require("../lib/cli");
}
cli.run(args);
cli.run(args).catch(function (err) {
console.error("Error: " + (err.message || err));
process.exit(1);
});
});
22 changes: 18 additions & 4 deletions packages/cli/lib/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ export async function run(args = null) {
false // setting this to `true` is supposed to exec the middleware after parsing and before arg validation
// but in reality it also does not trigger the command's handler (╯°□°)╯︵ ┻━┻
)
.fail((msg, err, yargs) => {
const message = err?.message ?? msg;
if (message) {
Util.error(message, "red");
yargs.showHelp();
process.exitCode = 1;
}
})
.help().alias("help", "h")
.parseAsync(
args, // the args to parse to argv
Expand Down Expand Up @@ -121,10 +129,16 @@ export async function run(args = null) {
App.testMode = !!argv.testMode;

if (!helpRequest && !ALL_COMMANDS.has(command?.toString())) {
Util.log("Starting Step by step mode.", "green");
Util.log("For available commands, stop this execution and use --help.", "green");
const prompts = new PromptSession(templateManager);
prompts.start();
if (command) {
process.exitCode = 1;
Util.error(`Unknown command: "${command}"`, "red");
yargsModule.showHelp();
} else {
Util.log("Starting Step by step mode.", "green");
Util.log("For available commands, stop this execution and use --help.", "green");
const prompts = new PromptSession(templateManager);
prompts.start();
}
}
}
);
Expand Down
22 changes: 20 additions & 2 deletions spec/unit/cli-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,33 @@ describe("Unit - Cli.ts", () => {
await run.run("list");
expect(list.handler).toHaveBeenCalled();
});
it("Should fire properly - fallback to default", async () => {
it("Should fire properly - fallback to default with no args", async () => {
spyOn(Util , "log");
spyOn(GoogleAnalytics, "post");
spyOn(PromptSession.prototype, "start");
await run.run("Nonexistent command");
await run.run([]);

// expected console output:
expect(Util.log).toHaveBeenCalledWith("Starting Step by step mode.", "green");
expect(Util.log).toHaveBeenCalledWith("For available commands, stop this execution and use --help.", "green");
expect(PromptSession.prototype.start).toHaveBeenCalled();
});
it("Should show error for unknown command", async () => {
spyOn(Util , "log");
spyOn(Util , "error");
spyOn(GoogleAnalytics, "post");
spyOn(PromptSession.prototype, "start");
await run.run("nonexistent");

expect(Util.error).toHaveBeenCalledWith(`Unknown command: "nonexistent"`, "red");
expect(PromptSession.prototype.start).not.toHaveBeenCalled();
});
it("Should gracefully handle subcommand validation errors", async () => {
spyOn(Util , "log");
spyOn(Util , "error");
spyOn(GoogleAnalytics, "post");
await run.run("generate");

expect(Util.error).toHaveBeenCalledWith("Please select command", "red");
});
});
Loading