diff --git a/getCollectionsForEach.js b/getCollectionsForEach.js index 23b378b..3f7a93c 100644 --- a/getCollectionsForEach.js +++ b/getCollectionsForEach.js @@ -1,5 +1,7 @@ 'use strict'; +// TODO: delete in next semver-major + module.exports = function getCollectionsforEach() { var mapForEach = (function () { if (typeof Map !== 'function') { return null; } diff --git a/getSymbolIterator.js b/getSymbolIterator.js index 1aa78b3..7c20166 100644 --- a/getSymbolIterator.js +++ b/getSymbolIterator.js @@ -1,5 +1,7 @@ 'use strict'; +// TODO: delete in next semver-major + var isSymbol = require('is-symbol'); module.exports = function getSymbolIterator() { diff --git a/package.json b/package.json index 8a59310..95a38f6 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "pretest": "npm run --silent lint", "test": "npm run --silent tests-only", "posttest": "npx aud", - "tests-only": "npm run --silent test:native && npm run --silent test:why && npm run --silent test:shimmed && npm run --silent test:corejs", + "tests-only": "npm run test:native && npm run test:why && npm run test:shimmed && npm run test:corejs", "test:native": "node test/native", "test:why": "node test/why", "test:shimmed": "node test/shimmed", @@ -40,6 +40,8 @@ "equality" ], "dependencies": { + "es-get-iterator": "^1.0.1", + "functions-have-names": "^1.2.0", "has": "^1.0.3", "is-arrow-function": "^2.0.3", "is-bigint": "^1.0.0", @@ -51,7 +53,11 @@ "is-regex": "^1.0.4", "is-string": "^1.0.4", "is-symbol": "^1.0.3", - "object.entries": "^1.1.0" + "isarray": "^2.0.5", + "object-inspect": "^1.7.0", + "object.entries": "^1.1.0", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.0" }, "devDependencies": { "@ljharb/eslint-config": "^15.0.2", @@ -61,7 +67,6 @@ "es6-shim": "^0.35.5", "eslint": "^6.7.1", "foreach": "^2.0.5", - "functions-have-names": "^1.2.0", "has-bigints": "^1.0.0", "has-symbols": "^1.0.1", "make-arrow-function": "^1.1.0", diff --git a/test/why.js b/test/why.js index ce92318..3c11541 100644 --- a/test/why.js +++ b/test/why.js @@ -499,22 +499,22 @@ test('iterables', function (t) { ); mt.equal( isEqualWhy(a, c), - 'second argument finished iterating before first', + 'second Map argument finished iterating before first Map', 'unequal Maps (a, c) are not equal' ); mt.equal( isEqualWhy(b, c), - 'second argument finished iterating before first', + 'second Map argument finished iterating before first Map', 'unequal Maps (b, c) are not equal' ); mt.equal( isEqualWhy(c, a), - 'first argument finished iterating before second', + 'first Map argument finished iterating before second Map', 'unequal Maps (c, a) are not equal' ); mt.equal( isEqualWhy(c, b), - 'first argument finished iterating before second', + 'first Map argument finished iterating before second Map', 'unequal Maps (c, b) are not equal' ); @@ -535,30 +535,22 @@ test('iterables', function (t) { st.equal('', isEqualWhy(b, a), 'equal Set (b, a) are equal'); st.equal( isEqualWhy(a, c), - symbolIterator - ? 'second argument finished iterating before first' - : 'Collection entries differ: arrays have different length: 2 !== 1', + 'second Set argument finished iterating before first Set', 'unequal Set (a, c) are not equal' ); st.equal( isEqualWhy(b, c), - symbolIterator - ? 'second argument finished iterating before first' - : 'Collection entries differ: arrays have different length: 2 !== 1', + 'second Set argument finished iterating before first Set', 'unequal Set (b, c) are not equal' ); st.equal( isEqualWhy(c, a), - symbolIterator - ? 'first argument finished iterating before second' - : 'Collection entries differ: arrays have different length: 1 !== 2', + 'first Set argument finished iterating before second Set', 'unequal Set (c, a) are not equal' ); st.equal( isEqualWhy(c, b), - symbolIterator - ? 'first argument finished iterating before second' - : 'Collection entries differ: arrays have different length: 1 !== 2', + 'first Set argument finished iterating before second Set', 'unequal Set (b, c) are not equal' ); @@ -581,9 +573,7 @@ test('iterables', function (t) { } st.equal( isEqualWhy(ab, ac), - symbolIterator - ? 'iteration results are not equal: value at key "value" differs: string values are different: "b" !== "c"' - : 'Collection entries differ: string values are different: "b" !== "c"', + 'iteration results are not equal: value at key "value" differs: string values are different: "b" !== "c"', 'Sets initially populated with different strings are not equal' ); sst.end(); @@ -638,12 +628,12 @@ test('iterables', function (t) { it.equal( isEqualWhy(c, d), - 'first argument finished iterating before second', + 'first object argument finished iterating before second object', 'iterable c / iterable d are not equal' ); it.equal( isEqualWhy(d, c), - 'second argument finished iterating before first', + 'second object argument finished iterating before first object', 'iterable d / iterable c are not equal' ); diff --git a/why.js b/why.js index f8ebac0..e0e0eeb 100644 --- a/why.js +++ b/why.js @@ -4,6 +4,7 @@ var ObjectPrototype = Object.prototype; var toStr = ObjectPrototype.toString; var booleanValue = Boolean.prototype.valueOf; var has = require('has'); +var isArray = require('isarray'); var isArrowFunction = require('is-arrow-function'); var isBoolean = require('is-boolean-object'); var isDate = require('is-date-object'); @@ -14,19 +15,20 @@ var isString = require('is-string'); var isSymbol = require('is-symbol'); var isCallable = require('is-callable'); var isBigInt = require('is-bigint'); +var getIterator = require('es-get-iterator'); +var whichCollection = require('which-collection'); +var whichBoxedPrimitive = require('which-boxed-primitive'); + +var objectType = function (v) { return whichCollection(v) || whichBoxedPrimitive(v) || typeof v; }; var isProto = Object.prototype.isPrototypeOf; -var namedFoo = function foo() {}; -var functionsHaveNames = namedFoo.name === 'foo'; +var functionsHaveNames = require('functions-have-names')(); var symbolValue = typeof Symbol === 'function' ? Symbol.prototype.valueOf : null; -var symbolIterator = require('./getSymbolIterator')(); var bigIntValue = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null; -var collectionsForEach = require('./getCollectionsForEach')(); - var getPrototypeOf = Object.getPrototypeOf; if (!getPrototypeOf) { /* eslint-disable no-proto */ @@ -54,33 +56,11 @@ if (!getPrototypeOf) { /* eslint-enable no-proto */ } -var isArray = Array.isArray || function (value) { - return toStr.call(value) === '[object Array]'; -}; - var normalizeFnWhitespace = function normalizeWhitespace(fnStr) { // this is needed in IE 9, at least, which has inconsistencies here. return fnStr.replace(/^function ?\(/, 'function (').replace('){', ') {'); }; -var tryMapSetEntries = function tryCollectionEntries(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) { @@ -238,44 +218,27 @@ module.exports = function whyNotEqual(value, other) { 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; - } + var valueIterator = getIterator(value); + var otherIterator = getIterator(other); + if (!!valueIterator !== !!otherIterator) { + if (valueIterator) { return 'first argument is iterable; second is not'; } + return 'second argument is iterable; first is not'; + } + if (valueIterator && otherIterator) { // both should be truthy or falsy at this point + 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; - } + } + } while (!valueNext.done && !otherNext.done); + if (valueNext.done && !otherNext.done) { return 'first ' + objectType(value) + ' argument finished iterating before second ' + objectType(other); } + if (!valueNext.done && otherNext.done) { return 'second ' + objectType(other) + ' argument finished iterating before first ' + objectType(value); } + return ''; } var key, valueKeyIsRecursive, otherKeyIsRecursive, keyWhy;