@v4fire/core
Version:
V4Fire core library
306 lines (243 loc) • 6.66 kB
text/typescript
/*!
* 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;
}
}
}
});