planning
All checks were successful
Publish To Prod / deploy_and_publish (push) Successful in 35s

This commit is contained in:
2024-10-14 09:15:30 +02:00
parent bcba00a730
commit 6e64e138e2
21059 changed files with 2317811 additions and 1 deletions

60
node_modules/immer/src/core/current.ts generated vendored Normal file
View File

@@ -0,0 +1,60 @@
import {
die,
isDraft,
shallowCopy,
each,
DRAFT_STATE,
get,
set,
ImmerState,
isDraftable,
Archtype,
getArchtype,
getPlugin
} from "../internal"
/** Takes a snapshot of the current state of a draft and finalizes it (but without freezing). This is a great utility to print the current state during debugging (no Proxies in the way). The output of current can also be safely leaked outside the producer. */
export function current<T>(value: T): T
export function current(value: any): any {
if (!isDraft(value)) die(22, value)
return currentImpl(value)
}
function currentImpl(value: any): any {
if (!isDraftable(value)) return value
const state: ImmerState | undefined = value[DRAFT_STATE]
let copy: any
const archType = getArchtype(value)
if (state) {
if (
!state.modified_ &&
(state.type_ < 4 || !getPlugin("ES5").hasChanges_(state as any))
)
return state.base_
// Optimization: avoid generating new drafts during copying
state.finalized_ = true
copy = copyHelper(value, archType)
state.finalized_ = false
} else {
copy = copyHelper(value, archType)
}
each(copy, (key, childValue) => {
if (state && get(state.base_, key) === childValue) return // no need to copy or search in something that didn't change
set(copy, key, currentImpl(childValue))
})
// In the future, we might consider freezing here, based on the current settings
return archType === Archtype.Set ? new Set(copy) : copy
}
function copyHelper(value: any, archType: number): any {
// creates a shallow copy, even if it is a map or set
switch (archType) {
case Archtype.Map:
return new Map(value)
case Archtype.Set:
// Set will be cloned as array temporarily, so that we can replace individual items
return Array.from(value)
}
return shallowCopy(value)
}

168
node_modules/immer/src/core/finalize.ts generated vendored Normal file
View File

