Skip to content

Commit fc0dc87

Browse files
committed
feat(commandpost): use CommandpostError instead of Error
1 parent ef91767 commit fc0dc87

File tree

5 files changed

+113
-8
lines changed

5 files changed

+113
-8
lines changed

lib/argument.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CommandpostError, ErrorReason } from "./error";
2+
13
// jsdoc, see constructor.
24
export default class Argument {
35
/** argument name */
@@ -27,7 +29,14 @@ export default class Argument {
2729
this.name = arg.slice(1, -1);
2830
break;
2931
default:
30-
throw new Error("unsupported format: " + arg);
32+
throw new CommandpostError({
33+
message: `unsupported format: ${arg}`,
34+
reason: ErrorReason.UnsupportedFormatArgument,
35+
params: {
36+
origin: this,
37+
arg,
38+
},
39+
});
3140
}
3241
if (/\.\.\.$/.test(this.name)) {
3342
this.name = this.name.slice(0, -3);
@@ -57,7 +66,15 @@ export default class Argument {
5766
*/
5867
parse(opts: any, args: string[]): string[] {
5968
if (this.required && this.variadic && args.length === 0) {
60-
throw new Error(this.name + " is required more than 1 argument");
69+
throw new CommandpostError({
70+
message: `${this.name} is required more than 1 argument`,
71+
reason: ErrorReason.ArgumentsRequired,
72+
params: {
73+
origin: this,
74+
opts,
75+
args,
76+
},
77+
});
6178
}
6279
if (this.variadic) {
6380
opts[this.name] = args;
@@ -66,7 +83,15 @@ export default class Argument {
6683
}
6784
let arg = args.shift();
6885
if (this.required && !arg) {
69-
throw new Error(this.name + " is required");
86+
throw new CommandpostError({
87+
message: `${this.name} is required`,
88+
reason: ErrorReason.ArgumentRequired,
89+
params: {
90+
origin: this,
91+
opts,
92+
args,
93+
},
94+
});
7095
}
7196
opts[this.name] = arg;
7297
return args;

lib/command.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Option from "./option";
22
import Argument from "./argument";
3+
import { CommandpostError, ErrorReason } from "./error";
34

45
import * as utils from "./utils";
56

@@ -122,11 +123,17 @@ export default class Command<Opt, Arg> {
122123
let findVariadic = false;
123124
this.args = args.map(argStr => {
124125
if (findVariadic) {
125-
throw new Error("parameter can not placed after variadic parameter");
126+
throw new CommandpostError({
127+
message: "parameter can not placed after variadic parameter",
128+
reason: ErrorReason.ParameterCantPlacedAfterVariadic,
129+
});
126130
}
127131
let arg = new Argument(argStr);
128132
if (arg.required && findOptional) {
129-
throw new Error("required parameter is not placed after optional parameter");
133+
throw new CommandpostError({
134+
message: "parameter can not placed after variadic parameter",
135+
reason: ErrorReason.ParameterCannPlacedAfterOptional,
136+
});
130137
}
131138
if (!arg.required) {
132139
findOptional = true;
@@ -354,7 +361,14 @@ export default class Command<Opt, Arg> {
354361
errMsg += this.unknownOptions.length === 1 ? " " : "s ";
355362
errMsg += this.unknownOptions.join(", ") + "\n";
356363
errMsg += this.helpText();
357-
throw new Error(errMsg);
364+
throw new CommandpostError({
365+
message: errMsg,
366+
reason: ErrorReason.UnknownOption,
367+
params: {
368+
origin: this,
369+
args,
370+
},
371+
});
358372
}
359373
if (this._matchSubCommand(rest)) {
360374
return rest;

lib/error.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as util from "util";
2+
3+
import Command from "./command";
4+
import Argument from "./argument";
5+
import Option from "./option";
6+
7+
export interface ErrorParameters {
8+
message?: string;
9+
reason: ErrorReason;
10+
params?:
11+
{ origin: Argument; arg: string; } |
12+
{ origin: Argument; opts: any; args: string[]; } |
13+
{ origin: Command<any, any>; args: string[]; } |
14+
{ option: Option; opts: any; args: string[]; };
15+
}
16+
17+
export const enum ErrorReason {
18+
UnsupportedFormatArgument = "unsupported format",
19+
ArgumentsRequired = "1 or more arguments required",
20+
ArgumentRequired = "argument required",
21+
ParameterCantPlacedAfterVariadic = "parameter can not placed after variadic parameter",
22+
ParameterCannPlacedAfterOptional = "required parameter is not placed after optional parameter",
23+
UnknownOption = "unknown option",
24+
OptionNameMismatch = "short or long option name mismatch",
25+
OptionValueRequired = "option value required",
26+
}
27+
28+
export class CommandpostError {
29+
stack?: string;
30+
31+
constructor(public params: ErrorParameters) {
32+
Error.captureStackTrace(this, this.constructor);
33+
}
34+
35+
get name() {
36+
return this.constructor.name;
37+
}
38+
39+
get message() {
40+
return this.params.message;
41+
}
42+
}
43+
44+
util.inherits(CommandpostError, Error);

lib/option.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CommandpostError, ErrorReason } from "./error";
12
import * as utils from "./utils";
23

34
// jsdoc, see constructor.
@@ -87,13 +88,29 @@ export default class Option {
8788
*/
8889
parse(opts: any, args: string[]): string[] {
8990
if (!this.is(args[0])) {
90-
throw new Error(args[0] + " is not match " + this.short + " or " + this.long);
91+
throw new CommandpostError({
92+
message: `${args[0]} is not match ${this.short} or ${this.long}`,
93+
reason: ErrorReason.OptionNameMismatch,
94+
params: {
95+
option: this,
96+
opts,
97+
args,
98+
},
99+
});
91100
}
92101
let next = args[1];
93102
let propertyName = utils.kebabToLowerCamelCase(this.name());
94103
if (this.required) {
95104
if (next == null) {
96-
throw new Error(args[0] + " is required parameter value");
105+
throw new CommandpostError({
106+
message: `${args[0]} is required parameter value`,
107+
reason: ErrorReason.OptionValueRequired,
108+
params: {
109+
option: this,
110+
opts,
111+
args,
112+
},
113+
});
97114
}
98115
opts[propertyName] = opts[propertyName] || [];
99116
opts[propertyName].push(next);

test/argumentSpec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as assert from "power-assert";
22

33
import Argument from "../lib/argument";
4+
import { CommandpostError } from "../lib/error";
45

56
describe("Argument", () => {
67
describe("constructor", () => {
@@ -51,6 +52,10 @@ describe("Argument", () => {
5152
arg.parse(parsed, []);
5253
} catch (e) {
5354
caught = true;
55+
assert(e instanceof Error);
56+
assert(e instanceof CommandpostError);
57+
let err = e as CommandpostError;
58+
assert(err.params.message === "foobar is required");
5459
}
5560

5661
assert(caught);

0 commit comments

Comments
 (0)