UNPKG

@v4fire/core

Version:
306 lines (243 loc) 6.66 kB
/*! * V4Fire Core * https://github.com/V4Fire/Core * * Released under the MIT license * https://github.com/V4Fire/Core/blob/master/LICENSE */ import * as proxyEngine from 'core/object/watch/engines/proxy'; import { getProxyValue } from 'core/object/watch/engines/helpers'; import type { WrapParams, WrapResult, StructureWrappers } from 'core/object/watch/wrap/interface'; export const iterators = { keys: { type: 'get', value(target: unknown[], opts: WrapParams): IterableIterator<unknown> { const iter = createIter(); return { [Symbol.iterator]() { return this; }, next: iter.next.bind(iter) }; function* createIter() { const iter = <IterableIterator<unknown>>opts.original.call(target); for (let el = iter.next(); !el.done; el = iter.next()) { yield el.value; } } } }, entries: { type: 'get', value(target: unknown[], opts: WrapParams): IterableIterator<[unknown, unknown]> { const iter = createIter(); return { [Symbol.iterator]() { return this; }, next: iter.next.bind(iter) }; function* createIter() { const iter = <IterableIterator<[unknown, unknown]>>opts.original.call(target); for (let el = iter.next(); !el.done; el = iter.next()) { const [key, val] = el.value; yield [ key, getProxyValue(val, key, opts.path, opts.handlers, opts.root, opts.top ?? opts.root, opts.watchOpts) ]; } } } }, values: { type: 'get', value(target: unknown[]): IterableIterator<unknown> { const iter = createIter(); return { [Symbol.iterator]() { return this; }, next: iter.next.bind(iter) }; function* createIter() { const iter = target.entries(); for (let el = iter.next(); !el.done; el = iter.next()) { const [, val] = el.value; yield val; } } } }, [Symbol.iterator]: { type: 'get', value: (target: unknown[]): IterableIterator<unknown> => Object.isMap(target) ? target.entries() : target.values() } }; export const deleteMethods = { delete: ( target: Map<unknown, unknown> | Set<unknown>, opts: WrapParams, key: unknown ): Nullable<WrapResult> => { if (target.has(key)) { return [[undefined, 'get' in target ? target.get(key) : key, [...opts.path, key]]]; } return null; } }; export const clearMethods = { clear: (target: Set<unknown>, opts: WrapParams): Nullable<WrapResult> => { if (target.size !== 0) { return [[undefined, undefined, opts.path]]; } return null; } }; export const weakMapMethods = { ...deleteMethods, get: { type: 'get', value: (target: WeakMap<any, any>, opts: WrapParams, key: unknown): unknown => { const val = opts.original.call(target, key); return getProxyValue(val, key, opts.path, opts.handlers, opts.root, opts.top ?? opts.root, opts.watchOpts); } }, set: (target: WeakMap<any, any>, opts: WrapParams, key: unknown, value: unknown): Nullable<WrapResult> => { const oldVal = target.get(key); return oldVal !== value ? [[value, oldVal, [...opts.path, key]]] : null; } }; export const weakSetMethods = { ...deleteMethods, add: (target: WeakMap<any, any>, opts: WrapParams, value: unknown): Nullable<WrapResult> => { if (!target.has(value)) { return [[value, undefined, [...opts.path, value]]]; } return null; } }; export const structureWrappers = Object.createDict<StructureWrappers>({ weakMap: { is: Object.isWeakMap.bind(Object), methods: weakMapMethods }, weakSet: { is: Object.isWeakSet.bind(Object), methods: weakSetMethods }, map: { is: Object.isMap.bind(Object), methods: { ...iterators, ...weakMapMethods, ...clearMethods } }, set: { is: Object.isSet.bind(Object), methods: { ...iterators, ...weakSetMethods, ...clearMethods } }, arrayProxyEngine: { is: (val, opts) => Object.isArray(val) && opts.watchOpts.engine === proxyEngine, methods: { ...iterators } }, array: { is: (val, opts) => Object.isArray(val) && opts.watchOpts.engine !== proxyEngine, methods: { ...iterators, push: (target: unknown[], opts: WrapParams, ...value: unknown[]): Nullable<WrapResult> => { const res = <WrapResult>[]; for (let i = 0; i < value.length; i++) { res.push([value[i], undefined, [...opts.path, target.length + i]]); } return res; }, pop: (target: unknown[], opts: WrapParams): Nullable<WrapResult> => { const l = target.length; return l > 0 ? [[l - 1, l, [...opts.path, 'length']]] : null; }, unshift: (target: unknown[], opts: WrapParams, ...value: unknown[]): Nullable<WrapResult> => { const res = <WrapResult>[]; for (let i = target.length - 1; i >= 0; i--) { res.push([target[i], target[i + value.length], [...opts.path, i + value.length]]); } for (let i = 0; i < value.length; i++) { res.push([value[i], target[i], [...opts.path, i]]); } return res; }, shift: (target: unknown[], opts: WrapParams): Nullable<WrapResult> => { if (target.length > 0) { const l = target.length, res = <WrapResult>[]; for (let i = 1; i < l; i++) { res.push([target[i], target[i - 1], [...opts.path, i - 1]]); } res.push([l - 1, l, [...opts.path, 'length']]); return res; } return null; }, splice: ( target: unknown[], opts: WrapParams, start?: number, deleteNumber?: number, ...newEls: unknown[] ): Nullable<WrapResult> => { const targetLength = target.length; if (start == null || start >= targetLength) { return null; } deleteNumber ??= target.length; if (deleteNumber <= 0 && newEls.length === 0) { return null; } const newLength = targetLength + newEls.length - deleteNumber; const res = <WrapResult>[]; const rightBorder = start + deleteNumber >= targetLength ? target.length - 1 : start + deleteNumber; if (newLength > targetLength) { const diff = newLength - targetLength; for (let i = targetLength - 1; i >= rightBorder; i--) { res.push([target[i], undefined, [...opts.path, i + diff]]); } } else { for (let i = rightBorder; i < targetLength; i++) { const newI = i - deleteNumber + 1; res.push([target[i], target[newI], [...opts.path, newI]]); } } for (let i = start, j = 0; j < newEls.length; i++, j++) { res.push([newEls[j], target[i], [...opts.path, i]]); } if (newLength < targetLength) { res.push([newLength, targetLength, [...opts.path, 'length']]); } return res; } } } });