@@ -0,0 +1,168 @@
import {
ImmerScope,
DRAFT_STATE,
isDraftable,
NOTHING,
PatchPath,
each,
has,
freeze,
ImmerState,
isDraft,
SetState,
set,
ProxyType,
getPlugin,
die,
revokeScope,
isFrozen,
shallowCopy
} from "../internal"
export function processResult(result: any, scope: ImmerScope) {
scope.unfinalizedDrafts_ = scope.drafts_.length
const baseDraft = scope.drafts_![0]
const isReplaced = result !== undefined && result !== baseDraft
if (!scope.immer_.useProxies_)
getPlugin("ES5").willFinalizeES5_(scope, result, isReplaced)
if (isReplaced) {
if (baseDraft[DRAFT_STATE].modified_) {
revokeScope(scope)
die(4)
}
if (isDraftable(result)) {
// Finalize the result in case it contains (or is) a subset of the draft.
result = finalize(scope, result)
if (!scope.parent_) maybeFreeze(scope, result)
}
if (scope.patches_) {
getPlugin("Patches").generateReplacementPatches_(
baseDraft[DRAFT_STATE].base_,
result,
scope.patches_,
scope.inversePatches_!
)
}
} else {
// Finalize the base draft.
result = finalize(scope, baseDraft, [])
}
revokeScope(scope)
if (scope.patches_) {
scope.patchListener_!(scope.patches_, scope.inversePatches_!)
}
return result !== NOTHING ? result : undefined
}
function finalize(rootScope: ImmerScope, value: any, path?: PatchPath) {
// Don't recurse in tho recursive data structures
if (isFrozen(value)) return value
const state: ImmerState = value[DRAFT_STATE]
// A plain object, might need freezing, might contain drafts
if (!state) {
each(
value,
(key, childValue) =>
finalizeProperty(rootScope, state, value, key, childValue, path),
true // See #590, don't recurse into non-enumerable of non drafted objects
)
return value
}
// Never finalize drafts owned by another scope.
if (state.scope_ !== rootScope) return value
// Unmodified draft, return the (frozen) original
if (!state.modified_) {
maybeFreeze(rootScope, state.base_, true)
return state.base_
}
// Not finalized yet, let's do that now
if (!state.finalized_) {
state.finalized_ = true
state.scope_.unfinalizedDrafts_--
const result =
// For ES5, create a good copy from the draft first, with added keys and without deleted keys.
state.type_ === ProxyType.ES5Object || state.type_ === ProxyType.ES5Array
? (state.copy_ = shallowCopy(state.draft_))
: state.copy_
// Finalize all children of the copy
// For sets we clone before iterating, otherwise we can get in endless loop due to modifying during iteration, see #628
// To preserve insertion order in all cases we then clear the set
// And we let finalizeProperty know it needs to re-add non-draft children back to the target
let resultEach = result
let isSet = false
if (state.type_ === ProxyType.Set) {
resultEach = new Set(result)
result.clear()
isSet = true
}
each(resultEach, (key, childValue) =>
finalizeProperty(rootScope, state, result, key, childValue, path, isSet)
)
// everything inside is frozen, we can freeze here
maybeFreeze(rootScope, result, false)
// first time finalizing, let's create those patches
if (path && rootScope.patches_) {
getPlugin("Patches").generatePatches_(
state,
path,
rootScope.patches_,
rootScope.inversePatches_!
)
}
}
return state.copy_
}
function finalizeProperty(
rootScope: ImmerScope,
parentState: undefined | ImmerState,
targetObject: any,
prop: string | number,
childValue: any,
rootPath?: PatchPath,
targetIsSet?: boolean
) {
if (__DEV__ && childValue === targetObject) die(5)
if (isDraft(childValue)) {
const path =
rootPath &&
parentState &&
parentState!.type_ !== ProxyType.Set && // Set objects are atomic since they have no keys.
!has((parentState as Exclude<ImmerState, SetState>).assigned_!, prop) // Skip deep patches for assigned keys.
? rootPath!.concat(prop)
: undefined
// Drafts owned by `scope` are finalized here.
const res = finalize(rootScope, childValue, path)
set(targetObject, prop, res)
// Drafts from another scope must prevented to be frozen
// if we got a draft back from finalize, we're in a nested produce and shouldn't freeze
if (isDraft(res)) {
rootScope.canAutoFreeze_ = false
} else return
} else if (targetIsSet) {
targetObject.add(childValue)
}
// Search new objects for unfinalized drafts. Frozen objects should never contain drafts.
if (isDraftable(childValue) && !isFrozen(childValue)) {
if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) {
// optimization: if an object is not a draft, and we don't have to
// deepfreeze everything, and we are sure that no drafts are left in the remaining object
// cause we saw and finalized all drafts already; we can stop visiting the rest of the tree.
// This benefits especially adding large data tree's without further processing.
// See add-data.js perf test
return
}
finalize(rootScope, childValue)
// immer deep freezes plain objects, so if there is no parent state, we freeze as well
if (!parentState || !parentState.scope_.parent_)
maybeFreeze(rootScope, childValue)
}
}
function maybeFreeze(scope: ImmerScope, value: any, deep = false) {
// we never freeze for a non-root scope; as it would prevent pruning for drafts inside wrapping objects
if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) {
freeze(value, deep)
}
}

241
node_modules/immer/src/core/immerClass.ts generated vendored Normal file
View File

