Skip to content

Commit a607061

Browse files
committed
fix: console.warn() rather than throwing errors when api signatures are incorrect (#804)
1 parent d78a0f5 commit a607061

File tree

3 files changed

+151
-95
lines changed

3 files changed

+151
-95
lines changed

lib/argsert.js

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
11
const command = require('./command')()
2+
const YError = require('./yerror')
23

34
const positionName = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth']
45

56
module.exports = function (expected, callerArguments, length) {
6-
// preface the argument description with "cmd", so
7-
// that we can run it through yargs' command parser.
8-
var position = 0
9-
var parsed = {demanded: [], optional: []}
10-
if (typeof expected === 'object') {
11-
length = callerArguments
12-
callerArguments = expected
13-
} else {
14-
parsed = command.parseCommand('cmd ' + expected)
15-
}
16-
const args = [].slice.call(callerArguments)
7+
// TODO: should this eventually raise an exception.
8+
try {
9+
// preface the argument description with "cmd", so
10+
// that we can run it through yargs' command parser.
11+
var position = 0
12+
var parsed = {demanded: [], optional: []}
13+
if (typeof expected === 'object') {
14+
length = callerArguments
15+
callerArguments = expected
16+
} else {
17+
parsed = command.parseCommand('cmd ' + expected)
18+
}
19+
const args = [].slice.call(callerArguments)
1720

18-
while (args.length && args[args.length - 1] === undefined) args.pop()
19-
length = length || args.length
21+
while (args.length && args[args.length - 1] === undefined) args.pop()
22+
length = length || args.length
2023

21-
if (length < parsed.demanded.length) {
22-
throw Error('Not enough arguments provided. Expected ' + parsed.demanded.length +
23-
' but received ' + args.length + '.')
24-
}
24+
if (length < parsed.demanded.length) {
25+
throw new YError('Not enough arguments provided. Expected ' + parsed.demanded.length +
26+
' but received ' + args.length + '.')
27+
}
2528

26-
const totalCommands = parsed.demanded.length + parsed.optional.length
27-
if (length > totalCommands) {
28-
throw Error('Too many arguments provided. Expected max ' + totalCommands +
29-
' but received ' + length + '.')
30-
}
29+
const totalCommands = parsed.demanded.length + parsed.optional.length
30+
if (length > totalCommands) {
31+
throw new YError('Too many arguments provided. Expected max ' + totalCommands +
32+
' but received ' + length + '.')
33+
}
3134

32-
parsed.demanded.forEach(function (demanded) {
33-
const arg = args.shift()
34-
const observedType = guessType(arg)
35-
const matchingTypes = demanded.cmd.filter(function (type) {
36-
return type === observedType || type === '*'
35+
parsed.demanded.forEach(function (demanded) {
36+
const arg = args.shift()
37+
const observedType = guessType(arg)
38+
const matchingTypes = demanded.cmd.filter(function (type) {
39+
return type === observedType || type === '*'
40+
})
41+
if (matchingTypes.length === 0) argumentTypeError(observedType, demanded.cmd, position, false)
42+
position += 1
3743
})
38-
if (matchingTypes.length === 0) argumentTypeError(observedType, demanded.cmd, position, false)
39-
position += 1
40-
})
4144

42-
parsed.optional.forEach(function (optional) {
43-
if (args.length === 0) return
44-
const arg = args.shift()
45-
const observedType = guessType(arg)
46-
const matchingTypes = optional.cmd.filter(function (type) {
47-
return type === observedType || type === '*'
45+
parsed.optional.forEach(function (optional) {
46+
if (args.length === 0) return
47+
const arg = args.shift()
48+
const observedType = guessType(arg)
49+
const matchingTypes = optional.cmd.filter(function (type) {
50+
return type === observedType || type === '*'
51+
})
52+
if (matchingTypes.length === 0) argumentTypeError(observedType, optional.cmd, position, true)
53+
position += 1
4854
})
49-
if (matchingTypes.length === 0) argumentTypeError(observedType, optional.cmd, position, true)
50-
position += 1
51-
})
55+
} catch (err) {
56+
console.warn(err.stack)
57+
}
5258
}
5359

5460
function guessType (arg) {
@@ -61,6 +67,6 @@ function guessType (arg) {
6167
}
6268

6369
function argumentTypeError (observedType, allowedTypes, position, optional) {
64-
throw Error('Invalid ' + (positionName[position] || 'manyith') + ' argument.' +
70+
throw new YError('Invalid ' + (positionName[position] || 'manyith') + ' argument.' +
6571
' Expected ' + allowedTypes.join(' or ') + ' but received ' + observedType + '.')
6672
}

test/argsert.js

Lines changed: 100 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,143 @@
11
/* global describe, it */
22

33
const argsert = require('../lib/argsert')
4-
const expect = require('chai').expect
4+
const checkOutput = require('./helpers/utils').checkOutput
55

66
require('chai').should()
77

88
describe('Argsert', function () {
9-
it('does not throw exception if optional argument is not provided', function () {
10-
argsert('[object]', [].slice.call(arguments))
9+
it('does not warn if optional argument is not provided', function () {
10+
var o = checkOutput(function () {
11+
argsert('[object]', [].slice.call(arguments))
12+
})
13+
14+
o.warnings.length.should.equal(0)
1115
})
1216

13-
it('throws exception if wrong type is provided for optional argument', function () {
14-
function foo (opts) {
15-
argsert('[object|number]', [].slice.call(arguments))
16-
}
17-
expect(function () {
17+
it('warn if wrong type is provided for optional argument', function () {
18+
var o = checkOutput(function () {
19+
function foo (opts) {
20+
argsert('[object|number]', [].slice.call(arguments))
21+
}
22+
1823
foo('hello')
19-
}).to.throw(/Invalid first argument. Expected object or number but received string./)
24+
})
25+
26+
o.warnings[0].should.match(/Invalid first argument. Expected object or number but received string./)
2027
})
2128

22-
it('does not throw exception if optional argument is valid', function () {
23-
function foo (opts) {
24-
argsert('[object]', [].slice.call(arguments))
25-
}
26-
foo({foo: 'bar'})
29+
it('does not warn if optional argument is valid', function () {
30+
var o = checkOutput(function () {
31+
function foo (opts) {
32+
argsert('[object]', [].slice.call(arguments))
33+
}
34+
35+
foo({foo: 'bar'})
36+
})
37+
38+
o.warnings.length.should.equal(0)
2739
})
2840

29-
it('throws exception if required argument is not provided', function () {
30-
expect(function () {
41+
it('warns if required argument is not provided', function () {
42+
var o = checkOutput(function () {
3143
argsert('<object>', [].slice.call(arguments))
32-
}).to.throw(/Not enough arguments provided. Expected 1 but received 0./)
44+
})
45+
46+
o.warnings[0].should.match(/Not enough arguments provided. Expected 1 but received 0./)
3347
})
3448

35-
it('throws exception if required argument is of wrong type', function () {
36-
function foo (opts) {
37-
argsert('<object>', [].slice.call(arguments))
38-
}
39-
expect(function () {
49+
it('warns if required argument is of wrong type', function () {
50+
var o = checkOutput(function () {
51+
function foo (opts) {
52+
argsert('<object>', [].slice.call(arguments))
53+
}
54+
4055
foo('bar')
41-
}).to.throw(/Invalid first argument. Expected object but received string./)
56+
})
57+
58+
o.warnings[0].should.match(/Invalid first argument. Expected object but received string./)
4259
})
4360

4461
it('supports a combination of required and optional arguments', function () {
45-
function foo (opts) {
46-
argsert('<array> <string|object> [string|object]', [].slice.call(arguments))
47-
}
48-
foo([], 'foo', {})
62+
var o = checkOutput(function () {
63+
function foo (opts) {
64+
argsert('<array> <string|object> [string|object]', [].slice.call(arguments))
65+
}
66+
67+
foo([], 'foo', {})
68+
})
69+
70+
o.warnings.length.should.equal(0)
4971
})
5072

51-
it('throws an exception if too many arguments are provided', function () {
52-
function foo (expected) {
53-
argsert('<array> [batman]', [].slice.call(arguments))
54-
}
55-
expect(function () {
73+
it('warns if too many arguments are provided', function () {
74+
var o = checkOutput(function () {
75+
function foo (expected) {
76+
argsert('<array> [batman]', [].slice.call(arguments))
77+
}
78+
5679
foo([], 33, 99)
57-
}).to.throw(/Too many arguments provided. Expected max 2 but received 3./)
80+
})
81+
82+
o.warnings[0].should.match(/Too many arguments provided. Expected max 2 but received 3./)
5883
})
5984

6085
it('configures function to accept 0 parameters, if only arguments object is provided', function () {
61-
function foo (expected) {
62-
argsert([].slice.call(arguments))
63-
}
64-
expect(function () {
86+
var o = checkOutput(function () {
87+
function foo (expected) {
88+
argsert([].slice.call(arguments))
89+
}
90+
6591
foo(99)
66-
}).to.throw(/Too many arguments provided. Expected max 0 but received 1./)
92+
})
93+
94+
o.warnings[0].should.match(/Too many arguments provided. Expected max 0 but received 1./)
6795
})
6896

6997
it('allows for any type if * is provided', function () {
70-
function foo (opts) {
71-
argsert('<*>', [].slice.call(arguments))
72-
}
73-
foo('bar')
98+
var o = checkOutput(function () {
99+
function foo (opts) {
100+
argsert('<*>', [].slice.call(arguments))
101+
}
102+
103+
foo('bar')
104+
})
105+
106+
o.warnings.length.should.equal(0)
74107
})
75108

76109
it('should ignore trailing undefined values', function () {
77-
function foo (opts) {
78-
argsert('<*>', [].slice.call(arguments))
79-
}
80-
foo('bar', undefined, undefined)
110+
var o = checkOutput(function () {
111+
function foo (opts) {
112+
argsert('<*>', [].slice.call(arguments))
113+
}
114+
115+
foo('bar', undefined, undefined)
116+
})
117+
118+
o.warnings.length.should.equal(0)
81119
})
82120

83121
it('should not ignore undefined values that are not trailing', function () {
84-
function foo (opts) {
85-
argsert('<*>', [].slice.call(arguments))
86-
}
87-
expect(function () {
122+
var o = checkOutput(function () {
123+
function foo (opts) {
124+
argsert('<*>', [].slice.call(arguments))
125+
}
126+
88127
foo('bar', undefined, undefined, 33)
89-
}).to.throw(/Too many arguments provided. Expected max 1 but received 4./)
128+
})
129+
130+
o.warnings[0].should.match(/Too many arguments provided. Expected max 1 but received 4./)
90131
})
91132

92133
it('supports null as special type', function () {
93-
function foo (arg) {
94-
argsert('<null>', [].slice.call(arguments))
95-
}
96-
foo(null)
134+
var o = checkOutput(function () {
135+
function foo (arg) {
136+
argsert('<null>', [].slice.call(arguments))
137+
}
138+
foo(null)
139+
})
140+
141+
o.warnings.length.should.equal(0)
97142
})
98143
})

test/helpers/utils.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@ exports.checkOutput = function (f, argv, cb) {
1010
var _argv = process.argv
1111
var _error = console.error
1212
var _log = console.log
13+
var _warn = console.warn
1314

1415
process.exit = function () { exit = true }
1516
process.env = Hash.merge(process.env, { _: 'node' })
1617
process.argv = argv || [ './usage' ]
1718

1819
var errors = []
1920
var logs = []
21+
var warnings = []
2022

2123
console.error = function (msg) { errors.push(msg) }
2224
console.log = function (msg) { logs.push(msg) }
25+
console.warn = function (msg) { warnings.push(msg) }
2326

2427
var result
2528

@@ -57,6 +60,7 @@ exports.checkOutput = function (f, argv, cb) {
5760

5861
console.error = _error
5962
console.log = _log
63+
console.warn = _warn
6064
}
6165

6266
function done () {
@@ -65,6 +69,7 @@ exports.checkOutput = function (f, argv, cb) {
6569
return {
6670
errors: errors,
6771
logs: logs,
72+
warnings: warnings,
6873
exit: exit,
6974
result: result
7075
}

0 commit comments

Comments
 (0)