OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-booking / node_modules / same-object / index.js

117 lines · javascript ref:
module.exports = same

const isPrimitive = require('./lib/is-primitive.js')
const sameKeys = require('./lib/same-keys.js')
const sameKeyValues = require('./lib/same-key-values.js')
const sameIterable = require('./lib/same-iterable.js')
const sameSet = require('./lib/same-set.js')
const sameMap = require('./lib/same-map.js')
const sameArray = require('./lib/same-array.js')
const sameRegExp = require('./lib/same-regexp.js')

function same (a, b, opts, memos) {
  // Short path optimization
  if (a === b) {
    if (a !== 0) return true
    return opts && opts.strict ? Object.is(a, b) : true
  }

  const aIsPrimitive = isPrimitive(a)
  const bIsPrimitive = isPrimitive(b)

  if (aIsPrimitive && bIsPrimitive) {
    if (opts && opts.strict) {
      return typeof a === 'number' ? (Number.isNaN(a) && Number.isNaN(b)) : a === b
    }
    return a == b || (Number.isNaN(a) && Number.isNaN(b)) // eslint-disable-line eqeqeq
  }

  if (aIsPrimitive !== bIsPrimitive) return false

  if (opts && opts.strict) {
    if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) return false
  }

  const aType = getType(a)
  const bType = getType(b)

  if (aType !== bType) return false

  if (!sameKeys(a, b, opts, memos)) return false

  switch (aType) {
    case 'String':
    case 'Number':
    case 'Boolean':
    case 'Date':
      return same(a.valueOf(), b.valueOf(), opts, memos)
  }

  if (aType === 'Error') {
    return sameKeyValues(a, b, opts, memos, ['name', 'message'])
  }

  switch (aType) {
    case 'Int8Array':
    case 'Uint8Array':
    case 'Uint8ClampedArray':
    case 'Int16Array':
    case 'Uint16Array':
    case 'Int32Array':
    case 'Uint32Array':
    case 'Float32Array':
    case 'Float64Array':
      return sameIterable(a, b, opts, memos)
    case 'DataView':
      return sameIterable(new Uint8Array(a.buffer, a.byteOffset, a.byteLength), new Uint8Array(b.buffer, b.byteOffset, b.byteLength), opts, memos)
    case 'ArrayBuffer':
      return sameIterable(new Uint8Array(a), new Uint8Array(b), opts, memos)
  }

  if (aType === 'RegExp') return sameRegExp(a, b)

  if (!memos) {
    memos = { a: new Map(), b: new Map(), position: 0 }
  } else {
    const memoA = memos.a.get(a)
    if (memoA !== undefined) {
      const memoB = memos.b.get(b)
      if (memoB !== undefined) return memoA === memoB
    }
    memos.position++
  }

  memos.a.set(a, memos.position)
  memos.b.set(b, memos.position)

  const equals = sameObject(a, b, opts, memos, aType)

  memos.a.delete(a)
  memos.b.delete(b)

  return equals
}

function sameObject (a, b, opts, memos, aType) {
  if (aType === 'Array' || aType === 'Arguments') {
    if (!sameArray(a, b, opts, memos)) return false
  }

  if (aType === 'Set') {
    if (!sameSet(a, b, opts, memos)) return false
  }

  if (aType === 'Map') {
    if (!sameMap(a, b, opts, memos)) return false
  }

  if (!sameKeyValues(a, b, opts, memos)) return false

  return true
}

function getType (o) {
  // Note: it returns 'Null' for null prototypes
  return Object.prototype.toString.call(o).slice(8, -1)
}