@@ -0,0 +1,241 @@
import {
IProduceWithPatches,
IProduce,
ImmerState,
Drafted,
isDraftable,
processResult,
Patch,
Objectish,
DRAFT_STATE,
Draft,
PatchListener,
isDraft,
isMap,
isSet,
createProxyProxy,
getPlugin,
die,
hasProxies,
enterScope,
revokeScope,
leaveScope,
usePatchesInScope,
getCurrentScope,
NOTHING,
freeze,
current
} from "../internal"
interface ProducersFns {
produce: IProduce
produceWithPatches: IProduceWithPatches
}
export class Immer implements ProducersFns {
useProxies_: boolean = hasProxies
autoFreeze_: boolean = true
constructor(config?: {useProxies?: boolean; autoFreeze?: boolean}) {
if (typeof config?.useProxies === "boolean")
this.setUseProxies(config!.useProxies)
if (typeof config?.autoFreeze === "boolean")
this.setAutoFreeze(config!.autoFreeze)
}
/**
* 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} recipe - 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
*/
produce: IProduce = (base: any, recipe?: any, patchListener?: any) => {
// curried invocation
if (typeof base === "function" && typeof recipe !== "function") {
const defaultBase = recipe
recipe = base
const self = this
return function curriedProduce(
this: any,
base = defaultBase,
...args: any[]
) {
return self.produce(base, (draft: Drafted) => recipe.call(this, draft, ...args)) // prettier-ignore
}
}
if (typeof recipe !== "function") die(6)
if (patchListener !== undefined && typeof patchListener !== "function")
die(7)
let result
// Only plain objects, arrays, and "immerable classes" are drafted.
if (isDraftable(base)) {
const scope = enterScope(this)
const proxy = createProxy(this, base, undefined)
let hasError = true
try {
result = recipe(proxy)
hasError = false
} finally {
// finally instead of catch + rethrow better preserves original stack
if (hasError) revokeScope(scope)
else leaveScope(scope)
}
if (typeof Promise !== "undefined" && result instanceof Promise) {
return result.then(
result => {
usePatchesInScope(scope, patchListener)
return processResult(result, scope)
},
error => {
revokeScope(scope)
throw error
}
)
}
usePatchesInScope(scope, patchListener)
return processResult(result, scope)
} else if (!base || typeof base !== "object") {
result = recipe(base)
if (result === undefined) result = base
if (result === NOTHING) result = undefined
if (this.autoFreeze_) freeze(result, true)
if (patchListener) {
const p: Patch[] = []
const ip: Patch[] = []
getPlugin("Patches").generateReplacementPatches_(base, result, p, ip)
patchListener(p, ip)
}
return result
} else die(21, base)
}
produceWithPatches: IProduceWithPatches = (base: any, recipe?: any): any => {
// curried invocation
if (typeof base === "function") {
return (state: any, ...args: any[]) =>
this.produceWithPatches(state, (draft: any) => base(draft, ...args))
}
let patches: Patch[], inversePatches: Patch[]
const result = this.produce(base, recipe, (p: Patch[], ip: Patch[]) => {
patches = p
inversePatches = ip
})
if (typeof Promise !== "undefined" && result instanceof Promise) {
return result.then(nextState => [nextState, patches!, inversePatches!])
}
return [result, patches!, inversePatches!]
}
createDraft<T extends Objectish>(base: T): Draft<T> {
if (!isDraftable(base)) die(8)
if (isDraft(base)) base = current(base)
const scope = enterScope(this)
const proxy = createProxy(this, base, undefined)
proxy[DRAFT_STATE].isManual_ = true
leaveScope(scope)
return proxy as any
}
finishDraft<D extends Draft<any>>(
draft: D,
patchListener?: PatchListener
): D extends Draft<infer T> ? T : never {
const state: ImmerState = draft && (draft as any)[DRAFT_STATE]
if (__DEV__) {
if (!state || !state.isManual_) die(9)
if (state.finalized_) die(10)
}
const {scope_: scope} = state
usePatchesInScope(scope, patchListener)
return processResult(undefined, scope)
}
/**
* Pass true to automatically freeze all copies created by Immer.
*
* By default, auto-freezing is enabled.
*/
setAutoFreeze(value: boolean) {
this.autoFreeze_ = value
}
/**
* 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.
*/
setUseProxies(value: boolean) {
if (value && !hasProxies) {
die(20)
}
this.useProxies_ = value
}
applyPatches<T extends Objectish>(base: T, patches: Patch[]): T {
// If a patch replaces the entire state, take that replacement as base
// before applying patches
let i: number
for (i = patches.length - 1; i >= 0; i--) {
const patch = patches[i]
if (patch.path.length === 0 && patch.op === "replace") {
base = patch.value
break
}
}
// If there was a patch that replaced the entire state, start from the
// patch after that.
if (i > -1) {
patches = patches.slice(i + 1)
}
const applyPatchesImpl = getPlugin("Patches").applyPatches_
if (isDraft(base)) {
// N.B: never hits if some patch a replacement, patches are never drafts
return applyPatchesImpl(base, patches)
}
// Otherwise, produce a copy of the base state.
return this.produce(base, (draft: Drafted) =>
applyPatchesImpl(draft, patches)
)
}
}
export function createProxy<T extends Objectish>(
immer: Immer,
value: T,
parent?: ImmerState
): Drafted<T, ImmerState> {
// precondition: createProxy should be guarded by isDraftable, so we know we can safely draft
const draft: Drafted = isMap(value)
? getPlugin("MapSet").proxyMap_(value, parent)
: isSet(value)
? getPlugin("MapSet").proxySet_(value, parent)
: immer.useProxies_
? createProxyProxy(value, parent)
: getPlugin("ES5").createES5Proxy_(value, parent)
const scope = parent ? parent.scope_ : getCurrentScope()
scope.drafts_.push(draft)
return draft
}

280
node_modules/immer/src/core/proxy.ts generated vendored Normal file
View File

