UNPKG

mutative

Version:

A JavaScript library for efficient immutable updates

180 lines (175 loc) 5.15 kB
import { DraftType, Operation, Patches, ProxyDraft } from './interface'; import { cloneIfNeeded, escapePath, get, has, isEqual } from './utils'; function generateArrayPatches( proxyState: ProxyDraft<Array<any>>, basePath: any[], patches: Patches, inversePatches: Patches, pathAsArray: boolean ) { let { original, assignedMap, options } = proxyState; let copy = proxyState.copy!; if (copy.length < original.length) { [original, copy] = [copy, original]; [patches, inversePatches] = [inversePatches, patches]; } for (let index = 0; index < original.length; index += 1) { if (assignedMap!.get(index.toString()) && copy[index] !== original[index]) { const _path = basePath.concat([index]); const path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Replace, path, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(copy[index]), }); inversePatches.push({ op: Operation.Replace, path, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(original[index]), }); } } for (let index = original.length; index < copy.length; index += 1) { const _path = basePath.concat([index]); const path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Add, path, // If it is a draft, it needs to be deep cloned, and it may also be non-draft. value: cloneIfNeeded(copy[index]), }); } if (original.length < copy.length) { // https://www.rfc-editor.org/rfc/rfc6902#appendix-A.4 // For performance, here we only generate an operation that replaces the length of the array, // which is inconsistent with JSON Patch specification const { arrayLengthAssignment = true } = options.enablePatches; if (arrayLengthAssignment) { const _path = basePath.concat(['length']); const path = escapePath(_path, pathAsArray); inversePatches.push({ op: Operation.Replace, path, value: original.length, }); } else { for (let index = copy.length; original.length < index; index -= 1) { const _path = basePath.concat([index - 1]); const path = escapePath(_path, pathAsArray); inversePatches.push({ op: Operation.Remove, path, }); } } } } function generatePatchesFromAssigned( { original, copy, assignedMap }: ProxyDraft<Record<string, any>>, basePath: any[], patches: Patches, inversePatches: Patches, pathAsArray: boolean ) { assignedMap!.forEach((assignedValue, key) => { const originalValue = get(original, key); const value = cloneIfNeeded(get(copy, key)); const op = !assignedValue ? Operation.Remove : has(original, key) ? Operation.Replace : Operation.Add; if (isEqual(originalValue, value) && op === Operation.Replace) return; const _path = basePath.concat(key); const path = escapePath(_path, pathAsArray); patches.push(op === Operation.Remove ? { op, path } : { op, path, value }); inversePatches.push( op === Operation.Add ? { op: Operation.Remove, path } : op === Operation.Remove ? { op: Operation.Add, path, value: originalValue } : { op: Operation.Replace, path, value: originalValue } ); }); } function generateSetPatches( { original, copy }: ProxyDraft<Set<any>>, basePath: any[], patches: Patches, inversePatches: Patches, pathAsArray: boolean ) { let index = 0; original.forEach((value: any) => { if (!copy!.has(value)) { const _path = basePath.concat([index]); const path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Remove, path, value, }); inversePatches.unshift({ op: Operation.Add, path, value, }); } index += 1; }); index = 0; copy!.forEach((value: any) => { if (!original.has(value)) { const _path = basePath.concat([index]); const path = escapePath(_path, pathAsArray); patches.push({ op: Operation.Add, path, value, }); inversePatches.unshift({ op: Operation.Remove, path, value, }); } index += 1; }); } export function generatePatches( proxyState: ProxyDraft, basePath: any[], patches: Patches, inversePatches: Patches ) { const { pathAsArray = true } = proxyState.options.enablePatches; switch (proxyState.type) { case DraftType.Object: case DraftType.Map: return generatePatchesFromAssigned( proxyState, basePath, patches, inversePatches, pathAsArray ); case DraftType.Array: return generateArrayPatches( proxyState, basePath, patches, inversePatches, pathAsArray ); case DraftType.Set: return generateSetPatches( proxyState, basePath, patches, inversePatches, pathAsArray ); } }