1
0
Fork 0

Added Statistics calculation

Statistics now show calculated values
This commit is contained in:
Techognito 2025-09-04 17:30:00 +02:00
parent fe87374e47
commit fc0f69dacb
2147 changed files with 141321 additions and 39 deletions

View file

@ -0,0 +1,100 @@
import { createNode, updateNode } from './proxy'
import type { Node } from './tracking'
import { createCacheKeyComparator, referenceEqualityCheck } from '../lruMemoize'
import type { AnyFunction, DefaultMemoizeFields, Simplify } from '../types'
import { createCache } from './autotracking'
/**
* Uses an "auto-tracking" approach inspired by the work of the Ember Glimmer team.
* It uses a Proxy to wrap arguments and track accesses to nested fields
* in your selector on first read. Later, when the selector is called with
* new arguments, it identifies which accessed fields have changed and
* only recalculates the result if one or more of those accessed fields have changed.
* This allows it to be more precise than the shallow equality checks in `lruMemoize`.
*
* __Design Tradeoffs for `autotrackMemoize`:__
* - Pros:
* - It is likely to avoid excess calculations and recalculate fewer times than `lruMemoize` will,
* which may also result in fewer component re-renders.
* - Cons:
* - It only has a cache size of 1.
* - It is slower than `lruMemoize`, because it has to do more work. (How much slower is dependent on the number of accessed fields in a selector, number of calls, frequency of input changes, etc)
* - It can have some unexpected behavior. Because it tracks nested field accesses,
* cases where you don't access a field will not recalculate properly.
* For example, a badly-written selector like:
* ```ts
* createSelector([state => state.todos], todos => todos)
* ```
* that just immediately returns the extracted value will never update, because it doesn't see any field accesses to check.
*
* __Use Cases for `autotrackMemoize`:__
* - It is likely best used for cases where you need to access specific nested fields
* in data, and avoid recalculating if other fields in the same data objects are immutably updated.
*
* @param func - The function to be memoized.
* @returns A memoized function with a `.clearCache()` method attached.
*
* @example
* <caption>Using `createSelector`</caption>
* ```ts
* import { unstable_autotrackMemoize as autotrackMemoize, createSelector } from 'reselect'
*
* const selectTodoIds = createSelector(
* [(state: RootState) => state.todos],
* (todos) => todos.map(todo => todo.id),
* { memoize: autotrackMemoize }
* )
* ```
*
* @example
* <caption>Using `createSelectorCreator`</caption>
* ```ts
* import { unstable_autotrackMemoize as autotrackMemoize, createSelectorCreator } from 'reselect'
*
* const createSelectorAutotrack = createSelectorCreator({ memoize: autotrackMemoize })
*
* const selectTodoIds = createSelectorAutotrack(
* [(state: RootState) => state.todos],
* (todos) => todos.map(todo => todo.id)
* )
* ```
*
* @template Func - The type of the function that is memoized.
*
* @see {@link https://reselect.js.org/api/unstable_autotrackMemoize autotrackMemoize}
*
* @since 5.0.0
* @public
* @experimental
*/
export function autotrackMemoize<Func extends AnyFunction>(func: Func) {
// we reference arguments instead of spreading them for performance reasons
const node: Node<Record<string, unknown>> = createNode(
[] as unknown as Record<string, unknown>
)
let lastArgs: IArguments | null = null
const shallowEqual = createCacheKeyComparator(referenceEqualityCheck)
const cache = createCache(() => {
const res = func.apply(null, node.proxy as unknown as any[])
return res
})
function memoized() {
if (!shallowEqual(lastArgs, arguments)) {
updateNode(node, arguments as unknown as Record<string, unknown>)
lastArgs = arguments
}
return cache.value
}
memoized.clearCache = () => {
return cache.clear()
}
return memoized as Func & Simplify<DefaultMemoizeFields>
}

View file

@ -0,0 +1,159 @@
// Original autotracking implementation source:
// - https://gist.github.com/pzuraq/79bf862e0f8cd9521b79c4b6eccdc4f9
// Additional references:
// - https://www.pzuraq.com/blog/how-autotracking-works
// - https://v5.chriskrycho.com/journal/autotracking-elegant-dx-via-cutting-edge-cs/
import type { EqualityFn } from '../types'
import { assertIsFunction } from '../utils'
// The global revision clock. Every time state changes, the clock increments.
export let $REVISION = 0
// The current dependency tracker. Whenever we compute a cache, we create a Set
// to track any dependencies that are used while computing. If no cache is
// computing, then the tracker is null.
let CURRENT_TRACKER: Set<Cell<any> | TrackingCache> | null = null
// Storage represents a root value in the system - the actual state of our app.
export class Cell<T> {
revision = $REVISION
_value: T
_lastValue: T
_isEqual: EqualityFn = tripleEq
constructor(initialValue: T, isEqual: EqualityFn = tripleEq) {
this._value = this._lastValue = initialValue
this._isEqual = isEqual
}
// Whenever a storage value is read, it'll add itself to the current tracker if
// one exists, entangling its state with that cache.
get value() {
CURRENT_TRACKER?.add(this)
return this._value
}
// Whenever a storage value is updated, we bump the global revision clock,
// assign the revision for this storage to the new value, _and_ we schedule a
// rerender. This is important, and it's what makes autotracking _pull_
// based. We don't actively tell the caches which depend on the storage that
// anything has happened. Instead, we recompute the caches when needed.
set value(newValue) {
if (this.value === newValue) return
this._value = newValue
this.revision = ++$REVISION
}
}
function tripleEq(a: unknown, b: unknown) {
return a === b
}
// Caches represent derived state in the system. They are ultimately functions
// that are memoized based on what state they use to produce their output,
// meaning they will only rerun IFF a storage value that could affect the output
// has changed. Otherwise, they'll return the cached value.
export class TrackingCache {
_cachedValue: any
_cachedRevision = -1
_deps: any[] = []
hits = 0
fn: () => any
constructor(fn: () => any) {
this.fn = fn
}
clear() {
this._cachedValue = undefined
this._cachedRevision = -1
this._deps = []
this.hits = 0
}
get value() {
// When getting the value for a Cache, first we check all the dependencies of
// the cache to see what their current revision is. If the current revision is
// greater than the cached revision, then something has changed.
if (this.revision > this._cachedRevision) {
const { fn } = this
// We create a new dependency tracker for this cache. As the cache runs
// its function, any Storage or Cache instances which are used while
// computing will be added to this tracker. In the end, it will be the
// full list of dependencies that this Cache depends on.
const currentTracker = new Set<Cell<any>>()
const prevTracker = CURRENT_TRACKER
CURRENT_TRACKER = currentTracker
// try {
this._cachedValue = fn()
// } finally {
CURRENT_TRACKER = prevTracker
this.hits++
this._deps = Array.from(currentTracker)
// Set the cached revision. This is the current clock count of all the
// dependencies. If any dependency changes, this number will be less
// than the new revision.
this._cachedRevision = this.revision
// }
}
// If there is a current tracker, it means another Cache is computing and
// using this one, so we add this one to the tracker.
CURRENT_TRACKER?.add(this)
// Always return the cached value.
return this._cachedValue
}
get revision() {
// The current revision is the max of all the dependencies' revisions.
return Math.max(...this._deps.map(d => d.revision), 0)
}
}
export function getValue<T>(cell: Cell<T>): T {
if (!(cell instanceof Cell)) {
console.warn('Not a valid cell! ', cell)
}
return cell.value
}
type CellValue<T extends Cell<unknown>> = T extends Cell<infer U> ? U : never
export function setValue<T extends Cell<unknown>>(
storage: T,
value: CellValue<T>
): void {
if (!(storage instanceof Cell)) {
throw new TypeError(
'setValue must be passed a tracked store created with `createStorage`.'
)
}
storage.value = storage._lastValue = value
}
export function createCell<T = unknown>(
initialValue: T,
isEqual: EqualityFn = tripleEq
): Cell<T> {
return new Cell(initialValue, isEqual)
}
export function createCache<T = unknown>(fn: () => T): TrackingCache {
assertIsFunction(
fn,
'the first parameter to `createCache` must be a function'
)
return new TrackingCache(fn)
}

230
node_modules/reselect/src/autotrackMemoize/proxy.ts generated vendored Normal file
View file

@ -0,0 +1,230 @@
// Original source:
// - https://github.com/simonihmig/tracked-redux/blob/master/packages/tracked-redux/src/-private/proxy.ts
import type { Node, Tag } from './tracking'
import {
consumeCollection,
consumeTag,
createTag,
dirtyCollection,
dirtyTag
} from './tracking'
export const REDUX_PROXY_LABEL = Symbol()
let nextId = 0
const proto = Object.getPrototypeOf({})
class ObjectTreeNode<T extends Record<string, unknown>> implements Node<T> {
proxy: T = new Proxy(this, objectProxyHandler) as unknown as T
tag = createTag()
tags = {} as Record<string, Tag>
children = {} as Record<string, Node>
collectionTag = null
id = nextId++
constructor(public value: T) {
this.value = value
this.tag.value = value
}
}
const objectProxyHandler = {
get(node: Node, key: string | symbol): unknown {
function calculateResult() {
const { value } = node
const childValue = Reflect.get(value, key)
if (typeof key === 'symbol') {
return childValue
}
if (key in proto) {
return childValue
}
if (typeof childValue === 'object' && childValue !== null) {
let childNode = node.children[key]
if (childNode === undefined) {
childNode = node.children[key] = createNode(childValue)
}
if (childNode.tag) {
consumeTag(childNode.tag)
}
return childNode.proxy
} else {
let tag = node.tags[key]
if (tag === undefined) {
tag = node.tags[key] = createTag()
tag.value = childValue
}
consumeTag(tag)
return childValue
}
}
const res = calculateResult()
return res
},
ownKeys(node: Node): ArrayLike<string | symbol> {
consumeCollection(node)
return Reflect.ownKeys(node.value)
},
getOwnPropertyDescriptor(
node: Node,
prop: string | symbol
): PropertyDescriptor | undefined {
return Reflect.getOwnPropertyDescriptor(node.value, prop)
},
has(node: Node, prop: string | symbol): boolean {
return Reflect.has(node.value, prop)
}
}
class ArrayTreeNode<T extends Array<unknown>> implements Node<T> {
proxy: T = new Proxy([this], arrayProxyHandler) as unknown as T
tag = createTag()
tags = {}
children = {}
collectionTag = null
id = nextId++
constructor(public value: T) {
this.value = value
this.tag.value = value
}
}
const arrayProxyHandler = {
get([node]: [Node], key: string | symbol): unknown {
if (key === 'length') {
consumeCollection(node)
}
return objectProxyHandler.get(node, key)
},
ownKeys([node]: [Node]): ArrayLike<string | symbol> {
return objectProxyHandler.ownKeys(node)
},
getOwnPropertyDescriptor(
[node]: [Node],
prop: string | symbol
): PropertyDescriptor | undefined {
return objectProxyHandler.getOwnPropertyDescriptor(node, prop)
},
has([node]: [Node], prop: string | symbol): boolean {
return objectProxyHandler.has(node, prop)
}
}
export function createNode<T extends Array<unknown> | Record<string, unknown>>(
value: T
): Node<T> {
if (Array.isArray(value)) {
return new ArrayTreeNode(value)
}
return new ObjectTreeNode(value) as Node<T>
}
const keysMap = new WeakMap<
Array<unknown> | Record<string, unknown>,
Set<string>
>()
export function updateNode<T extends Array<unknown> | Record<string, unknown>>(
node: Node<T>,
newValue: T
): void {
const { value, tags, children } = node
node.value = newValue
if (
Array.isArray(value) &&
Array.isArray(newValue) &&
value.length !== newValue.length
) {
dirtyCollection(node)
} else {
if (value !== newValue) {
let oldKeysSize = 0
let newKeysSize = 0
let anyKeysAdded = false
for (const _key in value) {
oldKeysSize++
}
for (const key in newValue) {
newKeysSize++
if (!(key in value)) {
anyKeysAdded = true
break
}
}
const isDifferent = anyKeysAdded || oldKeysSize !== newKeysSize
if (isDifferent) {
dirtyCollection(node)
}
}
}
for (const key in tags) {
const childValue = (value as Record<string, unknown>)[key]
const newChildValue = (newValue as Record<string, unknown>)[key]
if (childValue !== newChildValue) {
dirtyCollection(node)
dirtyTag(tags[key], newChildValue)
}
if (typeof newChildValue === 'object' && newChildValue !== null) {
delete tags[key]
}
}
for (const key in children) {
const childNode = children[key]
const newChildValue = (newValue as Record<string, unknown>)[key]
const childValue = childNode.value
if (childValue === newChildValue) {
continue
} else if (typeof newChildValue === 'object' && newChildValue !== null) {
updateNode(childNode, newChildValue as Record<string, unknown>)
} else {
deleteNode(childNode)
delete children[key]
}
}
}
function deleteNode(node: Node): void {
if (node.tag) {
dirtyTag(node.tag, null)
}
dirtyCollection(node)
for (const key in node.tags) {
dirtyTag(node.tags[key], null)
}
for (const key in node.children) {
deleteNode(node.children[key])
}
}

