• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2012, Mark Cavage. All rights reserved.
2// Copyright 2015 Joyent, Inc.
3
4var assert = require('assert');
5var Stream = require('stream').Stream;
6var util = require('util');
7
8
9///--- Globals
10
11/* JSSTYLED */
12var UUID_REGEXP = /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/;
13
14
15///--- Internal
16
17function _capitalize(str) {
18    return (str.charAt(0).toUpperCase() + str.slice(1));
19}
20
21function _toss(name, expected, oper, arg, actual) {
22    throw new assert.AssertionError({
23        message: util.format('%s (%s) is required', name, expected),
24        actual: (actual === undefined) ? typeof (arg) : actual(arg),
25        expected: expected,
26        operator: oper || '===',
27        stackStartFunction: _toss.caller
28    });
29}
30
31function _getClass(arg) {
32    return (Object.prototype.toString.call(arg).slice(8, -1));
33}
34
35function noop() {
36    // Why even bother with asserts?
37}
38
39
40///--- Exports
41
42var types = {
43    bool: {
44        check: function (arg) { return typeof (arg) === 'boolean'; }
45    },
46    func: {
47        check: function (arg) { return typeof (arg) === 'function'; }
48    },
49    string: {
50        check: function (arg) { return typeof (arg) === 'string'; }
51    },
52    object: {
53        check: function (arg) {
54            return typeof (arg) === 'object' && arg !== null;
55        }
56    },
57    number: {
58        check: function (arg) {
59            return typeof (arg) === 'number' && !isNaN(arg);
60        }
61    },
62    finite: {
63        check: function (arg) {
64            return typeof (arg) === 'number' && !isNaN(arg) && isFinite(arg);
65        }
66    },
67    buffer: {
68        check: function (arg) { return Buffer.isBuffer(arg); },
69        operator: 'Buffer.isBuffer'
70    },
71    array: {
72        check: function (arg) { return Array.isArray(arg); },
73        operator: 'Array.isArray'
74    },
75    stream: {
76        check: function (arg) { return arg instanceof Stream; },
77        operator: 'instanceof',
78        actual: _getClass
79    },
80    date: {
81        check: function (arg) { return arg instanceof Date; },
82        operator: 'instanceof',
83        actual: _getClass
84    },
85    regexp: {
86        check: function (arg) { return arg instanceof RegExp; },
87        operator: 'instanceof',
88        actual: _getClass
89    },
90    uuid: {
91        check: function (arg) {
92            return typeof (arg) === 'string' && UUID_REGEXP.test(arg);
93        },
94        operator: 'isUUID'
95    }
96};
97
98function _setExports(ndebug) {
99    var keys = Object.keys(types);
100    var out;
101
102    /* re-export standard assert */
103    if (process.env.NODE_NDEBUG) {
104        out = noop;
105    } else {
106        out = function (arg, msg) {
107            if (!arg) {
108                _toss(msg, 'true', arg);
109            }
110        };
111    }
112
113    /* standard checks */
114    keys.forEach(function (k) {
115        if (ndebug) {
116            out[k] = noop;
117            return;
118        }
119        var type = types[k];
120        out[k] = function (arg, msg) {
121            if (!type.check(arg)) {
122                _toss(msg, k, type.operator, arg, type.actual);
123            }
124        };
125    });
126
127    /* optional checks */
128    keys.forEach(function (k) {
129        var name = 'optional' + _capitalize(k);
130        if (ndebug) {
131            out[name] = noop;
132            return;
133        }
134        var type = types[k];
135        out[name] = function (arg, msg) {
136            if (arg === undefined || arg === null) {
137                return;
138            }
139            if (!type.check(arg)) {
140                _toss(msg, k, type.operator, arg, type.actual);
141            }
142        };
143    });
144
145    /* arrayOf checks */
146    keys.forEach(function (k) {
147        var name = 'arrayOf' + _capitalize(k);
148        if (ndebug) {
149            out[name] = noop;
150            return;
151        }
152        var type = types[k];
153        var expected = '[' + k + ']';
154        out[name] = function (arg, msg) {
155            if (!Array.isArray(arg)) {
156                _toss(msg, expected, type.operator, arg, type.actual);
157            }
158            var i;
159            for (i = 0; i < arg.length; i++) {
160                if (!type.check(arg[i])) {
161                    _toss(msg, expected, type.operator, arg, type.actual);
162                }
163            }
164        };
165    });
166
167    /* optionalArrayOf checks */
168    keys.forEach(function (k) {
169        var name = 'optionalArrayOf' + _capitalize(k);
170        if (ndebug) {
171            out[name] = noop;
172            return;
173        }
174        var type = types[k];
175        var expected = '[' + k + ']';
176        out[name] = function (arg, msg) {
177            if (arg === undefined || arg === null) {
178                return;
179            }
180            if (!Array.isArray(arg)) {
181                _toss(msg, expected, type.operator, arg, type.actual);
182            }
183            var i;
184            for (i = 0; i < arg.length; i++) {
185                if (!type.check(arg[i])) {
186                    _toss(msg, expected, type.operator, arg, type.actual);
187                }
188            }
189        };
190    });
191
192    /* re-export built-in assertions */
193    Object.keys(assert).forEach(function (k) {
194        if (k === 'AssertionError') {
195            out[k] = assert[k];
196            return;
197        }
198        if (ndebug) {
199            out[k] = noop;
200            return;
201        }
202        out[k] = assert[k];
203    });
204
205    /* export ourselves (for unit tests _only_) */
206    out._setExports = _setExports;
207
208    return out;
209}
210
211module.exports = _setExports(process.env.NODE_NDEBUG);
212