dist
.DS_Store
node_modules
+coverage
explorations
TODOs.md
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js",
- "lint": "prettier --write --parser typescript 'packages/**/*.ts'"
+ "lint": "prettier --write --parser typescript 'packages/**/*.ts'",
+ "test": "jest"
},
"gitHooks": {
"pre-commit": "lint-staged",
stop(runner)
obj.prop = 3
expect(dummy).toBe(2)
+
+ // stopped runner should still be manually callable
+ runner()
+ expect(dummy).toBe(3)
})
it('markNonReactive', () => {
observed.bar.baz = 3
expect(observed.bar.baz).toBe(2)
expect(warn).toHaveBeenCalledTimes(2)
+ delete observed.foo
+ expect(observed.foo).toBe(1)
+ expect(warn).toHaveBeenCalledTimes(3)
+ delete observed.bar.baz
+ expect(observed.bar.baz).toBe(2)
+ expect(warn).toHaveBeenCalledTimes(4)
})
it('should allow mutation when unlocked', () => {
- const observed = immutable({ foo: 1, bar: { baz: 2 } })
+ const observed: any = immutable({ foo: 1, bar: { baz: 2 } })
unlock()
- observed.foo = 2
- observed.bar.baz = 3
+ observed.prop = 2
+ observed.bar.qux = 3
+ delete observed.bar.baz
+ delete observed.foo
lock()
- expect(observed.foo).toBe(2)
- expect(observed.bar.baz).toBe(3)
+ expect(observed.prop).toBe(2)
+ expect(observed.foo).toBeUndefined()
+ expect(observed.bar.qux).toBe(3)
+ expect('baz' in observed.bar).toBe(false)
expect(warn).not.toHaveBeenCalled()
})
lock()
})
})
- ;[Map, WeakMap].forEach((Collection: any) => {
+
+ const maps = [Map, WeakMap]
+ maps.forEach((Collection: any) => {
describe(Collection.name, () => {
test('should make nested values immutable', () => {
const key1 = {}
test('should allow mutation & trigger autorun when unlocked', () => {
const map = immutable(new Collection())
+ const isWeak = Collection === WeakMap
const key = {}
let dummy
autorun(() => {
- dummy = map.get(key)
+ dummy = map.get(key) + (isWeak ? 0 : map.size)
})
- expect(dummy).toBeUndefined()
+ expect(dummy).toBeNaN()
unlock()
map.set(key, 1)
lock()
- expect(dummy).toBe(1)
+ expect(dummy).toBe(isWeak ? 1 : 2)
expect(map.get(key)).toBe(1)
expect(warn).not.toHaveBeenCalled()
})
})
})
- ;[Set, WeakSet].forEach((Collection: any) => {
+
+ const sets = [Set, WeakSet]
+ sets.forEach((Collection: any) => {
describe(Collection.name, () => {
test('should make nested values immutable', () => {
const key1 = {}
.filter(value => typeof value === 'symbol')
)
-function get(
- target: any,
- key: string | symbol,
- receiver: any,
- toObservable: (t: any) => any
-) {
- const res = Reflect.get(target, key, receiver)
- if (typeof key === 'symbol' && builtInSymbols.has(key)) {
- return res
+function makeGetter(isImmutable: boolean) {
+ return function get(target: any, key: string | symbol, receiver: any) {
+ const res = Reflect.get(target, key, receiver)
+ if (typeof key === 'symbol' && builtInSymbols.has(key)) {
+ return res
+ }
+ track(target, OperationTypes.GET, key)
+ return res !== null && typeof res === 'object'
+ ? isImmutable
+ ? // need to lazy access immutable and observable here to avoid
+ // circular dependency
+ immutable(res)
+ : observable(res)
+ : res
}
- track(target, OperationTypes.GET, key)
- return res !== null && typeof res === 'object' ? toObservable(res) : res
}
function set(
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
if (target === unwrap(receiver)) {
+ /* istanbul ignore else */
if (__DEV__) {
const extraInfo = { oldValue, newValue: value }
if (!hadKey) {
const oldValue = target[key]
const result = Reflect.deleteProperty(target, key)
if (hadKey) {
+ /* istanbul ignore else */
if (__DEV__) {
trigger(target, OperationTypes.DELETE, key, { oldValue })
} else {
}
export const mutableHandlers: ProxyHandler<any> = {
- get: (target: any, key: string | symbol, receiver: any) =>
- get(target, key, receiver, observable),
+ get: makeGetter(false),
set,
deleteProperty,
has,
}
export const immutableHandlers: ProxyHandler<any> = {
- get: (target: any, key: string | symbol, receiver: any) =>
- get(target, key, receiver, LOCKED ? immutable : observable),
+ get: makeGetter(true),
set(target: any, key: string | symbol, value: any, receiver: any): boolean {
if (LOCKED) {
const hadKey = proto.has.call(target, value)
const result = proto.add.call(target, value)
if (!hadKey) {
+ /* istanbul ignore else */
if (__DEV__) {
trigger(target, OperationTypes.ADD, value, { value })
} else {
const oldValue = proto.get.call(target, key)
const result = proto.set.call(target, key, value)
if (value !== oldValue) {
+ /* istanbul ignore else */
if (__DEV__) {
const extraInfo = { oldValue, newValue: value }
if (!hadKey) {
// forward the operation before queueing reactions
const result = proto.delete.call(target, key)
if (hadKey) {
+ /* istanbul ignore else */
if (__DEV__) {
trigger(target, OperationTypes.DELETE, key, { oldValue })
} else {
// forward the operation before queueing reactions
const result = proto.clear.call(target)
if (hadItems) {
+ /* istanbul ignore else */
if (__DEV__) {
trigger(target, OperationTypes.CLEAR, void 0, { oldTarget })
} else {
}
})
-function getInstrumented(
- target: any,
- key: string | symbol,
- receiver: any,
- instrumentations: any
-) {
- target = instrumentations.hasOwnProperty(key) ? instrumentations : target
- return Reflect.get(target, key, receiver)
+function makeInstrumentationGetter(instrumentations: any) {
+ return function getInstrumented(
+ target: any,
+ key: string | symbol,
+ receiver: any
+ ) {
+ target = instrumentations.hasOwnProperty(key) ? instrumentations : target
+ return Reflect.get(target, key, receiver)
+ }
}
export const mutableCollectionHandlers: ProxyHandler<any> = {
- get: (target: any, key: string | symbol, receiver: any) =>
- getInstrumented(target, key, receiver, mutableInstrumentations)
+ get: makeInstrumentationGetter(mutableInstrumentations)
}
export const immutableCollectionHandlers: ProxyHandler<any> = {
- get: (target: any, key: string | symbol, receiver: any) =>
- getInstrumented(target, key, receiver, immutableInstrumentations)
+ get: makeInstrumentationGetter(immutableInstrumentations)
}