50
node_modules/reselect/src/autotrackMemoize/tracking.ts generated vendored Normal file
View file

@ -0,0 +1,50 @@
import type { Cell } from './autotracking'
import {
getValue as consumeTag,
createCell as createStorage,
setValue
} from './autotracking'
export type Tag = Cell<unknown>
const neverEq = (a: any, b: any): boolean => false
export function createTag(): Tag {
return createStorage(null, neverEq)
}
export { consumeTag }
export function dirtyTag(tag: Tag, value: any): void {
setValue(tag, value)
}
export interface Node<
T extends Array<unknown> | Record<string, unknown> =
| Array<unknown>
| Record<string, unknown>
> {
collectionTag: Tag | null
tag: Tag | null
tags: Record<string, Tag>
children: Record<string, Node>
proxy: T
value: T
id: number
}
export const consumeCollection = (node: Node): void => {
let tag = node.collectionTag
if (tag === null) {
tag = node.collectionTag = createTag()
}
consumeTag(tag)
}
export const dirtyCollection = (node: Node): void => {
const tag = node.collectionTag
if (tag !== null) {
dirtyTag(tag, null)
}
}

9
node_modules/reselect/src/autotrackMemoize/utils.ts generated vendored Normal file
View file

@ -0,0 +1,9 @@
export function assert(
condition: any,
msg = 'Assertion failed!'
): asserts condition {
if (!condition) {
console.error(msg)
throw new Error(msg)
}
}

493
node_modules/reselect/src/createSelectorCreator.ts generated vendored Normal file
View file

