} from './reactive'
import { ITERATE_KEY, MAP_KEY_ITERATE_KEY, track, trigger } from './dep'
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
-import { capitalize, hasChanged, hasOwn, isMap, toRawType } from '@vue/shared'
+import {
+ capitalize,
+ extend,
+ hasChanged,
+ hasOwn,
+ isMap,
+ toRawType,
+} from '@vue/shared'
import { warn } from './warning'
type CollectionTypes = IterableCollections | WeakCollections
const getProto = <T extends CollectionTypes>(v: T): any =>
Reflect.getPrototypeOf(v)
-function get(
- target: MapTypes,
- key: unknown,
- isReadonly = false,
- isShallow = false,
-) {
- // #1772: readonly(reactive(Map)) should return readonly + reactive version
- // of the value
- target = target[ReactiveFlags.RAW]
- const rawTarget = toRaw(target)
- const rawKey = toRaw(key)
- if (!isReadonly) {
- if (hasChanged(key, rawKey)) {
- track(rawTarget, TrackOpTypes.GET, key)
- }
- track(rawTarget, TrackOpTypes.GET, rawKey)
- }
- const { has } = getProto(rawTarget)
- const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
- if (has.call(rawTarget, key)) {
- return wrap(target.get(key))
- } else if (has.call(rawTarget, rawKey)) {
- return wrap(target.get(rawKey))
- } else if (target !== rawTarget) {
- // #3602 readonly(reactive(Map))
- // ensure that the nested reactive `Map` can do tracking for itself
- target.get(key)
- }
-}
-
-function has(this: CollectionTypes, key: unknown, isReadonly = false): boolean {
- const target = this[ReactiveFlags.RAW]
- const rawTarget = toRaw(target)
- const rawKey = toRaw(key)
- if (!isReadonly) {
- if (hasChanged(key, rawKey)) {
- track(rawTarget, TrackOpTypes.HAS, key)
- }
- track(rawTarget, TrackOpTypes.HAS, rawKey)
- }
- return key === rawKey
- ? target.has(key)
- : target.has(key) || target.has(rawKey)
-}
-
-function size(target: IterableCollections, isReadonly = false) {
- target = target[ReactiveFlags.RAW]
- !isReadonly && track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY)
- return Reflect.get(target, 'size', target)
-}
-
-function add(this: SetTypes, value: unknown, _isShallow = false) {
- if (!_isShallow && !isShallow(value) && !isReadonly(value)) {
- value = toRaw(value)
- }
- const target = toRaw(this)
- const proto = getProto(target)
- const hadKey = proto.has.call(target, value)
- if (!hadKey) {
- target.add(value)
- trigger(target, TriggerOpTypes.ADD, value, value)
- }
- return this
-}
-
-function set(this: MapTypes, key: unknown, value: unknown, _isShallow = false) {
- if (!_isShallow && !isShallow(value) && !isReadonly(value)) {
- value = toRaw(value)
- }
- const target = toRaw(this)
- const { has, get } = getProto(target)
-
- let hadKey = has.call(target, key)
- if (!hadKey) {
- key = toRaw(key)
- hadKey = has.call(target, key)
- } else if (__DEV__) {
- checkIdentityKeys(target, has, key)
- }
-
- const oldValue = get.call(target, key)
- target.set(key, value)
- if (!hadKey) {
- trigger(target, TriggerOpTypes.ADD, key, value)
- } else if (hasChanged(value, oldValue)) {
- trigger(target, TriggerOpTypes.SET, key, value, oldValue)
- }
- return this
-}
-
-function deleteEntry(this: CollectionTypes, key: unknown) {
- const target = toRaw(this)
- const { has, get } = getProto(target)
- let hadKey = has.call(target, key)
- if (!hadKey) {
- key = toRaw(key)
- hadKey = has.call(target, key)
- } else if (__DEV__) {
- checkIdentityKeys(target, has, key)
- }
-
- const oldValue = get ? get.call(target, key) : undefined
- // forward the operation before queueing reactions
- const result = target.delete(key)
- if (hadKey) {
- trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
- }
- return result
-}
-
-function clear(this: IterableCollections) {
- const target = toRaw(this)
- const hadItems = target.size !== 0
- const oldTarget = __DEV__
- ? isMap(target)
- ? new Map(target)
- : new Set(target)
- : undefined
- // forward the operation before queueing reactions
- const result = target.clear()
- if (hadItems) {
- trigger(target, TriggerOpTypes.CLEAR, undefined, undefined, oldTarget)
- }
- return result
-}
-
-function createForEach(isReadonly: boolean, isShallow: boolean) {
- return function forEach(
- this: IterableCollections,
- callback: Function,
- thisArg?: unknown,
- ) {
- const observed = this
- const target = observed[ReactiveFlags.RAW]
- const rawTarget = toRaw(target)
- const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive
- !isReadonly && track(rawTarget, TrackOpTypes.ITERATE, ITERATE_KEY)
- return target.forEach((value: unknown, key: unknown) => {
- // important: make sure the callback is
- // 1. invoked with the reactive map as `this` and 3rd arg
- // 2. the value received should be a corresponding reactive/readonly.
- return callback.call(thisArg, wrap(value), wrap(key), observed)
- })
- }
-}
-
function createIterableMethod(
method: string | symbol,
isReadonly: boolean,
type Instrumentations = Record<string | symbol, Function | number>
-function createInstrumentations() {
- const mutableInstrumentations: Instrumentations = {
- get(this: MapTypes, key: unknown) {
- return get(this, key)
- },
- get size() {
- return size(this as unknown as IterableCollections)
- },
- has,
- add,
- set,
- delete: deleteEntry,
- clear,
- forEach: createForEach(false, false),
- }
-
- const shallowInstrumentations: Instrumentations = {
+function createInstrumentations(
+ readonly: boolean,
+ shallow: boolean,
+): Instrumentations {
+ const instrumentations: Instrumentations = {
get(this: MapTypes, key: unknown) {
- return get(this, key, false, true)
+ // #1772: readonly(reactive(Map)) should return readonly + reactive version
+ // of the value
+ const target = this[ReactiveFlags.RAW]
+ const rawTarget = toRaw(target)
+ const rawKey = toRaw(key)
+ if (!readonly) {
+ if (hasChanged(key, rawKey)) {
+ track(rawTarget, TrackOpTypes.GET, key)
+ }
+ track(rawTarget, TrackOpTypes.GET, rawKey)
+ }
+ const { has } = getProto(rawTarget)
+ const wrap = shallow ? toShallow : readonly ? toReadonly : toReactive
+ if (has.call(rawTarget, key)) {
+ return wrap(target.get(key))
+ } else if (has.call(rawTarget, rawKey)) {
+ return wrap(target.get(rawKey))
+ } else if (target !== rawTarget) {
+ // #3602 readonly(reactive(Map))
+ // ensure that the nested reactive `Map` can do tracking for itself
+ target.get(key)
+ }
},
get size() {
- return size(this as unknown as IterableCollections)
+ const target = (this as unknown as IterableCollections)[ReactiveFlags.RAW]
+ !readonly && track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY)
+ return Reflect.get(target, 'size', target)
},
- has,
- add(this: SetTypes, value: unknown) {
- return add.call(this, value, true)
+ has(this: CollectionTypes, key: unknown): boolean {
+ const target = this[ReactiveFlags.RAW]
+ const rawTarget = toRaw(target)
+ const rawKey = toRaw(key)
+ if (!readonly) {
+ if (hasChanged(key, rawKey)) {
+ track(rawTarget, TrackOpTypes.HAS, key)
+ }
+ track(rawTarget, TrackOpTypes.HAS, rawKey)
+ }
+ return key === rawKey
+ ? target.has(key)
+ : target.has(key) || target.has(rawKey)
},
- set(this: MapTypes, key: unknown, value: unknown) {
- return set.call(this, key, value, true)
+ forEach(this: IterableCollections, callback: Function, thisArg?: unknown) {
+ const observed = this
+ const target = observed[ReactiveFlags.RAW]
+ const rawTarget = toRaw(target)
+ const wrap = shallow ? toShallow : readonly ? toReadonly : toReactive
+ !readonly && track(rawTarget, TrackOpTypes.ITERATE, ITERATE_KEY)
+ return target.forEach((value: unknown, key: unknown) => {
+ // important: make sure the callback is
+ // 1. invoked with the reactive map as `this` and 3rd arg
+ // 2. the value received should be a corresponding reactive/readonly.
+ return callback.call(thisArg, wrap(value), wrap(key), observed)
+ })
},
- delete: deleteEntry,
- clear,
- forEach: createForEach(false, true),
}
- const readonlyInstrumentations: Instrumentations = {
- get(this: MapTypes, key: unknown) {
- return get(this, key, true)
- },
- get size() {
- return size(this as unknown as IterableCollections, true)
- },
- has(this: MapTypes, key: unknown) {
- return has.call(this, key, true)
- },
- add: createReadonlyMethod(TriggerOpTypes.ADD),
- set: createReadonlyMethod(TriggerOpTypes.SET),
- delete: createReadonlyMethod(TriggerOpTypes.DELETE),
- clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
- forEach: createForEach(true, false),
- }
+ extend(
+ instrumentations,
+ readonly
+ ? {
+ add: createReadonlyMethod(TriggerOpTypes.ADD),
+ set: createReadonlyMethod(TriggerOpTypes.SET),
+ delete: createReadonlyMethod(TriggerOpTypes.DELETE),
+ clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
+ }
+ : {
+ add(this: SetTypes, value: unknown) {
+ if (!shallow && !isShallow(value) && !isReadonly(value)) {
+ value = toRaw(value)
+ }
+ const target = toRaw(this)
+ const proto = getProto(target)
+ const hadKey = proto.has.call(target, value)
+ if (!hadKey) {
+ target.add(value)
+ trigger(target, TriggerOpTypes.ADD, value, value)
+ }
+ return this
+ },
+ set(this: MapTypes, key: unknown, value: unknown) {
+ if (!shallow && !isShallow(value) && !isReadonly(value)) {
+ value = toRaw(value)
+ }
+ const target = toRaw(this)
+ const { has, get } = getProto(target)
+
+ let hadKey = has.call(target, key)
+ if (!hadKey) {
+ key = toRaw(key)
+ hadKey = has.call(target, key)
+ } else if (__DEV__) {
+ checkIdentityKeys(target, has, key)
+ }
- const shallowReadonlyInstrumentations: Instrumentations = {
- get(this: MapTypes, key: unknown) {
- return get(this, key, true, true)
- },
- get size() {
- return size(this as unknown as IterableCollections, true)
- },
- has(this: MapTypes, key: unknown) {
- return has.call(this, key, true)
- },
- add: createReadonlyMethod(TriggerOpTypes.ADD),
- set: createReadonlyMethod(TriggerOpTypes.SET),
- delete: createReadonlyMethod(TriggerOpTypes.DELETE),
- clear: createReadonlyMethod(TriggerOpTypes.CLEAR),
- forEach: createForEach(true, true),
- }
+ const oldValue = get.call(target, key)
+ target.set(key, value)
+ if (!hadKey) {
+ trigger(target, TriggerOpTypes.ADD, key, value)
+ } else if (hasChanged(value, oldValue)) {
+ trigger(target, TriggerOpTypes.SET, key, value, oldValue)
+ }
+ return this
+ },
+ delete(this: CollectionTypes, key: unknown) {
+ const target = toRaw(this)
+ const { has, get } = getProto(target)
+ let hadKey = has.call(target, key)
+ if (!hadKey) {
+ key = toRaw(key)
+ hadKey = has.call(target, key)
+ } else if (__DEV__) {
+ checkIdentityKeys(target, has, key)
+ }
+
+ const oldValue = get ? get.call(target, key) : undefined
+ // forward the operation before queueing reactions
+ const result = target.delete(key)
+ if (hadKey) {
+ trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
+ }
+ return result
+ },
+ clear(this: IterableCollections) {
+ const target = toRaw(this)
+ const hadItems = target.size !== 0
+ const oldTarget = __DEV__
+ ? isMap(target)
+ ? new Map(target)
+ : new Set(target)
+ : undefined
+ // forward the operation before queueing reactions
+ const result = target.clear()
+ if (hadItems) {
+ trigger(
+ target,
+ TriggerOpTypes.CLEAR,
+ undefined,
+ undefined,
+ oldTarget,
+ )
+ }
+ return result
+ },
+ },
+ )
const iteratorMethods = [
'keys',
] as const
iteratorMethods.forEach(method => {
- mutableInstrumentations[method] = createIterableMethod(method, false, false)
- readonlyInstrumentations[method] = createIterableMethod(method, true, false)
- shallowInstrumentations[method] = createIterableMethod(method, false, true)
- shallowReadonlyInstrumentations[method] = createIterableMethod(
- method,
- true,
- true,
- )
+ instrumentations[method] = createIterableMethod(method, readonly, shallow)
})
- return [
- mutableInstrumentations,
- readonlyInstrumentations,
- shallowInstrumentations,
- shallowReadonlyInstrumentations,
- ]
+ return instrumentations
}
-const [
- mutableInstrumentations,
- readonlyInstrumentations,
- shallowInstrumentations,
- shallowReadonlyInstrumentations,
-] = /* @__PURE__*/ createInstrumentations()
-
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
- const instrumentations = shallow
- ? isReadonly
- ? shallowReadonlyInstrumentations
- : shallowInstrumentations
- : isReadonly
- ? readonlyInstrumentations
- : mutableInstrumentations
+ const instrumentations = createInstrumentations(isReadonly, shallow)
return (
target: CollectionTypes,