Code coverage report for index.js

# all files / __root__/ index.js

100% Statements 95/95
100% Branches 85/85
100% Functions 4/4
100% Lines 87/87
1 statement, 1 branch Ignored

    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
    161
    162
    163
    164
    165
    166

    1×
    158×

    1×

    1×
    59×

    1×

    1×
    133×
    133×
    41×
    41×

    92×
    1×
    1×

    91×
    5×
    5×

    86×
    3×
    3×

    83×
    2×
    2×

    81×
    1×
    1×

    80×
    2×
    2×
    2×

    78×
    1×
    1×

    77×
    1×
    1×

    76×
    4×
    4×

    72×
    1×
    1×

    71×
    1×
    1×

    70×
    1×
    1×

    69×
    1×
    1×

    68×
    2×
    2×

    66×
    10×
    10×

    56×
    4×
    4×

    52×
    6×
    6×
    3×

    3×

    3×

    1×

    46×
    46×
    46×
    46×

    46×

    44×
    44×
    44×
    43×
    43×

    43×
    43×
    72×
    72×
    72×

    34×
    34×

    34×
    34×

    1×

     'use strict'

    function isArguments (obj) {
      return Object.prototype.toString.call(obj) === '[object Arguments]'
    }

    module.exports = match

    function match (obj, pattern) {
      return match_(obj, pattern, [], [])
    }

    /**
    1. If the object is a string, and the pattern is a RegExp, then return
       true if `pattern.test(object)`.
    2. Use loose equality (`==`) only for all other value types
       (non-objects).  `tmatch` cares more about shape and contents than
       type. This step will also catch functions, with the useful
       (default) property that only references to the same function are
       considered equal.  'Ware the halting problem!
    3. `null` *is* an object – a singleton value object, in fact – so if
       either is `null`, return object == pattern.
    4. Since the only way to make it this far is for `object` or `pattern`
       to be an object, if `object` or `pattern` is *not* an object,
       they're clearly not a match.
    5. It's much faster to compare dates by numeric value (`.getTime()`)
       than by lexical value.
    6. Compare RegExps by their components, not the objects themselves.
    7. The parts of an arguments list most people care about are the
       arguments themselves, not the callee, which you shouldn't be
       looking at anyway.
    8. Objects are more complex:
       1. Return `true` if `object` and `pattern` both have no properties.
       2. Ensure that cyclical references don't blow up the stack.
       3. Ensure that all the key names in `pattern` exist in `object`.
       4. Ensure that all of the associated values match, recursively.
    */

    /* istanbul ignore next */
    var log = (/\btmatch\b/.test(process.env.NODE_DEBUG || '')) ?
      console.error : function () {}

    function match_ (obj, pattern, ca, cb) {
      log('TMATCH', typeof obj, pattern)
      if (obj == pattern) {
        log('TMATCH same object or simple value, true')
        return true

      } else if (obj === null || pattern === null) {
        log('TMATCH null test, already failed ==')
        return false

      } else if (typeof obj === 'string' && pattern instanceof RegExp) {
        log('TMATCH string~=regexp test')
        return pattern.test(obj)

      } else if (typeof obj === 'string' && typeof pattern === 'string' && pattern) {
        log('TMATCH string~=string test')
        return obj.indexOf(pattern) !== -1

      } else if (obj instanceof Date && pattern instanceof Date) {
        log('TMATCH date test')
        return obj.getTime() === pattern.getTime()

      } else if (obj instanceof Date && typeof pattern === 'string') {
        log('TMATCH date~=string test')
        return obj.getTime() === new Date(pattern).getTime()

      } else if (isArguments(obj) || isArguments(pattern)) {
        log('TMATCH arguments test')
        var slice = Array.prototype.slice
        return match_(slice.call(obj), slice.call(pattern), ca, cb)

      } else if (pattern === Buffer) {
        log('TMATCH Buffer ctor')
        return Buffer.isBuffer(obj)

      } else if (pattern === Function) {
        log('TMATCH Function ctor')
        return typeof obj === 'function'

      } else if (pattern === Number) {
        log('TMATCH Number ctor (finite, not NaN)')
        return typeof obj === 'number' && obj === obj && isFinite(obj)

      } else if (pattern !== pattern) {
        log('TMATCH NaN')
        return obj !== obj

      } else if (pattern === String) {
        log('TMATCH String ctor')
        return typeof obj === 'string'

      } else if (pattern === Boolean) {
        log('TMATCH Boolean ctor')
        return typeof obj === 'boolean'

      } else if (pattern === Array) {
        log('TMATCH Array ctor', pattern, Array.isArray(obj))
        return Array.isArray(obj)

      } else if (typeof pattern === 'function' && typeof obj === 'object') {
        log('TMATCH object~=function')
        return obj instanceof pattern

      } else if (typeof obj !== 'object' || typeof pattern !== 'object') {
        log('TMATCH obj is not object, pattern is not object, false')
        return false

      } else if (obj instanceof RegExp && pattern instanceof RegExp) {
        log('TMATCH regexp~=regexp test')
        return obj.source === pattern.source &&
          obj.global === pattern.global &&
          obj.multiline === pattern.multiline &&
          obj.lastIndex === pattern.lastIndex &&
          obj.ignoreCase === pattern.ignoreCase

      } else if (Buffer.isBuffer(obj) && Buffer.isBuffer(pattern)) {
        log('TMATCH buffer test')
        if (obj.equals) {
          return obj.equals(pattern)
        } else {
          if (obj.length !== pattern.length) return false

          for (var j = 0; j < obj.length; j++) if (obj[j] != pattern[j]) return false

          return true
        }

      } else {
        // both are objects.  interesting case!
        log('TMATCH object~=object test')
        var kobj = Object.keys(obj)
        var kpat = Object.keys(pattern)
        log('  TMATCH patternkeys=%j objkeys=%j', kpat, kobj)

        // don't bother with stack acrobatics if there's nothing there
        if (kobj.length === 0 && kpat.length === 0) return true

        // if we've seen this exact pattern and object already, then
        // it means that pattern and obj have matching cyclicalness
        // however, non-cyclical patterns can match cyclical objects
        log('  TMATCH check seen objects...')
        var cal = ca.length
        while (cal--) if (ca[cal] === obj && cb[cal] === pattern) return true
        ca.push(obj); cb.push(pattern)
        log('  TMATCH not seen previously')

        var key
        for (var l = kpat.length - 1; l >= 0; l--) {
          key = kpat[l]
          log('  TMATCH test obj[%j]', key, obj[key], pattern[key])
          if (!match_(obj[key], pattern[key], ca, cb)) return false
        }

        ca.pop()
        cb.pop()

        log('  TMATCH object pass')
        return true
      }

      /* istanbul ignore next */
      throw new Error('impossible to reach this point')
    }