@ -0,0 +1,493 @@
import { weakMapMemoize } from './weakMapMemoize'
import type {
Combiner,
CreateSelectorOptions,
DropFirstParameter,
ExtractMemoizerFields,
GetParamsFromSelectors,
GetStateFromSelectors,
InterruptRecursion,
OutputSelector,
Selector,
SelectorArray,
SetRequired,
Simplify,
UnknownMemoizer
} from './types'
import {
assertIsFunction,
collectInputSelectorResults,
ensureIsArray,
getDependencies,
getDevModeChecksExecutionInfo
} from './utils'
/**
* An instance of `createSelector`, customized with a given memoize implementation.
*
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
* @template StateType - The type of state that the selectors created with this selector creator will operate on.
*
* @public
*/
export interface CreateSelectorFunction<
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
StateType = any
> {
/**
* Creates a memoized selector function.
*
* @param createSelectorArgs - An arbitrary number of input selectors as separate inline arguments and a `combiner` function.
* @returns A memoized output selector.
*
* @template InputSelectors - The type of the input selectors as an array.
* @template Result - The return type of the `combiner` as well as the output selector.
* @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
* @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
*
* @see {@link https://reselect.js.org/api/createselector `createSelector`}
*/
<InputSelectors extends SelectorArray<StateType>, Result>(
...createSelectorArgs: [
...inputSelectors: InputSelectors,
combiner: Combiner<InputSelectors, Result>
]
): OutputSelector<
InputSelectors,
Result,
MemoizeFunction,
ArgsMemoizeFunction
> &
InterruptRecursion
/**
* Creates a memoized selector function.
*
* @param createSelectorArgs - An arbitrary number of input selectors as separate inline arguments, a `combiner` function and an `options` object.
* @returns A memoized output selector.
*
* @template InputSelectors - The type of the input selectors as an array.
* @template Result - The return type of the `combiner` as well as the output selector.
* @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
* @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
*
* @see {@link https://reselect.js.org/api/createselector `createSelector`}
*/
<
InputSelectors extends SelectorArray<StateType>,
Result,
OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
>(
...createSelectorArgs: [
...inputSelectors: InputSelectors,
combiner: Combiner<InputSelectors, Result>,
createSelectorOptions: Simplify<
CreateSelectorOptions<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
>
>
]
): OutputSelector<
InputSelectors,
Result,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
> &
InterruptRecursion
/**
* Creates a memoized selector function.
*
* @param inputSelectors - An array of input selectors.
* @param combiner - A function that Combines the input selectors and returns an output selector. Otherwise known as the result function.
* @param createSelectorOptions - An optional options object that allows for further customization per selector.
* @returns A memoized output selector.
*
* @template InputSelectors - The type of the input selectors array.
* @template Result - The return type of the `combiner` as well as the output selector.
* @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
* @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
*
* @see {@link https://reselect.js.org/api/createselector `createSelector`}
*/
<
InputSelectors extends SelectorArray<StateType>,
Result,
OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
>(
inputSelectors: [...InputSelectors],
combiner: Combiner<InputSelectors, Result>,
createSelectorOptions?: Simplify<
CreateSelectorOptions<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
>
>
): OutputSelector<
InputSelectors,
Result,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
> &
InterruptRecursion
/**
* Creates a "pre-typed" version of {@linkcode createSelector createSelector}
* where the `state` type is predefined.
*
* This allows you to set the `state` type once, eliminating the need to
* specify it with every {@linkcode createSelector createSelector} call.
*
* @returns A pre-typed `createSelector` with the state type already defined.
*
* @example
* ```ts
* import { createSelector } from 'reselect'
*
* export interface RootState {
* todos: { id: number; completed: boolean }[]
* alerts: { id: number; read: boolean }[]
* }
*
* export const createAppSelector = createSelector.withTypes<RootState>()
*
* const selectTodoIds = createAppSelector(
* [
* // Type of `state` is set to `RootState`, no need to manually set the type
* state => state.todos
* ],
* todos => todos.map(({ id }) => id)
* )
* ```
* @template OverrideStateType - The specific type of state used by all selectors created with this selector creator.
*
* @see {@link https://reselect.js.org/api/createselector#defining-a-pre-typed-createselector `createSelector.withTypes`}
*
* @since 5.1.0
*/
withTypes: <OverrideStateType extends StateType>() => CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideStateType
>
}
/**
* Creates a selector creator function with the specified memoization function
* and options for customizing memoization behavior.
*
* @param options - An options object containing the `memoize` function responsible for memoizing the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). It also provides additional options for customizing memoization. While the `memoize` property is mandatory, the rest are optional.
* @returns A customized `createSelector` function.
*
* @example
* ```ts
* const customCreateSelector = createSelectorCreator({
* memoize: customMemoize, // Function to be used to memoize `resultFunc`
* memoizeOptions: [memoizeOption1, memoizeOption2], // Options passed to `customMemoize` as the second argument onwards
* argsMemoize: customArgsMemoize, // Function to be used to memoize the selector's arguments
* argsMemoizeOptions: [argsMemoizeOption1, argsMemoizeOption2] // Options passed to `customArgsMemoize` as the second argument onwards
* })
*
* const customSelector = customCreateSelector(
* [inputSelector1, inputSelector2],
* resultFunc // `resultFunc` will be passed as the first argument to `customMemoize`
* )
*
* customSelector(
* ...selectorArgs // Will be memoized by `customArgsMemoize`
* )
* ```
*
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
*
* @see {@link https://reselect.js.org/api/createSelectorCreator#using-options-since-500 `createSelectorCreator`}
*
* @since 5.0.0
* @public
*/
export function createSelectorCreator<
MemoizeFunction extends UnknownMemoizer,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
>(
options: Simplify<
SetRequired<
CreateSelectorOptions<
typeof weakMapMemoize,
typeof weakMapMemoize,
MemoizeFunction,
ArgsMemoizeFunction
>,
'memoize'
>
>
): CreateSelectorFunction<MemoizeFunction, ArgsMemoizeFunction>
/**
* Creates a selector creator function with the specified memoization function
* and options for customizing memoization behavior.
*
* @param memoize - The `memoize` function responsible for memoizing the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @param memoizeOptionsFromArgs - Optional configuration options for the memoization function. These options are then passed to the memoize function as the second argument onwards.
* @returns A customized `createSelector` function.
*
* @example
* ```ts
* const customCreateSelector = createSelectorCreator(customMemoize, // Function to be used to memoize `resultFunc`
* option1, // Will be passed as second argument to `customMemoize`
* option2, // Will be passed as third argument to `customMemoize`
* option3 // Will be passed as fourth argument to `customMemoize`
* )
*
* const customSelector = customCreateSelector(
* [inputSelector1, inputSelector2],
* resultFunc // `resultFunc` will be passed as the first argument to `customMemoize`
* )
* ```
*
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
*
* @see {@link https://reselect.js.org/api/createSelectorCreator#using-memoize-and-memoizeoptions `createSelectorCreator`}
*
* @public
*/
export function createSelectorCreator<MemoizeFunction extends UnknownMemoizer>(
memoize: MemoizeFunction,
...memoizeOptionsFromArgs: DropFirstParameter<MemoizeFunction>
): CreateSelectorFunction<MemoizeFunction>
/**
* Creates a selector creator function with the specified memoization
* function and options for customizing memoization behavior.
*
* @param memoizeOrOptions - Either A `memoize` function or an `options` object containing the `memoize` function.
* @param memoizeOptionsFromArgs - Optional configuration options for the memoization function. These options are then passed to the memoize function as the second argument onwards.
* @returns A customized `createSelector` function.
*
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
* @template MemoizeOrOptions - The type of the first argument. It can either be a `memoize` function or an `options` object containing the `memoize` function.
*/
export function createSelectorCreator<
MemoizeFunction extends UnknownMemoizer,
ArgsMemoizeFunction extends UnknownMemoizer,
MemoizeOrOptions extends
| MemoizeFunction
| SetRequired<
CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
'memoize'
>
>(
memoizeOrOptions: MemoizeOrOptions,
...memoizeOptionsFromArgs: MemoizeOrOptions extends SetRequired<
CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
'memoize'
>
? never
: DropFirstParameter<MemoizeFunction>
) {
/** options initially passed into `createSelectorCreator`. */
const createSelectorCreatorOptions: SetRequired<
CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
'memoize'
> = typeof memoizeOrOptions === 'function'
? {
memoize: memoizeOrOptions as MemoizeFunction,
memoizeOptions: memoizeOptionsFromArgs
}
: memoizeOrOptions
const createSelector = <
InputSelectors extends SelectorArray,
Result,
OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
>(
...createSelectorArgs: [
...inputSelectors: [...InputSelectors],
combiner: Combiner<InputSelectors, Result>,
createSelectorOptions?: CreateSelectorOptions<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
>
]
) => {
let recomputations = 0
let dependencyRecomputations = 0
let lastResult: Result
// Due to the intricacies of rest params, we can't do an optional arg after `...createSelectorArgs`.
// So, start by declaring the default value here.
// (And yes, the words 'memoize' and 'options' appear too many times in this next sequence.)
let directlyPassedOptions: CreateSelectorOptions<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
> = {}
// Normally, the result func or "combiner" is the last arg
let resultFunc = createSelectorArgs.pop() as
| Combiner<InputSelectors, Result>
| CreateSelectorOptions<
MemoizeFunction,
ArgsMemoizeFunction,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
>
// If the result func is actually an _object_, assume it's our options object
if (typeof resultFunc === 'object') {
directlyPassedOptions = resultFunc
// and pop the real result func off
resultFunc = createSelectorArgs.pop() as Combiner<InputSelectors, Result>
}
assertIsFunction(
resultFunc,
`createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
)
// Determine which set of options we're using. Prefer options passed directly,
// but fall back to options given to `createSelectorCreator`.
const combinedOptions = {
...createSelectorCreatorOptions,
...directlyPassedOptions
}
const {
memoize,
memoizeOptions = [],
argsMemoize = weakMapMemoize,
argsMemoizeOptions = [],
devModeChecks = {}
} = combinedOptions
// Simplifying assumption: it's unlikely that the first options arg of the provided memoizer
// is an array. In most libs I've looked at, it's an equality function or options object.
// Based on that, if `memoizeOptions` _is_ an array, we assume it's a full
// user-provided array of options. Otherwise, it must be just the _first_ arg, and so
// we wrap it in an array so we can apply it.
const finalMemoizeOptions = ensureIsArray(memoizeOptions)
const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions)
const dependencies = getDependencies(createSelectorArgs) as InputSelectors
const memoizedResultFunc = memoize(function recomputationWrapper() {
recomputations++
// apply arguments instead of spreading for performance.
// @ts-ignore
return (resultFunc as Combiner<InputSelectors, Result>).apply(
null,
arguments as unknown as Parameters<Combiner<InputSelectors, Result>>
)
}, ...finalMemoizeOptions) as Combiner<InputSelectors, Result> &
ExtractMemoizerFields<OverrideMemoizeFunction>
let firstRun = true
// If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
const selector = argsMemoize(function dependenciesChecker() {
dependencyRecomputations++
/** Return values of input selectors which the `resultFunc` takes as arguments. */
const inputSelectorResults = collectInputSelectorResults(
dependencies,
arguments
)
// apply arguments instead of spreading for performance.
// @ts-ignore
lastResult = memoizedResultFunc.apply(null, inputSelectorResults)
if (process.env.NODE_ENV !== 'production') {
const { identityFunctionCheck, inputStabilityCheck } =
getDevModeChecksExecutionInfo(firstRun, devModeChecks)
if (identityFunctionCheck.shouldRun) {
identityFunctionCheck.run(
resultFunc as Combiner<InputSelectors, Result>,
inputSelectorResults,
lastResult
)
}
if (inputStabilityCheck.shouldRun) {
// make a second copy of the params, to check if we got the same results
const inputSelectorResultsCopy = collectInputSelectorResults(
dependencies,
arguments
)
inputStabilityCheck.run(
{ inputSelectorResults, inputSelectorResultsCopy },
{ memoize, memoizeOptions: finalMemoizeOptions },
arguments
)
}
if (firstRun) firstRun = false
}
return lastResult
}, ...finalArgsMemoizeOptions) as unknown as Selector<
GetStateFromSelectors<InputSelectors>,
Result,
GetParamsFromSelectors<InputSelectors>
> &
ExtractMemoizerFields<OverrideArgsMemoizeFunction>
return Object.assign(selector, {
resultFunc,
memoizedResultFunc,
dependencies,
dependencyRecomputations: () => dependencyRecomputations,
resetDependencyRecomputations: () => {
dependencyRecomputations = 0
},
lastResult: () => lastResult,
recomputations: () => recomputations,
resetRecomputations: () => {
recomputations = 0
},
memoize,
argsMemoize
}) as OutputSelector<
InputSelectors,
Result,
OverrideMemoizeFunction,
OverrideArgsMemoizeFunction
>
}
Object.assign(createSelector, {
withTypes: () => createSelector
})
return createSelector as CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction
>
}
/**
* Accepts one or more "input selectors" (either as separate arguments or a single array),
* a single "result function" / "combiner", and an optional options object, and
* generates a memoized selector function.
*
* @see {@link https://reselect.js.org/api/createSelector `createSelector`}
*
* @public
*/
export const createSelector =
/* #__PURE__ */ createSelectorCreator(weakMapMemoize)

454
node_modules/reselect/src/createStructuredSelector.ts generated vendored Normal file
View file

@ -0,0 +1,454 @@
import { createSelector } from './createSelectorCreator'
import type { CreateSelectorFunction } from './createSelectorCreator'
import type {
InterruptRecursion,
ObjectValuesToTuple,
OutputSelector,
Selector,
Simplify,
UnknownMemoizer
} from './types'
import { assertIsObject } from './utils'
import type { weakMapMemoize } from './weakMapMemoize'
/**
* Represents a mapping of selectors to their return types.
*
* @template TObject - An object type where each property is a selector function.
*
* @public
*/
export type SelectorResultsMap<TObject extends SelectorsObject> = {
[Key in keyof TObject]: ReturnType<TObject[Key]>
}
/**
* Represents a mapping of selectors for each key in a given root state.
*
* This type is a utility that takes a root state object type and
* generates a corresponding set of selectors. Each selector is associated
* with a key in the root state, allowing for the selection
* of specific parts of the state.
*
* @template RootState - The type of the root state object.
*
* @since 5.0.0
* @public
*/
export type RootStateSelectors<RootState = any> = {
[Key in keyof RootState]: Selector<RootState, RootState[Key], []>
}
/**
* @deprecated Please use {@linkcode StructuredSelectorCreator.withTypes createStructuredSelector.withTypes<RootState>()} instead. This type will be removed in the future.
* @template RootState - The type of the root state object.
*
* @since 5.0.0
* @public
*/
export type TypedStructuredSelectorCreator<RootState = any> =
/**
* A convenience function that simplifies returning an object
* made up of selector results.
*
* @param inputSelectorsObject - A key value pair consisting of input selectors.
* @param selectorCreator - A custom selector creator function. It defaults to `createSelector`.
* @returns A memoized structured selector.
*
* @example
* <caption>Modern Use Case</caption>
* ```ts
* import { createSelector, createStructuredSelector } from 'reselect'
*
* interface RootState {
* todos: {
* id: number
* completed: boolean
* title: string
* description: string
* }[]
* alerts: { id: number; read: boolean }[]
* }
*
* // This:
* const structuredSelector = createStructuredSelector(
* {
* todos: (state: RootState) => state.todos,
* alerts: (state: RootState) => state.alerts,
* todoById: (state: RootState, id: number) => state.todos[id]
* },
* createSelector
* )
*
* // Is essentially the same as this:
* const selector = createSelector(
* [
* (state: RootState) => state.todos,
* (state: RootState) => state.alerts,
* (state: RootState, id: number) => state.todos[id]
* ],
* (todos, alerts, todoById) => {
* return {
* todos,
* alerts,
* todoById
* }
* }
* )
* ```
*
* @example
* <caption>In your component:</caption>
* ```tsx
* import type { RootState } from 'createStructuredSelector/modernUseCase'
* import { structuredSelector } from 'createStructuredSelector/modernUseCase'
* import type { FC } from 'react'
* import { useSelector } from 'react-redux'
*
* interface Props {
* id: number
* }
*
* const MyComponent: FC<Props> = ({ id }) => {
* const { todos, alerts, todoById } = useSelector((state: RootState) =>
* structuredSelector(state, id)
* )
*
* return (
* <div>
* Next to do is:
* <h2>{todoById.title}</h2>
* <p>Description: {todoById.description}</p>
* <ul>
* <h3>All other to dos:</h3>
* {todos.map(todo => (
* <li key={todo.id}>{todo.title}</li>
* ))}
* </ul>
* </div>
* )
* }
* ```
*
* @example
* <caption>Simple Use Case</caption>
* ```ts
* const selectA = state => state.a
* const selectB = state => state.b
*
* // The result function in the following selector
* // is simply building an object from the input selectors
* const structuredSelector = createSelector(selectA, selectB, (a, b) => ({
* a,
* b
* }))
*
* const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 }
* ```
*
* @template InputSelectorsObject - The shape of the input selectors object.
* @template MemoizeFunction - The type of the memoize function that is used to create the structured selector. It defaults to `weakMapMemoize`.
* @template ArgsMemoizeFunction - The type of the of the memoize function that is used to memoize the arguments passed into the generated structured selector. It defaults to `weakMapMemoize`.
*
* @see {@link https://reselect.js.org/api/createStructuredSelector `createStructuredSelector`}
*/
<
InputSelectorsObject extends RootStateSelectors<RootState> = RootStateSelectors<RootState>,
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
>(
inputSelectorsObject: InputSelectorsObject,
selectorCreator?: CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction
>
) => OutputSelector<
ObjectValuesToTuple<InputSelectorsObject>,
Simplify<SelectorResultsMap<InputSelectorsObject>>,
MemoizeFunction,
ArgsMemoizeFunction
> &
InterruptRecursion
/**
* Represents an object where each property is a selector function.
*
* @template StateType - The type of state that all the selectors operate on.
*
* @public
*/
export type SelectorsObject<StateType = any> = Record<
string,
Selector<StateType>
>
/**
* It provides a way to create structured selectors.
* The structured selector can take multiple input selectors
* and map their output to an object with specific keys.
*
* @template StateType - The type of state that the structured selectors created with this structured selector creator will operate on.
*
* @see {@link https://reselect.js.org/api/createStructuredSelector `createStructuredSelector`}
*
* @public
*/
export interface StructuredSelectorCreator<StateType = any> {
/**
* A convenience function that simplifies returning an object
* made up of selector results.
*
* @param inputSelectorsObject - A key value pair consisting of input selectors.
* @param selectorCreator - A custom selector creator function. It defaults to `createSelector`.
* @returns A memoized structured selector.
*
* @example
* <caption>Modern Use Case</caption>
* ```ts
* import { createSelector, createStructuredSelector } from 'reselect'
*
* interface RootState {
* todos: {
* id: number
* completed: boolean
* title: string
* description: string
* }[]
* alerts: { id: number; read: boolean }[]
* }
*
* // This:
* const structuredSelector = createStructuredSelector(
* {
* todos: (state: RootState) => state.todos,
* alerts: (state: RootState) => state.alerts,
* todoById: (state: RootState, id: number) => state.todos[id]
* },
* createSelector
* )
*
* // Is essentially the same as this:
* const selector = createSelector(
* [
* (state: RootState) => state.todos,
* (state: RootState) => state.alerts,
* (state: RootState, id: number) => state.todos[id]
* ],
* (todos, alerts, todoById) => {
* return {
* todos,
* alerts,
* todoById
* }
* }
* )
* ```
*
* @example
* <caption>In your component:</caption>
* ```tsx
* import type { RootState } from 'createStructuredSelector/modernUseCase'
* import { structuredSelector } from 'createStructuredSelector/modernUseCase'
* import type { FC } from 'react'
* import { useSelector } from 'react-redux'
*
* interface Props {
* id: number
* }
*
* const MyComponent: FC<Props> = ({ id }) => {
* const { todos, alerts, todoById } = useSelector((state: RootState) =>
* structuredSelector(state, id)
* )
*
* return (
* <div>
* Next to do is:
* <h2>{todoById.title}</h2>
* <p>Description: {todoById.description}</p>
* <ul>
* <h3>All other to dos:</h3>
* {todos.map(todo => (
* <li key={todo.id}>{todo.title}</li>
* ))}
* </ul>
* </div>
* )
* }
* ```
*
* @example
* <caption>Simple Use Case</caption>
* ```ts
* const selectA = state => state.a
* const selectB = state => state.b
*
* // The result function in the following selector
* // is simply building an object from the input selectors
* const structuredSelector = createSelector(selectA, selectB, (a, b) => ({
* a,
* b
* }))
*
* const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 }
* ```
*
* @template InputSelectorsObject - The shape of the input selectors object.
* @template MemoizeFunction - The type of the memoize function that is used to create the structured selector. It defaults to `weakMapMemoize`.
* @template ArgsMemoizeFunction - The type of the of the memoize function that is used to memoize the arguments passed into the generated structured selector. It defaults to `weakMapMemoize`.
*
* @see {@link https://reselect.js.org/api/createStructuredSelector `createStructuredSelector`}
*/
<
InputSelectorsObject extends SelectorsObject<StateType>,
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
>(
inputSelectorsObject: InputSelectorsObject,
selectorCreator?: CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction
>
): OutputSelector<
ObjectValuesToTuple<InputSelectorsObject>,
Simplify<SelectorResultsMap<InputSelectorsObject>>,
MemoizeFunction,
ArgsMemoizeFunction
> &
InterruptRecursion
/**
* Creates a "pre-typed" version of
* {@linkcode createStructuredSelector createStructuredSelector}
* where the `state` type is predefined.
*
* This allows you to set the `state` type once, eliminating the need to
* specify it with every
* {@linkcode createStructuredSelector createStructuredSelector} call.
*
* @returns A pre-typed `createStructuredSelector` with the state type already defined.
*
* @example
* ```ts
* import { createStructuredSelector } from 'reselect'
*
* export interface RootState {
* todos: { id: number; completed: boolean }[]
* alerts: { id: number; read: boolean }[]
* }
*
* export const createStructuredAppSelector =
* createStructuredSelector.withTypes<RootState>()
*
* const structuredAppSelector = createStructuredAppSelector({
* // Type of `state` is set to `RootState`, no need to manually set the type
* todos: state => state.todos,
* alerts: state => state.alerts,
* todoById: (state, id: number) => state.todos[id]
* })
*
* ```
* @template OverrideStateType - The specific type of state used by all structured selectors created with this structured selector creator.
*
* @see {@link https://reselect.js.org/api/createstructuredselector#defining-a-pre-typed-createstructuredselector `createSelector.withTypes`}
*
* @since 5.1.0
*/
withTypes: <
OverrideStateType extends StateType
>() => StructuredSelectorCreator<OverrideStateType>
}
/**
* A convenience function that simplifies returning an object
* made up of selector results.
*
* @param inputSelectorsObject - A key value pair consisting of input selectors.
* @param selectorCreator - A custom selector creator function. It defaults to `createSelector`.
* @returns A memoized structured selector.
*
* @example
* <caption>Modern Use Case</caption>
* ```ts
* import { createSelector, createStructuredSelector } from 'reselect'
*
* interface RootState {
* todos: {
* id: number
* completed: boolean
* title: string
* description: string
* }[]
* alerts: { id: number; read: boolean }[]
* }
*
* // This:
* const structuredSelector = createStructuredSelector(
* {
* todos: (state: RootState) => state.todos,
* alerts: (state: RootState) => state.alerts,
* todoById: (state: RootState, id: number) => state.todos[id]
* },
* createSelector
* )
*
* // Is essentially the same as this:
* const selector = createSelector(
* [
* (state: RootState) => state.todos,
* (state: RootState) => state.alerts,
* (state: RootState, id: number) => state.todos[id]
* ],
* (todos, alerts, todoById) => {
* return {
* todos,
* alerts,
* todoById
* }
* }
* )
* ```
*
* @see {@link https://reselect.js.org/api/createStructuredSelector `createStructuredSelector`}
*
* @public
*/
export const createStructuredSelector: StructuredSelectorCreator =
Object.assign(
<
InputSelectorsObject extends SelectorsObject,
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
>(
inputSelectorsObject: InputSelectorsObject,
selectorCreator: CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction
> = createSelector as CreateSelectorFunction<
MemoizeFunction,
ArgsMemoizeFunction
>
) => {
assertIsObject(
inputSelectorsObject,
'createStructuredSelector expects first argument to be an object ' +
`where each property is a selector, instead received a ${typeof inputSelectorsObject}`
)
const inputSelectorKeys = Object.keys(inputSelectorsObject)
const dependencies = inputSelectorKeys.map(
key => inputSelectorsObject[key]
)
const structuredSelector = selectorCreator(
dependencies,
(...inputSelectorResults: any[]) => {
return inputSelectorResults.reduce((composition, value, index) => {
composition[inputSelectorKeys[index]] = value
return composition
}, {})
}
)
return structuredSelector
},
{ withTypes: () => createStructuredSelector }
) as StructuredSelectorCreator

View file

@ -0,0 +1,53 @@
import type { AnyFunction } from '../types'
/**
* Runs a check to determine if the given result function behaves as an
* identity function. An identity function is one that returns its
* input unchanged, for example, `x => x`. This check helps ensure
* efficient memoization and prevent unnecessary re-renders by encouraging
* proper use of transformation logic in result functions and
* extraction logic in input selectors.
*
* @param resultFunc - The result function to be checked.
* @param inputSelectorsResults - The results of the input selectors.
* @param outputSelectorResult - The result of the output selector.
*
* @see {@link https://reselect.js.org/api/development-only-stability-checks#identityfunctioncheck `identityFunctionCheck`}
*
* @since 5.0.0
* @internal
*/
export const runIdentityFunctionCheck = (
resultFunc: AnyFunction,
inputSelectorsResults: unknown[],
outputSelectorResult: unknown
) => {
if (
inputSelectorsResults.length === 1 &&
inputSelectorsResults[0] === outputSelectorResult
) {
let isInputSameAsOutput = false
try {
const emptyObject = {}
if (resultFunc(emptyObject) === emptyObject) isInputSameAsOutput = true
} catch {
// Do nothing
}
if (isInputSameAsOutput) {
let stack: string | undefined = undefined
try {
throw new Error()
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-extra-semi, no-extra-semi
;({ stack } = e as Error)
}
console.warn(
'The result function returned its own inputs without modification. e.g' +
'\n`createSelector([state => state.todos], todos => todos)`' +
'\nThis could lead to inefficient memoization and unnecessary re-renders.' +
'\nEnsure transformation logic is in the result function, and extraction logic is in the input selectors.',
{ stack }
)
}
}
}

View file

@ -0,0 +1,59 @@
import type { CreateSelectorOptions, UnknownMemoizer } from '../types'
/**
* Runs a stability check to ensure the input selector results remain stable
* when provided with the same arguments. This function is designed to detect
* changes in the output of input selectors, which can impact the performance of memoized selectors.
*
* @param inputSelectorResultsObject - An object containing two arrays: `inputSelectorResults` and `inputSelectorResultsCopy`, representing the results of input selectors.
* @param options - Options object consisting of a `memoize` function and a `memoizeOptions` object.
* @param inputSelectorArgs - List of arguments being passed to the input selectors.
*
* @see {@link https://reselect.js.org/api/development-only-stability-checks/#inputstabilitycheck `inputStabilityCheck`}
*
* @since 5.0.0
* @internal
*/
export const runInputStabilityCheck = (
inputSelectorResultsObject: {
inputSelectorResults: unknown[]
inputSelectorResultsCopy: unknown[]
},
options: Required<
Pick<
CreateSelectorOptions<UnknownMemoizer, UnknownMemoizer>,
'memoize' | 'memoizeOptions'
>
>,
inputSelectorArgs: unknown[] | IArguments
) => {
const { memoize, memoizeOptions } = options
const { inputSelectorResults, inputSelectorResultsCopy } =
inputSelectorResultsObject
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions)
// if the memoize method thinks the parameters are equal, these *should* be the same reference
const areInputSelectorResultsEqual =
createAnEmptyObject.apply(null, inputSelectorResults) ===
createAnEmptyObject.apply(null, inputSelectorResultsCopy)
if (!areInputSelectorResultsEqual) {
let stack: string | undefined = undefined
try {
throw new Error()
} catch (e) {
// eslint-disable-next-line @typescript-eslint/no-extra-semi, no-extra-semi
;({ stack } = e as Error)
}
console.warn(
'An input selector returned a different result when passed same arguments.' +
'\nThis means your output selector will likely run more frequently than intended.' +
'\nAvoid returning a new reference inside your input selector, e.g.' +
'\n`createSelector([state => state.todos.map(todo => todo.id)], todoIds => todoIds.length)`',
{
arguments: inputSelectorArgs,
firstInputs: inputSelectorResults,
secondInputs: inputSelectorResultsCopy,
stack
}
)
}
}

View file

@ -0,0 +1,63 @@
import type { DevModeChecks } from '../types'
/**
* Global configuration for development mode checks. This specifies the default
* frequency at which each development mode check should be performed.
*
* @since 5.0.0
* @internal
*/
export const globalDevModeChecks: DevModeChecks = {
inputStabilityCheck: 'once',
identityFunctionCheck: 'once'
}
/**
* Overrides the development mode checks settings for all selectors.
*
* Reselect performs additional checks in development mode to help identify and
* warn about potential issues in selector behavior. This function allows you to
* customize the behavior of these checks across all selectors in your application.
*
* **Note**: This setting can still be overridden per selector inside `createSelector`'s `options` object.
* See {@link https://github.com/reduxjs/reselect#2-per-selector-by-passing-an-identityfunctioncheck-option-directly-to-createselector per-selector-configuration}
* and {@linkcode CreateSelectorOptions.identityFunctionCheck identityFunctionCheck} for more details.
*
* _The development mode checks do not run in production builds._
*
* @param devModeChecks - An object specifying the desired settings for development mode checks. You can provide partial overrides. Unspecified settings will retain their current values.
*
* @example
* ```ts
* import { setGlobalDevModeChecks } from 'reselect'
* import { DevModeChecks } from '../types'
*
* // Run only the first time the selector is called. (default)
* setGlobalDevModeChecks({ inputStabilityCheck: 'once' })
*
* // Run every time the selector is called.
* setGlobalDevModeChecks({ inputStabilityCheck: 'always' })
*
* // Never run the input stability check.
* setGlobalDevModeChecks({ inputStabilityCheck: 'never' })
*
* // Run only the first time the selector is called. (default)
* setGlobalDevModeChecks({ identityFunctionCheck: 'once' })
*
* // Run every time the selector is called.
* setGlobalDevModeChecks({ identityFunctionCheck: 'always' })
*
* // Never run the identity function check.
* setGlobalDevModeChecks({ identityFunctionCheck: 'never' })
* ```
* @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks}
* @see {@link https://reselect.js.org/api/development-only-stability-checks#1-globally-through-setglobaldevmodechecks global-configuration}
*
* @since 5.0.0
* @public
*/
export const setGlobalDevModeChecks = (
devModeChecks: Partial<DevModeChecks>
) => {
Object.assign(globalDevModeChecks, devModeChecks)
}

36
node_modules/reselect/src/index.ts generated vendored Normal file
View file

@ -0,0 +1,36 @@
export { autotrackMemoize as unstable_autotrackMemoize } from './autotrackMemoize/autotrackMemoize'
export { createSelector, createSelectorCreator } from './createSelectorCreator'
export type { CreateSelectorFunction } from './createSelectorCreator'
export { createStructuredSelector } from './createStructuredSelector'
export type {
RootStateSelectors,
SelectorResultsMap,
SelectorsObject,
StructuredSelectorCreator,
TypedStructuredSelectorCreator
} from './createStructuredSelector'
export { setGlobalDevModeChecks } from './devModeChecks/setGlobalDevModeChecks'
export { lruMemoize, referenceEqualityCheck } from './lruMemoize'
export type { LruMemoizeOptions } from './lruMemoize'
export type {
Combiner,
CreateSelectorOptions,
DefaultMemoizeFields,
DevModeCheckFrequency,
DevModeChecks,
DevModeChecksExecutionInfo,
EqualityFn,
ExtractMemoizerFields,
GetParamsFromSelectors,
GetStateFromSelectors,
MemoizeOptionsFromParameters,
OutputSelector,
OutputSelectorFields,
OverrideMemoizeOptions,
Selector,
SelectorArray,
SelectorResultArray,
UnknownMemoizer
} from './types'
export { weakMapMemoize } from './weakMapMemoize'
export type { WeakMapMemoizeOptions } from './weakMapMemoize'

249
node_modules/reselect/src/lruMemoize.ts generated vendored Normal file
View file

@ -0,0 +1,249 @@
import type {
AnyFunction,
DefaultMemoizeFields,
EqualityFn,
Simplify
} from './types'
import type { NOT_FOUND_TYPE } from './utils'
import { NOT_FOUND } from './utils'
// Cache implementation based on Erik Rasmussen's `lru-memoize`:
// https://github.com/erikras/lru-memoize
interface Entry {
key: unknown
value: unknown
}
interface Cache {
get(key: unknown): unknown | NOT_FOUND_TYPE
put(key: unknown, value: unknown): void
getEntries(): Entry[]
clear(): void
}
function createSingletonCache(equals: EqualityFn): Cache {
let entry: Entry | undefined
return {
get(key: unknown) {
if (entry && equals(entry.key, key)) {
return entry.value
}
return NOT_FOUND
},
put(key: unknown, value: unknown) {
entry = { key, value }
},
getEntries() {
return entry ? [entry] : []
},
clear() {
entry = undefined
}
}
}
function createLruCache(maxSize: number, equals: EqualityFn): Cache {
let entries: Entry[] = []
function get(key: unknown) {
const cacheIndex = entries.findIndex(entry => equals(key, entry.key))
// We found a cached entry
if (cacheIndex > -1) {
const entry = entries[cacheIndex]
// Cached entry not at top of cache, move it to the top
if (cacheIndex > 0) {
entries.splice(cacheIndex, 1)
entries.unshift(entry)
}
return entry.value
}
// No entry found in cache, return sentinel
return NOT_FOUND
}
function put(key: unknown, value: unknown) {
if (get(key) === NOT_FOUND) {
// TODO Is unshift slow?
entries.unshift({ key, value })
if (entries.length > maxSize) {
entries.pop()
}
}
}
function getEntries() {
return entries
}
function clear() {
entries = []
}
return { get, put, getEntries, clear }
}
/**
* Runs a simple reference equality check.
* What {@linkcode lruMemoize lruMemoize} uses by default.
*
* **Note**: This function was previously known as `defaultEqualityCheck`.
*
* @public
*/
export const referenceEqualityCheck: EqualityFn = (a, b) => a === b
export function createCacheKeyComparator(equalityCheck: EqualityFn) {
return function areArgumentsShallowlyEqual(
prev: unknown[] | IArguments | null,
next: unknown[] | IArguments | null
): boolean {
if (prev === null || next === null || prev.length !== next.length) {
return false
}
// Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible.
const { length } = prev
for (let i = 0; i < length; i++) {
if (!equalityCheck(prev[i], next[i])) {
return false
}
}
return true
}
}
/**
* Options for configuring the behavior of a function memoized with
* LRU (Least Recently Used) caching.
*
* @template Result - The type of the return value of the memoized function.
*
* @public
*/
export interface LruMemoizeOptions<Result = any> {
/**
* Function used to compare the individual arguments of the
* provided calculation function.
*
* @default referenceEqualityCheck
*/
equalityCheck?: EqualityFn
/**
* If provided, used to compare a newly generated output value against
* previous values in the cache. If a match is found,
* the old value is returned. This addresses the common
* ```ts
* todos.map(todo => todo.id)
* ```
* use case, where an update to another field in the original data causes
* a recalculation due to changed references, but the output is still
* effectively the same.
*
* @since 4.1.0
*/
resultEqualityCheck?: EqualityFn<Result>
/**
* The maximum size of the cache used by the selector.
* A size greater than 1 means the selector will use an
* LRU (Least Recently Used) cache, allowing for the caching of multiple
* results based on different sets of arguments.
*
* @default 1
*/
maxSize?: number
}
/**
* Creates a memoized version of a function with an optional
* LRU (Least Recently Used) cache. The memoized function uses a cache to
* store computed values. Depending on the `maxSize` option, it will use
* either a singleton cache (for a single entry) or an
* LRU cache (for multiple entries).
*
* **Note**: This function was previously known as `defaultMemoize`.
*
* @param func - The function to be memoized.
* @param equalityCheckOrOptions - Either an equality check function or an options object.
* @returns A memoized function with a `.clearCache()` method attached.
*
* @template Func - The type of the function that is memoized.
*
* @see {@link https://reselect.js.org/api/lruMemoize `lruMemoize`}
*
* @public
*/
export function lruMemoize<Func extends AnyFunction>(
func: Func,
equalityCheckOrOptions?: EqualityFn | LruMemoizeOptions<ReturnType<Func>>
) {
const providedOptions =
typeof equalityCheckOrOptions === 'object'
? equalityCheckOrOptions
: { equalityCheck: equalityCheckOrOptions }
const {
equalityCheck = referenceEqualityCheck,
maxSize = 1,
resultEqualityCheck
} = providedOptions
const comparator = createCacheKeyComparator(equalityCheck)
let resultsCount = 0
const cache =
maxSize <= 1
? createSingletonCache(comparator)
: createLruCache(maxSize, comparator)
function memoized() {
let value = cache.get(arguments) as ReturnType<Func>
if (value === NOT_FOUND) {
// apply arguments instead of spreading for performance.
// @ts-ignore
value = func.apply(null, arguments) as ReturnType<Func>
resultsCount++
if (resultEqualityCheck) {
const entries = cache.getEntries()
const matchingEntry = entries.find(entry =>
resultEqualityCheck(entry.value as ReturnType<Func>, value)
)
if (matchingEntry) {
value = matchingEntry.value as ReturnType<Func>
resultsCount !== 0 && resultsCount--
}
}
cache.put(arguments, value)
}
return value
}
memoized.clearCache = () => {
cache.clear()
memoized.resetResultsCount()
}
memoized.resultsCount = () => resultsCount
memoized.resetResultsCount = () => {
resultsCount = 0
}
return memoized as Func & Simplify<DefaultMemoizeFields>
}

876
node_modules/reselect/src/types.ts generated vendored Normal file
View file

@ -0,0 +1,876 @@
import type { MergeParameters } from './versionedTypes'
import type { weakMapMemoize } from './weakMapMemoize'
export type { MergeParameters } from './versionedTypes'
/*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
* Reselect Data Types
*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*/
/**
* A standard selector function.
* @template State - The first value, often a Redux root state object.
* @template Result - The final result returned by the selector.
* @template Params - All additional arguments passed into the selector.
*
* @public
*/
export type Selector<
State = any,
Result = unknown,
Params extends readonly any[] = any[]
> = Distribute<
/**
* A function that takes a state and returns data that is based on that state.
*
* @param state - The first argument, often a Redux root state object.
* @param params - All additional arguments passed into the selector.
* @returns A derived value from the state.
*/
(state: State, ...params: FallbackIfNever<Params, []>) => Result
>
/**
* An array of input selectors.
*
* @public
*/
export type SelectorArray<State = any> = readonly Selector<State>[]
/**
* Extracts an array of all return types from all input selectors.
*
* @public
*/
export type SelectorResultArray<Selectors extends SelectorArray> =
ExtractReturnType<Selectors>
/**
* The options object used inside `createSelector` and `createSelectorCreator`.
*
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
* @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object inside `createSelector` to override the original `memoize` function that was initially passed into `createSelectorCreator`.
* @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object inside `createSelector` to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`. If none was initially provided, `weakMapMemoize` will be used.
*
* @public
*/
export interface CreateSelectorOptions<
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
OverrideMemoizeFunction extends UnknownMemoizer = never,
OverrideArgsMemoizeFunction extends UnknownMemoizer = never
> {
/**
* Reselect performs additional checks in development mode to help identify
* and warn about potential issues in selector behavior. This option
* allows you to customize the behavior of these checks per selector.
*
* @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks}
*
* @since 5.0.0
*/
devModeChecks?: Partial<DevModeChecks>
/**
* The memoize function that is used to memoize the {@linkcode OutputSelectorFields.resultFunc resultFunc}
* inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
*
* When passed directly into `createSelector`, it overrides the `memoize` function initially passed into `createSelectorCreator`.
*
* @example
* ```ts
* import { createSelector, weakMapMemoize } from 'reselect'
*
* const selectItemsByCategory = createSelector(
* [
* (state: RootState) => state.items,
* (state: RootState, category: string) => category
* ],
* (items, category) => items.filter(item => item.category === category),
* { memoize: weakMapMemoize }
* )
* ```
*
* @since 5.0.0
*/
memoize?: FallbackIfNever<OverrideMemoizeFunction, MemoizeFunction>
/**
* The optional memoize function that is used to memoize the arguments
* passed into the output selector generated by `createSelector`
* (e.g., `lruMemoize` or `weakMapMemoize`).
*
* When passed directly into `createSelector`, it overrides the
* `argsMemoize` function initially passed into `createSelectorCreator`.
* If none was initially provided, `weakMapMemoize` will be used.
*
* @example
* ```ts
* import { createSelector, weakMapMemoize } from 'reselect'
*
* const selectItemsByCategory = createSelector(
* [
* (state: RootState) => state.items,
* (state: RootState, category: string) => category
* ],
* (items, category) => items.filter(item => item.category === category),
* { argsMemoize: weakMapMemoize }
* )
* ```
*
* @default weakMapMemoize
*
* @since 5.0.0
*/
argsMemoize?: FallbackIfNever<
OverrideArgsMemoizeFunction,
ArgsMemoizeFunction
>
/**
* Optional configuration options for the {@linkcode CreateSelectorOptions.memoize memoize} function.
* These options are passed to the {@linkcode CreateSelectorOptions.memoize memoize} function as the second argument.
*
* @since 5.0.0
*/
memoizeOptions?: OverrideMemoizeOptions<
MemoizeFunction,
OverrideMemoizeFunction
>
/**
* Optional configuration options for the {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} function.
* These options are passed to the {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} function as the second argument.
*
* @since 5.0.0
*/
argsMemoizeOptions?: OverrideMemoizeOptions<
ArgsMemoizeFunction,
OverrideArgsMemoizeFunction
>
}
/**
* The additional fields attached to the output selector generated by `createSelector`.
*
* **Note**: Although {@linkcode CreateSelectorOptions.memoize memoize}
* and {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} are included in the attached fields,
* the fields themselves are independent of the type of
* {@linkcode CreateSelectorOptions.memoize memoize} and {@linkcode CreateSelectorOptions.argsMemoize argsMemoize} functions.
* Meaning this type is not going to generate additional fields based on what functions we use to memoize our selectors.
*
* _This type is not to be confused with {@linkcode ExtractMemoizerFields ExtractMemoizerFields}._
*
* @template InputSelectors - The type of the input selectors.
* @template Result - The type of the result returned by the `resultFunc`.
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
*
* @public
*/
export type OutputSelectorFields<
InputSelectors extends SelectorArray = SelectorArray,
Result = unknown,
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
> = {
/**
* The final function passed to `createSelector`. Otherwise known as the `combiner`.
*/
resultFunc: Combiner<InputSelectors, Result>
/**
* The memoized version of {@linkcode OutputSelectorFields.resultFunc resultFunc}.
*/
memoizedResultFunc: Combiner<InputSelectors, Result> &
ExtractMemoizerFields<MemoizeFunction>
/**
* @Returns The last result calculated by {@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc}.
*/
lastResult: () => Result
/**
* The array of the input selectors used by `createSelector` to compose the
* combiner ({@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc}).
*/
dependencies: InputSelectors
/**
* Counts the number of times {@linkcode OutputSelectorFields.memoizedResultFunc memoizedResultFunc} has been recalculated.
*/
recomputations: () => number
/**
* Resets the count of {@linkcode OutputSelectorFields.recomputations recomputations} count to 0.
*/
resetRecomputations: () => void
/**
* Counts the number of times the input selectors ({@linkcode OutputSelectorFields.dependencies dependencies})
* have been recalculated. This is distinct from {@linkcode OutputSelectorFields.recomputations recomputations},
* which tracks the recalculations of the result function.
*
* @since 5.0.0
*/
dependencyRecomputations: () => number
/**
* Resets the count {@linkcode OutputSelectorFields.dependencyRecomputations dependencyRecomputations}
* for the input selectors ({@linkcode OutputSelectorFields.dependencies dependencies})
* of a memoized selector.
*
* @since 5.0.0
*/
resetDependencyRecomputations: () => void
} & Simplify<
Required<
Pick<
CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
'argsMemoize' | 'memoize'
>
>
>
/**
* Represents the actual selectors generated by `createSelector`.
*
* @template InputSelectors - The type of the input selectors.
* @template Result - The type of the result returned by the `resultFunc`.
* @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
* @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
*
* @public
*/
export type OutputSelector<
InputSelectors extends SelectorArray = SelectorArray,
Result = unknown,
MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
> = Selector<
GetStateFromSelectors<InputSelectors>,
Result,
GetParamsFromSelectors<InputSelectors>
> &
ExtractMemoizerFields<ArgsMemoizeFunction> &
OutputSelectorFields<
InputSelectors,
Result,
MemoizeFunction,
ArgsMemoizeFunction
>
/**
* A function that takes input selectors' return values as arguments and returns a result. Otherwise known as `resultFunc`.
*
* @template InputSelectors - An array of input selectors.
* @template Result - Result returned by `resultFunc`.
*
* @public
*/
export type Combiner<InputSelectors extends SelectorArray, Result> = Distribute<
/**
* A function that takes input selectors' return values as arguments and returns a result. Otherwise known as `resultFunc`.
*
* @param resultFuncArgs - Return values of input selectors.
* @returns The return value of {@linkcode OutputSelectorFields.resultFunc resultFunc}.
*/
(...resultFuncArgs: SelectorResultArray<InputSelectors>) => Result
>
/**
* A standard function returning true if two values are considered equal.
*
* @public
*/
export type EqualityFn<T = any> = (a: T, b: T) => boolean
/**
* The frequency of development mode checks.
*
* @since 5.0.0
* @public
*/
export type DevModeCheckFrequency = 'always' | 'once' | 'never'
/**
* Represents the configuration for development mode checks.
*
* @since 5.0.0
* @public
*/
export interface DevModeChecks {
/**
* Overrides the global input stability check for the selector.
* - `once` - Run only the first time the selector is called.
* - `always` - Run every time the selector is called.
* - `never` - Never run the input stability check.
*
* @default 'once'
*
* @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks}
* @see {@link https://reselect.js.org/api/development-only-stability-checks#inputstabilitycheck `inputStabilityCheck`}
* @see {@link https://reselect.js.org/api/development-only-stability-checks#2-per-selector-by-passing-an-inputstabilitycheck-option-directly-to- per-selector-configuration}
*
* @since 5.0.0
*/
inputStabilityCheck: DevModeCheckFrequency
/**
* Overrides the global identity function check for the selector.
* - `once` - Run only the first time the selector is called.
* - `always` - Run every time the selector is called.
* - `never` - Never run the identity function check.
*
* @default 'once'
*
* @see {@link https://reselect.js.org/api/development-only-stability-checks Development-Only Stability Checks}
* @see {@link https://reselect.js.org/api/development-only-stability-checks#identityfunctioncheck `identityFunctionCheck`}
* @see {@link https://reselect.js.org/api/development-only-stability-checks#2-per-selector-by-passing-an-identityfunctioncheck-option-directly-to- per-selector-configuration}
*
* @since 5.0.0
*/
identityFunctionCheck: DevModeCheckFrequency
}
/**
* Represents execution information for development mode checks.
*
* @public
* @since 5.0.0
*/
export type DevModeChecksExecutionInfo = {
[K in keyof DevModeChecks]: {
/**
* A boolean indicating whether the check should be executed.
*/
shouldRun: boolean
/**
* The function to execute for the check.
*/
run: AnyFunction
}
}
/**
* Determines the combined single "State" type (first arg) from all input selectors.
*
* @public
*/
export type GetStateFromSelectors<Selectors extends SelectorArray> =
MergeParameters<Selectors>[0]
/**
* Determines the combined "Params" type (all remaining args) from all input selectors.
*
* @public
*/
export type GetParamsFromSelectors<Selectors extends SelectorArray> = ArrayTail<
MergeParameters<Selectors>
>
/**
* Any Memoizer function. A memoizer is a function that accepts another function and returns it.
*
* @template FunctionType - The type of the function that is memoized.
*
* @public
*/
export type UnknownMemoizer<
FunctionType extends UnknownFunction = UnknownFunction
> = (func: FunctionType, ...options: any[]) => FunctionType
/**
* Extracts the options type for a memoization function based on its parameters.
* The first parameter of the function is expected to be the function to be memoized,
* followed by options for the memoization process.
*
* @template MemoizeFunction - The type of the memoize function to be checked.
*
* @public
*/
export type MemoizeOptionsFromParameters<
MemoizeFunction extends UnknownMemoizer
> =
| (
| NonFunctionType<DropFirstParameter<MemoizeFunction>[0]>
| FunctionType<DropFirstParameter<MemoizeFunction>[0]>
)
| (
| NonFunctionType<DropFirstParameter<MemoizeFunction>[number]>
| FunctionType<DropFirstParameter<MemoizeFunction>[number]>
)[]
/**
* Derive the type of memoize options object based on whether the memoize function itself was overridden.
*
* _This type can be used for both `memoizeOptions` and `argsMemoizeOptions`._
*
* @template MemoizeFunction - The type of the `memoize` or `argsMemoize` function initially passed into `createSelectorCreator`.
* @template OverrideMemoizeFunction - The type of the optional `memoize` or `argsMemoize` function passed directly into `createSelector` which then overrides the original `memoize` or `argsMemoize` function passed into `createSelectorCreator`.
*
* @public
*/
export type OverrideMemoizeOptions<
MemoizeFunction extends UnknownMemoizer,
OverrideMemoizeFunction extends UnknownMemoizer = never
> = IfNever<
OverrideMemoizeFunction,
Simplify<MemoizeOptionsFromParameters<MemoizeFunction>>,
Simplify<MemoizeOptionsFromParameters<OverrideMemoizeFunction>>
>
/**
* Extracts the additional properties or methods that a memoize function attaches to
* the function it memoizes (e.g., `clearCache`).
*
* @template MemoizeFunction - The type of the memoize function to be checked.
*
* @public
*/
export type ExtractMemoizerFields<MemoizeFunction extends UnknownMemoizer> =
Simplify<OmitIndexSignature<ReturnType<MemoizeFunction>>>
/**
* Represents the additional properties attached to a function memoized by `reselect`.
*
* `lruMemoize`, `weakMapMemoize` and `autotrackMemoize` all return these properties.
*
* @see {@linkcode ExtractMemoizerFields ExtractMemoizerFields}
*
* @public
*/
export type DefaultMemoizeFields = {
/**
* Clears the memoization cache associated with a memoized function.
* This method is typically used to reset the state of the cache, allowing
* for the garbage collection of previously memoized results and ensuring
* that future calls to the function recompute the results.
*/
clearCache: () => void
resultsCount: () => number
resetResultsCount: () => void
}
/*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
* Reselect Internal Utility Types
*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*/
/**
* Any function with any arguments.
*
* @internal
*/
export type AnyFunction = (...args: any[]) => any
/**
* Any function with unknown arguments.
*
* @internal
*/
export type UnknownFunction = (...args: unknown[]) => unknown
/**
* When a generic type parameter is using its default value of `never`, fallback to a different type.
*
* @template T - Type to be checked.
* @template FallbackTo - Type to fallback to if `T` resolves to `never`.
*
* @internal
*/
export type FallbackIfNever<T, FallbackTo> = IfNever<T, FallbackTo, T>
/**
* Extracts the non-function part of a type.
*
* @template T - The input type to be refined by excluding function types and index signatures.
*
* @internal
*/
export type NonFunctionType<T> = Simplify<
OmitIndexSignature<Exclude<T, AnyFunction>>
>
/**
* Extracts the function part of a type.
*
* @template T - The input type to be refined by extracting function types.
*
* @internal
*/
export type FunctionType<T> = Extract<T, AnyFunction>
/**
* Extracts the return type from all functions as a tuple.
*
* @internal
*/
export type ExtractReturnType<FunctionsArray extends readonly AnyFunction[]> = {
[Index in keyof FunctionsArray]: FunctionsArray[Index] extends FunctionsArray[number]
? FallbackIfUnknown<ReturnType<FunctionsArray[Index]>, any>
: never
}
/**
* Utility type to infer the type of "all params of a function except the first",
* so we can determine what arguments a memoize function accepts.
*
* @internal
*/
export type DropFirstParameter<Func extends AnyFunction> = Func extends (
firstArg: any,
...restArgs: infer Rest
) => any
? Rest
: never
/**
* Distributes over a type. It is used mostly to expand a function type
* in hover previews while preserving their original JSDoc information.
*
* If preserving JSDoc information is not a concern, you can use {@linkcode ExpandFunction ExpandFunction}.
*
* @template T The type to be distributed.
*
* @internal
*/
export type Distribute<T> = T extends T ? T : never
/**
* Extracts the type of the first element of an array or tuple.
*
* @internal
*/
export type FirstArrayElement<ArrayType> = ArrayType extends readonly [
unknown,
...unknown[]
]
? ArrayType[0]
: never
/**
* Extracts the type of an array or tuple minus the first element.
*
* @internal
*/
export type ArrayTail<ArrayType> = ArrayType extends readonly [
unknown,
...infer Tail
]
? Tail
: []
/**
* An alias for type `{}`. Represents any value that is not `null` or `undefined`.
* It is mostly used for semantic purposes to help distinguish between an
* empty object type and `{}` as they are not the same.
*
* @internal
*/
export type AnyNonNullishValue = NonNullable<unknown>
/**
* Same as {@linkcode AnyNonNullishValue AnyNonNullishValue} but aliased
* for semantic purposes. It is intended to be used in scenarios where
* a recursive type definition needs to be interrupted to ensure type safety
* and to avoid excessively deep recursion that could lead to performance issues.
*
* @internal
*/
export type InterruptRecursion = AnyNonNullishValue
/*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
* External/Copied Utility Types
*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
*/
/**
* An if-else-like type that resolves depending on whether the given type is `never`.
* This is mainly used to conditionally resolve the type of a `memoizeOptions` object based on whether `memoize` is provided or not.
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/if-never.d.ts Source}
*
* @internal
*/
export type IfNever<T, TypeIfNever, TypeIfNotNever> = [T] extends [never]
? TypeIfNever
: TypeIfNotNever
/**
* Omit any index signatures from the given object type, leaving only explicitly defined properties.
* This is mainly used to remove explicit `any`s from the return type of some memoizers (e.g, `microMemoize`).
*
* __Disclaimer:__ When used on an intersection of a function and an object,
* the function is erased.
*
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/omit-index-signature.d.ts Source}
*
* @internal
*/
export type OmitIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
? never
: KeyType]: ObjectType[KeyType]
}
/**
* The infamous "convert a union type to an intersection type" hack
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts Source}
* @see {@link https://github.com/microsoft/TypeScript/issues/29594 Reference}
*
* @internal
*/
export type UnionToIntersection<Union> =
// `extends unknown` is always going to be the case and is used to convert the
// `Union` into a [distributive conditional
// type](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types).
(
Union extends unknown
? // The union type is used as the only argument to a function since the union
// of function arguments is an intersection.
(distributedUnion: Union) => void
: // This won't happen.
never
) extends // Infer the `Intersection` type since TypeScript represents the positional
// arguments of unions of functions as an intersection of the union.
(mergedIntersection: infer Intersection) => void
? // The `& Union` is to allow indexing by the resulting type
Intersection & Union
: never
/**
* Code to convert a union of values into a tuple.
* @see {@link https://stackoverflow.com/a/55128956/62937 Source}
*
* @internal
*/
type Push<T extends any[], V> = [...T, V]
/**
* @see {@link https://stackoverflow.com/a/55128956/62937 Source}
*
* @internal
*/
type LastOf<T> = UnionToIntersection<
T extends any ? () => T : never
> extends () => infer R
? R
: never
/**
* TS4.1+
* @see {@link https://stackoverflow.com/a/55128956/62937 Source}
*
* @internal
*/
export type TuplifyUnion<
T,
L = LastOf<T>,
N = [T] extends [never] ? true : false
> = true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>
/**
* Converts "the values of an object" into a tuple, like a type-level `Object.values()`
* @see {@link https://stackoverflow.com/a/68695508/62937 Source}
*
* @internal
*/
export type ObjectValuesToTuple<
T,
KS extends any[] = TuplifyUnion<keyof T>,
R extends any[] = []
> = KS extends [infer K, ...infer KT]
? ObjectValuesToTuple<T, KT, [...R, T[K & keyof T]]>
: R
/**
* Create a type that makes the given keys required.
* The remaining keys are kept as is.
*
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/set-required.d.ts Source}
*
* @internal
*/
export type SetRequired<BaseType, Keys extends keyof BaseType> = Omit<
BaseType,
Keys
> &
Required<Pick<BaseType, Keys>>
/**
* An if-else-like type that resolves depending on whether the given type is `unknown`.
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/if-unknown.d.ts Source}
*
* @internal
*/
export type IfUnknown<T, TypeIfUnknown, TypeIfNotUnknown> = unknown extends T // `T` can be `unknown` or `any`
? [T] extends [null] // `any` can be `null`, but `unknown` can't be
? TypeIfNotUnknown
: TypeIfUnknown
: TypeIfNotUnknown
/**
* When a type is resolves to `unknown`, fallback to a different type.
*
* @template T - Type to be checked.
* @template FallbackTo - Type to fallback to if `T` resolves to `unknown`.
*
* @internal
*/
export type FallbackIfUnknown<T, FallbackTo> = IfUnknown<T, FallbackTo, T>
/**
*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
* Type Expansion Utilities
*
* -----------------------------------------------------------------------------
* -----------------------------------------------------------------------------
*
*/
/**
* Check whether `U` contains `U1`.
* @see {@link https://millsp.github.io/ts-toolbelt/modules/union_has.html Source}
*
* @internal
*/
export type Has<U, U1> = [U1] extends [U] ? 1 : 0
/**
* @internal
*/
export type Boolean2 = 0 | 1
/**
* @internal
*/
export type If2<B extends Boolean2, Then, Else = never> = B extends 1
? Then
: Else
/**
* @internal
*/
export type BuiltIn =
| Function
| Error
| Date
| { readonly [Symbol.toStringTag]: string }
| RegExp
| Generator
/**
* Expand an item a single level.
* @see {@link https://stackoverflow.com/a/69288824/62937 Source}
*
* @internal
*/
export type Expand<T> = T extends (...args: infer A) => infer R
? (...args: Expand<A>) => Expand<R>
: T extends infer O
? { [K in keyof O]: O[K] }
: never
/**
* Expand an item recursively.
* @see {@link https://stackoverflow.com/a/69288824/62937 Source}
*
* @internal
*/
export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
: T extends object
? T extends infer O
? { [K in keyof O]: ExpandRecursively<O[K]> }
: never
: T
/**
* @internal
*/
export type Identity<T> = T
/**
* Another form of type value expansion
* @see {@link https://github.com/microsoft/TypeScript/issues/35247 Source}
*
* @internal
*/
export type Mapped<T> = Identity<{ [k in keyof T]: T[k] }>
/**
* This utility type is primarily used to expand a function type in order to
* improve its visual display in hover previews within IDEs.
*
* __Disclaimer:__ Functions expanded using this type will not display their
* original JSDoc information in hover previews.
*
* @template FunctionType - The type of the function to be expanded.
*
* @internal
*/
export type ExpandFunction<FunctionType extends AnyFunction> =
FunctionType extends FunctionType
? (...args: Parameters<FunctionType>) => ReturnType<FunctionType>
: never
/**
* Useful to flatten the type output to improve type hints shown in editors.
* And also to transform an interface into a type to aide with assignability.
* @see {@link https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts Source}
*
* @internal
*/
export type Simplify<T> = T extends AnyFunction
? T
: {
[KeyType in keyof T]: T[KeyType]
} & AnyNonNullishValue
/**
* Fully expand a type, deeply
* @see {@link https://github.com/millsp/ts-toolbelt Any.Compute}
*
* @internal
*/
export type ComputeDeep<A, Seen = never> = A extends BuiltIn
? A
: If2<
Has<Seen, A>,
A,
A extends any[]
? A extends Record<PropertyKey, any>[]
? ({
[K in keyof A[number]]: ComputeDeep<A[number][K], A | Seen>
} & unknown)[]
: A
: A extends readonly any[]
? A extends readonly Record<PropertyKey, any>[]
? readonly ({
[K in keyof A[number]]: ComputeDeep<A[number][K], A | Seen>
} & unknown)[]
: A
: { [K in keyof A]: ComputeDeep<A[K], A | Seen> } & unknown
>

