Skip to content

Commit

Permalink
refactor(errors): Node.js style validation/errors (#40)
Browse files Browse the repository at this point in the history
  • Loading branch information
bcoe authored Feb 4, 2022
1 parent a85e8dc commit 724df10
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 9 deletions.
22 changes: 22 additions & 0 deletions errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

class ERR_INVALID_ARG_TYPE extends TypeError {
constructor(name, expected, actual) {
super(`${name} must be ${expected} got ${actual}`);
this.code = 'ERR_INVALID_ARG_TYPE';
}
}

class ERR_NOT_IMPLEMENTED extends Error {
constructor(feature) {
super(`${feature} not implemented`);
this.code = 'ERR_NOT_IMPLEMENTED';
}
}

module.exports = {
codes: {
ERR_INVALID_ARG_TYPE,
ERR_NOT_IMPLEMENTED
}
};
26 changes: 19 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
'use strict';

const {
ArrayIsArray,
ArrayPrototypeConcat,
ArrayPrototypeIncludes,
ArrayPrototypeSlice,
ArrayPrototypePush,
ObjectHasOwn,
StringPrototypeCharAt,
StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeSlice,
StringPrototypeStartsWith,
} = require('./primordials');

const {
codes: {
ERR_NOT_IMPLEMENTED
}
} = require('./errors');

const {
validateArray,
validateObject
} = require('./validators');

function getMainArgs() {
// This function is a placeholder for proposed process.mainArgs.
// Work out where to slice process.argv for user supplied arguments.
Expand Down Expand Up @@ -75,11 +86,12 @@ const parseArgs = (
argv = getMainArgs(),
options = {}
) => {
if (typeof options !== 'object' || options === null) {
throw new Error('Whoops!');
}
if (options.withValue !== undefined && !ArrayIsArray(options.withValue)) {
throw new Error('Whoops! options.withValue should be an array.');
validateArray(argv, 'argv');
validateObject(options, 'options');
for (const key of ['withValue', 'multiples']) {
if (ObjectHasOwn(options, key)) {
validateArray(options[key], `options.${key}`);
}
}

const result = {
Expand All @@ -104,7 +116,7 @@ const parseArgs = (
} else if (
StringPrototypeCharAt(arg, 1) !== '-'
) { // Look for shortcodes: -fXzy
throw new Error('What are we doing with shortcodes!?!');
throw new ERR_NOT_IMPLEMENTED('shortcodes');
}

arg = StringPrototypeSlice(arg, 2); // remove leading --
Expand Down
18 changes: 16 additions & 2 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,23 @@ test('excess leading dashes on options are retained', function(t) {

// Test bad inputs

test('invalid argument passed for options', function(t) {
const passedArgs = ['--so=wat'];

t.throws(function() { parseArgs(passedArgs, 'bad value'); }, {
code: 'ERR_INVALID_ARG_TYPE'
});

t.end();
});

test('boolean passed to "withValue" option', function(t) {
const passedArgs = ['--so=wat'];
const passedOptions = { withValue: true };

t.throws(function() { parseArgs(passedArgs, passedOptions); });
t.throws(function() { parseArgs(passedArgs, passedOptions); }, {
code: 'ERR_INVALID_ARG_TYPE'
});

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

t.throws(function() { parseArgs(passedArgs, passedOptions); });
t.throws(function() { parseArgs(passedArgs, passedOptions); }, {
code: 'ERR_INVALID_ARG_TYPE'
});

t.end();
});
45 changes: 45 additions & 0 deletions validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict';

const {
ArrayIsArray,
} = require('./primordials');

const {
codes: {
ERR_INVALID_ARG_TYPE
}
} = require('./errors');

function validateArray(value, name) {
if (!ArrayIsArray(value)) {
throw new ERR_INVALID_ARG_TYPE(name, 'Array', value);
}
}

/**
* @param {unknown} value
* @param {string} name
* @param {{
* allowArray?: boolean,
* allowFunction?: boolean,
* nullable?: boolean
* }} [options]
*/
function validateObject(value, name, options) {
const useDefaultOptions = options == null;
const allowArray = useDefaultOptions ? false : options.allowArray;
const allowFunction = useDefaultOptions ? false : options.allowFunction;
const nullable = useDefaultOptions ? false : options.nullable;
if ((!nullable && value === null) ||
(!allowArray && ArrayIsArray(value)) ||
(typeof value !== 'object' && (
!allowFunction || typeof value !== 'function'
))) {
throw new ERR_INVALID_ARG_TYPE(name, 'Object', value);
}
}

module.exports = {
validateArray,
validateObject,
};

0 comments on commit 724df10

Please sign in to comment.