2019-05-29 15:44:59 +00:00
|
|
|
import {
|
|
|
|
effect,
|
|
|
|
stop,
|
2019-08-16 14:02:53 +00:00
|
|
|
isRef,
|
|
|
|
Ref,
|
2019-10-14 16:15:09 +00:00
|
|
|
ComputedRef,
|
2020-05-04 13:27:28 +00:00
|
|
|
ReactiveEffectOptions,
|
|
|
|
isReactive
|
2019-06-11 15:50:28 +00:00
|
|
|
} from '@vue/reactivity'
|
2020-08-05 14:55:23 +00:00
|
|
|
import { SchedulerJob, queuePreFlushCb } from './scheduler'
|
2019-10-23 15:53:43 +00:00
|
|
|
import {
|
|
|
|
EMPTY_OBJ,
|
|
|
|
isObject,
|
|
|
|
isArray,
|
|
|
|
isFunction,
|
|
|
|
isString,
|
2020-01-24 16:39:52 +00:00
|
|
|
hasChanged,
|
2020-02-18 18:52:59 +00:00
|
|
|
NOOP,
|
2020-09-14 15:26:34 +00:00
|
|
|
remove,
|
|
|
|
isMap,
|
|
|
|
isSet
|
2019-10-23 15:53:43 +00:00
|
|
|
} from '@vue/shared'
|
2019-09-11 14:09:00 +00:00
|
|
|
import {
|
|
|
|
currentInstance,
|
|
|
|
ComponentInternalInstance,
|
2020-02-14 05:13:54 +00:00
|
|
|
isInSSRComponentSetup,
|
|
|
|
recordInstanceBoundEffect
|
2019-09-11 14:09:00 +00:00
|
|
|
} from './component'
|
2019-08-30 19:05:39 +00:00
|
|
|
import {
|
2019-09-06 16:58:31 +00:00
|
|
|
ErrorCodes,
|
2019-08-30 19:05:39 +00:00
|
|
|
callWithErrorHandling,
|
|
|
|
callWithAsyncErrorHandling
|
|
|
|
} from './errorHandling'
|
2019-11-02 16:18:35 +00:00
|
|
|
import { queuePostRenderEffect } from './renderer'
|
2019-12-18 16:54:12 +00:00
|
|
|
import { warn } from './warning'
|
2021-04-07 19:38:04 +00:00
|
|
|
import { DeprecationTypes } from './compat/deprecations'
|
|
|
|
import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
|
2019-10-22 15:26:48 +00:00
|
|
|
|
2020-02-26 15:12:32 +00:00
|
|
|
export type WatchEffect = (onInvalidate: InvalidateCbRegistrator) => void
|
2019-12-30 16:30:12 +00:00
|
|
|
|
|
|
|
export type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T)
|
|
|
|
|
2020-02-13 17:08:21 +00:00
|
|
|
export type WatchCallback<V = any, OV = any> = (
|
|
|
|
value: V,
|
|
|
|
oldValue: OV,
|
2020-02-26 15:12:32 +00:00
|
|
|
onInvalidate: InvalidateCbRegistrator
|
2019-10-22 15:26:48 +00:00
|
|
|
) => any
|
2019-05-29 15:44:59 +00:00
|
|
|
|
2020-10-13 19:45:17 +00:00
|
|
|
type MapSources<T, Immediate> = {
|
2020-02-13 17:08:21 +00:00
|
|
|
[K in keyof T]: T[K] extends WatchSource<infer V>
|
2020-02-18 04:14:19 +00:00
|
|
|
? Immediate extends true ? (V | undefined) : V
|
2020-05-18 15:02:51 +00:00
|
|
|
: T[K] extends object
|
|
|
|
? Immediate extends true ? (T[K] | undefined) : T[K]
|
|
|
|
: never
|
2020-02-13 17:08:21 +00:00
|
|
|
}
|
|
|
|
|
2020-02-26 15:12:32 +00:00
|
|
|
type InvalidateCbRegistrator = (cb: () => void) => void
|
2019-12-30 16:30:12 +00:00
|
|
|
|
2020-04-27 17:33:57 +00:00
|
|
|
export interface WatchOptionsBase {
|
2019-05-29 15:44:59 +00:00
|
|
|
flush?: 'pre' | 'post' | 'sync'
|
|
|
|
onTrack?: ReactiveEffectOptions['onTrack']
|
|
|
|
onTrigger?: ReactiveEffectOptions['onTrigger']
|
|
|
|
}
|
|
|
|
|
2020-04-27 17:33:57 +00:00
|
|
|
export interface WatchOptions<Immediate = boolean> extends WatchOptionsBase {
|
2020-02-18 04:14:19 +00:00
|
|
|
immediate?: Immediate
|
|
|
|
deep?: boolean
|
|
|
|
}
|
|
|
|
|
2020-04-27 17:33:57 +00:00
|
|
|
export type WatchStopHandle = () => void
|
2019-08-19 02:49:08 +00:00
|
|
|
|
2020-02-22 07:19:10 +00:00
|
|
|
// Simple effect.
|
|
|
|
export function watchEffect(
|
|
|
|
effect: WatchEffect,
|
2020-04-27 17:33:57 +00:00
|
|
|
options?: WatchOptionsBase
|
|
|
|
): WatchStopHandle {
|
2020-02-22 07:19:10 +00:00
|
|
|
return doWatch(effect, null, options)
|
|
|
|
}
|
|
|
|
|
2020-02-04 14:59:04 +00:00
|
|
|
// initial value for watchers to trigger on undefined initial values
|
|
|
|
const INITIAL_WATCHER_VALUE = {}
|
|
|
|
|
2020-12-04 21:32:26 +00:00
|
|
|
type MultiWatchSources = (WatchSource<unknown> | object)[]
|
|
|
|
|
2021-02-04 06:34:40 +00:00
|
|
|
// overload: array of multiple sources + cb
|
2020-02-13 17:08:21 +00:00
|
|
|
export function watch<
|
2020-12-04 21:32:26 +00:00
|
|
|
T extends MultiWatchSources,
|
2020-02-18 04:14:19 +00:00
|
|
|
Immediate extends Readonly<boolean> = false
|
2020-02-13 17:08:21 +00:00
|
|
|
>(
|
2020-12-04 21:32:26 +00:00
|
|
|
sources: [...T],
|
|
|
|
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
|
|
|
options?: WatchOptions<Immediate>
|
|
|
|
): WatchStopHandle
|
|
|
|
|
2021-02-04 06:34:40 +00:00
|
|
|
// overload: multiple sources w/ `as const`
|
2020-12-04 21:32:26 +00:00
|
|
|
// watch([foo, bar] as const, () => {})
|
|
|
|
// somehow [...T] breaks when the type is readonly
|
|
|
|
export function watch<
|
|
|
|
T extends Readonly<MultiWatchSources>,
|
|
|
|
Immediate extends Readonly<boolean> = false
|
|
|
|
>(
|
|
|
|
source: T,
|
2020-10-13 19:45:17 +00:00
|
|
|
cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>,
|
2020-02-18 04:14:19 +00:00
|
|
|
options?: WatchOptions<Immediate>
|
2020-04-27 17:33:57 +00:00
|
|
|
): WatchStopHandle
|
2019-08-19 02:49:08 +00:00
|
|
|
|
2021-02-04 06:34:40 +00:00
|
|
|
// overload: single source + cb
|
2020-05-04 13:27:28 +00:00
|
|
|
export function watch<T, Immediate extends Readonly<boolean> = false>(
|
|
|
|
source: WatchSource<T>,
|
|
|
|
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
|
|
|
|
options?: WatchOptions<Immediate>
|
|
|
|
): WatchStopHandle
|
|
|
|
|
2021-02-04 06:34:40 +00:00
|
|
|
// overload: watching reactive object w/ cb
|
2020-05-04 13:27:28 +00:00
|
|
|
export function watch<
|
|
|
|
T extends object,
|
|
|
|
Immediate extends Readonly<boolean> = false
|
|
|
|
>(
|
|
|
|
source: T,
|
|
|
|
cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
|
|
|
|
options?: WatchOptions<Immediate>
|
|
|
|
): WatchStopHandle
|
|
|
|
|
2019-08-19 02:49:08 +00:00
|
|
|
// implementation
|
2020-10-13 19:45:17 +00:00
|
|
|
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
|
|
|
|
source: T | WatchSource<T>,
|
|
|
|
cb: any,
|
|
|
|
options?: WatchOptions<Immediate>
|
2020-04-27 17:33:57 +00:00
|
|
|
): WatchStopHandle {
|
2020-02-25 23:57:41 +00:00
|
|
|
if (__DEV__ && !isFunction(cb)) {
|
|
|
|
warn(
|
|
|
|
`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
|
|
|
|
`Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
|
|
|
|
`supports \`watch(source, cb, options?) signature.`
|
|
|
|
)
|
2019-08-19 02:49:08 +00:00
|
|
|
}
|
2020-10-13 19:45:17 +00:00
|
|
|
return doWatch(source as any, cb, options)
|
2019-08-19 02:49:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function doWatch(
|
2020-10-13 19:45:17 +00:00
|
|
|
source: WatchSource | WatchSource[] | WatchEffect | object,
|
2019-12-30 16:30:12 +00:00
|
|
|
cb: WatchCallback | null,
|
2020-07-07 01:50:56 +00:00
|
|
|
{ immediate, deep, flush, onTrack, onTrigger }: WatchOptions = EMPTY_OBJ,
|
|
|
|
instance = currentInstance
|
2020-04-27 17:33:57 +00:00
|
|
|
): WatchStopHandle {
|
2020-02-18 04:14:19 +00:00
|
|
|
if (__DEV__ && !cb) {
|
|
|
|
if (immediate !== undefined) {
|
|
|
|
warn(
|
|
|
|
`watch() "immediate" option is only respected when using the ` +
|
2020-02-24 17:03:02 +00:00
|
|
|
`watch(source, callback, options?) signature.`
|
2020-02-18 04:14:19 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
if (deep !== undefined) {
|
|
|
|
warn(
|
|
|
|
`watch() "deep" option is only respected when using the ` +
|
2020-02-24 17:03:02 +00:00
|
|
|
`watch(source, callback, options?) signature.`
|
2020-02-18 04:14:19 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-18 15:02:51 +00:00
|
|
|
const warnInvalidSource = (s: unknown) => {
|
|
|
|
warn(
|
|
|
|
`Invalid watch source: `,
|
|
|
|
s,
|
|
|
|
`A watch source can only be a getter/effect function, a ref, ` +
|
|
|
|
`a reactive object, or an array of these types.`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-10-08 17:48:13 +00:00
|
|
|
let getter: () => any
|
2020-10-06 22:16:20 +00:00
|
|
|
let forceTrigger = false
|
|
|
|
if (isRef(source)) {
|
2020-07-30 22:29:38 +00:00
|
|
|
getter = () => (source as Ref).value
|
2020-10-06 22:16:20 +00:00
|
|
|
forceTrigger = !!(source as Ref)._shallow
|
2020-07-20 16:39:22 +00:00
|
|
|
} else if (isReactive(source)) {
|
|
|
|
getter = () => source
|
|
|
|
deep = true
|
|
|
|
} else if (isArray(source)) {
|
2019-08-30 19:05:39 +00:00
|
|
|
getter = () =>
|
2020-05-18 15:02:51 +00:00
|
|
|
source.map(s => {
|
|
|
|
if (isRef(s)) {
|
|
|
|
return s.value
|
|
|
|
} else if (isReactive(s)) {
|
|
|
|
return traverse(s)
|
|
|
|
} else if (isFunction(s)) {
|
2021-02-09 07:00:32 +00:00
|
|
|
return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER, [
|
|
|
|
instance && (instance.proxy as any)
|
|
|
|
])
|
2020-05-18 15:02:51 +00:00
|
|
|
} else {
|
|
|
|
__DEV__ && warnInvalidSource(s)
|
|
|
|
}
|
|
|
|
})
|
2020-05-04 13:27:28 +00:00
|
|
|
} else if (isFunction(source)) {
|
|
|
|
if (cb) {
|
|
|
|
// getter with cb
|
|
|
|
getter = () =>
|
2021-02-09 07:00:32 +00:00
|
|
|
callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER, [
|
|
|
|
instance && (instance.proxy as any)
|
|
|
|
])
|
2020-05-04 13:27:28 +00:00
|
|
|
} else {
|
|
|
|
// no cb -> simple effect
|
|
|
|
getter = () => {
|
|
|
|
if (instance && instance.isUnmounted) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (cleanup) {
|
|
|
|
cleanup()
|
|
|
|
}
|
2021-03-23 09:20:52 +00:00
|
|
|
return callWithAsyncErrorHandling(
|
2020-05-04 13:27:28 +00:00
|
|
|
source,
|
|
|
|
instance,
|
|
|
|
ErrorCodes.WATCH_CALLBACK,
|
|
|
|
[onInvalidate]
|
|
|
|
)
|
2019-08-30 19:05:39 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-04 13:27:28 +00:00
|
|
|
} else {
|
|
|
|
getter = NOOP
|
2020-05-18 15:02:51 +00:00
|
|
|
__DEV__ && warnInvalidSource(source)
|
2019-08-30 19:05:39 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 16:24:45 +00:00
|
|
|
// 2.x array mutation watch compat
|
|
|
|
if (__COMPAT__ && cb && !deep) {
|
|
|
|
const baseGetter = getter
|
|
|
|
getter = () => {
|
|
|
|
const val = baseGetter()
|
2021-04-07 19:38:04 +00:00
|
|
|
if (
|
|
|
|
isArray(val) &&
|
2021-04-10 03:21:13 +00:00
|
|
|
softAssertCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance)
|
2021-04-07 19:38:04 +00:00
|
|
|
) {
|
|
|
|
traverse(val)
|
2021-04-07 16:24:45 +00:00
|
|
|
}
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-24 17:03:02 +00:00
|
|
|
if (cb && deep) {
|
2019-08-30 19:05:39 +00:00
|
|
|
const baseGetter = getter
|
|
|
|
getter = () => traverse(baseGetter())
|
|
|
|
}
|
|
|
|
|
2020-03-23 15:08:22 +00:00
|
|
|
let cleanup: () => void
|
2021-03-25 15:17:57 +00:00
|
|
|
let onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
|
2019-10-30 15:29:08 +00:00
|
|
|
cleanup = runner.options.onStop = () => {
|
2019-09-06 16:58:31 +00:00
|
|
|
callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
|
2019-08-30 19:05:39 +00:00
|
|
|
}
|
2019-06-06 07:19:04 +00:00
|
|
|
}
|
|
|
|
|
2020-02-18 04:14:19 +00:00
|
|
|
// in SSR there is no need to setup an actual effect, and it should be noop
|
|
|
|
// unless it's eager
|
|
|
|
if (__NODE_JS__ && isInSSRComponentSetup) {
|
2021-03-25 15:17:57 +00:00
|
|
|
// we will also not call the invalidate callback (+ runner is not set up)
|
|
|
|
onInvalidate = NOOP
|
2020-02-18 04:14:19 +00:00
|
|
|
if (!cb) {
|
|
|
|
getter()
|
|
|
|
} else if (immediate) {
|
|
|
|
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
|
|
|
|
getter(),
|
|
|
|
undefined,
|
2020-02-26 15:12:32 +00:00
|
|
|
onInvalidate
|
2020-02-18 04:14:19 +00:00
|
|
|
])
|
|
|
|
}
|
|
|
|
return NOOP
|
|
|
|
}
|
|
|
|
|
2020-02-04 14:59:04 +00:00
|
|
|
let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE
|
2020-07-30 21:54:05 +00:00
|
|
|
const job: SchedulerJob = () => {
|
2020-07-17 15:17:29 +00:00
|
|
|
if (!runner.active) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (cb) {
|
|
|
|
// watch(source, cb)
|
|
|
|
const newValue = runner()
|
2021-04-07 16:24:45 +00:00
|
|
|
if (
|
|
|
|
deep ||
|
|
|
|
forceTrigger ||
|
|
|
|
hasChanged(newValue, oldValue) ||
|
|
|
|
(__COMPAT__ &&
|
|
|
|
isArray(newValue) &&
|
2021-04-10 03:21:13 +00:00
|
|
|
isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))
|
2021-04-07 16:24:45 +00:00
|
|
|
) {
|
2020-07-17 15:17:29 +00:00
|
|
|
// cleanup before running cb again
|
|
|
|
if (cleanup) {
|
|
|
|
cleanup()
|
2019-05-29 15:44:59 +00:00
|
|
|
}
|
2020-07-17 15:17:29 +00:00
|
|
|
callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
|
|
|
|
newValue,
|
|
|
|
// pass undefined as the old value when it's changed for the first time
|
|
|
|
oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
|
|
|
|
onInvalidate
|
|
|
|
])
|
|
|
|
oldValue = newValue
|
2019-05-29 15:44:59 +00:00
|
|
|
}
|
2020-07-17 15:17:29 +00:00
|
|
|
} else {
|
|
|
|
// watchEffect
|
|
|
|
runner()
|
|
|
|
}
|
|
|
|
}
|
2019-05-29 15:44:59 +00:00
|
|
|
|
2020-10-13 19:45:17 +00:00
|
|
|
// important: mark the job as a watcher callback so that scheduler knows
|
2020-07-30 21:54:05 +00:00
|
|
|
// it is allowed to self-trigger (#1727)
|
2020-08-13 21:27:14 +00:00
|
|
|
job.allowRecurse = !!cb
|
2020-07-30 21:54:05 +00:00
|
|
|
|
2020-10-13 19:45:17 +00:00
|
|
|
let scheduler: ReactiveEffectOptions['scheduler']
|
2019-09-11 17:22:18 +00:00
|
|
|
if (flush === 'sync') {
|
2020-07-19 17:30:24 +00:00
|
|
|
scheduler = job
|
2020-09-18 03:17:21 +00:00
|
|
|
} else if (flush === 'post') {
|
|
|
|
scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
|
|
|
|
} else {
|
|
|
|
// default: 'pre'
|
2020-07-17 15:17:29 +00:00
|
|
|
scheduler = () => {
|
2020-03-18 22:14:51 +00:00
|
|
|
if (!instance || instance.isMounted) {
|
2020-08-05 14:55:23 +00:00
|
|
|
queuePreFlushCb(job)
|
2019-09-11 17:22:18 +00:00
|
|
|
} else {
|
|
|
|
// with 'pre' option, the first call must happen before
|
|
|
|
// the component is mounted so it is called synchronously.
|
|
|
|
job()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-19 18:44:52 +00:00
|
|
|
|
2019-05-29 15:44:59 +00:00
|
|
|
const runner = effect(getter, {
|
|
|
|
lazy: true,
|
2019-06-06 07:19:04 +00:00
|
|
|
onTrack,
|
|
|
|
onTrigger,
|
2020-07-17 15:17:29 +00:00
|
|
|
scheduler
|
2019-05-29 15:44:59 +00:00
|
|
|
})
|
|
|
|
|
2020-11-27 14:31:50 +00:00
|
|
|
recordInstanceBoundEffect(runner, instance)
|
2020-02-18 04:14:19 +00:00
|
|
|
|
|
|
|
// initial run
|
2020-07-17 15:17:29 +00:00
|
|
|
if (cb) {
|
2020-02-18 04:14:19 +00:00
|
|
|
if (immediate) {
|
2020-07-17 15:17:29 +00:00
|
|
|
job()
|
2019-08-19 18:44:52 +00:00
|
|
|
} else {
|
2020-02-18 04:14:19 +00:00
|
|
|
oldValue = runner()
|
2019-08-19 18:44:52 +00:00
|
|
|
}
|
2020-09-18 03:17:21 +00:00
|
|
|
} else if (flush === 'post') {
|
|
|
|
queuePostRenderEffect(runner, instance && instance.suspense)
|
2020-02-18 04:14:19 +00:00
|
|
|
} else {
|
|
|
|
runner()
|
2019-05-29 15:44:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
stop(runner)
|
2020-01-08 17:06:16 +00:00
|
|
|
if (instance) {
|
2020-02-18 18:52:59 +00:00
|
|
|
remove(instance.effects!, runner)
|
2020-01-08 17:06:16 +00:00
|
|
|
}
|
2019-05-29 15:44:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-04 15:36:27 +00:00
|
|
|
// this.$watch
|
|
|
|
export function instanceWatch(
|
2019-09-06 16:58:31 +00:00
|
|
|
this: ComponentInternalInstance,
|
2019-09-04 15:36:27 +00:00
|
|
|
source: string | Function,
|
2020-10-13 19:45:17 +00:00
|
|
|
cb: WatchCallback,
|
2019-09-04 15:36:27 +00:00
|
|
|
options?: WatchOptions
|
2020-04-27 17:33:57 +00:00
|
|
|
): WatchStopHandle {
|
2020-04-17 13:12:50 +00:00
|
|
|
const publicThis = this.proxy as any
|
|
|
|
const getter = isString(source)
|
2021-04-07 20:19:04 +00:00
|
|
|
? source.includes('.')
|
|
|
|
? createPathGetter(publicThis, source)
|
|
|
|
: () => publicThis[source]
|
2020-04-17 13:12:50 +00:00
|
|
|
: source.bind(publicThis)
|
2020-07-07 01:50:56 +00:00
|
|
|
return doWatch(getter, cb.bind(publicThis), options, this)
|
2019-09-04 15:36:27 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 20:19:04 +00:00
|
|
|
export function createPathGetter(ctx: any, path: string) {
|
|
|
|
const segments = path.split('.')
|
|
|
|
return () => {
|
|
|
|
let cur = ctx
|
|
|
|
for (let i = 0; i < segments.length && cur; i++) {
|
|
|
|
cur = cur[segments[i]]
|
|
|
|
}
|
|
|
|
return cur
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 15:26:48 +00:00
|
|
|
function traverse(value: unknown, seen: Set<unknown> = new Set()) {
|
2019-05-29 15:44:59 +00:00
|
|
|
if (!isObject(value) || seen.has(value)) {
|
2020-04-17 13:55:41 +00:00
|
|
|
return value
|
|
|
|
}
|
2019-05-29 15:44:59 +00:00
|
|
|
seen.add(value)
|
2020-08-23 18:41:11 +00:00
|
|
|
if (isRef(value)) {
|
|
|
|
traverse(value.value, seen)
|
|
|
|
} else if (isArray(value)) {
|
2019-05-29 15:44:59 +00:00
|
|
|
for (let i = 0; i < value.length; i++) {
|
|
|
|
traverse(value[i], seen)
|
|
|
|
}
|
2020-10-13 20:06:36 +00:00
|
|
|
} else if (isSet(value) || isMap(value)) {
|
|
|
|
value.forEach((v: any) => {
|
2019-05-29 15:44:59 +00:00
|
|
|
traverse(v, seen)
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
for (const key in value) {
|
|
|
|
traverse(value[key], seen)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value
|
|
|
|
}
|