UNPKG

immer

Version:

Create your next immutable state by mutating the current one

187 lines (170 loc) 4.77 kB
import { DRAFT_STATE, DRAFTABLE, hasSet, Objectish, Drafted, AnyObject, AnyArray, AnyMap, AnySet, ImmerState, hasMap, ArchtypeObject, ArchtypeArray, ArchtypeMap, ArchtypeSet, die } from "../internal" /** Returns true if the given value is an Immer draft */ /*#__PURE__*/ export function isDraft(value: any): boolean { return !!value && !!value[DRAFT_STATE] } /** Returns true if the given value can be drafted by Immer */ /*#__PURE__*/ export function isDraftable(value: any): boolean { if (!value) return false return ( isPlainObject(value) || Array.isArray(value) || !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE] || isMap(value) || isSet(value) ) } /*#__PURE__*/ export function isPlainObject(value: any): boolean { if (!value || typeof value !== "object") return false const proto = Object.getPrototypeOf(value) return !proto || proto === Object.prototype } /** Get the underlying object that is represented by the given draft */ /*#__PURE__*/ export function original<T>(value: T): T | undefined export function original(value: Drafted<any>): any { if (value && value[DRAFT_STATE]) { return value[DRAFT_STATE].base_ as any } // otherwise return undefined } /*#__PURE__*/ export const ownKeys: (target: AnyObject) => PropertyKey[] = typeof Reflect !== "undefined" && Reflect.ownKeys ? Reflect.ownKeys : typeof Object.getOwnPropertySymbols !== "undefined" ? obj => Object.getOwnPropertyNames(obj).concat( Object.getOwnPropertySymbols(obj) as any ) : /* istanbul ignore next */ Object.getOwnPropertyNames export function each<T extends Objectish>( obj: T, iter: (key: string | number, value: any, source: T) => void ): void export function each(obj: any, iter: any) { if (getArchtype(obj) === ArchtypeObject) { ownKeys(obj).forEach(key => iter(key, obj[key], obj)) } else { obj.forEach((entry: any, index: any) => iter(index, entry, obj)) } } /*#__PURE__*/ export function getArchtype(thing: any): 0 | 1 | 2 | 3 { /* istanbul ignore next */ const state: undefined | ImmerState = thing[DRAFT_STATE] return state ? state.type_ > 3 ? state.type_ - 4 // cause Object and Array map back from 4 and 5 : (state.type_ as any) // others are the same : Array.isArray(thing) ? ArchtypeArray : isMap(thing) ? ArchtypeMap : isSet(thing) ? ArchtypeSet : ArchtypeObject } /*#__PURE__*/ export function has(thing: any, prop: PropertyKey): boolean { return getArchtype(thing) === ArchtypeMap ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop) } /*#__PURE__*/ export function get(thing: AnyMap | AnyObject, prop: PropertyKey): any { // @ts-ignore return getArchtype(thing) === ArchtypeMap ? thing.get(prop) : thing[prop] } /*#__PURE__*/ export function set(thing: any, propOrOldValue: PropertyKey, value: any) { const t = getArchtype(thing) if (t === ArchtypeMap) thing.set(propOrOldValue, value) else if (t === ArchtypeSet) { thing.delete(propOrOldValue) thing.add(value) } else thing[propOrOldValue] = value } /*#__PURE__*/ export function is(x: any, y: any): boolean { // From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js if (x === y) { return x !== 0 || 1 / x === 1 / y } else { return x !== x && y !== y } } /*#__PURE__*/ export function isMap(target: any): target is AnyMap { return hasMap && target instanceof Map } /*#__PURE__*/ export function isSet(target: any): target is AnySet { return hasSet && target instanceof Set } /*#__PURE__*/ export function latest(state: ImmerState): any { return state.copy_ || state.base_ } /*#__PURE__*/ export function shallowCopy<T extends AnyObject | AnyArray>( base: T, invokeGetters?: boolean ): T export function shallowCopy(base: any, invokeGetters = false) { if (Array.isArray(base)) return base.slice() const clone = Object.create(Object.getPrototypeOf(base)) each(base, (key: any) => { if (key === DRAFT_STATE) { return // Never copy over draft state. } const desc = Object.getOwnPropertyDescriptor(base, key)! let {value} = desc if (desc.get) { if (!invokeGetters) die(1) value = desc.get.call(base) } if (desc.enumerable) { clone[key] = value } else { Object.defineProperty(clone, key, { value, writable: true, configurable: true }) } }) return clone } export function freeze(obj: any, deep: boolean): void { if (isDraft(obj) || Object.isFrozen(obj) || !isDraftable(obj)) return if (getArchtype(obj) > 1 /* Map or Set */) { obj.set = obj.add = obj.clear = obj.delete = dontMutateFrozenCollections as any } Object.freeze(obj) if (deep) each(obj, (_, value) => freeze(value, true)) } function dontMutateFrozenCollections() { die(2) }