156
node_modules/reselect/src/utils.ts generated vendored Normal file
View file

@ -0,0 +1,156 @@
import { runIdentityFunctionCheck } from './devModeChecks/identityFunctionCheck'
import { runInputStabilityCheck } from './devModeChecks/inputStabilityCheck'
import { globalDevModeChecks } from './devModeChecks/setGlobalDevModeChecks'
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import type {
DevModeChecks,
Selector,
SelectorArray,
DevModeChecksExecutionInfo
} from './types'
export const NOT_FOUND = /* @__PURE__ */ Symbol('NOT_FOUND')
export type NOT_FOUND_TYPE = typeof NOT_FOUND
/**
* Assert that the provided value is a function. If the assertion fails,
* a `TypeError` is thrown with an optional custom error message.
*
* @param func - The value to be checked.
* @param errorMessage - An optional custom error message to use if the assertion fails.
* @throws A `TypeError` if the assertion fails.
*/
export function assertIsFunction<FunctionType extends Function>(
func: unknown,
errorMessage = `expected a function, instead received ${typeof func}`
): asserts func is FunctionType {
if (typeof func !== 'function') {
throw new TypeError(errorMessage)
}
}
/**
* Assert that the provided value is an object. If the assertion fails,
* a `TypeError` is thrown with an optional custom error message.
*
* @param object - The value to be checked.
* @param errorMessage - An optional custom error message to use if the assertion fails.
* @throws A `TypeError` if the assertion fails.
*/
export function assertIsObject<ObjectType extends Record<string, unknown>>(
object: unknown,
errorMessage = `expected an object, instead received ${typeof object}`
): asserts object is ObjectType {
if (typeof object !== 'object') {
throw new TypeError(errorMessage)
}
}
/**
* Assert that the provided array is an array of functions. If the assertion fails,
* a `TypeError` is thrown with an optional custom error message.
*
* @param array - The array to be checked.
* @param errorMessage - An optional custom error message to use if the assertion fails.
* @throws A `TypeError` if the assertion fails.
*/
export function assertIsArrayOfFunctions<FunctionType extends Function>(
array: unknown[],
errorMessage = `expected all items to be functions, instead received the following types: `
): asserts array is FunctionType[] {
if (
!array.every((item): item is FunctionType => typeof item === 'function')
) {
const itemTypes = array
.map(item =>
typeof item === 'function'
? `function ${item.name || 'unnamed'}()`
: typeof item
)
.join(', ')
throw new TypeError(`${errorMessage}[${itemTypes}]`)
}
}
/**
* Ensure that the input is an array. If it's already an array, it's returned as is.
* If it's not an array, it will be wrapped in a new array.
*
* @param item - The item to be checked.
* @returns An array containing the input item. If the input is already an array, it's returned without modification.
*/
export const ensureIsArray = (item: unknown) => {
return Array.isArray(item) ? item : [item]
}
/**
* Extracts the "dependencies" / "input selectors" from the arguments of `createSelector`.
*
* @param createSelectorArgs - Arguments passed to `createSelector` as an array.
* @returns An array of "input selectors" / "dependencies".
* @throws A `TypeError` if any of the input selectors is not function.
*/
export function getDependencies(createSelectorArgs: unknown[]) {
const dependencies = Array.isArray(createSelectorArgs[0])
? createSelectorArgs[0]
: createSelectorArgs
assertIsArrayOfFunctions<Selector>(
dependencies,
`createSelector expects all input-selectors to be functions, but received the following types: `
)
return dependencies as SelectorArray
}
/**
* Runs each input selector and returns their collective results as an array.
*
* @param dependencies - An array of "dependencies" or "input selectors".
* @param inputSelectorArgs - An array of arguments being passed to the input selectors.
* @returns An array of input selector results.
*/
export function collectInputSelectorResults(
dependencies: SelectorArray,
inputSelectorArgs: unknown[] | IArguments
) {
const inputSelectorResults = []
const { length } = dependencies
for (let i = 0; i < length; i++) {
// @ts-ignore
// apply arguments instead of spreading and mutate a local list of params for performance.
inputSelectorResults.push(dependencies[i].apply(null, inputSelectorArgs))
}
return inputSelectorResults
}
/**
* Retrieves execution information for development mode checks.
*
* @param devModeChecks - Custom Settings for development mode checks. These settings will override the global defaults.
* @param firstRun - Indicates whether it is the first time the selector has run.
* @returns An object containing the execution information for each development mode check.
*/
export const getDevModeChecksExecutionInfo = (
firstRun: boolean,
devModeChecks: Partial<DevModeChecks>
) => {
const { identityFunctionCheck, inputStabilityCheck } = {
...globalDevModeChecks,
...devModeChecks
}
return {
identityFunctionCheck: {
shouldRun:
identityFunctionCheck === 'always' ||
(identityFunctionCheck === 'once' && firstRun),
run: runIdentityFunctionCheck
},
inputStabilityCheck: {
shouldRun:
inputStabilityCheck === 'always' ||
(inputStabilityCheck === 'once' && firstRun),
run: runInputStabilityCheck
}
} satisfies DevModeChecksExecutionInfo
}

