-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
160 lines (141 loc) · 3.33 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
* Type
*/
try {
var type = require('type');
} catch (e) {
var type = require('component-type');
}
/**
* Export `invalid`
*/
module.exports = invalid;
/**
* Initialize `invalid`
*
* @param {Mixed} obj
* @param {Mixed} schema
* @return {Boolean|TypeError}
* @api public
*/
function invalid(obj, schema) {
return 1 == arguments.length
? function (o) { return check(valid(o, obj)); }
: check(valid(obj, schema));
// pass the errors through
function check(errs) {
return errs.length
? new TypeError(errs.join(', '))
: false
}
}
/**
* Cast the string
*/
var cast = {
'undefined': undefined,
'function': Function,
'boolean': Boolean,
'number': Number,
'string': String,
'regexp': RegExp,
'object': Object,
'array': Array,
'date': Date,
'null': null,
'nan': NaN
};
/**
* Get the type
*
* @param {Mixed} val
* @return {String}
*/
function typecheck(val) {
switch(val) {
case undefined: return 'undefined';
case Function: return 'function';
case Boolean: return 'boolean';
case Number: return 'number';
case String: return 'string';
case RegExp: return 'regexp';
case Object: return 'object';
case Array: return 'Array';
case Date: return 'date';
case null: return 'null';
case NaN: return 'nan';
default: return type(val);
}
}
/**
* Validate `actual` against `expected`
*
* @param {Mixed} actual
* @param {Mixed} expected
* @param {String} key (private)
* @return {Array} errors
* @api public
*/
function valid(actual, expected, key) {
key = key || '';
var et = typecheck(expected);
var t = typecheck(actual);
var errs = [];
if ('object' == et && t == et) {
for (var k in expected)
errs = errs.concat(valid(actual[k], expected[k], key ? key + '.' + k : k));
} else if ('array' == et && t == et) {
for (var i = 0, v; v = actual[i]; i++)
errs = errs.concat(valid(v, expected[0], key ? key + '[' + i + ']': i));
} else if ('function' == et && !actual.prototype) {
!expected(actual, t) && errs.push(error(t, key, actual, expected));
} else if ('regexp' == et && expected instanceof RegExp) {
!expected.test(actual) && errs.push(error(t, key, actual, expected));
} else if (cast[t] != expected) {
errs.push(error(t, key, actual, expected));
}
return errs;
}
/**
* Format an error
*
* @param {String} type
* @param {String} key (optional)
* @param {Mixed} actual
* @param {Mixed} expected
* @return {String}
* @api private
*/
function error(type, key, actual, expected) {
var msg = key ? key + ': ' : '';
if (expected instanceof RegExp) {
msg += fmt(type, actual) + ' does not match regexp ' + expected;
} else if ('function' == type && !actual.prototype) {
msg += 'function(' + actual + ') returned false';
} else {
msg += fmt(type, actual) + ' is not a ' + typecheck(expected);
}
return msg;
}
/**
* Format based on type
*
* @param {String} type
* @param {Mixed} actual
* @return {String}
* @api private
*/
function fmt(type, actual) {
switch(type) {
case 'string': return actual = '"' + actual + '"';
case 'object': return JSON.stringify(actual);
case 'undefined':
case 'number':
case 'null':
return actual;
case 'function':
return actual.toString().replace(/\{[^\}]+/, '{ ... ');
default:
return actual.toString();
}
}