UNPKG

kitchensink

Version:

Dispatch's awesome components and style guide

295 lines (266 loc) 11.5 kB
'use strict'; var ObjectPrototype = Object.prototype; var toStr = ObjectPrototype.toString; var booleanValue = Boolean.prototype.valueOf; var has = require('has'); var isArrowFunction = require('is-arrow-function'); var isBoolean = require('is-boolean-object'); var isDate = require('is-date-object'); var isGenerator = require('is-generator-function'); var isNumber = require('is-number-object'); var isRegex = require('is-regex'); var isString = require('is-string'); var isSymbol = require('is-symbol'); var isCallable = require('is-callable'); var isProto = Object.prototype.isPrototypeOf; var foo = function foo() {}; var functionsHaveNames = foo.name === 'foo'; var symbolValue = typeof Symbol === 'function' ? Symbol.prototype.valueOf : null; var symbolIterator = require('./getSymbolIterator')(); var collectionsForEach = require('./getCollectionsForEach')(); var getPrototypeOf = Object.getPrototypeOf; if (!getPrototypeOf) { /* eslint-disable no-proto */ if (typeof 'test'.__proto__ === 'object') { getPrototypeOf = function (obj) { return obj.__proto__; }; } else { getPrototypeOf = function (obj) { var constructor = obj.constructor, oldConstructor; if (has(obj, 'constructor')) { oldConstructor = constructor; if (!(delete obj.constructor)) { // reset constructor return null; // can't delete obj.constructor, return null } constructor = obj.constructor; // get real constructor obj.constructor = oldConstructor; // restore constructor } return constructor ? constructor.prototype : ObjectPrototype; // needed for IE }; } /* eslint-enable no-proto */ } var isArray = Array.isArray || function (value) { return toStr.call(value) === '[object Array]'; }; var normalizeFnWhitespace = function normalizeFnWhitespace(fnStr) { // this is needed in IE 9, at least, which has inconsistencies here. return fnStr.replace(/^function ?\(/, 'function (').replace('){', ') {'); }; var tryMapSetEntries = function tryMapSetEntries(collection) { var foundEntries = []; try { collectionsForEach.Map.call(collection, function (key, value) { foundEntries.push([key, value]); }); } catch (notMap) { try { collectionsForEach.Set.call(collection, function (value) { foundEntries.push([value]); }); } catch (notSet) { return false; } } return foundEntries; }; module.exports = function whyNotEqual(value, other) { if (value === other) { return ''; } if (value == null || other == null) { return value === other ? '' : String(value) + ' !== ' + String(other); } var valToStr = toStr.call(value); var otherToStr = toStr.call(other); if (valToStr !== otherToStr) { return 'toStringTag is not the same: ' + valToStr + ' !== ' + otherToStr; } var valIsBool = isBoolean(value); var otherIsBool = isBoolean(other); if (valIsBool || otherIsBool) { if (!valIsBool) { return 'first argument is not a boolean; second argument is'; } if (!otherIsBool) { return 'second argument is not a boolean; first argument is'; } var valBoolVal = booleanValue.call(value); var otherBoolVal = booleanValue.call(other); if (valBoolVal === otherBoolVal) { return ''; } return 'primitive value of boolean arguments do not match: ' + valBoolVal + ' !== ' + otherBoolVal; } var valIsNumber = isNumber(value); var otherIsNumber = isNumber(value); if (valIsNumber || otherIsNumber) { if (!valIsNumber) { return 'first argument is not a number; second argument is'; } if (!otherIsNumber) { return 'second argument is not a number; first argument is'; } var valNum = Number(value); var otherNum = Number(other); if (valNum === otherNum) { return ''; } var valIsNaN = isNaN(value); var otherIsNaN = isNaN(other); if (valIsNaN && !otherIsNaN) { return 'first argument is NaN; second is not'; } else if (!valIsNaN && otherIsNaN) { return 'second argument is NaN; first is not'; } else if (valIsNaN && otherIsNaN) { return ''; } return 'numbers are different: ' + value + ' !== ' + other; } var valIsString = isString(value); var otherIsString = isString(other); if (valIsString || otherIsString) { if (!valIsString) { return 'second argument is string; first is not'; } if (!otherIsString) { return 'first argument is string; second is not'; } var stringVal = String(value); var otherVal = String(other); if (stringVal === otherVal) { return ''; } return 'string values are different: "' + stringVal + '" !== "' + otherVal + '"'; } var valIsDate = isDate(value); var otherIsDate = isDate(other); if (valIsDate || otherIsDate) { if (!valIsDate) { return 'second argument is Date, first is not'; } if (!otherIsDate) { return 'first argument is Date, second is not'; } var valTime = +value; var otherTime = +other; if (valTime === otherTime) { return ''; } return 'Dates have different time values: ' + valTime + ' !== ' + otherTime; } var valIsRegex = isRegex(value); var otherIsRegex = isRegex(other); if (valIsRegex || otherIsRegex) { if (!valIsRegex) { return 'second argument is RegExp, first is not'; } if (!otherIsRegex) { return 'first argument is RegExp, second is not'; } var regexStringVal = String(value); var regexStringOther = String(other); if (regexStringVal === regexStringOther) { return ''; } return 'regular expressions differ: ' + regexStringVal + ' !== ' + regexStringOther; } var valIsArray = isArray(value); var otherIsArray = isArray(other); if (valIsArray || otherIsArray) { if (!valIsArray) { return 'second argument is an Array, first is not'; } if (!otherIsArray) { return 'first argument is an Array, second is not'; } if (value.length !== other.length) { return 'arrays have different length: ' + value.length + ' !== ' + other.length; } if (String(value) !== String(other)) { return 'stringified Arrays differ'; } var index = value.length - 1; var equal = ''; var valHasIndex, otherHasIndex; while (equal === '' && index >= 0) { valHasIndex = has(value, index); otherHasIndex = has(other, index); if (!valHasIndex && otherHasIndex) { return 'second argument has index ' + index + '; first does not'; } if (valHasIndex && !otherHasIndex) { return 'first argument has index ' + index + '; second does not'; } equal = whyNotEqual(value[index], other[index]); index -= 1; } return equal; } var valueIsSym = isSymbol(value); var otherIsSym = isSymbol(other); if (valueIsSym !== otherIsSym) { if (valueIsSym) { return 'first argument is Symbol; second is not'; } return 'second argument is Symbol; first is not'; } if (valueIsSym && otherIsSym) { return symbolValue.call(value) === symbolValue.call(other) ? '' : 'first Symbol value !== second Symbol value'; } var valueIsGen = isGenerator(value); var otherIsGen = isGenerator(other); if (valueIsGen !== otherIsGen) { if (valueIsGen) { return 'first argument is a Generator; second is not'; } return 'second argument is a Generator; first is not'; } var valueIsArrow = isArrowFunction(value); var otherIsArrow = isArrowFunction(other); if (valueIsArrow !== otherIsArrow) { if (valueIsArrow) { return 'first argument is an Arrow function; second is not'; } return 'second argument is an Arrow function; first is not'; } if (isCallable(value) || isCallable(other)) { if (functionsHaveNames && whyNotEqual(value.name, other.name) !== '') { return 'Function names differ: "' + value.name + '" !== "' + other.name + '"'; } if (whyNotEqual(value.length, other.length) !== '') { return 'Function lengths differ: ' + value.length + ' !== ' + other.length; } var valueStr = normalizeFnWhitespace(String(value)); var otherStr = normalizeFnWhitespace(String(other)); if (whyNotEqual(valueStr, otherStr) === '') { return ''; } if (!valueIsGen && !valueIsArrow) { return whyNotEqual(valueStr.replace(/\)\s*\{/, '){'), otherStr.replace(/\)\s*\{/, '){')) === '' ? '' : 'Function string representations differ'; } return whyNotEqual(valueStr, otherStr) === '' ? '' : 'Function string representations differ'; } if (typeof value === 'object' || typeof other === 'object') { if (typeof value !== typeof other) { return 'arguments have a different typeof: ' + typeof value + ' !== ' + typeof other; } if (isProto.call(value, other)) { return 'first argument is the [[Prototype]] of the second'; } if (isProto.call(other, value)) { return 'second argument is the [[Prototype]] of the first'; } if (getPrototypeOf(value) !== getPrototypeOf(other)) { return 'arguments have a different [[Prototype]]'; } if (symbolIterator) { var valueIteratorFn = value[symbolIterator]; var valueIsIterable = isCallable(valueIteratorFn); var otherIteratorFn = other[symbolIterator]; var otherIsIterable = isCallable(otherIteratorFn); if (valueIsIterable !== otherIsIterable) { if (valueIsIterable) { return 'first argument is iterable; second is not'; } return 'second argument is iterable; first is not'; } if (valueIsIterable && otherIsIterable) { var valueIterator = valueIteratorFn.call(value); var otherIterator = otherIteratorFn.call(other); var valueNext, otherNext, nextWhy; do { valueNext = valueIterator.next(); otherNext = otherIterator.next(); if (!valueNext.done && !otherNext.done) { nextWhy = whyNotEqual(valueNext, otherNext); if (nextWhy !== '') { return 'iteration results are not equal: ' + nextWhy; } } } while (!valueNext.done && !otherNext.done); if (valueNext.done && !otherNext.done) { return 'first argument finished iterating before second'; } if (!valueNext.done && otherNext.done) { return 'second argument finished iterating before first'; } return ''; } } else if (collectionsForEach.Map || collectionsForEach.Set) { var valueEntries = tryMapSetEntries(value); var otherEntries = tryMapSetEntries(other); var valueEntriesIsArray = isArray(valueEntries); var otherEntriesIsArray = isArray(otherEntries); if (valueEntriesIsArray && !otherEntriesIsArray) { return 'first argument has Collection entries, second does not'; } if (!valueEntriesIsArray && otherEntriesIsArray) { return 'second argument has Collection entries, first does not'; } if (valueEntriesIsArray && otherEntriesIsArray) { var entriesWhy = whyNotEqual(valueEntries, otherEntries); return entriesWhy === '' ? '' : 'Collection entries differ: ' + entriesWhy; } } var key, valueKeyIsRecursive, otherKeyIsRecursive, keyWhy; for (key in value) { if (has(value, key)) { if (!has(other, key)) { return 'first argument has key "' + key + '"; second does not'; } valueKeyIsRecursive = !!value[key] && value[key][key] === value; otherKeyIsRecursive = !!other[key] && other[key][key] === other; if (valueKeyIsRecursive !== otherKeyIsRecursive) { if (valueKeyIsRecursive) { return 'first argument has a circular reference at key "' + key + '"; second does not'; } return 'second argument has a circular reference at key "' + key + '"; first does not'; } if (!valueKeyIsRecursive && !otherKeyIsRecursive) { keyWhy = whyNotEqual(value[key], other[key]); if (keyWhy !== '') { return 'value at key "' + key + '" differs: ' + keyWhy; } } } } for (key in other) { if (has(other, key) && !has(value, key)) { return 'second argument has key "' + key + '"; first does not'; } } return ''; } return false; };