UNPKG

mutative

Version:

A JavaScript library for efficient immutable updates

188 lines (183 loc) 5.99 kB
import { ProxyDraft } from './interface'; import { dataTypes, iteratorSymbol } from './constant'; import { internal } from './internal'; import { ensureShallowCopy, getProxyDraft, isDraftable, markChanged, markFinalization, } from './utils'; import { checkReadable } from './unsafe'; import { generatePatches } from './patch'; const getNextIterator = ( target: ProxyDraft<any>, iterator: IterableIterator<any>, { isValuesIterator }: { isValuesIterator: boolean } ) => () => { const result = iterator.next(); if (result.done) return result; const key = result.value as any; let value = target.setMap!.get(key); const currentDraft = getProxyDraft(value); const mutable = target.options.mark?.(value, dataTypes) === dataTypes.mutable; if (target.options.strict) { checkReadable(key, target.options, mutable); } if ( !mutable && !currentDraft && isDraftable(key, target.options) && !target.finalized && target.original!.has(key) ) { // draft a draftable original set item const proxy = internal.createDraft({ original: key, parentDraft: target, key, finalities: target.finalities, options: target.options, }); target.setMap!.set(key, proxy); value = proxy; } else if (currentDraft) { // drafted value = currentDraft.proxy; } return { done: false, value: isValuesIterator ? value : [value, value], }; }; export const setHandler = { get size() { const target: ProxyDraft<any> = getProxyDraft(this)!; return target.setMap!.size; }, has(value: any) { const target = getProxyDraft(this)!; // reassigned or non-draftable values if (target.setMap!.has(value)) return true; ensureShallowCopy(target); const valueProxyDraft = getProxyDraft(value)!; // drafted if (valueProxyDraft && target.setMap!.has(valueProxyDraft.original)) return true; return false; }, add(value: any) { const target = getProxyDraft(this)!; if (!this.has(value)) { ensureShallowCopy(target); markChanged(target); target.assignedMap!.set(value, true); target.setMap!.set(value, value); markFinalization(target, value, value, generatePatches); } return this; }, delete(value: any): boolean { if (!this.has(value)) { return false; } const target = getProxyDraft(this)!; ensureShallowCopy(target); markChanged(target); const valueProxyDraft = getProxyDraft(value)!; if (valueProxyDraft && target.setMap!.has(valueProxyDraft.original)) { // delete drafted target.assignedMap!.set(valueProxyDraft.original, false); return target.setMap!.delete(valueProxyDraft.original); } if (!valueProxyDraft && target.setMap!.has(value)) { // non-draftable values target.assignedMap!.set(value, false); } else { // reassigned target.assignedMap!.delete(value); } // delete reassigned or non-draftable values return target.setMap!.delete(value); }, clear() { if (!this.size) return; const target = getProxyDraft(this)!; ensureShallowCopy(target); markChanged(target); for (const value of target.original) { target.assignedMap!.set(value, false); } target.setMap!.clear(); }, values(): IterableIterator<any> { const target = getProxyDraft(this)!; ensureShallowCopy(target); const iterator = target.setMap!.keys(); return { [Symbol.iterator]: () => this.values(), next: getNextIterator(target, iterator, { isValuesIterator: true }), }; }, entries(): IterableIterator<[any, any]> { const target = getProxyDraft(this)!; ensureShallowCopy(target); const iterator = target.setMap!.keys(); return { [Symbol.iterator]: () => this.entries(), next: getNextIterator(target, iterator, { isValuesIterator: false, }) as () => IteratorReturnResult<any>, }; }, keys(): IterableIterator<any> { return this.values(); }, [iteratorSymbol]() { return this.values(); }, forEach(callback: any, thisArg?: any) { const iterator = this.values(); let result = iterator.next(); while (!result.done) { callback.call(thisArg, result.value, result.value, this); result = iterator.next(); } }, }; if (Set.prototype.difference) { // for compatibility with new Set methods // https://github.com/tc39/proposal-set-methods // And `https://github.com/tc39/proposal-set-methods/blob/main/details.md#symbolspecies` has some details about the `@@species` symbol. // So we can't use SubSet instance constructor to get the constructor of the SubSet instance. Object.assign(setHandler, { intersection(this: Set<any>, other: ReadonlySetLike<any>): Set<any> { return Set.prototype.intersection.call(new Set(this.values()), other); }, union(this: Set<any>, other: ReadonlySetLike<any>): Set<any> { return Set.prototype.union.call(new Set(this.values()), other); }, difference(this: Set<any>, other: ReadonlySetLike<any>): Set<any> { return Set.prototype.difference.call(new Set(this.values()), other); }, symmetricDifference(this: Set<any>, other: ReadonlySetLike<any>): Set<any> { return Set.prototype.symmetricDifference.call( new Set(this.values()), other ); }, isSubsetOf(this: Set<any>, other: ReadonlySetLike<any>): boolean { return Set.prototype.isSubsetOf.call(new Set(this.values()), other); }, isSupersetOf(this: Set<any>, other: ReadonlySetLike<any>): boolean { return Set.prototype.isSupersetOf.call(new Set(this.values()), other); }, isDisjointFrom(this: Set<any>, other: ReadonlySetLike<any>): boolean { return Set.prototype.isDisjointFrom.call(new Set(this.values()), other); }, }); } export const setHandlerKeys = Reflect.ownKeys(setHandler);