From 6353cf797bb9085569b54aac76bb4c72ac5c18f9 Mon Sep 17 00:00:00 2001 From: Anton Korzunov Date: Sat, 10 Mar 2018 08:34:33 +1100 Subject: [PATCH] add proxyShallowEqual. impliments #1 --- _tests/smoke.spec.js | 84 +++++++++++++++++++++++++++++++++++++++++++- lib/index.js | 21 ++++++++--- src/index.js | 35 ++++++++++++++++++ 3 files changed, 134 insertions(+), 6 deletions(-) diff --git a/_tests/smoke.spec.js b/_tests/smoke.spec.js index af764a8..a444120 100644 --- a/_tests/smoke.spec.js +++ b/_tests/smoke.spec.js @@ -1,6 +1,6 @@ import {expect} from 'chai'; -import {proxyState, proxyShallow, proxyEqual, drainDifference, deproxify, isProxyfied, getProxyKey} from '../src/index'; +import {proxyState, proxyShallow, proxyShallowEqual, proxyEqual, drainDifference, deproxify, isProxyfied, getProxyKey} from '../src/index'; describe('proxy', () => { it('arrays', () => { @@ -100,6 +100,88 @@ describe('proxy', () => { expect(proxyState(undefined).state).to.be.equal(undefined); }); + it('plain shallowEqual', () => { + const A1 = { + key1: 1, + key2: { + array: [1, 2, 4] + }, + key3: null + }; + const A2 = { + key1: 1, + key2: 2 + }; + const A3 = Object.assign({}, A1, { + key1: 2, + }); + const A4 = { + key1: 1, + key2: A1.key2 + }; + + const trapped = proxyState(A1); + expect(trapped.affected).to.be.deep.equal([]); + + expect(proxyShallowEqual(A1, A2, trapped.affected)).to.be.true; + expect(proxyShallowEqual(A1, A3, trapped.affected)).to.be.true; + + trapped.state.key1 += 0; + trapped.state.key1 += 0; + expect(trapped.affected).to.be.deep.equal(['.key1']); + + expect(proxyShallowEqual(A1, A2, trapped.affected)).to.be.true; + expect(proxyShallowEqual(A1, A3, trapped.affected)).to.be.false; + expect(drainDifference()).to.be.deep.equal([['.key1', 'not equal']]); + + trapped.reset(); + expect(trapped.affected).to.be.deep.equal([]); + trapped.state.key2.array[0] += 0; + expect(trapped.affected).to.be.deep.equal(['.key2', '.key2.array', '.key2.array.0']); + + expect(proxyShallowEqual(A1, A2, trapped.affected)).to.be.false; + expect(drainDifference()).to.be.deep.equal([['.key2.array.0', 'not equal']]); + + expect(proxyShallowEqual(A1, A4, trapped.affected)).to.be.true; + }); + + it('nested shallowEqual', () => { + const A1 = { + key1: 1, + key2: { + array: [1, 2, 4] + }, + key3: null + }; + const A2 = { + key1: 1, + key2: { + array: A1.key2.array + } + }; + + const trapped1 = proxyState(A1); + const trapped2 = proxyState(trapped1.state); + + trapped2.state.key1 += 0; + expect(trapped1.affected).to.be.deep.equal(['.key1']); + expect(trapped2.affected).to.be.deep.equal(['.key1']); + + expect(proxyShallowEqual(A1, A2, trapped1.affected)).to.be.true; + trapped2.state.key1 += 1; + expect(proxyShallowEqual(trapped2.state, A2, trapped1.affected)).to.be.true; + A1.key1 += 1; + expect(proxyShallowEqual(trapped2.state, A2, trapped1.affected)).to.be.false; + expect(drainDifference()).to.be.deep.equal([['.key1', 'not equal']]); + + trapped1.reset(); + trapped2.reset(); + trapped2.state.key2.array[0] += 0; + expect(trapped1.affected).to.be.deep.equal(['.key2', '.key2.array', '.key2.array.0']); + + expect(proxyShallowEqual(trapped2.state, A2, trapped1.affected)).to.be.true; + }); + it('can proxy via proxy', () => { const A = { b: { diff --git a/lib/index.js b/lib/index.js index 0cb0a89..680e1ca 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,6 +13,7 @@ var _searchTrie2 = _interopRequireDefault(_searchTrie); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +var deepDeproxySymbol = typeof Symbol !== 'undefined' ? Symbol('deepDeproxy') : '__magic__deepDeproxySymbol'; var deproxySymbol = typeof Symbol !== 'undefined' ? Symbol('deproxy') : '__magic__deproxySymbol'; var proxyKeySymbol = typeof Symbol !== 'undefined' ? Symbol('proxyKey') : '__magic__proxyKeySymbol'; @@ -27,6 +28,13 @@ var deproxify = function deproxify(object) { return object; }; +var deepDeproxify = function deepDeproxify(object) { + if (object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object') { + return object[deepDeproxySymbol] || object; + } + return object; +}; + var getProxyKey = function getProxyKey(object) { return object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' ? object[proxyKeySymbol] : {}; }; @@ -44,10 +52,13 @@ function proxyfy(state, report) { return storedValue[suffix]; } - var proxy = new Proxy(Array.isArray(state) ? state : Object.assign({}, state), { + var proxy = new Proxy(Array.isArray(state) || state[deproxySymbol] ? state : Object.assign({}, state), { get: function get(target, prop) { if (prop === deproxySymbol) { - return deproxify(state); + return state; + } + if (prop === deepDeproxySymbol) { + return deepDeproxify(state); } if (prop === proxyKeySymbol) { return { @@ -55,7 +66,7 @@ function proxyfy(state, report) { fingerPrint: fingerPrint }; } - var value = Reflect.get(target, prop); + var value = state[prop]; if (typeof prop === 'string') { var thisId = suffix + '.' + prop; var type = typeof value === 'undefined' ? 'undefined' : _typeof(value); @@ -114,8 +125,8 @@ var proxyCompare = function proxyCompare(a, b, locations) { var key = _step.value; var path = key.split('.'); - var la = deproxify(get(a, path)); - var lb = deproxify(get(b, path)); + var la = deepDeproxify(get(a, path)); + var lb = deepDeproxify(get(b, path)); if (la !== lb) { differs.push([key, 'differs', la, lb]); return false; diff --git a/src/index.js b/src/index.js index 702ffb6..961946a 100644 --- a/src/index.js +++ b/src/index.js @@ -104,6 +104,40 @@ const proxyCompare = (a, b, locations) => { return true; }; +const proxyShallowEqual = (a, b, locations) => { + const checkedPaths = new Map(); + const results = new Map(); + + for (const key of locations) { + const prevKey = key.substr(0, key.lastIndexOf('.')); + if (checkedPaths.has(prevKey)) { + checkedPaths.set(key, true); + continue; + } + + const path = key.split('.'); + const la = deepDeproxify(get(a, path)); + const lb = deepDeproxify(get(b, path)); + const equal = la === lb; + + results.delete(prevKey); + results.set(key, equal); + if (equal) { + checkedPaths.set(key, true); + } + } + + const tails = results.entries(); + for(const [key,value] of tails){ + if(!value){ + differs.push([key, 'not equal']); + return false; + } + } + + return true; +}; + const proxyEqual = (a, b, affected) => proxyCompare(a, b, collectValuables(affected)); const proxyShallow = (a, b, affected) => proxyCompare(a, b, collectShallows(affected)); @@ -139,6 +173,7 @@ const proxyState = (state, fingerPrint = '', _ProxyMap) => { export { proxyEqual, proxyShallow, + proxyShallowEqual, proxyState, proxyCompare,