1
node_modules/reselect/src/versionedTypes/index.ts generated vendored Normal file
View file

@ -0,0 +1 @@
export type { MergeParameters } from './ts47-mergeParameters'

View file

@ -0,0 +1,117 @@
// This entire implementation courtesy of Anders Hjelsberg:
// https://github.com/microsoft/TypeScript/pull/50831#issuecomment-1253830522
import type { AnyFunction } from '../types'
/**
* Represents the longest array within an array of arrays.
*
* @template ArrayOfTuples An array of arrays.
*
* @internal
*/
type LongestTuple<ArrayOfTuples extends readonly unknown[][]> =
ArrayOfTuples extends [infer FirstArray extends unknown[]]
? FirstArray
: ArrayOfTuples extends [
infer FirstArray,
...infer RestArrays extends unknown[][]
]
? LongerOfTwo<FirstArray, LongestTuple<RestArrays>>
: never
/**
* Determines the longer of two array types.
*
* @template ArrayOne First array type.
* @template ArrayTwo Second array type.
*
* @internal
*/
type LongerOfTwo<ArrayOne, ArrayTwo> = keyof ArrayTwo extends keyof ArrayOne
? ArrayOne
: ArrayTwo
/**
* Extracts the element at a specific index in an array.
*
* @template ArrayType The array type.
* @template Index The index type.
*
* @internal
*/
type ElementAt<
ArrayType extends unknown[],
Index extends PropertyKey
> = Index extends keyof ArrayType ? ArrayType[Index] : unknown
/**
* Maps each array in an array of arrays to its element at a given index.
*
* @template ArrayOfTuples An array of arrays.
* @template Index The index to extract from each array.
*
* @internal
*/
type ElementsAtGivenIndex<
ArrayOfTuples extends readonly unknown[][],
Index extends PropertyKey
> = {
[ArrayIndex in keyof ArrayOfTuples]: ElementAt<
ArrayOfTuples[ArrayIndex],
Index
>
}
/**
* Computes the intersection of all types in a tuple.
*
* @template Tuple A tuple of types.
*
* @internal
*/
type Intersect<Tuple extends readonly unknown[]> = Tuple extends []
? unknown
: Tuple extends [infer Head, ...infer Tail]
? Head & Intersect<Tail>
: Tuple[number]
/**
* Merges a tuple of arrays into a single tuple, intersecting types at each index.
*
* @template ArrayOfTuples An array of tuples.
* @template LongestArray The longest array in ArrayOfTuples.
*
* @internal
*/
type MergeTuples<
ArrayOfTuples extends readonly unknown[][],
LongestArray extends unknown[] = LongestTuple<ArrayOfTuples>
> = {
[Index in keyof LongestArray]: Intersect<
ElementsAtGivenIndex<ArrayOfTuples, Index>
>
}
/**
* Extracts the parameter types from a tuple of functions.
*
* @template FunctionsArray An array of function types.
*
* @internal
*/
type ExtractParameters<FunctionsArray extends readonly AnyFunction[]> = {
[Index in keyof FunctionsArray]: Parameters<FunctionsArray[Index]>
}
/**
* Merges the parameters of a tuple of functions into a single tuple.
*
* @template FunctionsArray An array of function types.
*
* @internal
*/
export type MergeParameters<FunctionsArray extends readonly AnyFunction[]> =
'0' extends keyof FunctionsArray
? MergeTuples<ExtractParameters<FunctionsArray>>
: Parameters<FunctionsArray[number]>

