immer-peasy
Version:
Easy Peasy friendly modification of immer
1,096 lines (892 loc) • 32 kB
JavaScript
var obj;
var NOTHING = typeof Symbol !== "undefined" ? Symbol("immer-nothing") : ( obj = {}, obj["immer-nothing"] = true, obj );
var DRAFTABLE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-draftable") : "__$immer_draftable";
var DRAFT_STATE = typeof Symbol !== "undefined" && Symbol.for ? Symbol.for("immer-state") : "__$immer_state";
function isDraft(value) {
return !!value && !!value[DRAFT_STATE];
}
function isDraftable(value) {
if (!value || typeof value !== "object") { return false; }
if (Array.isArray(value)) { return true; }
var proto = Object.getPrototypeOf(value);
if (!proto || proto === Object.prototype) { return true; }
return !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE];
}
function original(value) {
if (value && value[DRAFT_STATE]) {
return value[DRAFT_STATE].base;
} // otherwise return undefined
}
var assign = Object.assign || function assign(target, value) {
for (var key in value) {
if (has(value, key)) {
target[key] = value[key];
}
}
return target;
};
var ownKeys = typeof Reflect !== "undefined" && Reflect.ownKeys ? Reflect.ownKeys : typeof Object.getOwnPropertySymbols !== "undefined" ? function (obj) { return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); } : Object.getOwnPropertyNames;
function shallowCopy(base, invokeGetters) {
if ( invokeGetters === void 0 ) invokeGetters = false;
if (Array.isArray(base)) { return base.slice(); }
var clone = Object.create(Object.getPrototypeOf(base));
ownKeys(base).forEach(function (key) {
if (key === DRAFT_STATE) {
return; // Never copy over draft state.
}
var desc = Object.getOwnPropertyDescriptor(base, key);
var value = desc.value;
if (desc.get) {
if (invokeGetters) {
value = desc.get.call(base);
}
}
if (desc.enumerable) {
clone[key] = value;
} else if (invokeGetters) {
Object.defineProperty(clone, key, {
value: value,
writable: true,
configurable: true
});
}
});
return clone;
}
function each(value, cb) {
if (Array.isArray(value)) {
for (var i = 0; i < value.length; i++) { cb(i, value[i], value); }
} else {
ownKeys(value).forEach(function (key) { return cb(key, value[key], value); });
}
}
function isEnumerable(base, prop) {
var desc = Object.getOwnPropertyDescriptor(base, prop);
return !!desc && desc.enumerable;
}
function has(thing, prop) {
return Object.prototype.hasOwnProperty.call(thing, prop);
}
function is(x, y) {
// 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;
}
}
/** Each scope represents a `produce` call. */
var ImmerScope = function ImmerScope(parent) {
this.drafts = [];
this.parent = parent; // Whenever the modified draft contains a draft from another scope, we
// need to prevent auto-freezing so the unowned draft can be finalized.
this.canAutoFreeze = true; // To avoid prototype lookups:
this.patches = null;
};
ImmerScope.prototype.usePatches = function usePatches (patchListener) {
if (patchListener) {
this.patches = [];
this.inversePatches = [];
this.patchListener = patchListener;
}
};
ImmerScope.prototype.revoke = function revoke$1 () {
this.leave();
this.drafts.forEach(revoke);
this.drafts = null; // Make draft-related methods throw.
};
ImmerScope.prototype.leave = function leave () {
if (this === ImmerScope.current) {
ImmerScope.current = this.parent;
}
};
ImmerScope.current = null;
ImmerScope.enter = function () {
return this.current = new ImmerScope(this.current);
};
function revoke(draft) {
draft[DRAFT_STATE].revoke();
}
// but share them all instead
var descriptors = {};
function willFinalize(scope, result, isReplaced) {
scope.drafts.forEach(function (draft) {
draft[DRAFT_STATE].finalizing = true;
});
if (!isReplaced) {
if (scope.patches) {
markChangesRecursively(scope.drafts[0]);
} // This is faster when we don't care about which attributes changed.
markChangesSweep(scope.drafts);
} // When a child draft is returned, look for changes.
else if (isDraft(result) && result[DRAFT_STATE].scope === scope) {
markChangesSweep(scope.drafts);
}
}
function createProxy(base, parent) {
var isArray = Array.isArray(base);
var draft = clonePotentialDraft(base);
each(draft, function (prop) {
proxyProperty(draft, prop, isArray || isEnumerable(base, prop));
}); // See "proxy.js" for property documentation.
var scope = parent ? parent.scope : ImmerScope.current;
var state = {
scope: scope,
modified: false,
finalizing: false,
// es5 only
finalized: false,
assigned: {},
parent: parent,
base: base,
draft: draft,
copy: null,
revoke: revoke$1,
revoked: false // es5 only
};
createHiddenProperty(draft, DRAFT_STATE, state);
scope.drafts.push(draft);
return draft;
}
function revoke$1() {
this.revoked = true;
}
function source(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
function peek(draft, prop) {
var state = draft[DRAFT_STATE];
if (state && !state.finalizing) {
state.finalizing = true;
var value = draft[prop];
state.finalizing = false;
return value;
}
return draft[prop];
}
function get(state, prop) {
assertUnrevoked(state);
var value = peek(source(state), prop);
if (state.finalizing) { return value; } // Create a draft if the value is unmodified.
if (value === peek(state.base, prop) && isDraftable(value)) {
prepareCopy(state);
return state.copy[prop] = createProxy(value, state);
}
return value;
}
function set(state, prop, value) {
assertUnrevoked(state);
state.assigned[prop] = true;
if (!state.modified) {
if (is(value, peek(source(state), prop))) { return; }
markChanged(state);
prepareCopy(state);
}
state.copy[prop] = value;
}
function markChanged(state) {
if (!state.modified) {
state.modified = true;
if (state.parent) { markChanged(state.parent); }
}
}
function prepareCopy(state) {
if (!state.copy) { state.copy = clonePotentialDraft(state.base); }
}
function clonePotentialDraft(base) {
var state = base && base[DRAFT_STATE];
if (state) {
state.finalizing = true;
var draft = shallowCopy(state.draft, true);
state.finalizing = false;
return draft;
}
return shallowCopy(base);
}
function proxyProperty(draft, prop, enumerable) {
var desc = descriptors[prop];
if (desc) {
desc.enumerable = enumerable;
} else {
descriptors[prop] = desc = {
configurable: true,
enumerable: enumerable,
get: function get$1() {
return get(this[DRAFT_STATE], prop);
},
set: function set$1(value) {
set(this[DRAFT_STATE], prop, value);
}
};
}
Object.defineProperty(draft, prop, desc);
}
function assertUnrevoked(state) {
if (state.revoked === true) { throw new Error("Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + JSON.stringify(source(state))); }
} // This looks expensive, but only proxies are visited, and only objects without known changes are scanned.
function markChangesSweep(drafts) {
// The natural order of drafts in the `scope` array is based on when they
// were accessed. By processing drafts in reverse natural order, we have a
// better chance of processing leaf nodes first. When a leaf node is known to
// have changed, we can avoid any traversal of its ancestor nodes.
for (var i = drafts.length - 1; i >= 0; i--) {
var state = drafts[i][DRAFT_STATE];
if (!state.modified) {
if (Array.isArray(state.base)) {
if (hasArrayChanges(state)) { markChanged(state); }
} else if (hasObjectChanges(state)) { markChanged(state); }
}
}
}
function markChangesRecursively(object) {
if (!object || typeof object !== "object") { return; }
var state = object[DRAFT_STATE];
if (!state) { return; }
var base = state.base;
var draft = state.draft;
var assigned = state.assigned;
if (!Array.isArray(object)) {
// Look for added keys.
Object.keys(draft).forEach(function (key) {
// The `undefined` check is a fast path for pre-existing keys.
if (base[key] === undefined && !has(base, key)) {
assigned[key] = true;
markChanged(state);
} else if (!assigned[key]) {
// Only untouched properties trigger recursion.
markChangesRecursively(draft[key]);
}
}); // Look for removed keys.
Object.keys(base).forEach(function (key) {
// The `undefined` check is a fast path for pre-existing keys.
if (draft[key] === undefined && !has(draft, key)) {
assigned[key] = false;
markChanged(state);
}
});
} else if (hasArrayChanges(state)) {
markChanged(state);
assigned.length = true;
if (draft.length < base.length) {
for (var i = draft.length; i < base.length; i++) { assigned[i] = false; }
} else {
for (var i$1 = base.length; i$1 < draft.length; i$1++) { assigned[i$1] = true; }
}
for (var i$2 = 0; i$2 < draft.length; i$2++) {
// Only untouched indices trigger recursion.
if (assigned[i$2] === undefined) { markChangesRecursively(draft[i$2]); }
}
}
}
function hasObjectChanges(state) {
var base = state.base;
var draft = state.draft; // Search for added keys and changed keys. Start at the back, because
// non-numeric keys are ordered by time of definition on the object.
var keys = Object.keys(draft);
for (var i = keys.length - 1; i >= 0; i--) {
var key = keys[i];
var baseValue = base[key]; // The `undefined` check is a fast path for pre-existing keys.
if (baseValue === undefined && !has(base, key)) {
return true;
} // Once a base key is deleted, future changes go undetected, because its
// descriptor is erased. This branch detects any missed changes.
else {
var value = draft[key];
var state$1 = value && value[DRAFT_STATE];
if (state$1 ? state$1.base !== baseValue : !is(value, baseValue)) {
return true;
}
}
} // At this point, no keys were added or changed.
// Compare key count to determine if keys were deleted.
return keys.length !== Object.keys(base).length;
}
function hasArrayChanges(state) {
var draft = state.draft;
if (draft.length !== state.base.length) { return true; } // See #116
// If we first shorten the length, our array interceptors will be removed.
// If after that new items are added, result in the same original length,
// those last items will have no intercepting property.
// So if there is no own descriptor on the last position, we know that items were removed and added
// N.B.: splice, unshift, etc only shift values around, but not prop descriptors, so we only have to check
// the last one
var descriptor = Object.getOwnPropertyDescriptor(draft, draft.length - 1); // descriptor can be null, but only for newly created sparse arrays, eg. new Array(10)
if (descriptor && !descriptor.get) { return true; } // For all other cases, we don't have to compare, as they would have been picked up by the index setters
return false;
}
function createHiddenProperty(target, prop, value) {
Object.defineProperty(target, prop, {
value: value,
enumerable: false,
writable: true
});
}
var legacyProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize,
createProxy: createProxy
});
function willFinalize$1() {}
function createProxy$1(base, parent) {
var scope = parent ? parent.scope : ImmerScope.current;
var state = {
// Track which produce call this is associated with.
scope: scope,
// True for both shallow and deep changes.
modified: false,
// Used during finalization.
finalized: false,
// Track which properties have been assigned (true) or deleted (false).
assigned: {},
// The parent draft state.
parent: parent,
// The base state.
base: base,
// The base proxy.
draft: null,
// Any property proxies.
drafts: {},
// The base copy with any updated values.
copy: null,
// Called by the `produce` function.
revoke: null
};
var ref = Array.isArray(base) ? // [state] is used for arrays, to make sure the proxy is array-ish and not violate invariants,
// although state itself is an object
Proxy.revocable([state], arrayTraps) : Proxy.revocable(state, objectTraps);
var revoke = ref.revoke;
var proxy = ref.proxy;
state.draft = proxy;
state.revoke = revoke;
scope.drafts.push(proxy);
return proxy;
}
var objectTraps = {
get: get$1,
has: function has(target, prop) {
return prop in source$1(target);
},
ownKeys: function ownKeys(target) {
return Reflect.ownKeys(source$1(target));
},
set: set$1,
deleteProperty: deleteProperty,
getOwnPropertyDescriptor: getOwnPropertyDescriptor,
defineProperty: function defineProperty() {
throw new Error("Object.defineProperty() cannot be used on an Immer draft"); // prettier-ignore
},
getPrototypeOf: function getPrototypeOf(target) {
return Object.getPrototypeOf(target.base);
},
setPrototypeOf: function setPrototypeOf() {
throw new Error("Object.setPrototypeOf() cannot be used on an Immer draft"); // prettier-ignore
}
};
var arrayTraps = {};
each(objectTraps, function (key, fn) {
arrayTraps[key] = function () {
arguments[0] = arguments[0][0];
return fn.apply(this, arguments);
};
});
arrayTraps.deleteProperty = function (state, prop) {
if (isNaN(parseInt(prop))) {
throw new Error("Immer only supports deleting array indices"); // prettier-ignore
}
return objectTraps.deleteProperty.call(this, state[0], prop);
};
arrayTraps.set = function (state, prop, value) {
if (prop !== "length" && isNaN(parseInt(prop))) {
throw new Error("Immer only supports setting array indices and the 'length' property"); // prettier-ignore
}
return objectTraps.set.call(this, state[0], prop, value);
}; // returns the object we should be reading the current value from, which is base, until some change has been made
function source$1(state) {
return state.copy || state.base;
} // Access a property without creating an Immer draft.
function peek$1(draft, prop) {
var state = draft[DRAFT_STATE];
var desc = Reflect.getOwnPropertyDescriptor(state ? source$1(state) : draft, prop);
return desc && desc.value;
}
function get$1(state, prop) {
if (prop === DRAFT_STATE) { return state; }
var drafts = state.drafts; // Check for existing draft in unmodified state.
if (!state.modified && has(drafts, prop)) {
return drafts[prop];
}
var value = source$1(state)[prop];
if (state.finalized || !isDraftable(value)) {
return value;
} // Check for existing draft in modified state.
if (state.modified) {
// Assigned values are never drafted. This catches any drafts we created, too.
if (value !== peek$1(state.base, prop)) { return value; } // Store drafts on the copy (when one exists).
drafts = state.copy;
}
return drafts[prop] = createProxy$1(value, state);
}
function set$1(state, prop, value) {
if (!state.modified) {
var baseValue = peek$1(state.base, prop); // Optimize based on value's truthiness. Truthy values are guaranteed to
// never be undefined, so we can avoid the `in` operator. Lastly, truthy
// values may be drafts, but falsy values are never drafts.
var isUnchanged = value ? is(baseValue, value) || value === state.drafts[prop] : is(baseValue, value) && prop in state.base;
if (isUnchanged) { return true; }
markChanged$1(state);
}
state.assigned[prop] = true;
state.copy[prop] = value;
return true;
}
function deleteProperty(state, prop) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek$1(state.base, prop) !== undefined || prop in state.base) {
state.assigned[prop] = false;
markChanged$1(state);
}
if (state.copy) { delete state.copy[prop]; }
return true;
} // Note: We never coerce `desc.value` into an Immer draft, because we can't make
// the same guarantee in ES5 mode.
function getOwnPropertyDescriptor(state, prop) {
var owner = source$1(state);
var desc = Reflect.getOwnPropertyDescriptor(owner, prop);
if (desc) {
desc.writable = true;
desc.configurable = !Array.isArray(owner) || prop !== "length";
}
return desc;
}
function markChanged$1(state) {
if (!state.modified) {
state.modified = true;
state.copy = assign(shallowCopy(state.base), state.drafts);
state.drafts = null;
if (state.parent) { markChanged$1(state.parent); }
}
}
var modernProxy = /*#__PURE__*/Object.freeze({
willFinalize: willFinalize$1,
createProxy: createProxy$1
});
function generatePatches(state, basePath, patches, inversePatches) {
Array.isArray(state.base) ? generateArrayPatches(state, basePath, patches, inversePatches) : generateObjectPatches(state, basePath, patches, inversePatches);
}
function generateArrayPatches(state, basePath, patches, inversePatches) {
var assign, assign$1;
var base = state.base;
var copy = state.copy;
var assigned = state.assigned; // Reduce complexity by ensuring `base` is never longer.
if (copy.length < base.length) {
(assign = [copy, base], base = assign[0], copy = assign[1]);
(assign$1 = [inversePatches, patches], patches = assign$1[0], inversePatches = assign$1[1]);
}
var delta = copy.length - base.length; // Find the first replaced index.
var start = 0;
while (base[start] === copy[start] && start < base.length) {
++start;
} // Find the last replaced index. Search from the end to optimize splice patches.
var end = base.length;
while (end > start && base[end - 1] === copy[end + delta - 1]) {
--end;
} // Process replaced indices.
for (var i = start; i < end; ++i) {
if (assigned[i] && copy[i] !== base[i]) {
var path = basePath.concat([i]);
patches.push({
op: "replace",
path: path,
value: copy[i]
});
inversePatches.push({
op: "replace",
path: path,
value: base[i]
});
}
}
var useRemove = end != base.length;
var replaceCount = patches.length; // Process added indices.
for (var i$1 = end + delta - 1; i$1 >= end; --i$1) {
var path$1 = basePath.concat([i$1]);
patches[replaceCount + i$1 - end] = {
op: "add",
path: path$1,
value: copy[i$1]
};
if (useRemove) {
inversePatches.push({
op: "remove",
path: path$1
});
}
} // One "replace" patch reverses all non-splicing "add" patches.
if (!useRemove) {
inversePatches.push({
op: "replace",
path: basePath.concat(["length"]),
value: base.length
});
}
}
function generateObjectPatches(state, basePath, patches, inversePatches) {
var base = state.base;
var copy = state.copy;
each(state.assigned, function (key, assignedValue) {
var origValue = base[key];
var value = copy[key];
var op = !assignedValue ? "remove" : key in base ? "replace" : "add";
if (origValue === value && op === "replace") { return; }
var path = basePath.concat(key);
patches.push(op === "remove" ? {
op: op,
path: path
} : {
op: op,
path: path,
value: value
});
inversePatches.push(op === "add" ? {
op: "remove",
path: path
} : op === "remove" ? {
op: "add",
path: path,
value: origValue
} : {
op: "replace",
path: path,
value: origValue
});
});
}
function applyPatches(draft, patches) {
for (var i = 0; i < patches.length; i++) {
var patch = patches[i];
var path = patch.path;
if (path.length === 0 && patch.op === "replace") {
draft = patch.value;
} else {
var base = draft;
for (var i$1 = 0; i$1 < path.length - 1; i$1++) {
base = base[path[i$1]];
if (!base || typeof base !== "object") { throw new Error("Cannot apply patch, path doesn't resolve: " + path.join("/")); } // prettier-ignore
}
var key = path[path.length - 1];
switch (patch.op) {
case "replace":
base[key] = patch.value;
break;
case "add":
if (Array.isArray(base)) {
// TODO: support "foo/-" paths for appending to an array
base.splice(key, 0, patch.value);
} else {
base[key] = patch.value;
}
break;
case "remove":
if (Array.isArray(base)) {
base.splice(key, 1);
} else {
delete base[key];
}
break;
default:
throw new Error("Unsupported patch operation: " + patch.op);
}
}
}
return draft;
}
function verifyMinified() {}
var configDefaults = {
useProxies: typeof Proxy !== "undefined" && typeof Reflect !== "undefined",
autoFreeze: typeof process !== "undefined" ? process.env.NODE_ENV !== "production" : verifyMinified.name === "verifyMinified",
onAssign: null,
onDelete: null,
onCopy: null
};
var Immer = function Immer(config) {
assign(this, configDefaults, config);
this.setUseProxies(this.useProxies);
this.produce = this.produce.bind(this);
};
Immer.prototype.produce = function produce (base, recipe, patchListener) {
var this$1 = this;
// curried invocation
if (typeof base === "function" && typeof recipe !== "function") {
var defaultBase = recipe;
recipe = base;
var self = this;
return function curriedProduce(base) {
var this$1 = this;
if ( base === void 0 ) base = defaultBase;
var args = [], len = arguments.length - 1;
while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
return self.produce(base, function (draft) { return recipe.call.apply(recipe, [ this$1, draft ].concat( args )); }); // prettier-ignore
};
} // prettier-ignore
{
if (typeof recipe !== "function") {
throw new Error("The first or second argument to `produce` must be a function");
}
if (patchListener !== undefined && typeof patchListener !== "function") {
throw new Error("The third argument to `produce` must be a function or undefined");
}
}
var result; // Only plain objects, arrays, and "immerable classes" are drafted.
if (isDraftable(base)) {
var scope = ImmerScope.enter();
var proxy = this.createProxy(base);
var hasError = true;
try {
result = recipe(proxy);
hasError = false;
} finally {
// finally instead of catch + rethrow better preserves original stack
if (hasError) { scope.revoke(); }else { scope.leave(); }
}
if (result instanceof Promise) {
return result.then(function (result) {
scope.usePatches(patchListener);
return this$1.processResult(result, scope);
}, function (error) {
scope.revoke();
throw error;
});
}
scope.usePatches(patchListener);
return this.processResult(result, scope);
} else {
result = recipe(base);
if (result === undefined) { return base; }
return result !== NOTHING ? result : undefined;
}
};
Immer.prototype.createDraft = function createDraft (base) {
if (!isDraftable(base)) {
throw new Error("First argument to `createDraft` must be a plain object, an array, or an immerable object"); // prettier-ignore
}
var scope = ImmerScope.enter();
var proxy = this.createProxy(base);
proxy[DRAFT_STATE].isManual = true;
scope.leave();
return proxy;
};
Immer.prototype.finishDraft = function finishDraft (draft, patchListener) {
var state = draft && draft[DRAFT_STATE];
if (!state || !state.isManual) {
throw new Error("First argument to `finishDraft` must be a draft returned by `createDraft`"); // prettier-ignore
}
if (state.finalized) {
throw new Error("The given draft is already finalized"); // prettier-ignore
}
var scope = state.scope;
scope.usePatches(patchListener);
return this.processResult(undefined, scope);
};
Immer.prototype.setAutoFreeze = function setAutoFreeze (value) {
this.autoFreeze = value;
};
Immer.prototype.setUseProxies = function setUseProxies (value) {
this.useProxies = value;
assign(this, value ? modernProxy : legacyProxy);
};
Immer.prototype.applyPatches = function applyPatches$1 (base, patches) {
// Mutate the base state when a draft is passed.
if (isDraft(base)) {
return applyPatches(base, patches);
} // Otherwise, produce a copy of the base state.
return this.produce(base, function (draft) { return applyPatches(draft, patches); });
};
/** @internal */
Immer.prototype.processResult = function processResult (result, scope) {
var baseDraft = scope.drafts[0];
var isReplaced = result !== undefined && result !== baseDraft;
this.willFinalize(scope, result, isReplaced);
if (isReplaced) {
if (baseDraft[DRAFT_STATE].modified) {
scope.revoke();
throw new Error("An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft."); // prettier-ignore
}
if (isDraftable(result)) {
// Finalize the result in case it contains (or is) a subset of the draft.
result = this.finalize(result, null, scope);
}
if (scope.patches) {
scope.patches.push({
op: "replace",
path: [],
value: result
});
scope.inversePatches.push({
op: "replace",
path: [],
value: baseDraft[DRAFT_STATE].base
});
}
} else {
// Finalize the base draft.
result = this.finalize(baseDraft, [], scope);
}
scope.revoke();
if (scope.patches) {
scope.patchListener(scope.patches, scope.inversePatches);
}
return result !== NOTHING ? result : undefined;
};
/**
* @internal
* Finalize a draft, returning either the unmodified base state or a modified
* copy of the base state.
*/
Immer.prototype.finalize = function finalize (draft, path, scope) {
var this$1 = this;
var state = draft[DRAFT_STATE];
if (!state) {
if (Object.isFrozen(draft)) { return draft; }
return this.finalizeTree(draft, null, scope);
} // Never finalize drafts owned by another scope.
if (state.scope !== scope) {
return draft;
}
if (!state.modified) {
return state.base;
}
if (!state.finalized) {
state.finalized = true;
this.finalizeTree(state.draft, path, scope);
if (this.onDelete) {
// The `assigned` object is unreliable with ES5 drafts.
if (this.useProxies) {
var assigned = state.assigned;
for (var prop in assigned) {
if (!assigned[prop]) { this.onDelete(state, prop); }
}
} else {
var base = state.base;
var copy = state.copy;
each(base, function (prop) {
if (!has(copy, prop)) { this$1.onDelete(state, prop); }
});
}
}
if (this.onCopy) {
this.onCopy(state);
} // At this point, all descendants of `state.copy` have been finalized,
// so we can be sure that `scope.canAutoFreeze` is accurate.
if (this.autoFreeze && scope.canAutoFreeze) {
Object.freeze(state.copy);
}
if (path && scope.patches) {
generatePatches(state, path, scope.patches, scope.inversePatches);
}
}
return state.copy;
};
/**
* @internal
* Finalize all drafts in the given state tree.
*/
Immer.prototype.finalizeTree = function finalizeTree (root, rootPath, scope) {
var this$1 = this;
var state = root[DRAFT_STATE];
if (state) {
if (!this.useProxies) {
// Create the final copy, with added keys and without deleted keys.
state.copy = shallowCopy(state.draft, true);
}
root = state.copy;
}
var needPatches = !!rootPath && !!scope.patches;
var finalizeProperty = function (prop, value, parent) {
if (value === parent) {
throw Error("Immer forbids circular references");
} // In the `finalizeTree` method, only the `root` object may be a draft.
var isDraftProp = !!state && parent === root;
if (isDraft(value)) {
var path = isDraftProp && needPatches && !state.assigned[prop] ? rootPath.concat(prop) : null; // Drafts owned by `scope` are finalized here.
value = this$1.finalize(value, path, scope); // Drafts from another scope must prevent auto-freezing.
if (isDraft(value)) {
scope.canAutoFreeze = false;
} // Preserve non-enumerable properties.
if (Array.isArray(parent) || isEnumerable(parent, prop)) {
parent[prop] = value;
} else {
Object.defineProperty(parent, prop, {
value: value
});
} // Unchanged drafts are never passed to the `onAssign` hook.
if (isDraftProp && value === state.base[prop]) { return; }
} // Unchanged draft properties are ignored.
else if (isDraftProp && is(value, state.base[prop])) {
return;
} // Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
else if (isDraftable(value) && !Object.isFrozen(value)) {
each(value, finalizeProperty);
}
if (isDraftProp && this$1.onAssign) {
this$1.onAssign(state, prop, value);
}
};
each(root, finalizeProperty);
return root;
};
var immer = new Immer();
/**
* The `produce` function takes a value and a "recipe function" (whose
* return value often depends on the base state). The recipe function is
* free to mutate its first argument however it wants. All mutations are
* only ever applied to a __copy__ of the base state.
*
* Pass only a function to create a "curried producer" which relieves you
* from passing the recipe function every time.
*
* Only plain objects and arrays are made mutable. All other objects are
* considered uncopyable.
*
* Note: This function is __bound__ to its `Immer` instance.
*
* @param {any} base - the initial state
* @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified
* @param {Function} patchListener - optional function that will be called with all the patches produced here
* @returns {any} a new state, or the initial state if nothing was modified
*/
var produce = immer.produce;
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is disabled in production.
*/
var setAutoFreeze = immer.setAutoFreeze.bind(immer);
/**
* Pass true to use the ES2015 `Proxy` class when creating drafts, which is
* always faster than using ES5 proxies.
*
* By default, feature detection is used, so calling this is rarely necessary.
*/
var setUseProxies = immer.setUseProxies.bind(immer);
/**
* Apply an array of Immer patches to the first argument.
*
* This function is a producer, which means copy-on-write is in effect.
*/
var applyPatches$1 = immer.applyPatches.bind(immer);
/**
* Create an Immer draft from the given base state, which may be a draft itself.
* The draft can be modified until you finalize it with the `finishDraft` function.
*/
var createDraft = immer.createDraft.bind(immer);
/**
* Finalize an Immer draft from a `createDraft` call, returning the base state
* (if no changes were made) or a modified copy. The draft must *not* be
* mutated afterwards.
*
* Pass a function as the 2nd argument to generate Immer patches based on the
* changes that were made.
*/
var finishDraft = immer.finishDraft.bind(immer);
export default produce;
export { Immer, applyPatches$1 as applyPatches, createDraft, finishDraft, DRAFTABLE as immerable, isDraft, isDraftable, NOTHING as nothing, original, produce, setAutoFreeze, setUseProxies };
//# sourceMappingURL=immer.module.js.map