Skip to content

Commit 724df10

Browse files
author
Benjamin E. Coe
authored
refactor(errors): Node.js style validation/errors (#40)
1 parent a85e8dc commit 724df10

File tree

4 files changed

+102
-9
lines changed

4 files changed

+102
-9
lines changed

errors.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use strict';
2+
3+
class ERR_INVALID_ARG_TYPE extends TypeError {
4+
constructor(name, expected, actual) {
5+
super(`${name} must be ${expected} got ${actual}`);
6+
this.code = 'ERR_INVALID_ARG_TYPE';
7+
}
8+
}
9+
10+
class ERR_NOT_IMPLEMENTED extends Error {
11+
constructor(feature) {
12+
super(`${feature} not implemented`);
13+
this.code = 'ERR_NOT_IMPLEMENTED';
14+
}
15+
}
16+
17+
module.exports = {
18+
codes: {
19+
ERR_INVALID_ARG_TYPE,
20+
ERR_NOT_IMPLEMENTED
21+
}
22+
};

index.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,29 @@
11
'use strict';
22

33
const {
4-
ArrayIsArray,
54
ArrayPrototypeConcat,
65
ArrayPrototypeIncludes,
76
ArrayPrototypeSlice,
87
ArrayPrototypePush,
8+
ObjectHasOwn,
99
StringPrototypeCharAt,
1010
StringPrototypeIncludes,
1111
StringPrototypeIndexOf,
1212
StringPrototypeSlice,
1313
StringPrototypeStartsWith,
1414
} = require('./primordials');
1515

16+
const {
17+
codes: {
18+
ERR_NOT_IMPLEMENTED
19+
}
20+
} = require('./errors');
21+
22+
const {
23+
validateArray,
24+
validateObject
25+
} = require('./validators');
26+
1627
function getMainArgs() {
1728
// This function is a placeholder for proposed process.mainArgs.
1829
// Work out where to slice process.argv for user supplied arguments.
@@ -75,11 +86,12 @@ const parseArgs = (
7586
argv = getMainArgs(),
7687
options = {}
7788
) => {
78-
if (typeof options !== 'object' || options === null) {
79-
throw new Error('Whoops!');
80-
}
81-
if (options.withValue !== undefined && !ArrayIsArray(options.withValue)) {
82-
throw new Error('Whoops! options.withValue should be an array.');
89+
validateArray(argv, 'argv');
90+
validateObject(options, 'options');
91+
for (const key of ['withValue', 'multiples']) {
92+
if (ObjectHasOwn(options, key)) {
93+
validateArray(options[key], `options.${key}`);
94+
}
8395
}
8496

8597
const result = {
@@ -104,7 +116,7 @@ const parseArgs = (
104116
} else if (
105117
StringPrototypeCharAt(arg, 1) !== '-'
106118
) { // Look for shortcodes: -fXzy
107-
throw new Error('What are we doing with shortcodes!?!');
119+
throw new ERR_NOT_IMPLEMENTED('shortcodes');
108120
}
109121

110122
arg = StringPrototypeSlice(arg, 2); // remove leading --

test/index.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,23 @@ test('excess leading dashes on options are retained', function(t) {
205205

206206
// Test bad inputs
207207

208+
test('invalid argument passed for options', function(t) {
209+
const passedArgs = ['--so=wat'];
210+
211+
t.throws(function() { parseArgs(passedArgs, 'bad value'); }, {
212+
code: 'ERR_INVALID_ARG_TYPE'
213+
});
214+
215+
t.end();
216+
});
217+
208218
test('boolean passed to "withValue" option', function(t) {
209219
const passedArgs = ['--so=wat'];
210220
const passedOptions = { withValue: true };
211221

212-
t.throws(function() { parseArgs(passedArgs, passedOptions); });
222+
t.throws(function() { parseArgs(passedArgs, passedOptions); }, {
223+
code: 'ERR_INVALID_ARG_TYPE'
224+
});
213225

214226
t.end();
215227
});
@@ -218,7 +230,9 @@ test('string passed to "withValue" option', function(t) {
218230
const passedArgs = ['--so=wat'];
219231
const passedOptions = { withValue: 'so' };
220232

221-
t.throws(function() { parseArgs(passedArgs, passedOptions); });
233+
t.throws(function() { parseArgs(passedArgs, passedOptions); }, {
234+
code: 'ERR_INVALID_ARG_TYPE'
235+
});
222236

223237
t.end();
224238
});

validators.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
const {
4+
ArrayIsArray,
5+
} = require('./primordials');
6+
7+
const {
8+
codes: {
9+
ERR_INVALID_ARG_TYPE
10+
}
11+
} = require('./errors');
12+
13+
function validateArray(value, name) {
14+
if (!ArrayIsArray(value)) {
15+
throw new ERR_INVALID_ARG_TYPE(name, 'Array', value);
16+
}
17+
}
18+
19+
/**
20+
* @param {unknown} value
21+
* @param {string} name
22+
* @param {{
23+
* allowArray?: boolean,
24+
* allowFunction?: boolean,
25+
* nullable?: boolean
26+
* }} [options]
27+
*/
28+
function validateObject(value, name, options) {
29+
const useDefaultOptions = options == null;
30+
const allowArray = useDefaultOptions ? false : options.allowArray;
31+
const allowFunction = useDefaultOptions ? false : options.allowFunction;
32+
const nullable = useDefaultOptions ? false : options.nullable;
33+
if ((!nullable && value === null) ||
34+
(!allowArray && ArrayIsArray(value)) ||
35+
(typeof value !== 'object' && (
36+
!allowFunction || typeof value !== 'function'
37+
))) {
38+
throw new ERR_INVALID_ARG_TYPE(name, 'Object', value);
39+
}
40+
}
41+
42+
module.exports = {
43+
validateArray,
44+
validateObject,
45+
};

0 commit comments

Comments
 (0)