269
node_modules/reselect/src/weakMapMemoize.ts generated vendored Normal file
View file

@ -0,0 +1,269 @@
// Original source:
// - https://github.com/facebook/react/blob/0b974418c9a56f6c560298560265dcf4b65784bc/packages/react/src/ReactCache.js
import type {
AnyFunction,
DefaultMemoizeFields,
EqualityFn,
Simplify
} from './types'
class StrongRef<T> {
constructor(private value: T) {}
deref() {
return this.value
}
}
const Ref =
typeof WeakRef !== 'undefined'
? WeakRef
: (StrongRef as unknown as typeof WeakRef)
const UNTERMINATED = 0
const TERMINATED = 1
interface UnterminatedCacheNode<T> {
/**
* Status, represents whether the cached computation returned a value or threw an error.
*/
s: 0
/**
* Value, either the cached result or an error, depending on status.
*/
v: void
/**
* Object cache, a `WeakMap` where non-primitive arguments are stored.
*/
o: null | WeakMap<Function | Object, CacheNode<T>>
/**
* Primitive cache, a regular Map where primitive arguments are stored.
*/
p: null | Map<string | number | null | void | symbol | boolean, CacheNode<T>>
}
interface TerminatedCacheNode<T> {
/**
* Status, represents whether the cached computation returned a value or threw an error.
*/
s: 1
/**
* Value, either the cached result or an error, depending on status.
*/
v: T
/**
* Object cache, a `WeakMap` where non-primitive arguments are stored.
*/
o: null | WeakMap<Function | Object, CacheNode<T>>
/**
* Primitive cache, a regular `Map` where primitive arguments are stored.
*/
p: null | Map<string | number | null | void | symbol | boolean, CacheNode<T>>
}
type CacheNode<T> = TerminatedCacheNode<T> | UnterminatedCacheNode<T>
function createCacheNode<T>(): CacheNode<T> {
return {
s: UNTERMINATED,
v: undefined,
o: null,
p: null
}
}
/**
* Configuration options for a memoization function utilizing `WeakMap` for
* its caching mechanism.
*
* @template Result - The type of the return value of the memoized function.
*
* @since 5.0.0
* @public
*/
export interface WeakMapMemoizeOptions<Result = any> {
/**
* If provided, used to compare a newly generated output value against previous values in the cache.
* If a match is found, the old value is returned. This addresses the common
* ```ts
* todos.map(todo => todo.id)
* ```
* use case, where an update to another field in the original data causes a recalculation
* due to changed references, but the output is still effectively the same.
*
* @since 5.0.0
*/
resultEqualityCheck?: EqualityFn<Result>
}
/**
* Creates a tree of `WeakMap`-based cache nodes based on the identity of the
* arguments it's been called with (in this case, the extracted values from your input selectors).
* This allows `weakMapMemoize` to have an effectively infinite cache size.
* Cache results will be kept in memory as long as references to the arguments still exist,
* and then cleared out as the arguments are garbage-collected.
*
* __Design Tradeoffs for `weakMapMemoize`:__
* - Pros:
* - It has an effectively infinite cache size, but you have no control over
* how long values are kept in cache as it's based on garbage collection and `WeakMap`s.
* - Cons:
* - There's currently no way to alter the argument comparisons.
* They're based on strict reference equality.
* - It's roughly the same speed as `lruMemoize`, although likely a fraction slower.
*
* __Use Cases for `weakMapMemoize`:__
* - This memoizer is likely best used for cases where you need to call the
* same selector instance with many different arguments, such as a single
* selector instance that is used in a list item component and called with
* item IDs like:
* ```ts
* useSelector(state => selectSomeData(state, props.category))
* ```
* @param func - The function to be memoized.
* @returns A memoized function with a `.clearCache()` method attached.
*
* @example
* <caption>Using `createSelector`</caption>
* ```ts
* import { createSelector, weakMapMemoize } from 'reselect'
*
* interface RootState {
* items: { id: number; category: string; name: string }[]
* }
*
* const selectItemsByCategory = createSelector(
* [
* (state: RootState) => state.items,
* (state: RootState, category: string) => category
* ],
* (items, category) => items.filter(item => item.category === category),
* {
* memoize: weakMapMemoize,
* argsMemoize: weakMapMemoize
* }
* )
* ```
*
* @example
* <caption>Using `createSelectorCreator`</caption>
* ```ts
* import { createSelectorCreator, weakMapMemoize } from 'reselect'
*
* const createSelectorWeakMap = createSelectorCreator({ memoize: weakMapMemoize, argsMemoize: weakMapMemoize })
*
* const selectItemsByCategory = createSelectorWeakMap(
* [
* (state: RootState) => state.items,
* (state: RootState, category: string) => category
* ],
* (items, category) => items.filter(item => item.category === category)
* )
* ```
*
* @template Func - The type of the function that is memoized.
*
* @see {@link https://reselect.js.org/api/weakMapMemoize `weakMapMemoize`}
*
* @since 5.0.0
* @public
* @experimental
*/
export function weakMapMemoize<Func extends AnyFunction>(
func: Func,
options: WeakMapMemoizeOptions<ReturnType<Func>> = {}
) {
let fnNode = createCacheNode()
const { resultEqualityCheck } = options
let lastResult: WeakRef<object> | undefined
let resultsCount = 0
function memoized() {
let cacheNode = fnNode
const { length } = arguments
for (let i = 0, l = length; i < l; i++) {
const arg = arguments[i]
if (
typeof arg === 'function' ||
(typeof arg === 'object' && arg !== null)
) {
// Objects go into a WeakMap
let objectCache = cacheNode.o
if (objectCache === null) {
cacheNode.o = objectCache = new WeakMap()
}
const objectNode = objectCache.get(arg)
if (objectNode === undefined) {
cacheNode = createCacheNode()
objectCache.set(arg, cacheNode)
} else {
cacheNode = objectNode
}
} else {
// Primitives go into a regular Map
let primitiveCache = cacheNode.p
if (primitiveCache === null) {
cacheNode.p = primitiveCache = new Map()
}
const primitiveNode = primitiveCache.get(arg)
if (primitiveNode === undefined) {
cacheNode = createCacheNode()
primitiveCache.set(arg, cacheNode)
} else {
cacheNode = primitiveNode
}
}
}
const terminatedNode = cacheNode as unknown as TerminatedCacheNode<any>
let result
if (cacheNode.s === TERMINATED) {
result = cacheNode.v
} else {
// Allow errors to propagate
result = func.apply(null, arguments as unknown as any[])
resultsCount++
if (resultEqualityCheck) {
const lastResultValue = lastResult?.deref?.() ?? lastResult
if (
lastResultValue != null &&
resultEqualityCheck(lastResultValue as ReturnType<Func>, result)
) {
result = lastResultValue
resultsCount !== 0 && resultsCount--
}
const needsWeakRef =
(typeof result === 'object' && result !== null) ||
typeof result === 'function'
lastResult = needsWeakRef ? new Ref(result) : result
}
}
terminatedNode.s = TERMINATED
terminatedNode.v = result
return result
}
memoized.clearCache = () => {
fnNode = createCacheNode()
memoized.resetResultsCount()
}
memoized.resultsCount = () => resultsCount
memoized.resetResultsCount = () => {
resultsCount = 0
}
return memoized as Func & Simplify<DefaultMemoizeFields>
}