@@ -0,0 +1,280 @@
import {
each,
has,
is,
isDraftable,
shallowCopy,
latest,
ImmerBaseState,
ImmerState,
Drafted,
AnyObject,
AnyArray,
Objectish,
getCurrentScope,
DRAFT_STATE,
die,
createProxy,
ProxyType
} from "../internal"
interface ProxyBaseState extends ImmerBaseState {
assigned_: {
[property: string]: boolean
}
parent_?: ImmerState
revoke_(): void
}
export interface ProxyObjectState extends ProxyBaseState {
type_: ProxyType.ProxyObject
base_: any
copy_: any
draft_: Drafted<AnyObject, ProxyObjectState>
}
export interface ProxyArrayState extends ProxyBaseState {
type_: ProxyType.ProxyArray
base_: AnyArray
copy_: AnyArray | null
draft_: Drafted<AnyArray, ProxyArrayState>
}
type ProxyState = ProxyObjectState | ProxyArrayState
/**
* Returns a new draft of the `base` object.
*
* The second argument is the parent draft-state (used internally).
*/
export function createProxyProxy<T extends Objectish>(
base: T,
parent?: ImmerState
): Drafted<T, ProxyState> {
const isArray = Array.isArray(base)
const state: ProxyState = {
type_: isArray ? ProxyType.ProxyArray : (ProxyType.ProxyObject as any),
// Track which produce call this is associated with.
scope_: parent ? parent.scope_ : getCurrentScope()!,
// 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 as any, // set below
// The base copy with any updated values.
copy_: null,
// Called by the `produce` function.
revoke_: null as any,
isManual_: false
}
// the traps must target something, a bit like the 'real' base.
// but also, we need to be able to determine from the target what the relevant state is
// (to avoid creating traps per instance to capture the state in closure,
// and to avoid creating weird hidden properties as well)
// So the trick is to use 'state' as the actual 'target'! (and make sure we intercept everything)
// Note that in the case of an array, we put the state in an array to have better Reflect defaults ootb
let target: T = state as any
let traps: ProxyHandler<object | Array<any>> = objectTraps
if (isArray) {
target = [state] as any
traps = arrayTraps
}
const {revoke, proxy} = Proxy.revocable(target, traps)
state.draft_ = proxy as any
state.revoke_ = revoke
return proxy as any
}
/**
* Object drafts
*/
export const objectTraps: ProxyHandler<ProxyState> = {
get(state, prop) {
if (prop === DRAFT_STATE) return state
const source = latest(state)
if (!has(source, prop)) {
// non-existing or non-own property...
return readPropFromProto(state, source, prop)
}
const value = source[prop]
if (state.finalized_ || !isDraftable(value)) {
return value
}
// Check for existing draft in modified state.
// Assigned values are never drafted. This catches any drafts we created, too.
if (value === peek(state.base_, prop)) {
prepareCopy(state)
return (state.copy_![prop as any] = createProxy(
state.scope_.immer_,
value,
state
))
}
return value
},
has(state, prop) {
return prop in latest(state)
},
ownKeys(state) {
return Reflect.ownKeys(latest(state))
},
set(
state: ProxyObjectState,
prop: string /* strictly not, but helps TS */,
value
) {
const desc = getDescriptorFromProto(latest(state), prop)
if (desc?.set) {
// special case: if this write is captured by a setter, we have
// to trigger it with the correct context
desc.set.call(state.draft_, value)
return true
}
if (!state.modified_) {
// the last check is because we need to be able to distinguish setting a non-existing to undefined (which is a change)
// from setting an existing property with value undefined to undefined (which is not a change)
const current = peek(latest(state), prop)
// special case, if we assigning the original value to a draft, we can ignore the assignment
const currentState: ProxyObjectState = current?.[DRAFT_STATE]
if (currentState && currentState.base_ === value) {
state.copy_![prop] = value
state.assigned_[prop] = false
return true
}
if (is(value, current) && (value !== undefined || has(state.base_, prop)))
return true
prepareCopy(state)
markChanged(state)
}
if (
(state.copy_![prop] === value &&
// special case: handle new props with value 'undefined'
(value !== undefined || prop in state.copy_)) ||
// special case: NaN
(Number.isNaN(value) && Number.isNaN(state.copy_![prop]))
)
return true
// @ts-ignore
state.copy_![prop] = value
state.assigned_[prop] = true
return true
},
deleteProperty(state, prop: string) {
// The `undefined` check is a fast path for pre-existing keys.
if (peek(state.base_, prop) !== undefined || prop in state.base_) {
state.assigned_[prop] = false
prepareCopy(state)
markChanged(state)
} else {
// if an originally not assigned property was deleted
delete state.assigned_[prop]
}
// @ts-ignore
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.
getOwnPropertyDescriptor(state, prop) {
const owner = latest(state)
const desc = Reflect.getOwnPropertyDescriptor(owner, prop)
if (!desc) return desc
return {
writable: true,
configurable: state.type_ !== ProxyType.ProxyArray || prop !== "length",
enumerable: desc.enumerable,
value: owner[prop]
}
},
defineProperty() {
die(11)
},
getPrototypeOf(state) {
return Object.getPrototypeOf(state.base_)
},
setPrototypeOf() {
die(12)
}
}
/**
* Array drafts
*/
const arrayTraps: ProxyHandler<[ProxyArrayState]> = {}
each(objectTraps, (key, fn) => {
// @ts-ignore
arrayTraps[key] = function() {
arguments[0] = arguments[0][0]
return fn.apply(this, arguments)
}
})
arrayTraps.deleteProperty = function(state, prop) {
if (__DEV__ && isNaN(parseInt(prop as any))) die(13)
// @ts-ignore
return arrayTraps.set!.call(this, state, prop, undefined)
}
arrayTraps.set = function(state, prop, value) {
if (__DEV__ && prop !== "length" && isNaN(parseInt(prop as any))) die(14)
return objectTraps.set!.call(this, state[0], prop, value, state[0])
}
// Access a property without creating an Immer draft.
function peek(draft: Drafted, prop: PropertyKey) {
const state = draft[DRAFT_STATE]
const source = state ? latest(state) : draft
return source[prop]
}
function readPropFromProto(state: ImmerState, source: any, prop: PropertyKey) {
const desc = getDescriptorFromProto(source, prop)
return desc
? `value` in desc
? desc.value
: // This is a very special case, if the prop is a getter defined by the
// prototype, we should invoke it with the draft as context!
desc.get?.call(state.draft_)
: undefined
}
function getDescriptorFromProto(
source: any,
prop: PropertyKey
): PropertyDescriptor | undefined {
// 'in' checks proto!
if (!(prop in source)) return undefined
let proto = Object.getPrototypeOf(source)
while (proto) {
const desc = Object.getOwnPropertyDescriptor(proto, prop)
if (desc) return desc
proto = Object.getPrototypeOf(proto)
}
return undefined
}
export function markChanged(state: ImmerState) {
if (!state.modified_) {
state.modified_ = true
if (state.parent_) {
markChanged(state.parent_)
}
}
}
export function prepareCopy(state: {base_: any; copy_: any}) {
if (!state.copy_) {
state.copy_ = shallowCopy(state.base_)
}
}

85
node_modules/immer/src/core/scope.ts generated vendored Normal file
View File

@@ -0,0 +1,85 @@
import {
Patch,
PatchListener,
Drafted,
Immer,
DRAFT_STATE,
ImmerState,
ProxyType,
getPlugin
} from "../internal"
import {die} from "../utils/errors"
/** Each scope represents a `produce` call. */
export interface ImmerScope {
patches_?: Patch[]
inversePatches_?: Patch[]
canAutoFreeze_: boolean
drafts_: any[]
parent_?: ImmerScope
patchListener_?: PatchListener
immer_: Immer
unfinalizedDrafts_: number
}
let currentScope: ImmerScope | undefined
export function getCurrentScope() {
if (__DEV__ && !currentScope) die(0)
return currentScope!
}
function createScope(
parent_: ImmerScope | undefined,
immer_: Immer
): ImmerScope {
return {
drafts_: [],
parent_,
immer_,
// Whenever the modified draft contains a draft from another scope, we
// need to prevent auto-freezing so the unowned draft can be finalized.
canAutoFreeze_: true,
unfinalizedDrafts_: 0
}
}
export function usePatchesInScope(
scope: ImmerScope,
patchListener?: PatchListener
) {
if (patchListener) {
getPlugin("Patches") // assert we have the plugin
scope.patches_ = []
scope.inversePatches_ = []
scope.patchListener_ = patchListener
}
}
export function revokeScope(scope: ImmerScope) {
leaveScope(scope)
scope.drafts_.forEach(revokeDraft)
// @ts-ignore
scope.drafts_ = null
}
export function leaveScope(scope: ImmerScope) {
if (scope === currentScope) {
currentScope = scope.parent_
}
}
export function enterScope(immer: Immer) {
return (currentScope = createScope(currentScope, immer))
}
function revokeDraft(draft: Drafted) {
const state: ImmerState = draft[DRAFT_STATE]
if (
state.type_ === ProxyType.ProxyObject ||
state.type_ === ProxyType.ProxyArray
)
state.revoke_()
else state.revoked_ = true
}