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') } |