Blame view

node_modules/aproba/index.js 3.87 KB
aaac7fed   liuqimichale   add
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
  'use strict'
  
  function isArguments (thingy) {
    return thingy != null && typeof thingy === 'object' && thingy.hasOwnProperty('callee')
  }
  
  var types = {
    '*': {label: 'any', check: function () { return true }},
    A: {label: 'array', check: function (thingy) { return Array.isArray(thingy) || isArguments(thingy) }},
    S: {label: 'string', check: function (thingy) { return typeof thingy === 'string' }},
    N: {label: 'number', check: function (thingy) { return typeof thingy === 'number' }},
    F: {label: 'function', check: function (thingy) { return typeof thingy === 'function' }},
    O: {label: 'object', check: function (thingy) { return typeof thingy === 'object' && thingy != null && !types.A.check(thingy) && !types.E.check(thingy) }},
    B: {label: 'boolean', check: function (thingy) { return typeof thingy === 'boolean' }},
    E: {label: 'error', check: function (thingy) { return thingy instanceof Error }},
    Z: {label: 'null', check: function (thingy) { return thingy == null }}
  }
  
  function addSchema (schema, arity) {
    var group = arity[schema.length] = arity[schema.length] || []
    if (group.indexOf(schema) === -1) group.push(schema)
  }
  
  var validate = module.exports = function (rawSchemas, args) {
    if (arguments.length !== 2) throw wrongNumberOfArgs(['SA'], arguments.length)
    if (!rawSchemas) throw missingRequiredArg(0, 'rawSchemas')
    if (!args) throw missingRequiredArg(1, 'args')
    if (!types.S.check(rawSchemas)) throw invalidType(0, ['string'], rawSchemas)
    if (!types.A.check(args)) throw invalidType(1, ['array'], args)
    var schemas = rawSchemas.split('|')
    var arity = {}
  
    schemas.forEach(function (schema) {
      for (var ii = 0; ii < schema.length; ++ii) {
        var type = schema[ii]
        if (!types[type]) throw unknownType(ii, type)
      }
      if (/E.*E/.test(schema)) throw moreThanOneError(schema)
      addSchema(schema, arity)
      if (/E/.test(schema)) {
        addSchema(schema.replace(/E.*$/, 'E'), arity)
        addSchema(schema.replace(/E/, 'Z'), arity)
        if (schema.length === 1) addSchema('', arity)
      }
    })
    var matching = arity[args.length]
    if (!matching) {
      throw wrongNumberOfArgs(Object.keys(arity), args.length)
    }
    for (var ii = 0; ii < args.length; ++ii) {
      var newMatching = matching.filter(function (schema) {
        var type = schema[ii]
        var typeCheck = types[type].check
        return typeCheck(args[ii])
      })
      if (!newMatching.length) {
        var labels = matching.map(function (schema) {
          return types[schema[ii]].label
        }).filter(function (schema) { return schema != null })
        throw invalidType(ii, labels, args[ii])
      }
      matching = newMatching
    }
  }
  
  function missingRequiredArg (num) {
    return newException('EMISSINGARG', 'Missing required argument #' + (num + 1))
  }
  
  function unknownType (num, type) {
    return newException('EUNKNOWNTYPE', 'Unknown type ' + type + ' in argument #' + (num + 1))
  }
  
  function invalidType (num, expectedTypes, value) {
    var valueType
    Object.keys(types).forEach(function (typeCode) {
      if (types[typeCode].check(value)) valueType = types[typeCode].label
    })
    return newException('EINVALIDTYPE', 'Argument #' + (num + 1) + ': Expected ' +
      englishList(expectedTypes) + ' but got ' + valueType)
  }
  
  function englishList (list) {
    return list.join(', ').replace(/, ([^,]+)$/, ' or $1')
  }
  
  function wrongNumberOfArgs (expected, got) {
    var english = englishList(expected)
    var args = expected.every(function (ex) { return ex.length === 1 })
      ? 'argument'
      : 'arguments'
    return newException('EWRONGARGCOUNT', 'Expected ' + english + ' ' + args + ' but got ' + got)
  }
  
  function moreThanOneError (schema) {
    return newException('ETOOMANYERRORTYPES',
      'Only one error type per argument signature is allowed, more than one found in "' + schema + '"')
  }
  
  function newException (code, msg) {
    var e = new Error(msg)
    e.code = code
    if (Error.captureStackTrace) Error.captureStackTrace(e, validate)
    return e
  }