Comparing version 2.1.3 to 2.1.4
40
index.js
@@ -6,5 +6,4 @@ 'use strict'; | ||
const path = require('./lib/path'); | ||
const isArray = require('./lib/is-array'); | ||
const isSymbol = require('./lib/is-symbol'); | ||
const isObject = require('./lib/is-object'); | ||
const isSymbol = require('./lib/is-symbol'); | ||
const ignoreProperty = require('./lib/ignore-property'); | ||
@@ -14,5 +13,17 @@ const Cache = require('./lib/cache'); | ||
const defaultOptions = { | ||
equals: Object.is, | ||
isShallow: false, | ||
pathAsArray: false, | ||
ignoreSymbols: false, | ||
ignoreUnderscores: false | ||
}; | ||
const onChange = (object, onChange, options = {}) => { | ||
options = { | ||
...defaultOptions, | ||
...options | ||
}; | ||
const proxyTarget = Symbol('ProxyTarget'); | ||
const equals = options.equals || Object.is; | ||
const {equals, isShallow} = options; | ||
const cache = new Cache(equals); | ||
@@ -53,7 +64,10 @@ const smartClone = new SmartClone(); | ||
const value = Reflect.get(target, property, receiver); | ||
const value = isBuiltin.withMutableMethods(target) ? | ||
Reflect.get(target, property) : | ||
Reflect.get(target, property, receiver); | ||
if ( | ||
isBuiltin.withoutMutableMethods(value) || | ||
property === 'constructor' || | ||
(options.isShallow === true && !smartClone.isHandledMethod(target, property)) || | ||
(isShallow && !smartClone.isHandledMethod(target, property)) || | ||
ignoreProperty(cache, options, property) || | ||
@@ -69,3 +83,3 @@ cache.isGetInvariant(target, property) | ||
set(target, property, value, receiver) { | ||
if (value) { | ||
if (isObject(value)) { | ||
const valueProxyTarget = value[proxyTarget]; | ||
@@ -79,3 +93,3 @@ | ||
const reflectTarget = target[proxyTarget] || target; | ||
const previous = Reflect.get(reflectTarget, property, receiver); | ||
const previous = reflectTarget[property]; | ||
const hasProperty = property in target; | ||
@@ -131,3 +145,3 @@ | ||
if (smartClone.isCloning || cache.isUnsubscribed) { | ||
return Reflect.apply(target, thisArg, argumentsList); | ||
return Reflect.apply(target, thisProxyTarget, argumentsList); | ||
} | ||
@@ -137,4 +151,4 @@ | ||
if (isMutable || isArray(thisArg) || isObject(thisArg)) { | ||
smartClone.start(thisProxyTarget, applyPath); | ||
if (isMutable || smartClone.isHandledType(thisProxyTarget)) { | ||
smartClone.start(thisProxyTarget, applyPath, argumentsList); | ||
} | ||
@@ -148,3 +162,3 @@ | ||
if (smartClone.isChanged(isMutable, thisArg, equals)) { | ||
if (smartClone.isChanged(isMutable, thisProxyTarget, equals, argumentsList)) { | ||
const clone = smartClone.done(); | ||
@@ -157,3 +171,3 @@ handleChange(applyPath, '', clone, thisProxyTarget, target.name); | ||
if ( | ||
(isArray(result) || isObject(result)) && | ||
smartClone.isHandledType(result) && | ||
smartClone.isHandledMethod(thisProxyTarget, target.name) | ||
@@ -168,3 +182,3 @@ ) { | ||
const proxy = cache.getProxy(object, options.pathAsArray === true ? [] : '', handler); | ||
const proxy = cache.getProxy(object, options.pathAsArray ? [] : '', handler); | ||
onChange = onChange.bind(proxy); | ||
@@ -171,0 +185,0 @@ |
@@ -7,5 +7,5 @@ 'use strict'; | ||
return cache.isUnsubscribed || | ||
(options.ignoreSymbols === true && isSymbol(property)) || | ||
(options.ignoreUnderscores === true && property.charAt(0) === '_') || | ||
(options.ignoreSymbols && isSymbol(property)) || | ||
(options.ignoreUnderscores && property.charAt(0) === '_') || | ||
('ignoreKeys' in options && options.ignoreKeys.includes(property)); | ||
}; |
'use strict'; | ||
module.exports = { | ||
withMutableMethods: value => value instanceof Date, | ||
withoutMutableMethods: value => | ||
value === null || | ||
(typeof value !== 'object' && typeof value !== 'function') || | ||
value instanceof RegExp | ||
const isPrimitive = value => typeof value === 'object' ? value === null : typeof value !== 'function'; | ||
const isBuiltin = { | ||
withMutableMethods: value => { | ||
if (isPrimitive(value)) { | ||
return false; | ||
} | ||
return value instanceof Date || | ||
value instanceof Set || | ||
value instanceof Map || | ||
value instanceof WeakSet || | ||
value instanceof WeakMap; | ||
}, | ||
withoutMutableMethods: value => isPrimitive(value) || value instanceof RegExp | ||
}; | ||
module.exports = isBuiltin; |
@@ -5,8 +5,42 @@ 'use strict'; | ||
const isArray = require('./is-array'); | ||
const isBuiltin = require('./is-builtin'); | ||
const isObject = require('./is-object'); | ||
const shallowEqual = (clone, value) => { | ||
const certainChange = () => true; | ||
const shallowEqualArrays = (clone, value) => { | ||
return clone.length !== value.length || clone.some((item, index) => value[index] !== item); | ||
}; | ||
const shallowEqualSets = (a, b) => { | ||
if (a.size !== b.size) { | ||
return true; | ||
} | ||
for (const element of a) { | ||
if (!b.has(element)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
const shallowEqualMaps = (a, b) => { | ||
if (a.size !== b.size) { | ||
return true; | ||
} | ||
let bValue; | ||
for (const [key, aValue] of a) { | ||
bValue = b.get(key); | ||
if (bValue !== aValue || (bValue === undefined && !b.has(key))) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
const IMMUTABLE_OBJECT_METHODS = new Set([ | ||
@@ -29,16 +63,46 @@ 'hasOwnProperty', | ||
const IMMUTABLE_SET_METHODS = new Set([ | ||
'has', | ||
'toString', | ||
'keys' | ||
]); | ||
const IMMUTABLE_MAP_METHODS = new Set([...IMMUTABLE_SET_METHODS].concat(['get'])); | ||
const SHALLOW_MUTABLE_ARRAY_METHODS = { | ||
push: () => true, | ||
pop: () => true, | ||
shift: () => true, | ||
unshift: () => true, | ||
push: certainChange, | ||
pop: certainChange, | ||
shift: certainChange, | ||
unshift: certainChange, | ||
concat: (clone, value) => clone.length !== value.length, | ||
copyWithin: shallowEqual, | ||
reverse: shallowEqual, | ||
sort: shallowEqual, | ||
splice: shallowEqual, | ||
flat: shallowEqual, | ||
fill: shallowEqual | ||
copyWithin: shallowEqualArrays, | ||
reverse: shallowEqualArrays, | ||
sort: shallowEqualArrays, | ||
splice: shallowEqualArrays, | ||
flat: shallowEqualArrays, | ||
fill: shallowEqualArrays | ||
}; | ||
const SHALLOW_MUTABLE_SET_METHODS = { | ||
add: shallowEqualSets, | ||
clear: shallowEqualSets, | ||
delete: shallowEqualSets | ||
}; | ||
const SHALLOW_MUTABLE_MAP_METHODS = { | ||
set: shallowEqualMaps, | ||
clear: shallowEqualMaps, | ||
delete: shallowEqualMaps | ||
}; | ||
const HANDLED_ARRAY_METHODS = new Set([...IMMUTABLE_OBJECT_METHODS] | ||
.concat([...IMMUTABLE_ARRAY_METHODS]) | ||
.concat(Object.keys(SHALLOW_MUTABLE_ARRAY_METHODS))); | ||
const HANDLED_SET_METHODS = new Set([...IMMUTABLE_SET_METHODS] | ||
.concat(Object.keys(SHALLOW_MUTABLE_SET_METHODS))); | ||
const HANDLED_MAP_METHODS = new Set([...IMMUTABLE_MAP_METHODS] | ||
.concat(Object.keys(SHALLOW_MUTABLE_MAP_METHODS))); | ||
class smartClone { | ||
@@ -49,11 +113,15 @@ constructor() { | ||
shallowClone(value) { | ||
_shallowClone(value) { | ||
let clone; | ||
if (isArray(value)) { | ||
if (isObject(value)) { | ||
clone = {...value}; | ||
} else if (isArray(value)) { | ||
clone = [...value]; | ||
} else if (value instanceof Date) { | ||
clone = new Date(value); | ||
} else { | ||
clone = {...value}; | ||
} else if (value instanceof Set) { | ||
clone = new Set(value); | ||
} else if (value instanceof Map) { | ||
clone = new Map(value); | ||
} | ||
@@ -66,3 +134,3 @@ | ||
start(value, path) { | ||
start(value, path, argumentsList) { | ||
if (this._cache === undefined) { | ||
@@ -72,3 +140,10 @@ this._cache = new Set(); | ||
this.clone = path === undefined ? value : this.shallowClone(value); | ||
if (value instanceof WeakSet) { | ||
this._weakValue = value.has(argumentsList[0]); | ||
} else if (value instanceof WeakMap) { | ||
this._weakValue = value.get(argumentsList[0]); | ||
} else { | ||
this.clone = path === undefined ? value : this._shallowClone(value); | ||
} | ||
this._path = path; | ||
@@ -78,10 +153,26 @@ this.isCloning = true; | ||
isHandledType(value) { | ||
return isObject(value) || | ||
isArray(value) || | ||
isBuiltin.withMutableMethods(value); | ||
} | ||
isHandledMethod(target, name) { | ||
if (isArray(target) && (IMMUTABLE_OBJECT_METHODS.has(name) || | ||
IMMUTABLE_ARRAY_METHODS.has(name) || | ||
name in SHALLOW_MUTABLE_ARRAY_METHODS)) { | ||
return true; | ||
if (isObject(target)) { | ||
return IMMUTABLE_OBJECT_METHODS.has(name); | ||
} | ||
return isObject(target) && IMMUTABLE_OBJECT_METHODS.has(name); | ||
if (isArray(target)) { | ||
return HANDLED_ARRAY_METHODS.has(name); | ||
} | ||
if (target instanceof Set) { | ||
return HANDLED_SET_METHODS.has(name); | ||
} | ||
if (target instanceof Map) { | ||
return HANDLED_MAP_METHODS.has(name); | ||
} | ||
return target instanceof WeakSet || target instanceof WeakMap; | ||
} | ||
@@ -95,2 +186,6 @@ | ||
this._onIsChanged = SHALLOW_MUTABLE_ARRAY_METHODS[name]; | ||
} else if (thisProxyTarget instanceof Set) { | ||
this._onIsChanged = SHALLOW_MUTABLE_SET_METHODS[name]; | ||
} else if (thisProxyTarget instanceof Map) { | ||
this._onIsChanged = SHALLOW_MUTABLE_MAP_METHODS[name]; | ||
} | ||
@@ -110,3 +205,3 @@ | ||
if (!this._cache.has(object[key])) { | ||
object[key] = this.shallowClone(object[key]); | ||
object[key] = this._shallowClone(object[key]); | ||
} | ||
@@ -130,3 +225,4 @@ | ||
this.clone = null; | ||
this.clone = undefined; | ||
this._weakValue = undefined; | ||
this.isCloning = false; | ||
@@ -140,5 +236,15 @@ this._path = null; | ||
isChanged(isMutable, value, equals) { | ||
isChanged(isMutable, value, equals, argumentsList) { | ||
if (isMutable) { | ||
return !equals(this.clone.valueOf(), value.valueOf()); | ||
if (value instanceof Date) { | ||
return !equals(this.clone.valueOf(), value.valueOf()); | ||
} | ||
if (value instanceof WeakSet) { | ||
return this._weakValue !== value.has(argumentsList[0]); | ||
} | ||
if (value instanceof WeakMap) { | ||
return this._weakValue !== value.get(argumentsList[0]); | ||
} | ||
} | ||
@@ -145,0 +251,0 @@ |
{ | ||
"name": "on-change", | ||
"version": "2.1.3", | ||
"version": "2.1.4", | ||
"description": "Watch an object or array for changes", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -124,3 +124,3 @@ # on-change [![Build Status](https://travis-ci.org/sindresorhus/on-change.svg?branch=master)](https://travis-ci.org/sindresorhus/on-change) | ||
2. The new value at the path. | ||
3. The previous value at the path. | ||
3. The previous value at the path. Changes in `WeakSets` and `WeakMaps` will return `undefined`. | ||
4. The name of the method that produced the change. | ||
@@ -127,0 +127,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27541
743